summaryrefslogtreecommitdiffstats
path: root/writerfilter/source/dmapper
diff options
context:
space:
mode:
Diffstat (limited to 'writerfilter/source/dmapper')
-rw-r--r--writerfilter/source/dmapper/BorderHandler.cxx213
-rw-r--r--writerfilter/source/dmapper/BorderHandler.hxx78
-rw-r--r--writerfilter/source/dmapper/CellColorHandler.cxx331
-rw-r--r--writerfilter/source/dmapper/CellColorHandler.hxx66
-rw-r--r--writerfilter/source/dmapper/CellMarginHandler.cxx177
-rw-r--r--writerfilter/source/dmapper/CellMarginHandler.hxx64
-rw-r--r--writerfilter/source/dmapper/ConversionHelper.cxx691
-rw-r--r--writerfilter/source/dmapper/ConversionHelper.hxx60
-rw-r--r--writerfilter/source/dmapper/DocumentProtection.cxx239
-rw-r--r--writerfilter/source/dmapper/DocumentProtection.hxx88
-rw-r--r--writerfilter/source/dmapper/DomainMapper.cxx4510
-rw-r--r--writerfilter/source/dmapper/DomainMapper.hxx195
-rw-r--r--writerfilter/source/dmapper/DomainMapperTableHandler.cxx1700
-rw-r--r--writerfilter/source/dmapper/DomainMapperTableHandler.hxx126
-rw-r--r--writerfilter/source/dmapper/DomainMapperTableManager.cxx859
-rw-r--r--writerfilter/source/dmapper/DomainMapperTableManager.hxx172
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.cxx8792
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.hxx1235
-rw-r--r--writerfilter/source/dmapper/FFDataHandler.cxx190
-rw-r--r--writerfilter/source/dmapper/FFDataHandler.hxx99
-rw-r--r--writerfilter/source/dmapper/FieldTypes.hxx305
-rw-r--r--writerfilter/source/dmapper/FontTable.cxx299
-rw-r--r--writerfilter/source/dmapper/FontTable.hxx106
-rw-r--r--writerfilter/source/dmapper/FormControlHelper.cxx378
-rw-r--r--writerfilter/source/dmapper/FormControlHelper.hxx52
-rw-r--r--writerfilter/source/dmapper/GraphicHelpers.cxx348
-rw-r--r--writerfilter/source/dmapper/GraphicHelpers.hxx79
-rw-r--r--writerfilter/source/dmapper/GraphicImport.cxx2029
-rw-r--r--writerfilter/source/dmapper/GraphicImport.hxx140
-rw-r--r--writerfilter/source/dmapper/LatentStyleHandler.cxx71
-rw-r--r--writerfilter/source/dmapper/LatentStyleHandler.hxx35
-rw-r--r--writerfilter/source/dmapper/LoggedResources.cxx400
-rw-r--r--writerfilter/source/dmapper/LoggedResources.hxx144
-rw-r--r--writerfilter/source/dmapper/MeasureHandler.cxx136
-rw-r--r--writerfilter/source/dmapper/MeasureHandler.hxx60
-rw-r--r--writerfilter/source/dmapper/ModelEventListener.cxx117
-rw-r--r--writerfilter/source/dmapper/ModelEventListener.hxx40
-rw-r--r--writerfilter/source/dmapper/NumberingManager.cxx1210
-rw-r--r--writerfilter/source/dmapper/NumberingManager.hxx250
-rw-r--r--writerfilter/source/dmapper/OLEHandler.cxx331
-rw-r--r--writerfilter/source/dmapper/OLEHandler.hxx94
-rw-r--r--writerfilter/source/dmapper/PageBordersHandler.cxx145
-rw-r--r--writerfilter/source/dmapper/PageBordersHandler.hxx62
-rw-r--r--writerfilter/source/dmapper/PropertyIds.cxx386
-rw-r--r--writerfilter/source/dmapper/PropertyIds.hxx377
-rw-r--r--writerfilter/source/dmapper/PropertyMap.cxx2219
-rw-r--r--writerfilter/source/dmapper/PropertyMap.hxx614
-rw-r--r--writerfilter/source/dmapper/PropertyMapHelper.cxx98
-rw-r--r--writerfilter/source/dmapper/PropertyMapHelper.hxx34
-rw-r--r--writerfilter/source/dmapper/SdtHelper.cxx513
-rw-r--r--writerfilter/source/dmapper/SdtHelper.hxx208
-rw-r--r--writerfilter/source/dmapper/SectionColumnHandler.cxx90
-rw-r--r--writerfilter/source/dmapper/SectionColumnHandler.hxx62
-rw-r--r--writerfilter/source/dmapper/SettingsTable.cxx692
-rw-r--r--writerfilter/source/dmapper/SettingsTable.hxx111
-rw-r--r--writerfilter/source/dmapper/SmartTagHandler.cxx127
-rw-r--r--writerfilter/source/dmapper/SmartTagHandler.hxx60
-rw-r--r--writerfilter/source/dmapper/StyleSheetTable.cxx1707
-rw-r--r--writerfilter/source/dmapper/StyleSheetTable.hxx150
-rw-r--r--writerfilter/source/dmapper/TDefTableHandler.cxx458
-rw-r--r--writerfilter/source/dmapper/TDefTableHandler.hxx72
-rw-r--r--writerfilter/source/dmapper/TableData.hxx390
-rw-r--r--writerfilter/source/dmapper/TableManager.cxx609
-rw-r--r--writerfilter/source/dmapper/TableManager.hxx530
-rw-r--r--writerfilter/source/dmapper/TablePositionHandler.cxx154
-rw-r--r--writerfilter/source/dmapper/TablePositionHandler.hxx68
-rw-r--r--writerfilter/source/dmapper/TablePropertiesHandler.cxx398
-rw-r--r--writerfilter/source/dmapper/TablePropertiesHandler.hxx94
-rw-r--r--writerfilter/source/dmapper/TagLogger.cxx230
-rw-r--r--writerfilter/source/dmapper/TagLogger.hxx65
-rw-r--r--writerfilter/source/dmapper/TblStylePrHandler.cxx259
-rw-r--r--writerfilter/source/dmapper/TblStylePrHandler.hxx82
-rw-r--r--writerfilter/source/dmapper/TextEffectsHandler.cxx804
-rw-r--r--writerfilter/source/dmapper/TextEffectsHandler.hxx69
-rw-r--r--writerfilter/source/dmapper/ThemeTable.cxx563
-rw-r--r--writerfilter/source/dmapper/ThemeTable.hxx58
-rw-r--r--writerfilter/source/dmapper/TrackChangesHandler.cxx95
-rw-r--r--writerfilter/source/dmapper/TrackChangesHandler.hxx40
-rw-r--r--writerfilter/source/dmapper/WrapPolygonHandler.cxx214
-rw-r--r--writerfilter/source/dmapper/WrapPolygonHandler.hxx82
-rw-r--r--writerfilter/source/dmapper/WriteProtection.cxx140
-rw-r--r--writerfilter/source/dmapper/WriteProtection.hxx58
-rw-r--r--writerfilter/source/dmapper/domainmapperfactory.cxx39
-rw-r--r--writerfilter/source/dmapper/util.cxx70
-rw-r--r--writerfilter/source/dmapper/util.hxx32
85 files changed, 39837 insertions, 0 deletions
diff --git a/writerfilter/source/dmapper/BorderHandler.cxx b/writerfilter/source/dmapper/BorderHandler.cxx
new file mode 100644
index 000000000..cb8e38c75
--- /dev/null
+++ b/writerfilter/source/dmapper/BorderHandler.cxx
@@ -0,0 +1,213 @@
+/* -*- 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 "BorderHandler.hxx"
+#include "TDefTableHandler.hxx"
+#include "PropertyMap.hxx"
+#include "ConversionHelper.hxx"
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/enumrange.hxx>
+#include <ooxml/resourceids.hxx>
+#include <filter/msfilter/util.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/color.hxx>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+
+
+BorderHandler::BorderHandler( bool bOOXML ) :
+LoggedProperties("BorderHandler"),
+m_nLineWidth(15), // Word default, in twips
+m_nLineType(0),
+m_nLineColor(0),
+m_nLineDistance(0),
+m_bShadow(false),
+m_bOOXML( bOOXML )
+{
+ m_aFilledLines.fill(false);
+ m_aBorderLines.fill(table::BorderLine2());
+}
+
+BorderHandler::~BorderHandler()
+{
+}
+
+void BorderHandler::lcl_attribute(Id rName, Value & rVal)
+{
+ sal_Int32 nIntValue = rVal.getInt();
+ switch( rName )
+ {
+ case NS_ooxml::LN_CT_Border_sz:
+ // width of a single line in 1/8 pt, max of 32 pt -> twip * 5 / 2.
+ m_nLineWidth = nIntValue * 5 / 2;
+ appendGrabBag("sz", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Border_val:
+ m_nLineType = nIntValue;
+ appendGrabBag("val", TDefTableHandler::getBorderTypeString(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Border_color:
+ m_nLineColor = nIntValue;
+ appendGrabBag("color", msfilter::util::ConvertColorOU(Color(ColorTransparency, nIntValue)));
+ break;
+ case NS_ooxml::LN_CT_Border_space: // border distance in points
+ m_nLineDistance = ConversionHelper::convertTwipToMM100( nIntValue * 20 );
+ appendGrabBag("space", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Border_shadow:
+ m_bShadow = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Border_frame:
+ case NS_ooxml::LN_CT_Border_themeTint:
+ appendGrabBag("themeTint", OUString::number(nIntValue, 16));
+ break;
+ case NS_ooxml::LN_CT_Border_themeColor:
+ appendGrabBag("themeColor", TDefTableHandler::getThemeColorTypeString(nIntValue));
+ break;
+ default:
+ OSL_FAIL( "unknown attribute");
+ }
+}
+
+void BorderHandler::lcl_sprm(Sprm & rSprm)
+{
+ BorderPosition pos;
+ const bool rtl = false; // TODO detect
+ OUString aBorderPos;
+ switch( rSprm.getId())
+ {
+ case NS_ooxml::LN_CT_TblBorders_top:
+ pos = BorderPosition::Top;
+ aBorderPos = "top";
+ break;
+ case NS_ooxml::LN_CT_TblBorders_start:
+ pos = rtl ? BorderPosition::Right : BorderPosition::Left;
+ aBorderPos = "start";
+ break;
+ case NS_ooxml::LN_CT_TblBorders_left:
+ pos = BorderPosition::Left;
+ aBorderPos = "left";
+ break;
+ case NS_ooxml::LN_CT_TblBorders_bottom:
+ pos = BorderPosition::Bottom;
+ aBorderPos = "bottom";
+ break;
+ case NS_ooxml::LN_CT_TblBorders_end:
+ pos = rtl ? BorderPosition::Left : BorderPosition::Right;
+ aBorderPos = "end";
+ break;
+ case NS_ooxml::LN_CT_TblBorders_right:
+ pos = BorderPosition::Right;
+ aBorderPos = "right";
+ break;
+ case NS_ooxml::LN_CT_TblBorders_insideH:
+ pos = BorderPosition::Horizontal;
+ aBorderPos = "insideH";
+ break;
+ case NS_ooxml::LN_CT_TblBorders_insideV:
+ pos = BorderPosition::Vertical;
+ aBorderPos = "insideV";
+ break;
+ default:
+ return;
+ }
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties)
+ {
+ std::vector<beans::PropertyValue> aSavedGrabBag;
+ if (!m_aInteropGrabBagName.isEmpty())
+ {
+ aSavedGrabBag = m_aInteropGrabBag;
+ m_aInteropGrabBag.clear();
+ }
+ pProperties->resolve(*this);
+ if (!m_aInteropGrabBagName.isEmpty())
+ {
+ aSavedGrabBag.push_back(getInteropGrabBag(aBorderPos));
+ m_aInteropGrabBag = aSavedGrabBag;
+ }
+ }
+ ConversionHelper::MakeBorderLine( m_nLineWidth, m_nLineType, m_nLineColor,
+ m_aBorderLines[ pos ], m_bOOXML );
+ m_aFilledLines[ pos ] = true;
+}
+
+PropertyMapPtr BorderHandler::getProperties()
+{
+ static const o3tl::enumarray<BorderPosition, PropertyIds> aPropNames =
+ {
+ PROP_TOP_BORDER,
+ PROP_LEFT_BORDER,
+ PROP_BOTTOM_BORDER,
+ PROP_RIGHT_BORDER,
+ META_PROP_HORIZONTAL_BORDER,
+ META_PROP_VERTICAL_BORDER
+ };
+ PropertyMapPtr pPropertyMap(new PropertyMap);
+ // don't fill in default properties
+ if( m_bOOXML )
+ {
+ for( auto nProp: o3tl::enumrange<BorderPosition>())
+ {
+ if ( m_aFilledLines[nProp] ) {
+ pPropertyMap->Insert( aPropNames[nProp], uno::Any( m_aBorderLines[nProp] ) );
+ }
+ }
+ }
+ return pPropertyMap;
+}
+
+table::BorderLine2 BorderHandler::getBorderLine()
+{
+ table::BorderLine2 aBorderLine;
+ ConversionHelper::MakeBorderLine( m_nLineWidth, m_nLineType, m_nLineColor, aBorderLine, m_bOOXML );
+ return aBorderLine;
+}
+
+
+void BorderHandler::enableInteropGrabBag(const OUString& aName)
+{
+ m_aInteropGrabBagName = aName;
+}
+
+beans::PropertyValue BorderHandler::getInteropGrabBag(const OUString& aName)
+{
+ beans::PropertyValue aRet;
+ if (aName.isEmpty())
+ aRet.Name = m_aInteropGrabBagName;
+ else
+ aRet.Name = aName;
+
+ aRet.Value <<= comphelper::containerToSequence(m_aInteropGrabBag);
+ return aRet;
+}
+
+void BorderHandler::appendGrabBag(const OUString& aKey, const OUString& aValue)
+{
+ beans::PropertyValue aProperty;
+ aProperty.Name = aKey;
+ aProperty.Value <<= aValue;
+ m_aInteropGrabBag.push_back(aProperty);
+}
+
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/BorderHandler.hxx b/writerfilter/source/dmapper/BorderHandler.hxx
new file mode 100644
index 000000000..6c607ca5a
--- /dev/null
+++ b/writerfilter/source/dmapper/BorderHandler.hxx
@@ -0,0 +1,78 @@
+/* -*- 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 <vector>
+#include "LoggedResources.hxx"
+#include "PropertyMap.hxx"
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <o3tl/enumarray.hxx>
+
+namespace writerfilter::dmapper
+{
+class PropertyMap;
+class BorderHandler : public LoggedProperties
+{
+private:
+ //todo: order is a guess
+ enum class BorderPosition
+ {
+ Top,
+ Left,
+ Bottom,
+ Right,
+ Horizontal,
+ Vertical,
+ LAST = Vertical
+ };
+
+ //values of the current border
+ sal_Int32 m_nLineWidth;
+ sal_Int32 m_nLineType;
+ sal_Int32 m_nLineColor;
+ sal_Int32 m_nLineDistance;
+ bool m_bShadow;
+ bool m_bOOXML;
+
+ o3tl::enumarray<BorderPosition, bool> m_aFilledLines;
+ o3tl::enumarray<BorderPosition, css::table::BorderLine2> m_aBorderLines;
+ OUString m_aInteropGrabBagName;
+ std::vector<css::beans::PropertyValue> m_aInteropGrabBag;
+ void appendGrabBag(const OUString& aKey, const OUString& aValue);
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+public:
+ explicit BorderHandler( bool bOOXML );
+ virtual ~BorderHandler() override;
+
+ PropertyMapPtr getProperties();
+ css::table::BorderLine2 getBorderLine();
+ sal_Int32 getLineDistance() const { return m_nLineDistance;}
+ sal_Int32 getLineType() const { return m_nLineType;}
+ bool getShadow() const { return m_bShadow;}
+ void enableInteropGrabBag(const OUString& aName);
+ css::beans::PropertyValue getInteropGrabBag(const OUString& aName = OUString());
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/CellColorHandler.cxx b/writerfilter/source/dmapper/CellColorHandler.cxx
new file mode 100644
index 000000000..439806e20
--- /dev/null
+++ b/writerfilter/source/dmapper/CellColorHandler.cxx
@@ -0,0 +1,331 @@
+/* -*- 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 "CellColorHandler.hxx"
+#include "PropertyMap.hxx"
+#include "TDefTableHandler.hxx"
+#include <ooxml/resourceids.hxx>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/ShadingPattern.hpp>
+#include <filter/msfilter/util.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/color.hxx>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+
+CellColorHandler::CellColorHandler() :
+LoggedProperties("CellColorHandler"),
+m_nShadingPattern( drawing::ShadingPattern::CLEAR ),
+m_nColor( 0xffffffff ),
+m_nFillColor( 0xffffffff ),
+m_bAutoFillColor( true ),
+m_bFillSpecified( false ),
+ m_OutputFormat( Form )
+{
+}
+
+CellColorHandler::~CellColorHandler()
+{
+}
+
+// ST_Shd strings are converted to integers by the tokenizer, store strings in
+// the InteropGrabBag
+static uno::Any lcl_ConvertShd(sal_Int32 nIntValue)
+{
+ OUString aRet;
+ // This should be in sync with the ST_Shd list in ooxml's model.xml.
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_Shd_clear: aRet = "clear"; break;
+ case NS_ooxml::LN_Value_ST_Shd_solid: aRet = "solid"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct5: aRet = "pct5"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct10: aRet = "pct10"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct20: aRet = "pct20"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct25: aRet = "pct25"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct30: aRet = "pct30"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct40: aRet = "pct40"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct50: aRet = "pct50"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct60: aRet = "pct60"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct70: aRet = "pct70"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct75: aRet = "pct75"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct80: aRet = "pct80"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct90: aRet = "pct90"; break;
+ case NS_ooxml::LN_Value_ST_Shd_horzStripe: aRet = "horzStripe"; break;
+ case NS_ooxml::LN_Value_ST_Shd_vertStripe: aRet = "vertStripe"; break;
+ case NS_ooxml::LN_Value_ST_Shd_reverseDiagStripe: aRet = "reverseDiagStripe"; break;
+ case NS_ooxml::LN_Value_ST_Shd_diagStripe: aRet = "diagStripe"; break;
+ case NS_ooxml::LN_Value_ST_Shd_horzCross: aRet = "horzCross"; break;
+ case NS_ooxml::LN_Value_ST_Shd_diagCross: aRet = "diagCross"; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinHorzStripe: aRet = "thinHorzStripe"; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinVertStripe: aRet = "thinVertStripe"; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinReverseDiagStripe: aRet = "thinReverseDiagStripe"; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinDiagStripe: aRet = "thinDiagStripe"; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinHorzCross: aRet = "thinHorzCross"; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinDiagCross: aRet = "thinDiagCross"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct12: aRet = "pct12"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct15: aRet = "pct15"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct35: aRet = "pct35"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct37: aRet = "pct37"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct45: aRet = "pct45"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct55: aRet = "pct55"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct62: aRet = "pct62"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct65: aRet = "pct65"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct85: aRet = "pct85"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct87: aRet = "pct87"; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct95: aRet = "pct95"; break;
+ case NS_ooxml::LN_Value_ST_Shd_nil: aRet = "nil"; break;
+ }
+ return uno::Any(aRet);
+}
+
+void CellColorHandler::lcl_attribute(Id rName, Value & rVal)
+{
+ sal_Int32 nIntValue = rVal.getInt();
+ switch( rName )
+ {
+ case NS_ooxml::LN_CT_Shd_val:
+ {
+ createGrabBag("val", lcl_ConvertShd(nIntValue));
+ m_nShadingPattern = nIntValue;
+ }
+ break;
+ case NS_ooxml::LN_CT_Shd_fill:
+ createGrabBag("fill", uno::Any(msfilter::util::ConvertColorOU(Color(ColorTransparency, nIntValue))));
+ if( nIntValue == sal_Int32(COL_AUTO) )
+ nIntValue = 0xffffff; //fill color auto means white
+ else
+ m_bAutoFillColor = false;
+
+ m_nFillColor = nIntValue;
+ m_bFillSpecified = true;
+ break;
+ case NS_ooxml::LN_CT_Shd_color:
+ createGrabBag("color", uno::Any(msfilter::util::ConvertColorOU(Color(ColorTransparency, nIntValue))));
+ if( nIntValue == sal_Int32(COL_AUTO) )
+ nIntValue = 0; //shading color auto means black
+ //color of the shading
+ m_nColor = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Shd_themeFill:
+ createGrabBag("themeFill", uno::Any(TDefTableHandler::getThemeColorTypeString(nIntValue)));
+ break;
+ case NS_ooxml::LN_CT_Shd_themeFillShade:
+ createGrabBag("themeFillShade", uno::Any(OUString::number(nIntValue, 16)));
+ break;
+ case NS_ooxml::LN_CT_Shd_themeFillTint:
+ createGrabBag("themeFillTint", uno::Any(OUString::number(nIntValue, 16)));
+ break;
+ case NS_ooxml::LN_CT_Shd_themeColor:
+ createGrabBag("themeColor", uno::Any(TDefTableHandler::getThemeColorTypeString(nIntValue)));
+ break;
+ case NS_ooxml::LN_CT_Shd_themeShade:
+ createGrabBag("themeShade", uno::Any(OUString::number(nIntValue, 16)));
+ break;
+ case NS_ooxml::LN_CT_Shd_themeTint:
+ createGrabBag("themeTint", uno::Any(OUString::number(nIntValue, 16)));
+ break;
+ default:
+ OSL_FAIL( "unknown attribute");
+ }
+}
+
+void CellColorHandler::lcl_sprm(Sprm &) {}
+
+TablePropertyMapPtr CellColorHandler::getProperties()
+{
+ TablePropertyMapPtr pPropertyMap(new TablePropertyMap);
+
+ // Code from binary word filter (the values are out of 1000)
+ sal_Int32 nWW8BrushStyle = 0;
+ switch (m_nShadingPattern)
+ {
+ // Clear-Brush
+ case NS_ooxml::LN_Value_ST_Shd_clear: nWW8BrushStyle = 0; break;
+ // Solid-Brush
+ case NS_ooxml::LN_Value_ST_Shd_solid: nWW8BrushStyle = 1000; break;
+ // Percent values
+ case NS_ooxml::LN_Value_ST_Shd_pct5: nWW8BrushStyle = 50; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct10: nWW8BrushStyle = 100; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct20: nWW8BrushStyle = 200; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct25: nWW8BrushStyle = 250; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct30: nWW8BrushStyle = 300; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct40: nWW8BrushStyle = 400; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct50: nWW8BrushStyle = 500; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct60: nWW8BrushStyle = 600; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct70: nWW8BrushStyle = 700; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct75: nWW8BrushStyle = 750; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct80: nWW8BrushStyle = 800; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct90: nWW8BrushStyle = 900; break;
+ // Special cases
+ case NS_ooxml::LN_Value_ST_Shd_horzStripe: nWW8BrushStyle = 333; break; // Dark Horizontal
+ case NS_ooxml::LN_Value_ST_Shd_vertStripe: nWW8BrushStyle = 333; break; // Dark Vertical
+ case NS_ooxml::LN_Value_ST_Shd_reverseDiagStripe: nWW8BrushStyle = 333; break; // Dark Forward Diagonal
+ case NS_ooxml::LN_Value_ST_Shd_diagStripe: nWW8BrushStyle = 333; break; // Dark Backward Diagonal
+ case NS_ooxml::LN_Value_ST_Shd_horzCross: nWW8BrushStyle = 333; break; // Dark Cross
+ case NS_ooxml::LN_Value_ST_Shd_diagCross: nWW8BrushStyle = 333; break; // Dark Diagonal Cross
+ case NS_ooxml::LN_Value_ST_Shd_thinHorzStripe: nWW8BrushStyle = 333; break; // Horizontal
+ case NS_ooxml::LN_Value_ST_Shd_thinVertStripe: nWW8BrushStyle = 333; break; // Vertical
+ case NS_ooxml::LN_Value_ST_Shd_thinReverseDiagStripe: nWW8BrushStyle = 333; break; // Forward Diagonal
+ case NS_ooxml::LN_Value_ST_Shd_thinDiagStripe: nWW8BrushStyle = 333; break; // Backward Diagonal
+ case NS_ooxml::LN_Value_ST_Shd_thinHorzCross: nWW8BrushStyle = 333; break; // Cross
+ case NS_ooxml::LN_Value_ST_Shd_thinDiagCross: nWW8BrushStyle = 333; break; // 25 Diagonal Cross
+ // Different shading types
+ case NS_ooxml::LN_Value_ST_Shd_pct12: nWW8BrushStyle = 125; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct15: nWW8BrushStyle = 150; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct35: nWW8BrushStyle = 350; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct37: nWW8BrushStyle = 375; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct45: nWW8BrushStyle = 450; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct55: nWW8BrushStyle = 550; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct62: nWW8BrushStyle = 625; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct65: nWW8BrushStyle = 650; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct85: nWW8BrushStyle = 850; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct87: nWW8BrushStyle = 875; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct95: nWW8BrushStyle = 950; break;
+ }
+
+ sal_Int32 nApplyColor = 0;
+ if( !nWW8BrushStyle )
+ {
+ // Clear-Brush
+ if ( m_bFillSpecified && m_bAutoFillColor )
+ nApplyColor = sal_Int32(COL_AUTO);
+ else
+ nApplyColor = m_nFillColor;
+ }
+ else
+ {
+ sal_Int32 nFore = m_nColor;
+ sal_Int32 nBack = m_nFillColor;
+
+ sal_uInt32 nRed = ((nFore & 0xff0000)>>0x10) * nWW8BrushStyle;
+ sal_uInt32 nGreen = ((nFore & 0xff00)>>0x8) * nWW8BrushStyle;
+ sal_uInt32 nBlue = (nFore & 0xff) * nWW8BrushStyle;
+ nRed += ((nBack & 0xff0000)>>0x10) * (1000L - nWW8BrushStyle);
+ nGreen += ((nBack & 0xff00)>>0x8)* (1000L - nWW8BrushStyle);
+ nBlue += (nBack & 0xff) * (1000L - nWW8BrushStyle);
+
+ nApplyColor = ( (nRed/1000) << 0x10 ) + ((nGreen/1000) << 8) + nBlue/1000;
+ }
+
+ // Check if it is a 'Character'
+ if (m_OutputFormat == Character)
+ {
+ sal_Int32 nShadingPattern = drawing::ShadingPattern::CLEAR;
+ switch (m_nShadingPattern)
+ {
+ case NS_ooxml::LN_Value_ST_Shd_clear: nShadingPattern = drawing::ShadingPattern::CLEAR; break;
+ case NS_ooxml::LN_Value_ST_Shd_solid: nShadingPattern = drawing::ShadingPattern::SOLID; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct5: nShadingPattern = drawing::ShadingPattern::PCT5; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct10: nShadingPattern = drawing::ShadingPattern::PCT10; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct20: nShadingPattern = drawing::ShadingPattern::PCT20; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct25: nShadingPattern = drawing::ShadingPattern::PCT25; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct30: nShadingPattern = drawing::ShadingPattern::PCT30; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct40: nShadingPattern = drawing::ShadingPattern::PCT40; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct50: nShadingPattern = drawing::ShadingPattern::PCT50; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct60: nShadingPattern = drawing::ShadingPattern::PCT60; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct70: nShadingPattern = drawing::ShadingPattern::PCT70; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct75: nShadingPattern = drawing::ShadingPattern::PCT75; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct80: nShadingPattern = drawing::ShadingPattern::PCT80; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct90: nShadingPattern = drawing::ShadingPattern::PCT90; break;
+ case NS_ooxml::LN_Value_ST_Shd_horzStripe: nShadingPattern = drawing::ShadingPattern::HORZ_STRIPE; break;
+ case NS_ooxml::LN_Value_ST_Shd_vertStripe: nShadingPattern = drawing::ShadingPattern::VERT_STRIPE; break;
+ case NS_ooxml::LN_Value_ST_Shd_reverseDiagStripe: nShadingPattern = drawing::ShadingPattern::REVERSE_DIAG_STRIPE; break;
+ case NS_ooxml::LN_Value_ST_Shd_diagStripe: nShadingPattern = drawing::ShadingPattern::DIAG_STRIPE; break;
+ case NS_ooxml::LN_Value_ST_Shd_horzCross: nShadingPattern = drawing::ShadingPattern::HORZ_CROSS; break;
+ case NS_ooxml::LN_Value_ST_Shd_diagCross: nShadingPattern = drawing::ShadingPattern::DIAG_CROSS; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinHorzStripe: nShadingPattern = drawing::ShadingPattern::THIN_HORZ_STRIPE; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinVertStripe: nShadingPattern = drawing::ShadingPattern::THIN_VERT_STRIPE; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinReverseDiagStripe: nShadingPattern = drawing::ShadingPattern::THIN_REVERSE_DIAG_STRIPE; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinDiagStripe: nShadingPattern = drawing::ShadingPattern::THIN_DIAG_STRIPE; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinHorzCross: nShadingPattern = drawing::ShadingPattern::THIN_HORZ_CROSS; break;
+ case NS_ooxml::LN_Value_ST_Shd_thinDiagCross: nShadingPattern = drawing::ShadingPattern::THIN_DIAG_CROSS; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct12: nShadingPattern = drawing::ShadingPattern::PCT12; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct15: nShadingPattern = drawing::ShadingPattern::PCT15; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct35: nShadingPattern = drawing::ShadingPattern::PCT35; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct37: nShadingPattern = drawing::ShadingPattern::PCT37; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct45: nShadingPattern = drawing::ShadingPattern::PCT45; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct55: nShadingPattern = drawing::ShadingPattern::PCT55; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct62: nShadingPattern = drawing::ShadingPattern::PCT62; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct65: nShadingPattern = drawing::ShadingPattern::PCT65; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct85: nShadingPattern = drawing::ShadingPattern::PCT85; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct87: nShadingPattern = drawing::ShadingPattern::PCT87; break;
+ case NS_ooxml::LN_Value_ST_Shd_pct95: nShadingPattern = drawing::ShadingPattern::PCT95; break;
+ }
+
+ // Write the shading pattern property
+ pPropertyMap->Insert(PROP_CHAR_SHADING_VALUE, uno::Any( nShadingPattern ));
+ }
+
+ if (m_OutputFormat == Paragraph && m_nShadingPattern != NS_ooxml::LN_Value_ST_Shd_nil)
+ {
+ if (nWW8BrushStyle || !m_bAutoFillColor)
+ pPropertyMap->Insert(PROP_FILL_STYLE, uno::Any(drawing::FillStyle_SOLID));
+ else if (m_bFillSpecified) // m_bAutoFillColor == true
+ pPropertyMap->Insert(PROP_FILL_STYLE, uno::Any(drawing::FillStyle_NONE));
+
+ pPropertyMap->Insert(PROP_FILL_COLOR, uno::Any(nApplyColor));
+ }
+ else if ( nWW8BrushStyle || !m_bAutoFillColor || m_bFillSpecified )
+ pPropertyMap->Insert( m_OutputFormat == Form ? PROP_BACK_COLOR
+ : PROP_CHAR_BACK_COLOR, uno::Any( nApplyColor ));
+
+ createGrabBag("originalColor", uno::Any(msfilter::util::ConvertColorOU(Color(ColorTransparency, nApplyColor))));
+
+ return pPropertyMap;
+}
+
+void CellColorHandler::createGrabBag(const OUString& aName, const uno::Any& rAny)
+{
+ if (m_aInteropGrabBagName.isEmpty())
+ return;
+
+ beans::PropertyValue aValue;
+ aValue.Name = aName;
+ aValue.Value = rAny;
+ m_aInteropGrabBag.push_back(aValue);
+}
+
+void CellColorHandler::enableInteropGrabBag(const OUString& aName)
+{
+ m_aInteropGrabBagName = aName;
+}
+
+beans::PropertyValue CellColorHandler::getInteropGrabBag()
+{
+ beans::PropertyValue aRet;
+ aRet.Name = m_aInteropGrabBagName;
+ aRet.Value <<= comphelper::containerToSequence(m_aInteropGrabBag);
+ return aRet;
+}
+
+void CellColorHandler::disableInteropGrabBag()
+{
+ m_aInteropGrabBagName.clear();
+ m_aInteropGrabBag.clear();
+}
+
+bool CellColorHandler::isInteropGrabBagEnabled() const
+{
+ return !(m_aInteropGrabBagName.isEmpty());
+}
+
+} //namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/CellColorHandler.hxx b/writerfilter/source/dmapper/CellColorHandler.hxx
new file mode 100644
index 000000000..a011cd9fd
--- /dev/null
+++ b/writerfilter/source/dmapper/CellColorHandler.hxx
@@ -0,0 +1,66 @@
+/* -*- 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 "LoggedResources.hxx"
+#include "PropertyMap.hxx"
+#include <vector>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+namespace writerfilter::dmapper
+{
+class TablePropertyMap;
+class CellColorHandler : public LoggedProperties
+{
+public:
+ enum OutputFormat { Form, Paragraph, Character }; // for what part of the document
+private:
+ sal_Int32 m_nShadingPattern;
+ sal_Int32 m_nColor;
+ sal_Int32 m_nFillColor;
+ bool m_bAutoFillColor;
+ bool m_bFillSpecified;
+ OutputFormat m_OutputFormat;
+
+ OUString m_aInteropGrabBagName;
+ std::vector<css::beans::PropertyValue> m_aInteropGrabBag;
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+ void createGrabBag(const OUString& aName, const css::uno::Any& rValue);
+
+public:
+ CellColorHandler( );
+ virtual ~CellColorHandler() override;
+
+ TablePropertyMapPtr getProperties();
+
+ void setOutputFormat( OutputFormat format ) { m_OutputFormat = format; }
+
+ void enableInteropGrabBag(const OUString& aName);
+ css::beans::PropertyValue getInteropGrabBag();
+ void disableInteropGrabBag();
+ bool isInteropGrabBagEnabled() const;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/CellMarginHandler.cxx b/writerfilter/source/dmapper/CellMarginHandler.cxx
new file mode 100644
index 000000000..8b7b5fa77
--- /dev/null
+++ b/writerfilter/source/dmapper/CellMarginHandler.cxx
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "CellMarginHandler.hxx"
+#include "ConversionHelper.hxx"
+#include <ooxml/resourceids.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+using namespace ::writerfilter;
+
+CellMarginHandler::CellMarginHandler() :
+LoggedProperties("CellMarginHandler"),
+m_nValue( 0 ),
+m_nWidth( 0 ),
+m_nType( 0 ),
+m_nLeftMargin( 0 ),
+m_bLeftMarginValid( false ),
+m_nRightMargin( 0 ),
+m_bRightMarginValid( false ),
+m_nTopMargin( 0 ),
+m_bTopMarginValid( false ),
+m_nBottomMargin( 0 ),
+m_bBottomMarginValid( false )
+{
+}
+
+CellMarginHandler::~CellMarginHandler()
+{
+}
+
+void CellMarginHandler::lcl_attribute(Id rName, Value & rVal)
+{
+ sal_Int32 nIntValue = rVal.getInt();
+ switch( rName )
+ {
+ case NS_ooxml::LN_CT_TblWidth_w:
+ m_nWidth = nIntValue;
+ m_nValue = ConversionHelper::convertTwipToMM100Unsigned( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_TblWidth_type:
+ SAL_WARN_IF(NS_ooxml::LN_Value_ST_TblWidth_dxa != sal::static_int_cast<Id>(nIntValue), "writerfilter", "CellMarginHandler: cell margins work for absolute values only");
+ m_nType = nIntValue;
+ break;
+ default:
+ SAL_WARN("writerfilter", "CellMarginHandler::lcl_attribute: unknown attribute");
+ }
+}
+
+void CellMarginHandler::createGrabBag(const OUString& aName)
+{
+ if (m_aInteropGrabBagName.isEmpty())
+ return;
+
+ beans::PropertyValue aRet;
+ aRet.Name = aName;
+
+ OUString sType;
+ switch (m_nType)
+ {
+ case NS_ooxml::LN_Value_ST_TblWidth_nil: sType = "nil"; break;
+ case NS_ooxml::LN_Value_ST_TblWidth_pct: sType = "pct"; break;
+ case NS_ooxml::LN_Value_ST_TblWidth_dxa: sType = "dxa"; break;
+ case NS_ooxml::LN_Value_ST_TblWidth_auto: sType = "auto"; break;
+ }
+ uno::Sequence<beans::PropertyValue> aSeq( comphelper::InitPropertySequence({
+ { "w", uno::Any(m_nWidth) },
+ { "type", uno::Any(sType) }
+ }));
+
+ aRet.Value <<= aSeq;
+ m_aInteropGrabBag.push_back(aRet);
+}
+
+void CellMarginHandler::lcl_sprm(Sprm & rSprm)
+{
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties)
+ {
+ pProperties->resolve( *this );
+ const bool rtl = false; // TODO
+ switch( rSprm.getId() )
+ {
+ case NS_ooxml::LN_CT_TblCellMar_top:
+ case NS_ooxml::LN_CT_TcMar_top:
+ m_nTopMargin = m_nValue;
+ m_bTopMarginValid = true;
+ createGrabBag("top");
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_start:
+ case NS_ooxml::LN_CT_TcMar_start:
+ if( rtl )
+ {
+ m_nRightMargin = m_nValue;
+ m_bRightMarginValid = true;
+ }
+ else
+ {
+ m_nLeftMargin = m_nValue;
+ m_bLeftMarginValid = true;
+ }
+ createGrabBag("start");
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_left:
+ case NS_ooxml::LN_CT_TcMar_left:
+ m_nLeftMargin = m_nValue;
+ m_bLeftMarginValid = true;
+ createGrabBag("left");
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_bottom:
+ case NS_ooxml::LN_CT_TcMar_bottom:
+ m_nBottomMargin = m_nValue;
+ m_bBottomMarginValid = true;
+ createGrabBag("bottom");
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_end:
+ case NS_ooxml::LN_CT_TcMar_end:
+ if( rtl )
+ {
+ m_nLeftMargin = m_nValue;
+ m_bLeftMarginValid = true;
+ }
+ else
+ {
+ m_nRightMargin = m_nValue;
+ m_bRightMarginValid = true;
+ }
+ createGrabBag("end");
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_right:
+ case NS_ooxml::LN_CT_TcMar_right:
+ m_nRightMargin = m_nValue;
+ m_bRightMarginValid = true;
+ createGrabBag("right");
+ break;
+ default:
+ SAL_WARN("writerfilter", "CellMarginHandler::lcl_sprm: unknown sprm");
+ }
+ }
+ m_nValue = 0;
+}
+
+void CellMarginHandler::enableInteropGrabBag(const OUString& aName)
+{
+ m_aInteropGrabBagName = aName;
+}
+
+beans::PropertyValue CellMarginHandler::getInteropGrabBag()
+{
+ beans::PropertyValue aRet;
+ aRet.Name = m_aInteropGrabBagName;
+ aRet.Value <<= comphelper::containerToSequence(m_aInteropGrabBag);
+ return aRet;
+}
+
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/CellMarginHandler.hxx b/writerfilter/source/dmapper/CellMarginHandler.hxx
new file mode 100644
index 000000000..8dcbf2dc5
--- /dev/null
+++ b/writerfilter/source/dmapper/CellMarginHandler.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "LoggedResources.hxx"
+#include <vector>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+namespace writerfilter::dmapper
+{
+class TablePropertyMap;
+class CellMarginHandler : public LoggedProperties
+{
+private:
+ sal_Int32 m_nValue; ///< Converted value.
+ sal_Int32 m_nWidth; ///< Original value.
+ sal_Int32 m_nType; ///< Unit of the value (dxa, etc).
+
+ OUString m_aInteropGrabBagName;
+ std::vector<css::beans::PropertyValue> m_aInteropGrabBag;
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+ void createGrabBag(const OUString& aName);
+
+public:
+ sal_Int32 m_nLeftMargin;
+ bool m_bLeftMarginValid;
+ sal_Int32 m_nRightMargin;
+ bool m_bRightMarginValid;
+ sal_Int32 m_nTopMargin;
+ bool m_bTopMarginValid;
+ sal_Int32 m_nBottomMargin;
+ bool m_bBottomMarginValid;
+
+public:
+ CellMarginHandler( );
+ virtual ~CellMarginHandler() override;
+
+ void enableInteropGrabBag(const OUString& aName);
+ css::beans::PropertyValue getInteropGrabBag();
+
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/ConversionHelper.cxx b/writerfilter/source/dmapper/ConversionHelper.cxx
new file mode 100644
index 000000000..5c3b0831f
--- /dev/null
+++ b/writerfilter/source/dmapper/ConversionHelper.cxx
@@ -0,0 +1,691 @@
+/* -*- 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 "ConversionHelper.hxx"
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <editeng/borderline.hxx>
+#include <ooxml/resourceids.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/color.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/UnitConversion.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace com::sun::star;
+
+namespace writerfilter::dmapper::ConversionHelper{
+
+/// Convert OOXML border style to WW8 that editeng can handle.
+static sal_Int32 lcl_convertBorderStyleFromToken(sal_Int32 nOOXMLType)
+{
+ switch (nOOXMLType)
+ {
+ case NS_ooxml::LN_Value_ST_Border_nil: return 255;
+ case NS_ooxml::LN_Value_ST_Border_none: return 0;
+ case NS_ooxml::LN_Value_ST_Border_single: return 1;
+ case NS_ooxml::LN_Value_ST_Border_thick: return 2;
+ case NS_ooxml::LN_Value_ST_Border_double: return 3;
+ case NS_ooxml::LN_Value_ST_Border_dotted: return 6;
+ case NS_ooxml::LN_Value_ST_Border_dashed: return 7;
+ case NS_ooxml::LN_Value_ST_Border_dotDash: return 8;
+ case NS_ooxml::LN_Value_ST_Border_dotDotDash: return 9;
+ case NS_ooxml::LN_Value_ST_Border_triple: return 10;
+ case NS_ooxml::LN_Value_ST_Border_thinThickSmallGap: return 11;
+ case NS_ooxml::LN_Value_ST_Border_thickThinSmallGap: return 12;
+ case NS_ooxml::LN_Value_ST_Border_thinThickThinSmallGap: return 13;
+ case NS_ooxml::LN_Value_ST_Border_thinThickMediumGap: return 14;
+ case NS_ooxml::LN_Value_ST_Border_thickThinMediumGap: return 15;
+ case NS_ooxml::LN_Value_ST_Border_thinThickThinMediumGap: return 16;
+ case NS_ooxml::LN_Value_ST_Border_thinThickLargeGap: return 17;
+ case NS_ooxml::LN_Value_ST_Border_thickThinLargeGap: return 18;
+ case NS_ooxml::LN_Value_ST_Border_thinThickThinLargeGap: return 19;
+ case NS_ooxml::LN_Value_ST_Border_wave: return 20;
+ case NS_ooxml::LN_Value_ST_Border_doubleWave: return 21;
+ case NS_ooxml::LN_Value_ST_Border_dashSmallGap: return 22;
+ case NS_ooxml::LN_Value_ST_Border_dashDotStroked: return 23;
+ case NS_ooxml::LN_Value_ST_Border_threeDEmboss: return 24;
+ case NS_ooxml::LN_Value_ST_Border_threeDEngrave: return 25;
+ case NS_ooxml::LN_Value_ST_Border_outset: return 26;
+ case NS_ooxml::LN_Value_ST_Border_inset: return 27;
+ case NS_ooxml::LN_Value_ST_Border_apples: return 64;
+ case NS_ooxml::LN_Value_ST_Border_archedScallops: return 65;
+ case NS_ooxml::LN_Value_ST_Border_babyPacifier: return 66;
+ case NS_ooxml::LN_Value_ST_Border_babyRattle: return 67;
+ case NS_ooxml::LN_Value_ST_Border_balloons3Colors: return 68;
+ case NS_ooxml::LN_Value_ST_Border_balloonsHotAir: return 69;
+ case NS_ooxml::LN_Value_ST_Border_basicBlackDashes: return 70;
+ case NS_ooxml::LN_Value_ST_Border_basicBlackDots: return 71;
+ case NS_ooxml::LN_Value_ST_Border_basicBlackSquares: return 72;
+ case NS_ooxml::LN_Value_ST_Border_basicThinLines: return 73;
+ case NS_ooxml::LN_Value_ST_Border_basicWhiteDashes: return 74;
+ case NS_ooxml::LN_Value_ST_Border_basicWhiteDots: return 75;
+ case NS_ooxml::LN_Value_ST_Border_basicWhiteSquares: return 76;
+ case NS_ooxml::LN_Value_ST_Border_basicWideInline: return 77;
+ case NS_ooxml::LN_Value_ST_Border_basicWideMidline: return 78;
+ case NS_ooxml::LN_Value_ST_Border_basicWideOutline: return 79;
+ case NS_ooxml::LN_Value_ST_Border_bats: return 80;
+ case NS_ooxml::LN_Value_ST_Border_birds: return 81;
+ case NS_ooxml::LN_Value_ST_Border_birdsFlight: return 82;
+ case NS_ooxml::LN_Value_ST_Border_cabins: return 83;
+ case NS_ooxml::LN_Value_ST_Border_cakeSlice: return 84;
+ case NS_ooxml::LN_Value_ST_Border_candyCorn: return 85;
+ case NS_ooxml::LN_Value_ST_Border_celticKnotwork: return 86;
+ case NS_ooxml::LN_Value_ST_Border_certificateBanner: return 87;
+ case NS_ooxml::LN_Value_ST_Border_chainLink: return 88;
+ case NS_ooxml::LN_Value_ST_Border_champagneBottle: return 89;
+ case NS_ooxml::LN_Value_ST_Border_checkedBarBlack: return 90;
+ case NS_ooxml::LN_Value_ST_Border_checkedBarColor: return 91;
+ case NS_ooxml::LN_Value_ST_Border_checkered: return 92;
+ case NS_ooxml::LN_Value_ST_Border_christmasTree: return 93;
+ case NS_ooxml::LN_Value_ST_Border_circlesLines: return 94;
+ case NS_ooxml::LN_Value_ST_Border_circlesRectangles: return 95;
+ case NS_ooxml::LN_Value_ST_Border_classicalWave: return 96;
+ case NS_ooxml::LN_Value_ST_Border_clocks: return 97;
+ case NS_ooxml::LN_Value_ST_Border_compass: return 98;
+ case NS_ooxml::LN_Value_ST_Border_confetti: return 99;
+ case NS_ooxml::LN_Value_ST_Border_confettiGrays: return 100;
+ case NS_ooxml::LN_Value_ST_Border_confettiOutline: return 101;
+ case NS_ooxml::LN_Value_ST_Border_confettiStreamers: return 102;
+ case NS_ooxml::LN_Value_ST_Border_confettiWhite: return 103;
+ case NS_ooxml::LN_Value_ST_Border_cornerTriangles: return 104;
+ case NS_ooxml::LN_Value_ST_Border_couponCutoutDashes: return 105;
+ case NS_ooxml::LN_Value_ST_Border_couponCutoutDots: return 106;
+ case NS_ooxml::LN_Value_ST_Border_crazyMaze: return 107;
+ case NS_ooxml::LN_Value_ST_Border_creaturesButterfly: return 108;
+ case NS_ooxml::LN_Value_ST_Border_creaturesFish: return 109;
+ case NS_ooxml::LN_Value_ST_Border_creaturesInsects: return 110;
+ case NS_ooxml::LN_Value_ST_Border_creaturesLadyBug: return 111;
+ case NS_ooxml::LN_Value_ST_Border_crossStitch: return 112;
+ case NS_ooxml::LN_Value_ST_Border_cup: return 113;
+ case NS_ooxml::LN_Value_ST_Border_decoArch: return 114;
+ case NS_ooxml::LN_Value_ST_Border_decoArchColor: return 115;
+ case NS_ooxml::LN_Value_ST_Border_decoBlocks: return 116;
+ case NS_ooxml::LN_Value_ST_Border_diamondsGray: return 117;
+ case NS_ooxml::LN_Value_ST_Border_doubleD: return 118;
+ case NS_ooxml::LN_Value_ST_Border_doubleDiamonds: return 119;
+ case NS_ooxml::LN_Value_ST_Border_earth1: return 120;
+ case NS_ooxml::LN_Value_ST_Border_earth2: return 121;
+ case NS_ooxml::LN_Value_ST_Border_eclipsingSquares1: return 122;
+ case NS_ooxml::LN_Value_ST_Border_eclipsingSquares2: return 123;
+ case NS_ooxml::LN_Value_ST_Border_eggsBlack: return 124;
+ case NS_ooxml::LN_Value_ST_Border_fans: return 125;
+ case NS_ooxml::LN_Value_ST_Border_film: return 126;
+ case NS_ooxml::LN_Value_ST_Border_firecrackers: return 127;
+ case NS_ooxml::LN_Value_ST_Border_flowersBlockPrint: return 128;
+ case NS_ooxml::LN_Value_ST_Border_flowersDaisies: return 129;
+ case NS_ooxml::LN_Value_ST_Border_flowersModern1: return 130;
+ case NS_ooxml::LN_Value_ST_Border_flowersModern2: return 131;
+ case NS_ooxml::LN_Value_ST_Border_flowersPansy: return 132;
+ case NS_ooxml::LN_Value_ST_Border_flowersRedRose: return 133;
+ case NS_ooxml::LN_Value_ST_Border_flowersRoses: return 134;
+ case NS_ooxml::LN_Value_ST_Border_flowersTeacup: return 135;
+ case NS_ooxml::LN_Value_ST_Border_flowersTiny: return 136;
+ case NS_ooxml::LN_Value_ST_Border_gems: return 137;
+ case NS_ooxml::LN_Value_ST_Border_gingerbreadMan: return 138;
+ case NS_ooxml::LN_Value_ST_Border_gradient: return 139;
+ case NS_ooxml::LN_Value_ST_Border_handmade1: return 140;
+ case NS_ooxml::LN_Value_ST_Border_handmade2: return 141;
+ case NS_ooxml::LN_Value_ST_Border_heartBalloon: return 142;
+ case NS_ooxml::LN_Value_ST_Border_heartGray: return 143;
+ case NS_ooxml::LN_Value_ST_Border_hearts: return 144;
+ case NS_ooxml::LN_Value_ST_Border_heebieJeebies: return 145;
+ case NS_ooxml::LN_Value_ST_Border_holly: return 146;
+ case NS_ooxml::LN_Value_ST_Border_houseFunky: return 147;
+ case NS_ooxml::LN_Value_ST_Border_hypnotic: return 148;
+ case NS_ooxml::LN_Value_ST_Border_iceCreamCones: return 149;
+ case NS_ooxml::LN_Value_ST_Border_lightBulb: return 150;
+ case NS_ooxml::LN_Value_ST_Border_lightning1: return 151;
+ case NS_ooxml::LN_Value_ST_Border_lightning2: return 152;
+ case NS_ooxml::LN_Value_ST_Border_mapPins: return 153;
+ case NS_ooxml::LN_Value_ST_Border_mapleLeaf: return 154;
+ case NS_ooxml::LN_Value_ST_Border_mapleMuffins: return 155;
+ case NS_ooxml::LN_Value_ST_Border_marquee: return 156;
+ case NS_ooxml::LN_Value_ST_Border_marqueeToothed: return 157;
+ case NS_ooxml::LN_Value_ST_Border_moons: return 158;
+ case NS_ooxml::LN_Value_ST_Border_mosaic: return 159;
+ case NS_ooxml::LN_Value_ST_Border_musicNotes: return 160;
+ case NS_ooxml::LN_Value_ST_Border_northwest: return 161;
+ case NS_ooxml::LN_Value_ST_Border_ovals: return 162;
+ case NS_ooxml::LN_Value_ST_Border_packages: return 163;
+ case NS_ooxml::LN_Value_ST_Border_palmsBlack: return 164;
+ case NS_ooxml::LN_Value_ST_Border_palmsColor: return 165;
+ case NS_ooxml::LN_Value_ST_Border_paperClips: return 166;
+ case NS_ooxml::LN_Value_ST_Border_papyrus: return 167;
+ case NS_ooxml::LN_Value_ST_Border_partyFavor: return 168;
+ case NS_ooxml::LN_Value_ST_Border_partyGlass: return 169;
+ case NS_ooxml::LN_Value_ST_Border_pencils: return 170;
+ case NS_ooxml::LN_Value_ST_Border_people: return 171;
+ case NS_ooxml::LN_Value_ST_Border_peopleWaving: return 172;
+ case NS_ooxml::LN_Value_ST_Border_peopleHats: return 173;
+ case NS_ooxml::LN_Value_ST_Border_poinsettias: return 174;
+ case NS_ooxml::LN_Value_ST_Border_postageStamp: return 175;
+ case NS_ooxml::LN_Value_ST_Border_pumpkin1: return 176;
+ case NS_ooxml::LN_Value_ST_Border_pushPinNote2: return 177;
+ case NS_ooxml::LN_Value_ST_Border_pushPinNote1: return 178;
+ case NS_ooxml::LN_Value_ST_Border_pyramids: return 179;
+ case NS_ooxml::LN_Value_ST_Border_pyramidsAbove: return 180;
+ case NS_ooxml::LN_Value_ST_Border_quadrants: return 181;
+ case NS_ooxml::LN_Value_ST_Border_rings: return 182;
+ case NS_ooxml::LN_Value_ST_Border_safari: return 183;
+ case NS_ooxml::LN_Value_ST_Border_sawtooth: return 184;
+ case NS_ooxml::LN_Value_ST_Border_sawtoothGray: return 185;
+ case NS_ooxml::LN_Value_ST_Border_scaredCat: return 186;
+ case NS_ooxml::LN_Value_ST_Border_seattle: return 187;
+ case NS_ooxml::LN_Value_ST_Border_shadowedSquares: return 188;
+ case NS_ooxml::LN_Value_ST_Border_sharksTeeth: return 189;
+ case NS_ooxml::LN_Value_ST_Border_shorebirdTracks: return 190;
+ case NS_ooxml::LN_Value_ST_Border_skyrocket: return 191;
+ case NS_ooxml::LN_Value_ST_Border_snowflakeFancy: return 192;
+ case NS_ooxml::LN_Value_ST_Border_snowflakes: return 193;
+ case NS_ooxml::LN_Value_ST_Border_sombrero: return 194;
+ case NS_ooxml::LN_Value_ST_Border_southwest: return 195;
+ case NS_ooxml::LN_Value_ST_Border_stars: return 196;
+ case NS_ooxml::LN_Value_ST_Border_starsTop: return 197;
+ case NS_ooxml::LN_Value_ST_Border_stars3d: return 198;
+ case NS_ooxml::LN_Value_ST_Border_starsBlack: return 199;
+ case NS_ooxml::LN_Value_ST_Border_starsShadowed: return 200;
+ case NS_ooxml::LN_Value_ST_Border_sun: return 201;
+ case NS_ooxml::LN_Value_ST_Border_swirligig: return 202;
+ case NS_ooxml::LN_Value_ST_Border_tornPaper: return 203;
+ case NS_ooxml::LN_Value_ST_Border_tornPaperBlack: return 204;
+ case NS_ooxml::LN_Value_ST_Border_trees: return 205;
+ case NS_ooxml::LN_Value_ST_Border_triangleParty: return 206;
+ case NS_ooxml::LN_Value_ST_Border_triangles: return 207;
+ case NS_ooxml::LN_Value_ST_Border_tribal1: return 208;
+ case NS_ooxml::LN_Value_ST_Border_tribal2: return 209;
+ case NS_ooxml::LN_Value_ST_Border_tribal3: return 210;
+ case NS_ooxml::LN_Value_ST_Border_tribal4: return 211;
+ case NS_ooxml::LN_Value_ST_Border_tribal5: return 212;
+ case NS_ooxml::LN_Value_ST_Border_tribal6: return 213;
+ case NS_ooxml::LN_Value_ST_Border_twistedLines1: return 214;
+ case NS_ooxml::LN_Value_ST_Border_twistedLines2: return 215;
+ case NS_ooxml::LN_Value_ST_Border_vine: return 216;
+ case NS_ooxml::LN_Value_ST_Border_waveline: return 217;
+ case NS_ooxml::LN_Value_ST_Border_weavingAngles: return 218;
+ case NS_ooxml::LN_Value_ST_Border_weavingBraid: return 219;
+ case NS_ooxml::LN_Value_ST_Border_weavingRibbon: return 220;
+ case NS_ooxml::LN_Value_ST_Border_weavingStrips: return 221;
+ case NS_ooxml::LN_Value_ST_Border_whiteFlowers: return 222;
+ case NS_ooxml::LN_Value_ST_Border_woodwork: return 223;
+ case NS_ooxml::LN_Value_ST_Border_xIllusions: return 224;
+ case NS_ooxml::LN_Value_ST_Border_zanyTriangles: return 225;
+ case NS_ooxml::LN_Value_ST_Border_zigZag: return 226;
+ case NS_ooxml::LN_Value_ST_Border_zigZagStitch: return 227;
+ default: break;
+ }
+ return 0;
+}
+
+void MakeBorderLine( sal_Int32 nLineThickness, sal_Int32 nLineToken,
+ sal_Int32 nLineColor,
+ table::BorderLine2& rToFill, bool bIsOOXML )
+{
+ static const Color aBorderDefColor[] =
+ {
+ // The first item means automatic color (COL_AUTO), but we
+ // do not use it anyway (see the next statement) .-)
+ // See also GetLineIndex in sw/source/filter/ww8/ww8par6.cxx
+ COL_AUTO, COL_BLACK, COL_LIGHTBLUE, COL_LIGHTCYAN, COL_LIGHTGREEN,
+ COL_LIGHTMAGENTA, COL_LIGHTRED, COL_YELLOW, COL_WHITE, COL_BLUE,
+ COL_CYAN, COL_GREEN, COL_MAGENTA, COL_RED, COL_BROWN, COL_GRAY,
+ COL_LIGHTGRAY
+ };
+ if(!bIsOOXML && sal::static_int_cast<sal_uInt32>(nLineColor) < SAL_N_ELEMENTS(aBorderDefColor))
+ nLineColor = sal_Int32(aBorderDefColor[nLineColor]);
+ //no auto color for borders
+ if (nLineColor == sal_Int32(COL_AUTO))
+ nLineColor = sal_Int32(COL_BLACK);
+
+ sal_Int32 nLineType = lcl_convertBorderStyleFromToken(nLineToken);
+
+ // Map to our border types, we should use of one equal line
+ // thickness, or one of smaller thickness. If too small we
+ // can make the deficit up in additional white space or
+ // object size
+ SvxBorderLineStyle const nLineStyle(
+ ::editeng::ConvertBorderStyleFromWord(nLineType));
+ rToFill.LineStyle = static_cast<sal_Int16>(nLineStyle);
+ double const fConverted( (SvxBorderLineStyle::NONE == nLineStyle) ? 0.0 :
+ ::editeng::ConvertBorderWidthFromWord(nLineStyle, nLineThickness,
+ nLineType));
+ rToFill.LineWidth = convertTwipToMM100(fConverted);
+ rToFill.Color = nLineColor;
+}
+
+namespace {
+void lcl_SwapQuotesInField(OUString &rFmt)
+{
+ //Swap unescaped " and ' with ' and "
+ sal_Int32 nLen = rFmt.getLength();
+ OUStringBuffer aBuffer( rFmt );
+ const sal_Unicode* pFmt = rFmt.getStr();
+ for (sal_Int32 nI = 0; nI < nLen; ++nI)
+ {
+ if ((pFmt[nI] == '\"') && (!nI || pFmt[nI-1] != '\\'))
+ aBuffer[nI] = '\'';
+ else if ((pFmt[nI] == '\'') && (!nI || pFmt[nI-1] != '\\'))
+ aBuffer[nI] = '\"';
+ }
+ rFmt = aBuffer.makeStringAndClear();
+}
+bool lcl_IsNotAM(OUString const & rFmt, sal_Int32 nPos)
+{
+ return (
+ (nPos == rFmt.getLength() - 1) ||
+ (
+ (rFmt[nPos+1] != 'M') &&
+ (rFmt[nPos+1] != 'm')
+ )
+ );
+}
+bool IsPreviousAM(std::u16string_view rParams, sal_Int32 nPos)
+{
+ return nPos >= 2 && o3tl::matchIgnoreAsciiCase(rParams, u"am", nPos - 2);
+}
+bool IsNextPM(std::u16string_view rParams, sal_Int32 nPos)
+{
+ return o3tl::make_unsigned(nPos + 2) < rParams.size() && o3tl::matchIgnoreAsciiCase(rParams, u"pm", nPos + 1);
+}
+}
+
+// See also sw::ms::MSDateTimeFormatToSwFormat
+OUString ConvertMSFormatStringToSO(
+ const OUString& rFormat, lang::Locale& rLocale, bool bHijri)
+{
+ OUString sFormat(rFormat);
+ lcl_SwapQuotesInField(sFormat);
+
+ //#102782#, #102815#, #108341# & #111944# have to work at the same time :-)
+ bool bForceJapanese(false);
+ bool bForceNatNum(false);
+ const sal_Int32 nLen = sFormat.getLength();
+ sal_Int32 nI = 0;
+ sal_Int32 nAddedChars = 0;
+// const sal_Unicode* pFormat = sFormat.getStr();
+ OUStringBuffer aNewFormat( sFormat );
+ while (nI < nLen)
+ {
+ if (sFormat[nI] == '\\')
+ ++nI;
+ else if (sFormat[nI] == '\"')
+ {
+ ++nI;
+ //While not at the end and not at an unescaped end quote
+ while ((nI < nLen) && ((sFormat[nI] != '\"') && (sFormat[nI-1] != '\\')))
+ ++nI;
+ }
+ else //normal unquoted section
+ {
+ sal_Unicode nChar = sFormat[nI];
+ if (nChar == 'O')
+ {
+ aNewFormat[nI + nAddedChars] = 'M';
+ bForceNatNum = true;
+ }
+ else if (nChar == 'o')
+ {
+ aNewFormat[nI + nAddedChars] = 'm';
+ bForceNatNum = true;
+ }
+ else if ((nChar == 'A') && lcl_IsNotAM(sFormat, nI))
+ {
+ aNewFormat[nI + nAddedChars] = 'D';
+ bForceNatNum = true;
+ }
+ else if ((nChar == 'g') || (nChar == 'G'))
+ bForceJapanese = true;
+ else if ((nChar == 'a') && lcl_IsNotAM(sFormat, nI))
+ bForceJapanese = true;
+ else if (nChar == 'E')
+ {
+ if ((nI != nLen-1) && (sFormat[nI+1] == 'E'))
+ {
+ //todo: this cannot be the right way to replace a part of the string!
+ aNewFormat[nI + nAddedChars] = 'Y';
+ aNewFormat[nI + nAddedChars + 1] = 'Y';
+ aNewFormat.insert(nI + nAddedChars + 2, "YY");
+ nAddedChars += 2;
+ ++nI;
+ }
+ bForceJapanese = true;
+ }
+ else if (nChar == 'e')
+ {
+ if ((nI != nLen-1) && (sFormat[nI+1] == 'e'))
+ {
+ //todo: this cannot be the right way to replace a part of the string!
+ aNewFormat[nI + nAddedChars] = 'y';
+ aNewFormat[nI + nAddedChars + 1] = 'y';
+ aNewFormat.insert(nI + nAddedChars + 2, "yy");
+ nAddedChars += 2;
+ ++nI;
+ }
+ bForceJapanese = true;
+ }
+ else if (nChar == '/' && !(IsPreviousAM(sFormat, nI) && IsNextPM(sFormat, nI)))
+ {
+ // MM We have to escape '/' in case it's used as a char
+ //todo: this cannot be the right way to replace a part of the string!
+ aNewFormat[nI + nAddedChars] = '\\';
+ aNewFormat.insert(nI + nAddedChars + 1, "/");
+ ++nAddedChars;
+ ++nI;
+ }
+ }
+ ++nI;
+ }
+
+ if (bForceNatNum)
+ bForceJapanese = true;
+
+ if (bForceJapanese)
+ {
+ rLocale.Language = "ja";
+ rLocale.Country = "JP";
+ }
+
+ if (bForceNatNum)
+ {
+ aNewFormat.insert( 0, "[NatNum1][$-411]");
+ }
+
+ if (bHijri)
+ {
+ aNewFormat.insert( 0, "[~hijri]");
+ }
+ return aNewFormat.makeStringAndClear();
+
+}
+
+sal_Int32 convertTwipToMM100(sal_Int32 _t)
+{
+ // It appears that MSO handles large twip values specially, probably legacy 16bit handling,
+ // anything that's bigger than 32767 appears to be simply ignored.
+ if( _t >= 0x8000 )
+ return 0;
+ return ::convertTwipToMm100( _t );
+}
+
+sal_Int32 convertTwipToMM100WithoutLimit(sal_Int32 _t)
+{
+ return ::convertTwipToMm100(_t);
+}
+
+double convertTwipToMM100Double(sal_Int32 _t)
+{
+ // It appears that MSO handles large twip values specially, probably legacy 16bit handling,
+ // anything that's bigger than 32767 appears to be simply ignored.
+ if( _t >= 0x8000 )
+ return 0.0;
+ return o3tl::convert<double>(_t, o3tl::Length::twip, o3tl::Length::mm100);
+}
+
+sal_uInt32 convertTwipToMM100Unsigned(sal_Int32 _t)
+{
+ if( _t < 0 )
+ return 0;
+ return convertTwipToMM100( _t );
+}
+
+text::RubyAdjust convertRubyAlign( sal_Int32 nIntValue )
+{
+ text::RubyAdjust rubyAdjust = text::RubyAdjust_LEFT;
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_ST_RubyAlign_center:
+ case NS_ooxml::LN_Value_ST_RubyAlign_rightVertical:
+ rubyAdjust = text::RubyAdjust_CENTER;
+ break;
+ case NS_ooxml::LN_Value_ST_RubyAlign_distributeLetter:
+ rubyAdjust = text::RubyAdjust_BLOCK;
+ break;
+ case NS_ooxml::LN_Value_ST_RubyAlign_distributeSpace:
+ rubyAdjust = text::RubyAdjust_INDENT_BLOCK;
+ break;
+ case NS_ooxml::LN_Value_ST_RubyAlign_left:
+ rubyAdjust = text::RubyAdjust_LEFT;
+ break;
+ case NS_ooxml::LN_Value_ST_RubyAlign_right:
+ rubyAdjust = text::RubyAdjust_RIGHT;
+ break;
+ }
+ return rubyAdjust;
+}
+
+sal_Int16 convertTableJustification( sal_Int32 nIntValue )
+{
+ sal_Int16 nOrient = text::HoriOrientation::LEFT_AND_WIDTH;
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_ST_Jc_center:
+ nOrient = text::HoriOrientation::CENTER;
+ break;
+ case NS_ooxml::LN_Value_ST_Jc_right:
+ case NS_ooxml::LN_Value_ST_Jc_end:
+ nOrient = text::HoriOrientation::RIGHT;
+ break;
+ case NS_ooxml::LN_Value_ST_Jc_left:
+ case NS_ooxml::LN_Value_ST_Jc_start:
+ //no break
+ default:;
+
+ }
+ return nOrient;
+}
+
+// Return the suggested default if the given format has no known conversion
+sal_Int16 ConvertNumberingType(const sal_Int32 nFmt, const sal_Int16 nDefault)
+{
+ sal_Int16 nRet;
+ switch(nFmt)
+ {
+ case NS_ooxml::LN_Value_ST_NumberFormat_decimal:
+ nRet = style::NumberingType::ARABIC;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_upperRoman:
+ nRet = style::NumberingType::ROMAN_UPPER;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_lowerRoman:
+ nRet = style::NumberingType::ROMAN_LOWER;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_ordinal:
+ nRet = style::NumberingType::TEXT_NUMBER;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_bullet:
+ nRet = style::NumberingType::CHAR_SPECIAL;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_none:
+ nRet = style::NumberingType::NUMBER_NONE;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_upperLetter:
+ nRet = style::NumberingType::CHARS_UPPER_LETTER_N;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_lowerLetter:
+ nRet = style::NumberingType::CHARS_LOWER_LETTER_N;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_iroha:
+ nRet = style::NumberingType::IROHA_HALFWIDTH_JA;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_irohaFullWidth:
+ nRet = style::NumberingType::IROHA_FULLWIDTH_JA;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_aiueo:
+ nRet = style::NumberingType::AIU_HALFWIDTH_JA;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_aiueoFullWidth:
+ nRet = style::NumberingType::AIU_FULLWIDTH_JA;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_hebrew2:
+ nRet = style::NumberingType::CHARS_HEBREW;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_thaiLetters:
+ nRet = style::NumberingType::CHARS_THAI;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_russianLower:
+ nRet = style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_russianUpper:
+ nRet = style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedCircleChinese:
+ case NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedCircle:
+ case NS_ooxml::LN_Value_ST_NumberFormat_ideographEnclosedCircle:
+ nRet = style::NumberingType::CIRCLE_NUMBER;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_ideographTraditional:
+ nRet = style::NumberingType::TIAN_GAN_ZH;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_ideographZodiac:
+ nRet = style::NumberingType::DI_ZI_ZH;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_ganada:
+ nRet = style::NumberingType::HANGUL_SYLLABLE_KO;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_chosung:
+ nRet = style::NumberingType::HANGUL_JAMO_KO;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_koreanLegal:
+ nRet = style::NumberingType::NUMBER_LEGAL_KO;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_koreanDigital:
+ nRet = style::NumberingType::NUMBER_DIGITAL_KO;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_koreanCounting:
+ nRet = style::NumberingType::NUMBER_HANGUL_KO;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_koreanDigital2:
+ nRet = style::NumberingType::NUMBER_DIGITAL2_KO;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_ideographLegalTraditional:
+ nRet = style::NumberingType::NUMBER_UPPER_ZH_TW;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_arabicAlpha:
+ nRet = style::NumberingType::CHARS_ARABIC;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_arabicAbjad:
+ nRet = style::NumberingType::CHARS_ARABIC_ABJAD;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_hindiVowels:
+ nRet = style::NumberingType::CHARS_NEPALI;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_japaneseLegal:
+ nRet = style::NumberingType::NUMBER_TRADITIONAL_JA;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_chineseCounting:
+ case NS_ooxml::LN_Value_ST_NumberFormat_japaneseCounting:
+ case NS_ooxml::LN_Value_ST_NumberFormat_taiwaneseCounting:
+ case NS_ooxml::LN_Value_ST_NumberFormat_taiwaneseCountingThousand:
+ case NS_ooxml::LN_Value_ST_NumberFormat_ideographDigital:
+ case NS_ooxml::LN_Value_ST_NumberFormat_chineseCountingThousand:
+ nRet = style::NumberingType::NUMBER_LOWER_ZH;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_chineseLegalSimplified:
+ nRet = style::NumberingType::NUMBER_UPPER_ZH;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_hebrew1:
+ //91726
+ nRet = style::NumberingType::NUMBER_HEBREW;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_decimalFullWidth:
+ case NS_ooxml::LN_Value_ST_NumberFormat_decimalFullWidth2:
+ nRet = style::NumberingType::FULLWIDTH_ARABIC;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_cardinalText:
+ nRet = style::NumberingType::TEXT_CARDINAL;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_ordinalText:
+ nRet = style::NumberingType::TEXT_ORDINAL;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_chicago:
+ nRet = style::NumberingType::SYMBOL_CHICAGO;
+ break;
+ case NS_ooxml::LN_Value_ST_NumberFormat_decimalZero:
+ nRet = style::NumberingType::ARABIC_ZERO;
+ break;
+ default: nRet = nDefault;
+ }
+/* TODO: Lots of additional values are available - some are supported in the I18 framework
+ NS_ooxml::LN_Value_ST_NumberFormat_hex = 91685;
+ NS_ooxml::LN_Value_ST_NumberFormat_decimalHalfWidth = 91692;
+ NS_ooxml::LN_Value_ST_NumberFormat_japaneseDigitalTenThousand = 91694;
+ NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedFullstop = 91703;
+ NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedParen = 91704;
+ NS_ooxml::LN_Value_ST_NumberFormat_ideographZodiacTraditional = 91709;
+ NS_ooxml::LN_Value_ST_NumberFormat_taiwaneseDigital = 91713;
+ NS_ooxml::LN_Value_ST_NumberFormat_vietnameseCounting = 91721;
+ NS_ooxml::LN_Value_ST_NumberFormat_numberInDash = 91725;
+ NS_ooxml::LN_Value_ST_NumberFormat_hindiConsonants = 91731;
+ NS_ooxml::LN_Value_ST_NumberFormat_hindiNumbers = 91732;
+ NS_ooxml::LN_Value_ST_NumberFormat_hindiCounting = 91733;
+ NS_ooxml::LN_Value_ST_NumberFormat_thaiNumbers = 91735;
+ NS_ooxml::LN_Value_ST_NumberFormat_thaiCounting = 91736;*/
+ return nRet;
+}
+
+sal_Int16 ConvertCustomNumberFormat(std::u16string_view rFormat)
+{
+ sal_Int16 nRet = -1;
+
+ if (rFormat == u"001, 002, 003, ...")
+ {
+ nRet = style::NumberingType::ARABIC_ZERO3;
+ }
+ else if (rFormat == u"0001, 0002, 0003, ...")
+ {
+ nRet = style::NumberingType::ARABIC_ZERO4;
+ }
+ else if (rFormat == u"00001, 00002, 00003, ...")
+ {
+ nRet = style::NumberingType::ARABIC_ZERO5;
+ }
+
+ return nRet;
+}
+
+util::DateTime ConvertDateStringToDateTime( std::u16string_view rDateTime )
+{
+ util::DateTime aDateTime;
+ //xsd::DateTime in the format [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm] example: 2008-01-21T10:42:00Z
+ //OUString getToken( sal_Int32 token, sal_Unicode cTok, sal_Int32& index ) const
+ sal_Int32 nIndex = 0;
+ std::u16string_view sDate = o3tl::getToken(rDateTime, 0, 'T', nIndex );
+ // HACK: this is broken according to the spec, but MSOffice always treats the time as local,
+ // and writes it as Z (=UTC+0)
+ std::u16string_view sTime = o3tl::getToken(rDateTime, 0, 'Z', nIndex );
+ nIndex = 0;
+ aDateTime.Year = sal_uInt16( o3tl::toInt32(o3tl::getToken(sDate, 0, '-', nIndex )) );
+ aDateTime.Month = sal_uInt16( o3tl::toInt32(o3tl::getToken(sDate, 0, '-', nIndex )) );
+ if (nIndex != -1)
+ aDateTime.Day = sal_uInt16( o3tl::toInt32(sDate.substr( nIndex )) );
+
+ nIndex = 0;
+ aDateTime.Hours = sal_uInt16( o3tl::toInt32(o3tl::getToken(sTime, 0, ':', nIndex )) );
+ aDateTime.Minutes = sal_uInt16( o3tl::toInt32(o3tl::getToken(sTime, 0, ':', nIndex )) );
+ if (nIndex != -1)
+ aDateTime.Seconds = sal_uInt16( o3tl::toInt32(sTime.substr( nIndex )) );
+
+ return aDateTime;
+}
+
+
+} //namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/ConversionHelper.hxx b/writerfilter/source/dmapper/ConversionHelper.hxx
new file mode 100644
index 000000000..c14c8033e
--- /dev/null
+++ b/writerfilter/source/dmapper/ConversionHelper.hxx
@@ -0,0 +1,60 @@
+/* -*- 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 <sal/types.h>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/text/RubyAdjust.hpp>
+
+namespace com::sun::star{
+ namespace lang{
+ struct Locale;
+ }
+ namespace table{
+ struct BorderLine2;
+ }
+}
+
+namespace writerfilter::dmapper::ConversionHelper{
+
+ // create a border line and return the distance value
+ void MakeBorderLine(sal_Int32 nLineThickness,
+ sal_Int32 nLineType,
+ sal_Int32 nLineColor,
+ css::table::BorderLine2& rToFill,
+ bool bIsOOXML);
+ //convert the number format string form MS format to SO format
+ OUString ConvertMSFormatStringToSO(const OUString& rFormat, css::lang::Locale& rLocale, bool bHijri);
+ // export just for test
+ SAL_DLLPUBLIC_EXPORT sal_Int32 convertTwipToMM100(sal_Int32 _t);
+ sal_Int32 convertTwipToMM100WithoutLimit(sal_Int32 _t);
+ double convertTwipToMM100Double(sal_Int32 _t);
+ SAL_DLLPUBLIC_EXPORT sal_uInt32 convertTwipToMM100Unsigned(sal_Int32 _t);
+ sal_Int16 convertTableJustification( sal_Int32 nIntValue );
+ css::text::RubyAdjust convertRubyAlign( sal_Int32 nIntValue );
+ sal_Int16 ConvertNumberingType(const sal_Int32 nFmt, const sal_Int16 nDefault = css::style::NumberingType::ARABIC);
+ sal_Int16 ConvertCustomNumberFormat(std::u16string_view rFormat);
+
+ css::util::DateTime ConvertDateStringToDateTime(std::u16string_view rDateTime);
+} // namespace writerfilter::dmapper::ConversionHelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DocumentProtection.cxx b/writerfilter/source/dmapper/DocumentProtection.cxx
new file mode 100644
index 000000000..dddf964c4
--- /dev/null
+++ b/writerfilter/source/dmapper/DocumentProtection.cxx
@@ -0,0 +1,239 @@
+/* -*- 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 "DocumentProtection.hxx"
+#include "TagLogger.hxx"
+#include <vector>
+#include <comphelper/sequence.hxx>
+
+using namespace com::sun::star;
+
+namespace writerfilter::dmapper
+{
+DocumentProtection::DocumentProtection()
+ : LoggedProperties("DocumentProtection")
+ , m_nEdit(
+ NS_ooxml::
+ LN_Value_doc_ST_DocProtect_none) // Specifies that no editing restrictions have been applied to the document
+ , m_bProtectForm(false)
+ , m_bRedlineProtection(false)
+ , m_bReadOnly(false)
+ , m_bEnforcement(false)
+ , m_bFormatting(false)
+ , m_nCryptProviderType(NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES)
+ , m_sCryptAlgorithmClass("hash")
+ , m_sCryptAlgorithmType("typeAny")
+ , m_CryptSpinCount(0)
+{
+}
+
+DocumentProtection::~DocumentProtection() {}
+
+void DocumentProtection::lcl_attribute(Id nName, Value& val)
+{
+ int nIntValue = val.getInt();
+ OUString sStringValue = val.getString();
+
+ switch (nName)
+ {
+ case NS_ooxml::LN_CT_DocProtect_edit: // 92037
+ m_nEdit = nIntValue;
+ // multiple DocProtect_edits should not exist. If they do, last one wins
+ m_bRedlineProtection = false;
+ m_bProtectForm = false;
+ m_bReadOnly = false;
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_doc_ST_DocProtect_trackedChanges:
+ {
+ m_bRedlineProtection = true;
+ m_sRedlineProtectionKey = m_sHash;
+ break;
+ }
+ case NS_ooxml::LN_Value_doc_ST_DocProtect_forms:
+ m_bProtectForm = true;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_DocProtect_readOnly:
+ m_bReadOnly = true;
+ break;
+ }
+ break;
+ case NS_ooxml::LN_CT_DocProtect_enforcement: // 92039
+ m_bEnforcement = (nIntValue != 0);
+ break;
+ case NS_ooxml::LN_CT_DocProtect_formatting: // 92038
+ m_bFormatting = (nIntValue != 0);
+ break;
+ case NS_ooxml::LN_AG_Password_cryptProviderType: // 92025
+ m_nCryptProviderType = nIntValue;
+ break;
+ case NS_ooxml::LN_AG_Password_cryptAlgorithmClass: // 92026
+ if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgClass_hash) // 92023
+ m_sCryptAlgorithmClass = "hash";
+ break;
+ case NS_ooxml::LN_AG_Password_cryptAlgorithmType: // 92027
+ if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgType_typeAny) // 92024
+ m_sCryptAlgorithmType = "typeAny";
+ break;
+ case NS_ooxml::LN_AG_Password_cryptAlgorithmSid: // 92028
+ m_sCryptAlgorithmSid = sStringValue;
+ break;
+ case NS_ooxml::LN_AG_Password_cryptSpinCount: // 92029
+ m_CryptSpinCount = nIntValue;
+ break;
+ case NS_ooxml::LN_AG_Password_hash: // 92035
+ m_sHash = sStringValue;
+ break;
+ case NS_ooxml::LN_AG_Password_salt: // 92036
+ m_sSalt = sStringValue;
+ break;
+ default:
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ }
+}
+
+void DocumentProtection::lcl_sprm(Sprm& /*rSprm*/) {}
+
+uno::Sequence<beans::PropertyValue> DocumentProtection::toSequence() const
+{
+ std::vector<beans::PropertyValue> documentProtection;
+
+ if (enabled())
+ {
+ // w:edit
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "edit";
+
+ switch (m_nEdit)
+ {
+ case NS_ooxml::LN_Value_doc_ST_DocProtect_none:
+ aValue.Value <<= OUString("none");
+ break;
+ case NS_ooxml::LN_Value_doc_ST_DocProtect_readOnly:
+ aValue.Value <<= OUString("readOnly");
+ break;
+ case NS_ooxml::LN_Value_doc_ST_DocProtect_comments:
+ aValue.Value <<= OUString("comments");
+ break;
+ case NS_ooxml::LN_Value_doc_ST_DocProtect_trackedChanges:
+ aValue.Value <<= OUString("trackedChanges");
+ break;
+ case NS_ooxml::LN_Value_doc_ST_DocProtect_forms:
+ aValue.Value <<= OUString("forms");
+ break;
+ default:
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ }
+
+ documentProtection.push_back(aValue);
+ }
+
+ // w:enforcement
+ if (m_bEnforcement)
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "enforcement";
+ aValue.Value <<= OUString("1");
+ documentProtection.push_back(aValue);
+ }
+
+ // w:formatting
+ if (m_bFormatting)
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "formatting";
+ aValue.Value <<= OUString("1");
+ documentProtection.push_back(aValue);
+ }
+
+ // w:cryptProviderType
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "cryptProviderType";
+ if (m_nCryptProviderType == NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES)
+ aValue.Value <<= OUString("rsaAES");
+ else if (m_nCryptProviderType == NS_ooxml::LN_Value_doc_ST_CryptProv_rsaFull)
+ aValue.Value <<= OUString("rsaFull");
+ documentProtection.push_back(aValue);
+ }
+
+ // w:cryptAlgorithmClass
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "cryptAlgorithmClass";
+ aValue.Value <<= m_sCryptAlgorithmClass;
+ documentProtection.push_back(aValue);
+ }
+
+ // w:cryptAlgorithmType
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "cryptAlgorithmType";
+ aValue.Value <<= m_sCryptAlgorithmType;
+ documentProtection.push_back(aValue);
+ }
+
+ // w:cryptAlgorithmSid
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "cryptAlgorithmSid";
+ aValue.Value <<= m_sCryptAlgorithmSid;
+ documentProtection.push_back(aValue);
+ }
+
+ // w:cryptSpinCount
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "cryptSpinCount";
+ aValue.Value <<= OUString::number(m_CryptSpinCount);
+ documentProtection.push_back(aValue);
+ }
+
+ // w:hash
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "hash";
+ aValue.Value <<= m_sHash;
+ documentProtection.push_back(aValue);
+ }
+
+ // w:salt
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "salt";
+ aValue.Value <<= m_sSalt;
+ documentProtection.push_back(aValue);
+ }
+ }
+
+ return comphelper::containerToSequence(documentProtection);
+}
+
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DocumentProtection.hxx b/writerfilter/source/dmapper/DocumentProtection.hxx
new file mode 100644
index 000000000..2ec0f3f21
--- /dev/null
+++ b/writerfilter/source/dmapper/DocumentProtection.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "LoggedResources.hxx"
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <ooxml/resourceids.hxx>
+
+namespace writerfilter::dmapper
+{
+/** Document protection restrictions
+ *
+ * This element specifies the set of document protection restrictions which have been applied to the contents of a
+ * WordprocessingML document.These restrictions should be enforced by applications editing this document
+ * when the enforcement attribute is turned on, and ignored(but persisted) otherwise.Document protection is a
+ * set of restrictions used to prevent unintentional changes to all or part of a WordprocessingML document.
+ */
+class DocumentProtection : public LoggedProperties
+{
+private:
+ /** Document Editing Restrictions
+ *
+ * Possible values:
+ * - NS_ooxml::LN_Value_doc_ST_DocProtect_none
+ * - NS_ooxml::LN_Value_doc_ST_DocProtect_readOnly
+ * - NS_ooxml::LN_Value_doc_ST_DocProtect_comments
+ * - NS_ooxml::LN_Value_doc_ST_DocProtect_trackedChanges
+ * - NS_ooxml::LN_Value_doc_ST_DocProtect_forms
+ */
+ sal_Int32 m_nEdit;
+ bool m_bProtectForm;
+ bool m_bRedlineProtection;
+ OUString m_sRedlineProtectionKey;
+ bool m_bReadOnly;
+ bool m_bEnforcement;
+ bool m_bFormatting;
+
+ /** Provider type
+ *
+ * Possible values:
+ * "rsaAES" - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES
+ * "rsaFull" - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaFull
+ */
+ sal_Int32 m_nCryptProviderType;
+ OUString m_sCryptAlgorithmClass;
+ OUString m_sCryptAlgorithmType;
+ OUString m_sCryptAlgorithmSid;
+ sal_Int32 m_CryptSpinCount;
+ OUString m_sHash;
+ OUString m_sSalt;
+
+ virtual void lcl_attribute(Id Name, Value& val) override;
+ virtual void lcl_sprm(Sprm& sprm) override;
+
+ bool enabled() const { return !isNone(); }
+ bool isNone() const { return m_nEdit == NS_ooxml::LN_Value_doc_ST_DocProtect_none; };
+
+public:
+ DocumentProtection();
+ virtual ~DocumentProtection() override;
+
+ css::uno::Sequence<css::beans::PropertyValue> toSequence() const;
+
+ bool getProtectForm() const { return m_bProtectForm; }
+ bool getRedlineProtection() const { return m_bRedlineProtection; }
+ bool getReadOnly() const { return m_bReadOnly; }
+ bool getEnforcement() const { return m_bEnforcement; }
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx
new file mode 100644
index 000000000..e4b507957
--- /dev/null
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -0,0 +1,4510 @@
+/* -*- 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 "BorderHandler.hxx"
+#include "PageBordersHandler.hxx"
+
+#include "util.hxx"
+#include "SdtHelper.hxx"
+#include "TagLogger.hxx"
+#include "TDefTableHandler.hxx"
+#include "DomainMapper_Impl.hxx"
+#include "ConversionHelper.hxx"
+#include "ModelEventListener.hxx"
+#include "MeasureHandler.hxx"
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/paper.hxx>
+#include <ooxml/resourceids.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XOOXMLDocumentPropertiesImporter.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/table/BorderLineStyle.hpp>
+#include <com/sun/star/table/ShadowFormat.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/XEndnotesSupplier.hpp>
+#include <com/sun/star/text/XFootnotesSupplier.hpp>
+#include <com/sun/star/text/XLineNumberingProperties.hpp>
+#include <com/sun/star/awt/FontRelief.hpp>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/document/XEventBroadcaster.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/style/BreakType.hpp>
+#include <com/sun/star/style/CaseMap.hpp>
+#include <com/sun/star/style/LineSpacing.hpp>
+#include <com/sun/star/style/LineSpacingMode.hpp>
+#include <com/sun/star/text/FootnoteNumbering.hpp>
+#include <com/sun/star/text/TextGridMode.hpp>
+#include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
+#include <com/sun/star/text/XTextFieldsSupplier.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/text/XFootnote.hpp>
+#include <com/sun/star/text/XTextColumns.hpp>
+#include <com/sun/star/text/RubyPosition.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/text/FontEmphasis.hpp>
+#include <com/sun/star/awt/CharSet.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <comphelper/types.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <editeng/escapementitem.hxx>
+#include <filter/msfilter/util.hxx>
+#include <sfx2/DocumentMetadataAccess.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/mediadescriptor.hxx>
+
+#include "TextEffectsHandler.hxx"
+#include "CellColorHandler.hxx"
+#include "SectionColumnHandler.hxx"
+#include "GraphicHelpers.hxx"
+#include <dmapper/GraphicZOrderHelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <tools/UnitConversion.hxx>
+
+using namespace ::com::sun::star;
+using namespace oox;
+
+namespace writerfilter::dmapper{
+
+struct
+{
+ sal_Int32 h;
+ bool orient;
+ sal_Int32 w;
+} CT_PageSz;
+
+
+DomainMapper::DomainMapper( const uno::Reference< uno::XComponentContext >& xContext,
+ uno::Reference<io::XInputStream> const& xInputStream,
+ uno::Reference<lang::XComponent> const& xModel,
+ bool bRepairStorage,
+ SourceDocumentType eDocumentType,
+ utl::MediaDescriptor const & rMediaDesc) :
+ LoggedProperties("DomainMapper"),
+ LoggedTable("DomainMapper"),
+ LoggedStream("DomainMapper"),
+ m_pImpl(new DomainMapper_Impl(*this, xContext, xModel, eDocumentType, rMediaDesc)),
+ mbIsSplitPara(false),
+ mbHasControls(false),
+ mbWasShapeInPara(false)
+{
+ // #i24363# tab stops relative to indent
+ m_pImpl->SetDocumentSettingsProperty(
+ getPropertyName( PROP_TABS_RELATIVE_TO_INDENT ),
+ uno::Any( false ) );
+ m_pImpl->SetDocumentSettingsProperty(
+ getPropertyName( PROP_SURROUND_TEXT_WRAP_SMALL ),
+ uno::Any( true ) );
+ m_pImpl->SetDocumentSettingsProperty(
+ getPropertyName( PROP_APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING ),
+ uno::Any( true ) );
+
+ // Don't load the default style definitions to avoid weird mix
+ m_pImpl->SetDocumentSettingsProperty("StylesNoDefault", uno::Any(true));
+ m_pImpl->SetDocumentSettingsProperty("MsWordCompTrailingBlanks", uno::Any(true));
+ m_pImpl->SetDocumentSettingsProperty("HeaderSpacingBelowLastPara",
+ uno::Any(true));
+ m_pImpl->SetDocumentSettingsProperty("FrameAutowidthWithMorePara", uno::Any(true));
+ m_pImpl->SetDocumentSettingsProperty("FootnoteInColumnToPageEnd", uno::Any(true));
+ m_pImpl->SetDocumentSettingsProperty("TabAtLeftIndentForParagraphsInList", uno::Any(true));
+ m_pImpl->SetDocumentSettingsProperty("NoNumberingShowFollowBy", uno::Any(true));
+
+ // Initialize RDF metadata, to be able to add statements during the import.
+ try
+ {
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY_THROW);
+ uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
+ OUString aBaseURL = rMediaDesc.getUnpackedValueOrDefault("URL", OUString());
+ const uno::Reference<frame::XModel> xModel_(xModel,
+ uno::UNO_QUERY_THROW);
+ const uno::Reference<rdf::XURI> xBaseURI(sfx2::createBaseURI(xContext, xModel_, aBaseURL, u""));
+ const uno::Reference<task::XInteractionHandler> xHandler;
+ xDocumentMetadataAccess->loadMetadataFromStorage(xStorage, xBaseURI, xHandler);
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "failed to initialize RDF metadata");
+ }
+
+ if (eDocumentType == SourceDocumentType::OOXML) {
+ // tdf#108350
+ // In Word since version 2007, the default document font is Calibri 11 pt.
+ // If a DOCX document doesn't contain font information, we should assume
+ // the intended font to provide best layout match.
+ try
+ {
+ uno::Reference< beans::XPropertySet > xDefProps(GetTextFactory()->createInstance("com.sun.star.text.Defaults"),
+ uno::UNO_QUERY_THROW);
+ xDefProps->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME), css::uno::Any(OUString("Calibri")));
+ xDefProps->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT), css::uno::Any(double(11)));
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "failed to initialize default font");
+ }
+ }
+
+ //import document properties
+ try
+ {
+ m_pImpl->m_xDocumentStorage = comphelper::OStorageHelper::GetStorageOfFormatFromInputStream(
+ OFOPXML_STORAGE_FORMAT_STRING, xInputStream, xContext, bRepairStorage);
+
+ uno::Reference< uno::XInterface > xTemp = xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.document.OOXMLDocumentPropertiesImporter",
+ xContext);
+
+ uno::Reference< document::XOOXMLDocumentPropertiesImporter > xImporter( xTemp, uno::UNO_QUERY_THROW );
+ uno::Reference< document::XDocumentPropertiesSupplier > xPropSupplier( xModel, uno::UNO_QUERY_THROW);
+ xImporter->importProperties(m_pImpl->m_xDocumentStorage,
+ xPropSupplier->getDocumentProperties());
+ }
+ catch( const uno::Exception& ) {}
+}
+
+void DomainMapper::setDocumentReference(writerfilter::ooxml::OOXMLDocument* pDocument)
+{
+ m_pImpl->setDocumentReference(pDocument);
+}
+
+DomainMapper::~DomainMapper()
+{
+ try
+ {
+ // Remove temporary footnotes and endnotes
+ m_pImpl->RemoveTemporaryFootOrEndnotes();
+
+ uno::Reference< text::XDocumentIndexesSupplier> xIndexesSupplier( m_pImpl->GetTextDocument(), uno::UNO_QUERY );
+ sal_Int32 nIndexes = 0;
+ if( xIndexesSupplier.is() )
+ {
+ uno::Reference< container::XIndexAccess > xIndexes = xIndexesSupplier->getDocumentIndexes();
+ nIndexes = xIndexes->getCount();
+ }
+ // If we have page references, those need updating as well, similar to the indexes.
+ uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(m_pImpl->GetTextDocument(), uno::UNO_QUERY);
+ if(xTextFieldsSupplier.is())
+ {
+ uno::Reference<container::XEnumeration> xEnumeration = xTextFieldsSupplier->getTextFields()->createEnumeration();
+ while(xEnumeration->hasMoreElements())
+ {
+ ++nIndexes;
+ xEnumeration->nextElement();
+ }
+ }
+
+ mbHasControls |= m_pImpl->m_pSdtHelper->hasElements();
+ if ( nIndexes || mbHasControls )
+ {
+ //index update has to wait until first view is created
+ uno::Reference< document::XEventBroadcaster > xBroadcaster(xIndexesSupplier, uno::UNO_QUERY);
+ if (xBroadcaster.is())
+ xBroadcaster->addEventListener(uno::Reference< document::XEventListener >(new ModelEventListener(nIndexes, mbHasControls)));
+ }
+
+
+ // Apply the document settings for both DOCX and RTF after everything else
+ m_pImpl->GetSettingsTable()->ApplyProperties( m_pImpl->GetTextDocument( ) );
+
+ // now that importing is finished, re-enable default styles for any that were never defined/imported.
+ m_pImpl->SetDocumentSettingsProperty("StylesNoDefault", uno::Any(false));
+
+ // Grab-bag handling
+ comphelper::SequenceAsHashMap aProperties;
+
+ // Add the saved w:themeFontLang setting
+ aProperties["ThemeFontLangProps"] <<= m_pImpl->GetSettingsTable()->GetThemeFontLangProperties();
+
+ // Add the saved compat settings
+ aProperties["CompatSettings"] <<= m_pImpl->GetSettingsTable()->GetCompatSettings();
+
+ // Add the saved DocumentProtection settings
+ aProperties["DocumentProtection"] <<= m_pImpl->GetSettingsTable()->GetDocumentProtectionSettings();
+
+ // Add the saved w:doNotHyphenateCaps setting
+ aProperties["NoHyphenateCaps"] <<= m_pImpl->GetSettingsTable()->GetNoHyphenateCaps();
+
+ uno::Reference<beans::XPropertySet> xDocProps(m_pImpl->GetTextDocument(), uno::UNO_QUERY);
+ if (xDocProps.is())
+ {
+ comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
+ aGrabBag.update(aProperties);
+ xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
+ }
+
+ // tdf#138782: for docs created in MS Word 2010 and older (compatibilityMode <= 14)
+ m_pImpl->SetDocumentSettingsProperty(
+ "AddFrameOffsets",
+ uno::Any(14 >= m_pImpl->GetSettingsTable()->GetWordCompatibilityMode()));
+ }
+ catch( const uno::Exception& ) {}
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endDocument();
+#endif
+}
+
+void DomainMapper::lcl_attribute(Id nName, Value & val)
+{
+ if (m_pImpl->hasTableManager() && m_pImpl->getTableManager().attribute(nName, val))
+ return;
+
+ static const int nSingleLineSpacing = 240;
+ sal_Int32 nIntValue = val.getInt();
+ OUString sStringValue = val.getString();
+
+ SectionPropertyMap * pSectionContext = m_pImpl->GetSectionContext();
+ switch( nName )
+ {
+ case NS_ooxml::LN_CT_Lvl_start:
+ break;
+ case NS_ooxml::LN_CT_Lvl_numFmt:
+ break;
+ case NS_ooxml::LN_CT_Lvl_isLgl:
+ break;
+ case NS_ooxml::LN_CT_Lvl_legacy:
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_nsid:
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_tmpl:
+ break;
+ case NS_ooxml::LN_CT_Border_sz:
+ break;
+ case NS_ooxml::LN_CT_Border_val:
+ break;
+ case NS_ooxml::LN_CT_Border_space:
+ break;
+ case NS_ooxml::LN_CT_Border_shadow:
+ break;
+ case NS_ooxml::LN_CT_Border_frame:
+ break;
+ case NS_ooxml::LN_headerr:
+ break;
+ case NS_ooxml::LN_footerr:
+ break;
+ case NS_ooxml::LN_endnote:
+ break;
+ case NS_ooxml::LN_CT_Bookmark_name:
+ m_pImpl->SetBookmarkName( sStringValue );
+ break;
+ case NS_ooxml::LN_CT_MarkupRangeBookmark_id:
+ // add a bookmark range -- this remembers a bookmark starting here
+ // or, if the bookmark was already started or, if the bookmark was
+ // already started before, writes out the bookmark
+ m_pImpl->StartOrEndBookmark( sStringValue );
+ break;
+ case NS_ooxml::LN_CT_MarkupRange_displacedByCustomXml:
+ break;
+ case NS_ooxml::LN_NUMBERING:
+ break;
+ case NS_ooxml::LN_FONTTABLE:
+ break;
+ case NS_ooxml::LN_STYLESHEET:
+ break;
+
+ case NS_ooxml::LN_CT_Sym_char:
+ m_pImpl->SetSymbolChar(nIntValue);
+ break;
+ case NS_ooxml::LN_CT_Sym_font:
+ m_pImpl->SetSymbolFont(sStringValue);
+ break;
+ case NS_ooxml::LN_CT_Underline_val:
+ if (m_pImpl->GetTopContext())
+ handleUnderlineType(nIntValue, m_pImpl->GetTopContext());
+ break;
+ case NS_ooxml::LN_CT_Color_val:
+ if (m_pImpl->GetTopContext())
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_COLOR, uno::Any( nIntValue ) );
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "val", msfilter::util::ConvertColorOU(Color(ColorTransparency,nIntValue)));
+ break;
+ case NS_ooxml::LN_CT_Underline_color:
+ if (m_pImpl->GetTopContext())
+ {
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_UNDERLINE_HAS_COLOR, uno::Any( true ) );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_UNDERLINE_COLOR, uno::Any( nIntValue ) );
+ }
+ break;
+
+ case NS_ooxml::LN_CT_TabStop_val:
+ if (sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_TabJc_clear)
+ {
+ m_pImpl->m_aCurrentTabStop.bDeleted = true;
+ }
+ else
+ {
+ m_pImpl->m_aCurrentTabStop.bDeleted = false;
+ m_pImpl->m_aCurrentTabStop.Alignment = getTabAlignFromValue(nIntValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_TabStop_leader:
+ m_pImpl->m_aCurrentTabStop.FillChar = getFillCharFromValue(nIntValue);
+ break;
+ case NS_ooxml::LN_CT_TabStop_pos:
+ m_pImpl->m_aCurrentTabStop.Position = ConversionHelper::convertTwipToMM100(nIntValue);
+ break;
+
+ case NS_ooxml::LN_CT_Fonts_ascii:
+ if (m_pImpl->GetTopContext())
+ {
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_FONT_NAME, uno::Any( sStringValue ));
+ }
+ break;
+ case NS_ooxml::LN_CT_Fonts_asciiTheme:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "asciiTheme", ThemeTable::getStringForTheme(nIntValue));
+ if (m_pImpl->GetTopContext())
+ {
+ // note: overwrite Fonts_ascii with Fonts_asciiTheme *even if*
+ // theme font is empty - this is apparently what Word 2013 does
+ uno::Any aPropValue( m_pImpl->GetThemeTable()->getFontNameForTheme( nIntValue ) );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_FONT_NAME, aPropValue );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_FONT_NAME_ASCII, aPropValue, true, CHAR_GRAB_BAG );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_NAME_ASCII, uno::Any( ThemeTable::getStringForTheme(nIntValue) ), true, CHAR_GRAB_BAG);
+ }
+ break;
+ case NS_ooxml::LN_CT_Fonts_hAnsi:
+ break;//unsupported
+ case NS_ooxml::LN_CT_Fonts_hAnsiTheme:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "hAnsiTheme", ThemeTable::getStringForTheme(nIntValue));
+ if (m_pImpl->GetTopContext())
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_NAME_H_ANSI, uno::Any( ThemeTable::getStringForTheme(nIntValue) ), true, CHAR_GRAB_BAG);
+ break;
+ case NS_ooxml::LN_CT_Fonts_eastAsia:
+ if (m_pImpl->GetTopContext())
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_FONT_NAME_ASIAN, uno::Any( sStringValue ));
+ break;
+ case NS_ooxml::LN_CT_Fonts_eastAsiaTheme:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "eastAsiaTheme", ThemeTable::getStringForTheme(nIntValue));
+ if (m_pImpl->GetTopContext())
+ {
+ uno::Any aPropValue( m_pImpl->GetThemeTable()->getFontNameForTheme( nIntValue ) );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_FONT_NAME_ASIAN, aPropValue );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_FONT_NAME_EAST_ASIA, aPropValue, true, CHAR_GRAB_BAG );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_NAME_EAST_ASIA, uno::Any( ThemeTable::getStringForTheme(nIntValue) ), true, CHAR_GRAB_BAG);
+ }
+ break;
+ case NS_ooxml::LN_CT_Fonts_cs:
+ if (m_pImpl->GetTopContext())
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_FONT_NAME_COMPLEX, uno::Any( sStringValue ));
+ break;
+ case NS_ooxml::LN_CT_Fonts_cstheme:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "cstheme", ThemeTable::getStringForTheme(nIntValue));
+ if (m_pImpl->GetTopContext())
+ {
+ uno::Any aPropValue( m_pImpl->GetThemeTable()->getFontNameForTheme( nIntValue ) );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_FONT_NAME_COMPLEX, aPropValue );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_FONT_NAME_CS, aPropValue, true, CHAR_GRAB_BAG );
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_NAME_CS, uno::Any( ThemeTable::getStringForTheme(nIntValue) ), true, CHAR_GRAB_BAG);
+ }
+ break;
+ case NS_ooxml::LN_CT_Spacing_before:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "before", OUString::number(nIntValue));
+ if (m_pImpl->GetTopContext())
+ // Don't overwrite NS_ooxml::LN_CT_Spacing_beforeAutospacing.
+ m_pImpl->GetTopContext()->Insert(
+ PROP_PARA_TOP_MARGIN,
+ uno::Any(static_cast<sal_Int32>(convertTwipToMm100(nIntValue))), false);
+ break;
+ case NS_ooxml::LN_CT_Spacing_beforeLines:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "beforeLines", OUString::number(nIntValue));
+ // We would need to make sure that this doesn't overwrite any
+ // NS_ooxml::LN_CT_Spacing_before in parent styles before style
+ // sheet support can be enabled.
+ if (m_pImpl->GetTopContext() && !IsStyleSheetImport())
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_TOP_MARGIN, uno::Any(ConversionHelper::convertTwipToMM100(nIntValue * nSingleLineSpacing / 100)), false);
+ break;
+ case NS_ooxml::LN_CT_Spacing_after:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "after", OUString::number(nIntValue));
+ if (m_pImpl->GetTopContext())
+ {
+ // Don't overwrite NS_ooxml::LN_CT_Spacing_afterAutospacing.
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_BOTTOM_MARGIN, uno::Any( ConversionHelper::convertTwipToMM100( nIntValue ) ), false);
+
+ uno::Any aContextualSpacingFromStyle = m_pImpl->GetPropertyFromParaStyleSheet(PROP_PARA_CONTEXT_MARGIN);
+ if (aContextualSpacingFromStyle.hasValue())
+ // Setting "after" spacing means Writer doesn't inherit
+ // contextual spacing anymore from style, but Word does.
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_CONTEXT_MARGIN, aContextualSpacingFromStyle);
+ }
+ break;
+ case NS_ooxml::LN_CT_Spacing_afterLines:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "afterLines", OUString::number(nIntValue));
+ // We would need to make sure that this doesn't overwrite any
+ // NS_ooxml::LN_CT_Spacing_after in parent styles before style
+ // sheet support can be enabled.
+ if (m_pImpl->GetTopContext() && !IsStyleSheetImport())
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_BOTTOM_MARGIN, uno::Any(ConversionHelper::convertTwipToMM100(nIntValue * nSingleLineSpacing / 100)), false);
+ break;
+ case NS_ooxml::LN_CT_Spacing_line: //91434
+ case NS_ooxml::LN_CT_Spacing_lineRule: //91435
+ {
+ style::LineSpacing aSpacing;
+ PropertyMapPtr pTopContext = m_pImpl->GetTopContext();
+ std::optional<PropertyMap::Property> aLineSpacingVal;
+ if (pTopContext && (aLineSpacingVal = pTopContext->getProperty(PROP_PARA_LINE_SPACING)) )
+ {
+ aLineSpacingVal->second >>= aSpacing;
+ }
+ else
+ {
+ //default to single line spacing
+ aSpacing.Mode = style::LineSpacingMode::FIX;
+ aSpacing.Height = sal_Int16(ConversionHelper::convertTwipToMM100( nSingleLineSpacing ));
+ }
+ if( nName == NS_ooxml::LN_CT_Spacing_line )
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "line", OUString::number(nIntValue));
+ //now set the value depending on the Mode
+ if( aSpacing.Mode == style::LineSpacingMode::PROP )
+ aSpacing.Height = sal_Int16(nIntValue * 100 / nSingleLineSpacing );
+ else
+ aSpacing.Height = sal_Int16(ConversionHelper::convertTwipToMM100( nIntValue ));
+ }
+ else //NS_ooxml::LN_CT_Spacing_lineRule:
+ {
+ // exactly, atLeast, auto
+ if( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto)
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "lineRule", "auto");
+ if (aSpacing.Height >= 0)
+ {
+ aSpacing.Mode = style::LineSpacingMode::PROP;
+ //reinterpret the already set value
+ aSpacing.Height = sal_Int16( aSpacing.Height * 100 / ConversionHelper::convertTwipToMM100( nSingleLineSpacing ));
+ }
+ else
+ {
+ // Negative value still means a positive height,
+ // just the mode is "exact".
+ aSpacing.Mode = style::LineSpacingMode::FIX;
+ aSpacing.Height *= -1;
+ }
+ }
+ else if( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_LineSpacingRule_atLeast)
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "lineRule", "atLeast");
+ aSpacing.Mode = style::LineSpacingMode::MINIMUM;
+ }
+ else // NS_ooxml::LN_Value_doc_ST_LineSpacingRule_exact
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "lineRule", "exact");
+ aSpacing.Mode = style::LineSpacingMode::FIX;
+ }
+ }
+ if (pTopContext)
+ pTopContext->Insert(PROP_PARA_LINE_SPACING, uno::Any( aSpacing ));
+ }
+ break;
+ case NS_ooxml::LN_CT_Ind_start:
+ case NS_ooxml::LN_CT_Ind_left:
+ if (m_pImpl->GetTopContext())
+ {
+ // Word inherits FirstLineIndent property of the numbering, even if ParaLeftMargin is set, Writer does not.
+ // So copy it explicitly, if necessary.
+ sal_Int32 nFirstLineIndent = m_pImpl->getCurrentNumberingProperty("FirstLineIndent");
+ sal_Int32 nIndentAt = m_pImpl->getCurrentNumberingProperty("IndentAt");
+
+ sal_Int32 nParaLeftMargin = ConversionHelper::convertTwipToMM100(nIntValue);
+ if (nParaLeftMargin != 0 && nIndentAt == nParaLeftMargin)
+ // Avoid direct left margin when it's the same as from the
+ // numbering.
+ break;
+
+ if (nFirstLineIndent != 0)
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::Any(nFirstLineIndent), /*bOverwrite=*/false);
+
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_LEFT_MARGIN,
+ uno::Any(nParaLeftMargin));
+ }
+ break;
+ case NS_ooxml::LN_CT_Ind_end:
+ case NS_ooxml::LN_CT_Ind_right:
+ if (m_pImpl->GetTopContext())
+ {
+ // Word inherits FirstLineIndent/ParaLeftMargin property of the numbering, even if ParaRightMargin is set, Writer does not.
+ // So copy it explicitly, if necessary.
+ sal_Int32 nFirstLineIndent = m_pImpl->getCurrentNumberingProperty("FirstLineIndent");
+ sal_Int32 nParaLeftMargin = m_pImpl->getCurrentNumberingProperty("IndentAt");
+
+ if (nFirstLineIndent != 0)
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::Any(nFirstLineIndent), /*bOverwrite=*/false);
+ if (nParaLeftMargin != 0)
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_LEFT_MARGIN, uno::Any(nParaLeftMargin), /*bOverwrite=*/false);
+
+ m_pImpl->GetTopContext()->Insert(
+ PROP_PARA_RIGHT_MARGIN, uno::Any( ConversionHelper::convertTwipToMM100(nIntValue ) ));
+ }
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "right", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Ind_hanging:
+ if (m_pImpl->GetTopContext())
+ {
+ sal_Int32 nValue = ConversionHelper::convertTwipToMM100( nIntValue );
+ m_pImpl->GetTopContext()->Insert(
+ PROP_PARA_FIRST_LINE_INDENT, uno::Any( - nValue ));
+
+ // See above, need to inherit left margin from list style when first is set.
+ sal_Int32 nParaLeftMargin = m_pImpl->getCurrentNumberingProperty("IndentAt");
+ if (nParaLeftMargin != 0)
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_LEFT_MARGIN, uno::Any(nParaLeftMargin), /*bOverwrite=*/false);
+ }
+ break;
+ case NS_ooxml::LN_CT_Ind_firstLine:
+ if (m_pImpl->GetTopContext())
+ {
+ sal_Int32 nFirstLineIndent
+ = m_pImpl->getCurrentNumberingProperty("FirstLineIndent");
+ sal_Int32 nParaFirstLineIndent = ConversionHelper::convertTwipToMM100(nIntValue);
+ if (nParaFirstLineIndent != 0 && nFirstLineIndent == nParaFirstLineIndent)
+ // Avoid direct first margin when it's the same as from the
+ // numbering.
+ break;
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_FIRST_LINE_INDENT,
+ uno::Any(nParaFirstLineIndent));
+ }
+ break;
+ case NS_ooxml::LN_CT_Ind_rightChars:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "rightChars", OUString::number(nIntValue));
+ break;
+
+ case NS_ooxml::LN_CT_EastAsianLayout_id:
+ break;
+ case NS_ooxml::LN_CT_EastAsianLayout_combine:
+ if (m_pImpl->GetTopContext())
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_COMBINE_IS_ON, uno::Any ( nIntValue != 0 ));
+ break;
+ case NS_ooxml::LN_CT_EastAsianLayout_combineBrackets:
+ if (m_pImpl->GetTopContext())
+ {
+ OUString sCombinePrefix = getBracketStringFromEnum(nIntValue);
+ OUString sCombineSuffix = getBracketStringFromEnum(nIntValue, false);
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_COMBINE_PREFIX, uno::Any ( sCombinePrefix ));
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_COMBINE_SUFFIX, uno::Any ( sCombineSuffix ));
+ }
+ break;
+ case NS_ooxml::LN_CT_EastAsianLayout_vert:
+ if (m_pImpl->GetTopContext())
+ {
+ sal_Int16 nRotationAngle = (nIntValue ? 900 : 0);
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_ROTATION, uno::Any ( nRotationAngle ));
+ }
+ break;
+ case NS_ooxml::LN_CT_EastAsianLayout_vertCompress:
+ if (m_pImpl->GetTopContext())
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_ROTATION_IS_FIT_TO_LINE, uno::Any ( nIntValue != 0 ));
+ break;
+
+ case NS_ooxml::LN_CT_PageSz_code:
+ break;
+ case NS_ooxml::LN_CT_PageSz_h:
+ {
+ sal_Int32 nHeight = ConversionHelper::convertTwipToMM100WithoutLimit(nIntValue);
+ CT_PageSz.h = PaperInfo::sloppyFitPageDimension(nHeight);
+ }
+ break;
+ case NS_ooxml::LN_CT_PageSz_orient:
+ CT_PageSz.orient = (nIntValue != NS_ooxml::LN_Value_ST_PageOrientation_portrait);
+ break;
+ case NS_ooxml::LN_CT_PageSz_w:
+ {
+ sal_Int32 nWidth = ConversionHelper::convertTwipToMM100WithoutLimit(nIntValue);
+ CT_PageSz.w = PaperInfo::sloppyFitPageDimension(nWidth);
+ }
+ break;
+
+ case NS_ooxml::LN_CT_PageMar_top:
+ m_pImpl->SetPageMarginTwip( PAGE_MAR_TOP, nIntValue );
+ break;
+ case NS_ooxml::LN_CT_PageMar_right:
+ m_pImpl->SetPageMarginTwip( PAGE_MAR_RIGHT, nIntValue );
+ break;
+ case NS_ooxml::LN_CT_PageMar_bottom:
+ m_pImpl->SetPageMarginTwip( PAGE_MAR_BOTTOM, nIntValue );
+ break;
+ case NS_ooxml::LN_CT_PageMar_left:
+ m_pImpl->SetPageMarginTwip( PAGE_MAR_LEFT, nIntValue );
+ break;
+ case NS_ooxml::LN_CT_PageMar_header:
+ m_pImpl->SetPageMarginTwip( PAGE_MAR_HEADER, nIntValue );
+ break;
+ case NS_ooxml::LN_CT_PageMar_footer:
+ m_pImpl->SetPageMarginTwip( PAGE_MAR_FOOTER, nIntValue );
+ break;
+ case NS_ooxml::LN_CT_PageMar_gutter:
+ m_pImpl->SetPageMarginTwip( PAGE_MAR_GUTTER, nIntValue );
+ break;
+ case NS_ooxml::LN_CT_Language_val: //90314
+ case NS_ooxml::LN_CT_Language_eastAsia: //90315
+ case NS_ooxml::LN_CT_Language_bidi: //90316
+ {
+ // store decimal symbol associated to the language of the document
+ if ( m_pImpl->IsDocDefaultsImport() && ( nName == NS_ooxml::LN_CT_Language_val ) )
+ {
+ LanguageTag aLanguageTag( sStringValue );
+ LocaleDataWrapper aLocaleWrapper( std::move(aLanguageTag) );
+ if ( aLocaleWrapper.getNumDecimalSep() == "," )
+ m_pImpl->SetIsDecimalComma();
+
+ }
+ if (nName == NS_ooxml::LN_CT_Language_eastAsia)
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "eastAsia", sStringValue);
+ else if (nName == NS_ooxml::LN_CT_Language_val)
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "val", sStringValue);
+ else if (nName == NS_ooxml::LN_CT_Language_bidi)
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "bidi", sStringValue);
+ lang::Locale aLocale;
+ if (sStringValue.getLength() <= 3 && sStringValue.getLength() >= 1)
+ {
+ // Cheesy Google Docs is known to tag language-only even for
+ // "en" or others that need some region to distinguish language
+ // variants for spell-checker and hyphenation. Obtain our known
+ // fallback to clarify and match. The original value/context is
+ // unknown anyway.
+ LanguageTag aLanguageTag( sStringValue);
+ aLanguageTag.makeFallback();
+ if (aLanguageTag.getLanguage() == sStringValue)
+ aLocale = aLanguageTag.getLocale();
+ else
+ {
+ // Do not fallback for an unknown language, which usually
+ // results in "en-US", or any other non-matching case.
+ aLocale = LanguageTag::convertToLocale( sStringValue);
+ }
+ }
+ else
+ {
+ aLocale = LanguageTag::convertToLocale( sStringValue);
+ }
+ if (m_pImpl->GetTopContext())
+ m_pImpl->GetTopContext()->Insert(NS_ooxml::LN_CT_Language_val== nName ? PROP_CHAR_LOCALE :
+ NS_ooxml::LN_CT_Language_eastAsia == nName ? PROP_CHAR_LOCALE_ASIAN : PROP_CHAR_LOCALE_COMPLEX,
+ uno::Any( aLocale ) );
+ }
+ break;
+ // See SwWW8ImplReader::GetParagraphAutoSpace() on why these are 100 and 280
+ case NS_ooxml::LN_CT_Spacing_beforeAutospacing:
+ {
+ sal_Int32 default_spacing = -1;
+ if (nIntValue)
+ {
+ m_pImpl->SetParaAutoBefore(true);
+
+ default_spacing = 100;
+ if (!m_pImpl->GetSettingsTable()->GetDoNotUseHTMLParagraphAutoSpacing())
+ {
+ // 49 is just the old value that should be removed, once the
+ // root cause in SwTabFrm::MakeAll() is fixed.
+ if (m_pImpl->GetSettingsTable()->GetView() == NS_ooxml::LN_Value_doc_ST_View_web)
+ default_spacing = 49;
+ else
+ default_spacing = 280;
+ }
+ // required at export (here mainly for StyleSheets) to determine if the setting has changed from grab_bag
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_TOP_MARGIN, uno::Any(ConversionHelper::convertTwipToMM100(default_spacing)));
+ }
+ m_pImpl->GetTopContext()->Insert( PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING, uno::Any( ConversionHelper::convertTwipToMM100(default_spacing) ),true, PARA_GRAB_BAG );
+ }
+ break;
+ case NS_ooxml::LN_CT_Spacing_afterAutospacing:
+ {
+ sal_Int32 default_spacing = -1;
+ if (nIntValue)
+ {
+ default_spacing = 100;
+ if (!m_pImpl->GetSettingsTable()->GetDoNotUseHTMLParagraphAutoSpacing())
+ {
+ if (m_pImpl->GetSettingsTable()->GetView() == NS_ooxml::LN_Value_doc_ST_View_web)
+ default_spacing = 49;
+ else
+ default_spacing = 280;
+ }
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_BOTTOM_MARGIN, uno::Any(ConversionHelper::convertTwipToMM100(default_spacing)));
+ }
+ m_pImpl->GetTopContext()->Insert( PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING, uno::Any( ConversionHelper::convertTwipToMM100(default_spacing) ),true, PARA_GRAB_BAG );
+ }
+ break;
+ case NS_ooxml::LN_CT_SmartTagRun_uri:
+ m_pImpl->getSmartTagHandler().setURI(val.getString());
+ break;
+ case NS_ooxml::LN_CT_SmartTagRun_element:
+ m_pImpl->getSmartTagHandler().setElement(val.getString());
+ break;
+ case NS_ooxml::LN_CT_Br_type:
+ // Handled in the OOXMLBreakHandler dtor.
+ break;
+ case NS_ooxml::LN_CT_Br_clear:
+ m_pImpl->HandleLineBreakClear(val.getInt());
+ break;
+ case NS_ooxml::LN_CT_Fonts_hint :
+ /* assigns script type to ambiguous characters, values can be:
+ NS_ooxml::LN_Value_ST_Hint_default
+ NS_ooxml::LN_Value_ST_Hint_eastAsia
+ NS_ooxml::LN_Value_ST_Hint_cs
+ */
+ //TODO: unsupported?
+ break;
+ case NS_ooxml::LN_CT_TblBorders_right:
+ case NS_ooxml::LN_CT_TblBorders_top:
+ case NS_ooxml::LN_CT_TblBorders_left:
+ case NS_ooxml::LN_CT_TblBorders_bottom:
+ //todo: handle cell mar
+ break;
+ case NS_ooxml::LN_blip: // contains the binary graphic
+ case NS_ooxml::LN_shape:
+ {
+ //looks a bit like a hack - and it is. The graphic import is split into the inline_inline part and
+ //afterwards the adding of the binary data.
+ m_pImpl->GetGraphicImport( IMPORT_AS_DETECTED_INLINE )->attribute(nName, val);
+ m_pImpl->ImportGraphic( val.getProperties(), IMPORT_AS_DETECTED_INLINE );
+ }
+ break;
+ case NS_ooxml::LN_Value_math_ST_Jc_centerGroup:
+ case NS_ooxml::LN_Value_math_ST_Jc_center:
+ m_pImpl->appendStarMath(val);
+ m_pImpl->adjustLastPara(sal_Int8(style::ParagraphAdjust::ParagraphAdjust_CENTER));
+ break;
+ case NS_ooxml::LN_Value_math_ST_Jc_left:
+ m_pImpl->appendStarMath(val);
+ m_pImpl->adjustLastPara(sal_Int8(style::ParagraphAdjust::ParagraphAdjust_LEFT));
+ break;
+ case NS_ooxml::LN_Value_math_ST_Jc_right:
+ m_pImpl->appendStarMath(val);
+ m_pImpl->adjustLastPara(sal_Int8(style::ParagraphAdjust::ParagraphAdjust_RIGHT));
+ break;
+ case NS_ooxml::LN_starmath:
+ m_pImpl->appendStarMath(val);
+ break;
+ case NS_ooxml::LN_CT_FramePr_dropCap:
+ case NS_ooxml::LN_CT_FramePr_lines:
+ case NS_ooxml::LN_CT_FramePr_hAnchor:
+ case NS_ooxml::LN_CT_FramePr_vAnchor:
+ case NS_ooxml::LN_CT_FramePr_x:
+ case NS_ooxml::LN_CT_FramePr_xAlign:
+ case NS_ooxml::LN_CT_FramePr_y:
+ case NS_ooxml::LN_CT_FramePr_yAlign:
+ case NS_ooxml::LN_CT_FramePr_hRule:
+ case NS_ooxml::LN_CT_FramePr_w:
+ case NS_ooxml::LN_CT_FramePr_h:
+ case NS_ooxml::LN_CT_FramePr_wrap:
+ case NS_ooxml::LN_CT_FramePr_hSpace:
+ case NS_ooxml::LN_CT_FramePr_vSpace:
+ {
+ ParagraphProperties* pParaProperties = nullptr;
+ // handle frame properties at styles
+ if( m_pImpl->GetTopContextType() == CONTEXT_STYLESHEET )
+ pParaProperties = dynamic_cast< ParagraphProperties*>( m_pImpl->GetTopContextOfType( CONTEXT_STYLESHEET ).get() );
+ else
+ pParaProperties = dynamic_cast< ParagraphProperties*>( m_pImpl->GetTopContextOfType( CONTEXT_PARAGRAPH ).get() );
+
+ if( pParaProperties )
+ {
+ switch( nName )
+ {
+ case NS_ooxml::LN_CT_FramePr_dropCap:
+ pParaProperties->SetDropCap( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_FramePr_lines:
+ pParaProperties->SetLines( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_FramePr_hAnchor:
+ switch(nIntValue)
+ {
+ case NS_ooxml::LN_Value_doc_ST_HAnchor_text: //relative to column
+ nIntValue = text::RelOrientation::FRAME; break;
+ case NS_ooxml::LN_Value_doc_ST_HAnchor_margin: nIntValue = text::RelOrientation::PAGE_PRINT_AREA; break;
+ case NS_ooxml::LN_Value_doc_ST_HAnchor_page: nIntValue = text::RelOrientation::PAGE_FRAME; break;
+ default:;
+ }
+ pParaProperties->SethAnchor( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_FramePr_vAnchor:
+ switch(nIntValue)
+ {
+ case NS_ooxml::LN_Value_doc_ST_VAnchor_text: //relative to paragraph
+ nIntValue = text::RelOrientation::FRAME; break;
+ case NS_ooxml::LN_Value_doc_ST_VAnchor_margin:nIntValue = text::RelOrientation::PAGE_PRINT_AREA ; break;
+ case NS_ooxml::LN_Value_doc_ST_VAnchor_page: nIntValue = text::RelOrientation::PAGE_FRAME; break;
+ default:;
+ }
+ pParaProperties->SetvAnchor( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_FramePr_x:
+ pParaProperties->Setx( ConversionHelper::convertTwipToMM100(nIntValue ));
+ pParaProperties->SetxAlign( text::HoriOrientation::NONE );
+ break;
+ case NS_ooxml::LN_CT_FramePr_xAlign:
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_doc_ST_XAlign_center : nIntValue = text::HoriOrientation::CENTER; break;
+ case NS_ooxml::LN_Value_doc_ST_XAlign_right : nIntValue = text::HoriOrientation::RIGHT; break;
+ case NS_ooxml::LN_Value_doc_ST_XAlign_inside : nIntValue = text::HoriOrientation::INSIDE; break;
+ case NS_ooxml::LN_Value_doc_ST_XAlign_outside : nIntValue = text::HoriOrientation::OUTSIDE; break;
+ case NS_ooxml::LN_Value_doc_ST_XAlign_left : nIntValue = text::HoriOrientation::LEFT; break;
+ default: nIntValue = text::HoriOrientation::NONE;
+ }
+ pParaProperties->SetxAlign( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_FramePr_y:
+ pParaProperties->Sety( ConversionHelper::convertTwipToMM100(nIntValue ));
+ pParaProperties->SetyAlign( text::VertOrientation::NONE );
+ break;
+ case NS_ooxml::LN_CT_FramePr_yAlign:
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_doc_ST_YAlign_top :
+ case NS_ooxml::LN_Value_doc_ST_YAlign_inside :nIntValue = text::VertOrientation::TOP; break;
+ case NS_ooxml::LN_Value_doc_ST_YAlign_center :nIntValue = text::VertOrientation::CENTER;break;
+ case NS_ooxml::LN_Value_doc_ST_YAlign_bottom :
+ case NS_ooxml::LN_Value_doc_ST_YAlign_outside :nIntValue = text::VertOrientation::BOTTOM;break;
+ case NS_ooxml::LN_Value_doc_ST_YAlign_inline :
+ {
+ // HACK: This is for bnc#780851, where a table has one cell that has w:framePr,
+ // which causes that paragraph to be converted to a text frame, and the original
+ // paragraph object no longer exists, which makes table creation fail and furthermore
+ // it would be missing in the table layout anyway. So actually no letting that paragraph
+ // be a text frame "fixes" it. I'm not sure what "inline" is supposed to mean in practice
+ // anyway, so as long as this doesn't cause trouble elsewhere ...
+ PropertyMapPtr pContext = m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH);
+ if( pContext )
+ {
+ ParagraphPropertyMap* pParaContext = dynamic_cast< ParagraphPropertyMap* >( pContext.get() );
+ if (pParaContext)
+ pParaContext->SetFrameMode(false);
+ }
+ nIntValue = text::VertOrientation::NONE;
+ break;
+ }
+ default:
+ nIntValue = text::VertOrientation::NONE;
+ break;
+ }
+ pParaProperties->SetyAlign( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_FramePr_hRule:
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_doc_ST_HeightRule_exact:
+ nIntValue = text::SizeType::FIX;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_HeightRule_atLeast:
+ nIntValue = text::SizeType::MIN;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_HeightRule_auto:
+ //no break;
+ default:;
+ nIntValue = text::SizeType::VARIABLE;
+ }
+ pParaProperties->SethRule( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_FramePr_wrap:
+ {
+ //should be either LN_Value_doc_ST_Wrap_notBeside or LN_Value_doc_ST_Wrap_around or LN_Value_doc_ST_Wrap_auto
+ OSL_ENSURE( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_Wrap_around ||
+ sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_Wrap_notBeside ||
+ sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_Wrap_through ||
+ sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_Wrap_none ||
+ sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_Wrap_auto,
+ "wrap not around, not_Beside, through, none or auto?");
+ if( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_Wrap_through ||
+ sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_Wrap_auto )
+ pParaProperties->SetWrap ( text::WrapTextMode_DYNAMIC ) ;
+ else if (sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_Wrap_around)
+ pParaProperties->SetWrap(text::WrapTextMode_PARALLEL);
+ else if (sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_doc_ST_Wrap_none)
+ pParaProperties->SetWrap ( text::WrapTextMode_THROUGH ) ;
+ else
+ pParaProperties->SetWrap ( text::WrapTextMode_NONE ) ;
+ }
+ break;
+ case NS_ooxml::LN_CT_FramePr_w:
+ pParaProperties->Setw(ConversionHelper::convertTwipToMM100(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_FramePr_h:
+ pParaProperties->Seth(ConversionHelper::convertTwipToMM100(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_FramePr_hSpace:
+ pParaProperties->SethSpace( ConversionHelper::convertTwipToMM100(nIntValue ));
+ break;
+ case NS_ooxml::LN_CT_FramePr_vSpace:
+ pParaProperties->SetvSpace( ConversionHelper::convertTwipToMM100(nIntValue ));
+ break;
+ default:;
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TrackChange_author:
+ m_pImpl->SetCurrentRedlineAuthor( sStringValue );
+ break;
+ case NS_ooxml::LN_CT_TrackChange_date:
+ m_pImpl->SetCurrentRedlineDate( sStringValue );
+ break;
+ case NS_ooxml::LN_CT_Markup_id:
+ m_pImpl->SetCurrentRedlineId( nIntValue );
+ break;
+ case NS_ooxml::LN_EG_RangeMarkupElements_commentRangeStart:
+ m_pImpl->AddAnnotationPosition( true, nIntValue );
+ break;
+ case NS_ooxml::LN_EG_RangeMarkupElements_commentRangeEnd:
+ m_pImpl->AddAnnotationPosition( false, nIntValue );
+ break;
+ case NS_ooxml::LN_CT_Comment_initials:
+ m_pImpl->SetCurrentRedlineInitials(sStringValue);
+ break;
+ case NS_ooxml::LN_token:
+ m_pImpl->SetCurrentRedlineToken( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_LineNumber_start:
+ case NS_ooxml::LN_CT_LineNumber_distance:
+ case NS_ooxml::LN_CT_LineNumber_countBy:
+ case NS_ooxml::LN_CT_LineNumber_restart:
+ {
+ //line numbering in Writer is a global document setting
+ //in Word is a section setting
+ //if line numbering is switched on anywhere in the document it's set at the global settings
+ LineNumberSettings aSettings = m_pImpl->GetLineNumberSettings();
+ switch( nName )
+ {
+ case NS_ooxml::LN_CT_LineNumber_countBy:
+ aSettings.nInterval = nIntValue;
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if( pSectionContext )
+ pSectionContext->SetLnnMod( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_LineNumber_start:
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if( pSectionContext )
+ pSectionContext->SetLnnMin( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_LineNumber_distance:
+ aSettings.nDistance = ConversionHelper::convertTwipToMM100( nIntValue );
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if( pSectionContext )
+ pSectionContext->SetdxaLnn( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_LineNumber_restart:
+ aSettings.bRestartAtEachPage = nIntValue == NS_ooxml::LN_Value_ST_LineNumberRestart_newPage;
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if( pSectionContext )
+ pSectionContext->SetLnc( nIntValue );
+ break;
+ default:;
+ }
+ m_pImpl->SetLineNumberSettings( aSettings );
+ }
+ break;
+ case NS_ooxml::LN_CT_FtnEdnRef_customMarkFollows:
+ m_pImpl->StartCustomFootnote(m_pImpl->GetTopContext());
+ break;
+ case NS_ooxml::LN_CT_FtnEdnRef_id:
+ // footnote or endnote reference id - not needed
+ break;
+ case NS_ooxml::LN_CT_Color_themeColor:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "themeColor", TDefTableHandler::getThemeColorTypeString(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Color_themeTint:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "themeTint", OUString::number(nIntValue, 16));
+ break;
+ case NS_ooxml::LN_CT_Color_themeShade:
+ m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "themeShade", OUString::number(nIntValue, 16));
+ break;
+ case NS_ooxml::LN_CT_DocGrid_linePitch:
+ {
+ //see SwWW8ImplReader::SetDocumentGrid
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if(pSectionContext)
+ {
+ pSectionContext->SetGridLinePitch( ConversionHelper::convertTwipToMM100( nIntValue ) );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_DocGrid_charSpace:
+ {
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if(pSectionContext)
+ {
+ pSectionContext->SetDxtCharSpace( nIntValue );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_DocGrid_type:
+ {
+ if (pSectionContext != nullptr)
+ {
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_doc_ST_DocGrid_default:
+ pSectionContext->SetGridType(text::TextGridMode::NONE);
+ break;
+ case NS_ooxml::LN_Value_doc_ST_DocGrid_lines:
+ pSectionContext->SetGridType(text::TextGridMode::LINES);
+ break;
+ case NS_ooxml::LN_Value_doc_ST_DocGrid_linesAndChars:
+ pSectionContext->SetGridType(text::TextGridMode::LINES_AND_CHARS);
+ pSectionContext->SetGridSnapToChars( false );
+ break;
+ case NS_ooxml::LN_Value_doc_ST_DocGrid_snapToChars:
+ pSectionContext->SetGridType(text::TextGridMode::LINES_AND_CHARS);
+ pSectionContext->SetGridSnapToChars( true );
+ break;
+ default :
+ OSL_FAIL("unknown SwTextGrid value");
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtBlock_sdtContent:
+ case NS_ooxml::LN_CT_SdtRun_sdtContent:
+ if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::unknown)
+ {
+ // Still not determined content type? and it is even not unsupported? Then it is plain text field
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::plainText);
+ }
+ if (nName == NS_ooxml::LN_CT_SdtRun_sdtContent)
+ {
+ if (m_pImpl->GetSdtStarts().empty() && !m_pImpl->m_pSdtHelper->getSdtTexts().isEmpty())
+ {
+ // A non-inline SDT is already started, first convert that to a field and only
+ // then map the inline SDT to a content control.
+ if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::plainText)
+ {
+ m_pImpl->m_pSdtHelper->createPlainTextControl();
+ }
+ }
+
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::richText);
+ m_pImpl->PushSdt();
+ break;
+ }
+ m_pImpl->SetSdt(true);
+ break;
+ case NS_ooxml::LN_CT_SdtBlock_sdtEndContent:
+ case NS_ooxml::LN_CT_SdtRun_sdtEndContent:
+ if (nName == NS_ooxml::LN_CT_SdtRun_sdtEndContent)
+ {
+ // Inline SDT.
+ switch (m_pImpl->m_pSdtHelper->getControlType())
+ {
+ case SdtControlType::richText:
+ case SdtControlType::checkBox:
+ case SdtControlType::dropDown:
+ case SdtControlType::picture:
+ case SdtControlType::datePicker:
+ m_pImpl->PopSdt();
+ break;
+ default:
+ break;
+ }
+ }
+
+ m_pImpl->SetSdt(false);
+
+ // It's not possible to insert the relevant property to the character context here:
+ // the previous, already sent character context may be still active, so the property would be lost.
+ if (m_pImpl->m_pSdtHelper->isOutsideAParagraph())
+ m_pImpl->setParaSdtEndDeferred(true);
+ else
+ m_pImpl->setSdtEndDeferred(true);
+
+ switch (m_pImpl->m_pSdtHelper->getControlType())
+ {
+ case SdtControlType::dropDown:
+ m_pImpl->m_pSdtHelper->createDropDownControl();
+ break;
+ case SdtControlType::plainText:
+ m_pImpl->m_pSdtHelper->createPlainTextControl();
+ break;
+ case SdtControlType::datePicker:
+ m_pImpl->m_pSdtHelper->createDateContentControl();
+ break;
+ case SdtControlType::unknown:
+ default:;
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtListItem_displayText:
+ m_pImpl->m_pSdtHelper->getDropDownDisplayTexts().push_back(sStringValue);
+ break;
+ case NS_ooxml::LN_CT_SdtListItem_value:
+ m_pImpl->m_pSdtHelper->getDropDownItems().push_back(sStringValue);
+ break;
+ case NS_ooxml::LN_CT_SdtDate_fullDate:
+ m_pImpl->m_pSdtHelper->getDate().append(sStringValue);
+ break;
+ case NS_ooxml::LN_CT_Background_color:
+ if (m_pImpl->GetSettingsTable()->GetDisplayBackgroundShape())
+ m_pImpl->m_oBackgroundColor = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_PageNumber_start:
+ if (pSectionContext != nullptr && !m_pImpl->IsAltChunk())
+ pSectionContext->SetPageNumber(nIntValue);
+ break;
+ case NS_ooxml::LN_CT_PageNumber_fmt:
+ if (pSectionContext)
+ {
+ sal_Int16 nNumberType = ConversionHelper::ConvertNumberingType(nIntValue, -1);
+ if (nNumberType != -1)
+ pSectionContext->SetPageNumberType(nNumberType);
+ }
+ break;
+ case NS_ooxml::LN_CT_FtnEdn_type:
+ // This is the "separator" footnote, ignore its linebreaks/text.
+ if (static_cast<sal_uInt32>(nIntValue) == NS_ooxml::LN_Value_doc_ST_FtnEdn_separator)
+ m_pImpl->SetSkipFootnoteState( SkipFootnoteSeparator::ON );
+ else
+ m_pImpl->SetSkipFootnoteState( SkipFootnoteSeparator::OFF );
+ break;
+ case NS_ooxml::LN_CT_FtnEdn_id:
+ {
+ SkipFootnoteSeparator eSkip = m_pImpl->GetSkipFootnoteState();
+ if ( eSkip == SkipFootnoteSeparator::ON )
+ m_pImpl->SetSkipFootnoteState( SkipFootnoteSeparator::SKIPPING );
+ else if ( eSkip == SkipFootnoteSeparator::SKIPPING )
+ m_pImpl->SetSkipFootnoteState( SkipFootnoteSeparator::OFF );
+ }
+ break;
+ case NS_ooxml::LN_CT_DataBinding_prefixMappings:
+ m_pImpl->m_pSdtHelper->setDataBindingPrefixMapping(sStringValue);
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_DataBinding_prefixMappings", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_DataBinding_xpath:
+ m_pImpl->m_pSdtHelper->setDataBindingXPath(sStringValue);
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_DataBinding_xpath", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_DataBinding_storeItemID:
+ m_pImpl->m_pSdtHelper->setDataBindingStoreItemID(sStringValue);
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_DataBinding_storeItemID", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_SdtPlaceholder_docPart_val:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtPlaceholder_docPart_val", sStringValue);
+ m_pImpl->m_pSdtHelper->SetPlaceholderDocPart(sStringValue);
+ break;
+ case NS_ooxml::LN_CT_SdtColor_val:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtColor_val", sStringValue);
+ m_pImpl->m_pSdtHelper->SetColor(sStringValue);
+ break;
+ case NS_ooxml::LN_CT_SdtText_multiLine:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtText_multiLine", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_PTab_leader:
+ case NS_ooxml::LN_CT_PTab_relativeTo:
+ break;
+ case NS_ooxml::LN_CT_PTab_alignment:
+ m_pImpl->HandlePTab(nIntValue);
+ break;
+ case NS_ooxml::LN_CT_Cnf_lastRowLastColumn:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "lastRowLastColumn", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_lastRowFirstColumn:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "lastRowFirstColumn", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_firstRowLastColumn:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "firstRowLastColumn", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_oddHBand:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "oddHBand", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_firstRowFirstColumn:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "firstRowFirstColumn", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_evenVBand:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "evenVBand", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_evenHBand:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "evenHBand", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_lastColumn:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "lastColumn", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_firstColumn:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "firstColumn", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_oddVBand:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "oddVBand", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_lastRow:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "lastRow", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_firstRow:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "firstRow", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Cnf_val:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "val", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_DocPartName_val:
+ {
+ m_sGlossaryEntryName = sStringValue;
+ break;
+ }
+ case NS_ooxml::LN_CT_DocPartGallery_val:
+ {
+ const OUString& sGlossaryEntryGallery = sStringValue;
+ if(m_pImpl->GetTopContext())
+ {
+ OUString sName = sGlossaryEntryGallery + ":" + m_sGlossaryEntryName;
+ // Add glossary entry name as a first paragraph in section
+ m_pImpl->appendTextPortion(sName, m_pImpl->GetTopContext());
+ }
+ break;
+ }
+ case NS_ooxml::LN_CT_PermStart_ed:
+ {
+ m_pImpl->setPermissionRangeEd(sStringValue);
+ break;
+ }
+ case NS_ooxml::LN_CT_PermStart_edGrp:
+ {
+ m_pImpl->setPermissionRangeEdGrp(sStringValue);
+ break;
+ }
+ case NS_ooxml::LN_CT_PermStart_id:
+ {
+ m_pImpl->startOrEndPermissionRange(nIntValue);
+ break;
+ }
+ case NS_ooxml::LN_CT_PermEnd_id:
+ {
+ m_pImpl->startOrEndPermissionRange(nIntValue);
+ break;
+ }
+ case NS_ooxml::LN_CT_NumFmt_val:
+ {
+ try
+ {
+ uno::Reference<beans::XPropertySet> xFtnEdnSettings;
+ if (m_pImpl->IsInFootnoteProperties())
+ {
+ uno::Reference<text::XFootnotesSupplier> xFootnotesSupplier(
+ m_pImpl->GetTextDocument(), uno::UNO_QUERY);
+ if (xFootnotesSupplier.is())
+ xFtnEdnSettings = xFootnotesSupplier->getFootnoteSettings();
+ }
+ else
+ {
+ uno::Reference<text::XEndnotesSupplier> xEndnotesSupplier(
+ m_pImpl->GetTextDocument(), uno::UNO_QUERY);
+ if (xEndnotesSupplier.is())
+ xFtnEdnSettings = xEndnotesSupplier->getEndnoteSettings();
+ }
+ if (xFtnEdnSettings.is())
+ {
+ sal_Int16 nNumType = ConversionHelper::ConvertNumberingType(nIntValue);
+ xFtnEdnSettings->setPropertyValue(getPropertyName(PROP_NUMBERING_TYPE),
+ uno::Any(nNumType));
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_AltChunk:
+ {
+ m_pImpl->HandleAltChunk(sStringValue);
+ }
+ break;
+ case NS_ooxml::LN_AG_Parids_paraId:
+ if (ParagraphPropertyMap* pParaContext
+ = dynamic_cast<ParagraphPropertyMap*>(m_pImpl->GetTopContext().get()))
+ {
+ pParaContext->SetParaId(sStringValue);
+ }
+ break;
+ default:
+ SAL_WARN("writerfilter", "DomainMapper::lcl_attribute: unhandled token: " << nName);
+ }
+}
+
+void DomainMapper::lcl_sprm(Sprm & rSprm)
+{
+ if (!m_pImpl->hasTableManager() || !m_pImpl->getTableManager().sprm(rSprm))
+ sprmWithProps(rSprm, m_pImpl->GetTopContext());
+}
+
+// In rtl-paragraphs the meaning of left/right are to be exchanged
+static bool ExchangeLeftRight(const PropertyMapPtr& rContext, DomainMapper_Impl& rImpl)
+{
+ bool bExchangeLeftRight = false;
+ sal_Int32 aAdjust;
+ uno::Any aPropPara = rImpl.GetAnyProperty(PROP_WRITING_MODE, rContext);
+ if( (aPropPara >>= aAdjust) && aAdjust == text::WritingMode2::RL_TB )
+ bExchangeLeftRight = true;
+ return bExchangeLeftRight;
+}
+
+void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
+{
+ // These SPRM's are not specific to any section, so it's expected that there is no context yet.
+ switch (rSprm.getId())
+ {
+ case NS_ooxml::LN_background_background:
+ return;
+ default:
+ break;
+ }
+
+ OSL_ENSURE(rContext, "PropertyMap has to be valid!");
+ if(!rContext)
+ return ;
+
+ sal_uInt32 nSprmId = rSprm.getId();
+ //needed for page properties
+ SectionPropertyMap * pSectionContext = m_pImpl->GetSectionContext();
+ Value::Pointer_t pValue = rSprm.getValue();
+ sal_Int32 nIntValue = pValue->getInt();
+ const OUString sStringValue = pValue->getString();
+
+ switch(nSprmId)
+ {
+ case NS_ooxml::LN_CT_PPrBase_jc:
+ {
+ bool bExchangeLeftRight = !IsRTFImport() && ExchangeLeftRight(rContext, *m_pImpl);
+ handleParaJustification(nIntValue, rContext, bExchangeLeftRight);
+ break;
+ }
+ case NS_ooxml::LN_CT_PPrBase_keepLines:
+ rContext->Insert(PROP_PARA_SPLIT, uno::Any(nIntValue == 0));
+ break;
+ case NS_ooxml::LN_CT_PPrBase_keepNext:
+ rContext->Insert(PROP_PARA_KEEP_TOGETHER, uno::Any( nIntValue != 0 ) );
+ break;
+ case NS_ooxml::LN_CT_PPrBase_pageBreakBefore:
+ rContext->Insert(PROP_BREAK_TYPE, uno::Any(nIntValue ? style::BreakType_PAGE_BEFORE : style::BreakType_NONE), /*bOverwrite=*/bool(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_NumPr_ilvl:
+ if (nIntValue < 0 || 10 <= nIntValue)
+ {
+ SAL_INFO("writerfilter",
+ "unsupported numbering level " << nIntValue);
+ break;
+ }
+ if( IsStyleSheetImport() )
+ {
+ //style sheets cannot have a numbering rule attached
+ StyleSheetPropertyMap* pStyleSheetPropertyMap = dynamic_cast< StyleSheetPropertyMap* >( rContext.get() );
+ if (pStyleSheetPropertyMap)
+ pStyleSheetPropertyMap->SetListLevel( static_cast<sal_Int16>(nIntValue) );
+ }
+ // 0-8 are the 9 levels that Microsoft supports. (LO supports 10 levels).
+ // 9 indicates "no numbering", for which LO has no corresponding concept,
+ // and so it will be treated as the 10th level.
+ // finishParagraph() will convert the 9 into "no numbering" for direct formatting.
+ // (Styles only use this PROP for round-tripping and UI, but cannot trust it for import)
+ if (!IsStyleSheetImport() || nIntValue != 9)
+ rContext->Insert(PROP_NUMBERING_LEVEL, uno::Any(static_cast<sal_Int16>(nIntValue)));
+ break;
+ case NS_ooxml::LN_CT_NumPr_numId:
+ {
+ //convert the ListTable entry to a NumberingRules property and apply it
+ ListsManager::Pointer pListTable = m_pImpl->GetListTable();
+ ListDef::Pointer pList = pListTable->GetList( nIntValue );
+ if( IsStyleSheetImport() )
+ {
+ //style sheets cannot have a numbering rule attached
+ StyleSheetPropertyMap* pStyleSheetPropertyMap = dynamic_cast< StyleSheetPropertyMap* >( rContext.get() );
+ if (pStyleSheetPropertyMap)
+ pStyleSheetPropertyMap->SetListId( nIntValue );
+ }
+ if( pList )
+ {
+ if( !IsStyleSheetImport() )
+ {
+ uno::Any aRules( pList->GetNumberingRules( ) );
+ rContext->Insert( PROP_NUMBERING_RULES, aRules );
+ PropertyMapPtr pContext = m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH);
+ if (pContext)
+ {
+ assert(dynamic_cast<ParagraphPropertyMap*>(pContext.get()));
+ static_cast<ParagraphPropertyMap*>(pContext.get())->SetListId(pList->GetId());
+ }
+
+ // Indentation can came from:
+ // 1) Paragraph style's numbering's indentation: the current non-style numId has priority over it.
+ // 2) Numbering's indentation: Writer handles that natively, so it should not be set on rContext.
+ // 3) Paragraph style's indentation: ditto.
+ // 4) Direct paragraph formatting: that will came later.
+ // So no situation where keeping indentation at this point would make sense -> erase.
+ rContext->Erase(PROP_PARA_FIRST_LINE_INDENT);
+ rContext->Erase(PROP_PARA_LEFT_MARGIN);
+ rContext->Erase(PROP_PARA_RIGHT_MARGIN);
+ }
+ }
+ else
+ {
+ if( !IsStyleSheetImport() )
+ {
+ // eg. disabled numbering using non-existent numId "0"
+ rContext->Insert( PROP_NUMBERING_STYLE_NAME, uno::Any( OUString() ) );
+ // disable inheritance of indentation of parent styles
+ rContext->Insert( PROP_PARA_LEFT_MARGIN, uno::Any( sal_Int32(0) ), /*bOverwrite=*/false);
+ rContext->Insert( PROP_PARA_FIRST_LINE_INDENT,
+ uno::Any( sal_Int32(0) ), /*bOverwrite=*/false);
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrBase_suppressLineNumbers:
+ rContext->Insert(PROP_PARA_LINE_NUMBER_COUNT, uno::Any( nIntValue == 0 ) );
+ break;
+ case NS_ooxml::LN_inTbl:
+ break;
+ case NS_ooxml::LN_tblDepth:
+ //not handled via sprm but via text( 0x07 )
+ break;
+ case NS_ooxml::LN_CT_FramePr_w:
+ break;
+ case NS_ooxml::LN_CT_FramePr_wrap:
+ break;
+
+ case NS_ooxml::LN_CT_PrBase_pBdr: //paragraph border
+ resolveSprmProps(*this, rSprm);
+ break;
+ case NS_ooxml::LN_CT_PBdr_top:
+ case NS_ooxml::LN_CT_PBdr_left:
+ case NS_ooxml::LN_CT_PBdr_bottom:
+ case NS_ooxml::LN_CT_PBdr_right:
+ case NS_ooxml::LN_CT_PBdr_between:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pBorderHandler = std::make_shared<BorderHandler>( true );
+ pProperties->resolve(*pBorderHandler);
+ PropertyIds eBorderId = PropertyIds( 0 );
+ PropertyIds eBorderDistId = PropertyIds( 0 );
+ switch( nSprmId )
+ {
+ case NS_ooxml::LN_CT_PBdr_top:
+ eBorderId = PROP_TOP_BORDER;
+ eBorderDistId = PROP_TOP_BORDER_DISTANCE;
+ break;
+ case NS_ooxml::LN_CT_PBdr_left:
+ eBorderId = PROP_LEFT_BORDER;
+ eBorderDistId = PROP_LEFT_BORDER_DISTANCE;
+ break;
+ case NS_ooxml::LN_CT_PBdr_bottom:
+ eBorderId = PROP_BOTTOM_BORDER ;
+ eBorderDistId = PROP_BOTTOM_BORDER_DISTANCE;
+ break;
+ case NS_ooxml::LN_CT_PBdr_right:
+ eBorderId = PROP_RIGHT_BORDER;
+ eBorderDistId = PROP_RIGHT_BORDER_DISTANCE ;
+ break;
+ case NS_ooxml::LN_CT_PBdr_between:
+ if (m_pImpl->handlePreviousParagraphBorderInBetween())
+ {
+ // If previous paragraph also had border in between property
+ // then it is possible to emulate this border as top border
+ // for current paragraph
+ eBorderId = PROP_TOP_BORDER;
+ eBorderDistId = PROP_TOP_BORDER_DISTANCE;
+ }
+ // Since there are borders in between, each paragraph will have own borders. No more joining
+ rContext->Insert(PROP_PARA_CONNECT_BORDERS, uno::Any(false));
+ break;
+ default:;
+ }
+ if( eBorderId )
+ rContext->Insert( eBorderId, uno::Any( pBorderHandler->getBorderLine()) );
+ if(eBorderDistId)
+ rContext->Insert(eBorderDistId, uno::Any( pBorderHandler->getLineDistance()));
+ if ( nSprmId == NS_ooxml::LN_CT_PBdr_right )
+ {
+ table::ShadowFormat aFormat;
+ // Word only allows shadows on visible borders
+ if ( pBorderHandler->getShadow() && pBorderHandler->getBorderLine().LineStyle != table::BorderLineStyle::NONE )
+ aFormat = writerfilter::dmapper::PropertyMap::getShadowFromBorder(pBorderHandler->getBorderLine());
+ rContext->Insert(PROP_PARA_SHADOW_FORMAT, uno::Any(aFormat));
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_PBdr_bar:
+ break;
+ case NS_ooxml::LN_CT_PPrBase_suppressAutoHyphens:
+ rContext->Insert(PROP_PARA_IS_HYPHENATION, uno::Any( nIntValue == 0 ));
+ break;
+ case NS_ooxml::LN_CT_FramePr_h:
+ break;
+ case NS_ooxml::LN_CT_PrBase_shd:
+ {
+ //contains fore color, back color and shadow percentage, results in a brush
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pCellColorHandler = std::make_shared<CellColorHandler>();
+ pCellColorHandler->setOutputFormat( CellColorHandler::Paragraph );
+ bool bEnableTempGrabBag = !pCellColorHandler->isInteropGrabBagEnabled();
+ if( bEnableTempGrabBag )
+ pCellColorHandler->enableInteropGrabBag( "TempShdPropsGrabBag" );
+
+ pProperties->resolve(*pCellColorHandler);
+ rContext->InsertProps(pCellColorHandler->getProperties().get());
+
+ rContext->Insert(PROP_CHAR_THEME_FILL, pCellColorHandler->getInteropGrabBag().Value, true, PARA_GRAB_BAG);
+ if(bEnableTempGrabBag)
+ pCellColorHandler->disableInteropGrabBag();
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_FramePr_vSpace:
+ break; // sprmPDyaFromText
+ case NS_ooxml::LN_CT_FramePr_hSpace:
+ break; // sprmPDxaFromText
+ case NS_ooxml::LN_CT_FramePr_anchorLock:
+ break;
+ case NS_ooxml::LN_CT_PPrBase_widowControl:
+ {
+ uno::Any aVal( uno::Any( sal_Int8(nIntValue ? 2 : 0 )));
+ rContext->Insert( PROP_PARA_WIDOWS, aVal );
+ rContext->Insert( PROP_PARA_ORPHANS, aVal );
+ }
+ break; // sprmPFWidowControl
+ case NS_ooxml::LN_CT_PPrBase_overflowPunct:
+ rContext->Insert(PROP_PARA_IS_HANGING_PUNCTUATION, uno::Any( nIntValue == 0 ));
+ break;
+ case NS_ooxml::LN_CT_PPrBase_topLinePunct:
+ break;
+ case NS_ooxml::LN_CT_PPrBase_autoSpaceDE:
+ break;
+ case NS_ooxml::LN_CT_PPrBase_autoSpaceDN:
+ break;
+ case NS_ooxml::LN_CT_PPrBase_textAlignment:
+ {
+ sal_Int16 nAlignment = 0;
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_doc_ST_TextAlignment_top:
+ nAlignment = 2;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_TextAlignment_center:
+ nAlignment = 3;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_TextAlignment_baseline:
+ nAlignment = 1;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_TextAlignment_bottom:
+ nAlignment = 4;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_TextAlignment_auto:
+ default:
+ break;
+ }
+ rContext->Insert( PROP_PARA_VERT_ALIGNMENT, uno::Any( nAlignment) );
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrBase_textDirection:
+ {
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_TextDirection_tbRl:
+ {
+ m_pImpl->SetFrameDirection(text::WritingMode2::TB_RL);
+ break;
+ }
+ case NS_ooxml::LN_Value_ST_TextDirection_btLr:
+ {
+ m_pImpl->SetFrameDirection(text::WritingMode2::BT_LR);
+ break;
+ }
+ case NS_ooxml::LN_Value_ST_TextDirection_lrTbV:
+ {
+ m_pImpl->SetFrameDirection(text::WritingMode2::LR_TB);
+ break;
+ }
+ case NS_ooxml::LN_Value_ST_TextDirection_tbRlV:
+ {
+ m_pImpl->SetFrameDirection(text::WritingMode2::TB_RL);
+ break;
+ }
+ case NS_ooxml::LN_Value_ST_TextDirection_lrTb:
+ case NS_ooxml::LN_Value_ST_TextDirection_tbLrV:
+ default:
+ SAL_WARN("writerfilter", "DomainMapper::sprmWithProps: unhandled textDirection");
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrBase_outlineLvl:
+ {
+ if (nIntValue < WW_OUTLINE_MIN || nIntValue > WW_OUTLINE_MAX)
+ break; // invalid value is ignored by MS Word
+
+ if( IsStyleSheetImport() )
+ {
+ StyleSheetPropertyMap* pStyleSheetPropertyMap = dynamic_cast< StyleSheetPropertyMap* >( rContext.get() );
+ if (pStyleSheetPropertyMap)
+ pStyleSheetPropertyMap->SetOutlineLevel(nIntValue);
+ }
+ else
+ {
+ // convert MS body level (9) to LO body level (0) and equivalent outline levels
+ sal_Int16 nLvl = nIntValue == WW_OUTLINE_MAX ? 0 : nIntValue + 1;
+ rContext->Insert(PROP_OUTLINE_LEVEL, uno::Any ( nLvl ));
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrBase_bidi:
+ {
+ // Four situations to handle:
+ // 1.) bidi same as previous setting: no adjust change
+ // 2.) no previous adjust: set appropriate default for this bidi
+ // 3.) previous adjust and bidi different from previous: swap adjusts
+ // 4.) previous adjust and no previous bidi: RTL swaps adjust
+
+ const sal_Int16 nWritingMode = nIntValue ? text::WritingMode2::RL_TB : text::WritingMode2::LR_TB;
+ sal_Int16 nParentBidi = -1;
+ m_pImpl->GetPropertyFromParaStyleSheet(PROP_WRITING_MODE) >>= nParentBidi;
+ // Paragraph justification reverses its meaning in an RTL context.
+ // 1. Only make adjustments if the BiDi changes.
+ if ( nParentBidi != nWritingMode && !IsRTFImport() )
+ {
+ style::ParagraphAdjust eAdjust = style::ParagraphAdjust(-1);
+ // 2. no adjust property exists yet
+ if ( !(m_pImpl->GetAnyProperty(PROP_PARA_ADJUST, rContext) >>= eAdjust) )
+ {
+ // RTL defaults to right adjust
+ eAdjust = nIntValue ? style::ParagraphAdjust_RIGHT : style::ParagraphAdjust_LEFT;
+ rContext->Insert(PROP_PARA_ADJUST, uno::Any( eAdjust ), /*bOverwrite=*/false);
+ }
+ // 3,4. existing adjust: if RTL, then swap. If LTR, but previous was RTL, also swap.
+ else if ( nIntValue || nParentBidi == sal_Int16(text::WritingMode2::RL_TB) )
+ {
+ if ( eAdjust == style::ParagraphAdjust_RIGHT )
+ rContext->Insert(PROP_PARA_ADJUST, uno::Any( style::ParagraphAdjust_LEFT ));
+ else if ( eAdjust == style::ParagraphAdjust_LEFT )
+ rContext->Insert(PROP_PARA_ADJUST, uno::Any( style::ParagraphAdjust_RIGHT ));
+ }
+ }
+ rContext->Insert(PROP_WRITING_MODE, uno::Any( nWritingMode ));
+ }
+
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_bidi:
+ if (pSectionContext != nullptr)
+ {
+ const sal_Int16 writingMode = (nIntValue != 0) ? sal_Int16(text::WritingMode2::RL_TB) : sal_Int16(text::WritingMode2::LR_TB);
+ pSectionContext->Insert(PROP_WRITING_MODE, uno::Any(writingMode));
+ }
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_rtlGutter:
+ if (pSectionContext != nullptr)
+ {
+ bool bRtlGutter = nIntValue != 0;
+ pSectionContext->Insert(PROP_RTL_GUTTER, uno::Any(bRtlGutter));
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_highlight:
+ {
+ // MS Word completely ignores character highlighting in character styles.
+ if ( IsStyleSheetImport() )
+ {
+ const StyleSheetEntryPtr pCurrStyle = GetStyleSheetTable()->GetCurrentEntry();
+ if ( pCurrStyle && pCurrStyle->nStyleTypeCode == STYLE_TYPE_CHAR )
+ break;
+ }
+
+ // OOXML import uses an ID
+ if( IsOOXMLImport() )
+ {
+ sal_Int32 nColor = 0;
+ if( getColorFromId(nIntValue, nColor) )
+ rContext->Insert(PROP_CHAR_HIGHLIGHT, uno::Any( nColor ));
+ }
+ // RTF import uses the actual color value
+ else if( IsRTFImport() )
+ {
+ rContext->Insert(PROP_CHAR_HIGHLIGHT, uno::Any( nIntValue ));
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_em:
+ rContext->Insert(PROP_CHAR_EMPHASIS, uno::Any ( getEmphasisValue (nIntValue)));
+ break;
+ case NS_ooxml::LN_EG_RPrBase_emboss:
+ case NS_ooxml::LN_EG_RPrBase_b:
+ case NS_ooxml::LN_EG_RPrBase_bCs:
+ case NS_ooxml::LN_EG_RPrBase_i:
+ case NS_ooxml::LN_EG_RPrBase_iCs:
+ case NS_ooxml::LN_EG_RPrBase_strike:
+ case NS_ooxml::LN_EG_RPrBase_dstrike:
+ case NS_ooxml::LN_EG_RPrBase_outline:
+ case NS_ooxml::LN_EG_RPrBase_shadow:
+ case NS_ooxml::LN_EG_RPrBase_caps:
+ case NS_ooxml::LN_EG_RPrBase_smallCaps:
+ case NS_ooxml::LN_EG_RPrBase_vanish:
+ case NS_ooxml::LN_EG_RPrBase_webHidden:
+ {
+ PropertyIds ePropertyId = PROP_CHAR_WEIGHT; //initialized to prevent warning!
+ switch( nSprmId )
+ {
+ case NS_ooxml::LN_EG_RPrBase_b:
+ case NS_ooxml::LN_EG_RPrBase_bCs:
+ ePropertyId = nSprmId != NS_ooxml::LN_EG_RPrBase_bCs ? PROP_CHAR_WEIGHT : PROP_CHAR_WEIGHT_COMPLEX;
+ break;
+ case NS_ooxml::LN_EG_RPrBase_i:
+ case NS_ooxml::LN_EG_RPrBase_iCs:
+ ePropertyId = nSprmId == NS_ooxml::LN_EG_RPrBase_i ? PROP_CHAR_POSTURE : PROP_CHAR_POSTURE_COMPLEX;
+ break;
+ case NS_ooxml::LN_EG_RPrBase_strike:
+ case NS_ooxml::LN_EG_RPrBase_dstrike:
+ ePropertyId = PROP_CHAR_STRIKEOUT;
+ break;
+ case NS_ooxml::LN_EG_RPrBase_outline:
+ ePropertyId = PROP_CHAR_CONTOURED;
+ break;
+ case NS_ooxml::LN_EG_RPrBase_shadow:
+ ePropertyId = PROP_CHAR_SHADOWED;
+ break;
+ case NS_ooxml::LN_EG_RPrBase_caps:
+ case NS_ooxml::LN_EG_RPrBase_smallCaps:
+ ePropertyId = PROP_CHAR_CASE_MAP;
+ break;
+ case NS_ooxml::LN_EG_RPrBase_vanish:
+ case NS_ooxml::LN_EG_RPrBase_webHidden:
+ ePropertyId = PROP_CHAR_HIDDEN;
+ break;
+ case NS_ooxml::LN_EG_RPrBase_emboss:
+ ePropertyId = PROP_CHAR_RELIEF;
+ break;
+ }
+ //expected: 0,1,128,129
+ if(nIntValue != 128) //inherited from paragraph - ignore
+ {
+ if( nIntValue == 129) //inverted style sheet value
+ {
+ //get value from style sheet and invert it
+ sal_Int16 nStyleValue = 0;
+ uno::Any aStyleVal = m_pImpl->GetPropertyFromParaStyleSheet(ePropertyId);
+ if( !aStyleVal.hasValue() )
+ {
+ nIntValue = NS_ooxml::LN_EG_RPrBase_smallCaps == nSprmId ?
+ 4 : 1;
+ }
+ else if(aStyleVal.getValueTypeClass() == uno::TypeClass_FLOAT )
+ {
+ double fDoubleValue = 0;
+ //only in case of awt::FontWeight
+ aStyleVal >>= fDoubleValue;
+ nIntValue = fDoubleValue > 100. ? 0 : 1;
+ }
+ else if((aStyleVal >>= nStyleValue) ||
+ (nStyleValue = static_cast<sal_Int16>(comphelper::getEnumAsINT32(aStyleVal))) >= 0 )
+ {
+ nIntValue = NS_ooxml::LN_EG_RPrBase_smallCaps == nSprmId ?
+ nStyleValue ? 0 : 4 :
+ nStyleValue ? 0 : 1;
+ }
+ else
+ {
+ OSL_FAIL( "what type was it");
+ }
+ }
+
+ switch( nSprmId )
+ {
+ case NS_ooxml::LN_EG_RPrBase_b:
+ case NS_ooxml::LN_EG_RPrBase_bCs:
+ {
+ uno::Any aBold( uno::Any( nIntValue ? awt::FontWeight::BOLD : awt::FontWeight::NORMAL ) );
+
+ rContext->Insert(ePropertyId, aBold );
+ if( nSprmId != NS_ooxml::LN_EG_RPrBase_bCs )
+ rContext->Insert(PROP_CHAR_WEIGHT_ASIAN, aBold );
+
+ if (nSprmId == NS_ooxml::LN_EG_RPrBase_b)
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "b", OUString::number(nIntValue));
+ else if (nSprmId == NS_ooxml::LN_EG_RPrBase_bCs)
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "bCs", OUString::number(nIntValue));
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_i:
+ case NS_ooxml::LN_EG_RPrBase_iCs:
+ {
+ uno::Any aPosture( uno::Any( nIntValue ? awt::FontSlant_ITALIC : awt::FontSlant_NONE ) );
+ rContext->Insert( ePropertyId, aPosture );
+ if (nSprmId != NS_ooxml::LN_EG_RPrBase_iCs)
+ rContext->Insert(PROP_CHAR_POSTURE_ASIAN, aPosture );
+ if (nSprmId == NS_ooxml::LN_EG_RPrBase_i)
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "i", OUString::number(nIntValue));
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_strike:
+ rContext->Insert(ePropertyId,
+ uno::Any( nIntValue ? awt::FontStrikeout::SINGLE : awt::FontStrikeout::NONE ) );
+ break;
+ case NS_ooxml::LN_EG_RPrBase_dstrike:
+ rContext->Insert(ePropertyId,
+ uno::Any( nIntValue ? awt::FontStrikeout::DOUBLE : awt::FontStrikeout::NONE ) );
+ break;
+ case NS_ooxml::LN_EG_RPrBase_outline:
+ case NS_ooxml::LN_EG_RPrBase_shadow:
+ case NS_ooxml::LN_EG_RPrBase_vanish:
+ case NS_ooxml::LN_EG_RPrBase_webHidden:
+ rContext->Insert(ePropertyId, uno::Any( nIntValue != 0 ));
+ break;
+ case NS_ooxml::LN_EG_RPrBase_smallCaps:
+ // If smallcaps would be just disabled and another casemap is already inserted, don't do anything.
+ if (nIntValue || !rContext->isSet(ePropertyId) )
+ rContext->Insert(ePropertyId, uno::Any( nIntValue ? style::CaseMap::SMALLCAPS : style::CaseMap::NONE));
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "smallCaps", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_EG_RPrBase_caps:
+ rContext->Insert(ePropertyId,
+ uno::Any( nIntValue ? style::CaseMap::UPPERCASE : style::CaseMap::NONE));
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "caps", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_EG_RPrBase_emboss:
+ rContext->Insert(ePropertyId,
+ uno::Any( nIntValue ? awt::FontRelief::EMBOSSED : awt::FontRelief::NONE ));
+ break;
+
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_sz:
+ case NS_ooxml::LN_EG_RPrBase_szCs:
+ {
+ //multiples of half points (12pt == 24)
+ double fVal = double(nIntValue) / 2.;
+ uno::Any aVal( fVal );
+ if( NS_ooxml::LN_EG_RPrBase_szCs == nSprmId )
+ {
+ rContext->Insert( PROP_CHAR_HEIGHT_COMPLEX, aVal );
+ }
+ else
+ {
+ const RubyInfo &aInfo = m_pImpl->GetRubyInfo();
+ if (aInfo.nSprmId == NS_ooxml::LN_CT_Ruby_rt && aInfo.nHps > 0 )
+ {
+ fVal = double(aInfo.nHps) / 2.;
+ aVal <<= fVal;
+ }
+ else if (aInfo.nSprmId == NS_ooxml::LN_CT_Ruby_rubyBase && aInfo.nHpsBaseText > 0 )
+ {
+ fVal = double(aInfo.nHpsBaseText) / 2.;
+ aVal <<= fVal;
+ }
+ //Asian get the same value as Western
+ rContext->Insert( PROP_CHAR_HEIGHT, aVal );
+ rContext->Insert( PROP_CHAR_HEIGHT_ASIAN, aVal );
+ }
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, (nSprmId == NS_ooxml::LN_EG_RPrBase_sz ? OUString("sz") : OUString("szCs")), OUString::number(nIntValue));
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_position:
+ // The spec says 0 is the same as the lack of the value, so don't parse that.
+ if ( nIntValue )
+ {
+ if (!IsStyleSheetImport() && !IsNumberingImport())
+ m_pImpl->deferCharacterProperty( nSprmId, uno::Any( nIntValue ));
+ else if (!m_pImpl->IsDocDefaultsImport())
+ {
+ // For some undocumented reason, MS Word seems to ignore this in docDefaults
+
+ // DON'T FIXME: Truly calculating this for Character Styles will be tricky,
+ // because it depends on the final fontsize - regardless of
+ // where it is set. So at the style level,
+ // the escapement value would need to be grabbagged.
+ // At appendText time the final fontsize needs to be determined, and then
+ // the escapement can be calculated from the grabbag'd half-point value
+ // and directly applied. Yuck.
+ // It seems best to just treat charstyle escapement like
+ // pre-commit e70df84352d3670508a4666c97df44f82c1ce934
+ // which just assigned default values and ignored the actual/given escapement.
+ sal_Int16 nEscapement = nIntValue > 0 ? DFLT_ESC_AUTO_SUPER : DFLT_ESC_AUTO_SUB;
+ sal_Int8 nProp = DFLT_ESC_PROP;
+ rContext->Insert(PROP_CHAR_ESCAPEMENT, uno::Any( nEscapement ) );
+ rContext->Insert(PROP_CHAR_ESCAPEMENT_HEIGHT, uno::Any( nProp ) );
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_spacing:
+ {
+ //Kerning half point values
+ //TODO: there are two kerning values -
+ // in ww8par6.cxx NS_sprm::LN_CHpsKern is used as boolean AutoKerning
+ sal_Int16 nResult = static_cast<sal_Int16>(ConversionHelper::convertTwipToMM100(nIntValue));
+ if (m_pImpl->IsInComments())
+ {
+ nResult = static_cast<sal_Int16>(nIntValue);
+ }
+ rContext->Insert(PROP_CHAR_CHAR_KERNING, uno::Any(nResult));
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "spacing", OUString::number(nIntValue));
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_kern: // auto kerning is bound to a minimum font size in Word - but not in Writer :-(
+ rContext->Insert(PROP_CHAR_AUTO_KERNING, uno::Any( nIntValue != 0 ) );
+ break;
+ case NS_ooxml::LN_EG_RPrBase_w:
+ // ST_TextScale must fall between 1% and 600% according to spec, otherwise resets to 100% according to experience
+ if ((1 <= nIntValue) && (nIntValue <= 600))
+ {
+ rContext->Insert(PROP_CHAR_SCALE_WIDTH,
+ uno::Any( sal_Int16(nIntValue) ));
+ }
+ else
+ {
+ rContext->Insert(PROP_CHAR_SCALE_WIDTH,
+ uno::Any( sal_Int16(100) ));
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_imprint:
+ // FontRelief: NONE, EMBOSSED, ENGRAVED
+ rContext->Insert(PROP_CHAR_RELIEF,
+ uno::Any( nIntValue ? awt::FontRelief::ENGRAVED : awt::FontRelief::NONE ));
+ break;
+ case NS_ooxml::LN_EG_RPrBase_effect:
+ // The file-format has many character animations. We have only
+ // one, so we use it always. Suboptimal solution though.
+ if (nIntValue != NS_ooxml::LN_Value_ST_TextEffect_none)
+ rContext->Insert(PROP_CHAR_FLASH, uno::Any( true ));
+ else
+ rContext->Insert(PROP_CHAR_FLASH, uno::Any( false ));
+ break;
+ case NS_ooxml::LN_EG_RPrBase_rtl:
+ break;
+ case NS_ooxml::LN_EG_RPrBase_shd:
+ {
+ //contains fore color, back color and shadow percentage, results in a brush
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pCellColorHandler = std::make_shared<CellColorHandler>();
+ pCellColorHandler->setOutputFormat( CellColorHandler::Character );
+ pProperties->resolve(*pCellColorHandler);
+ rContext->InsertProps(pCellColorHandler->getProperties().get());
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_SHADING_MARKER, uno::Any(true), true, CHAR_GRAB_BAG );
+ }
+ break;
+ }
+ case NS_ooxml::LN_EG_SectPrContents_type:
+ /* break type
+ 0 - No break
+ 1 - New Column
+ 2 - New page
+ 3 - Even page
+ 4 - odd page
+ */
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if(pSectionContext)
+ {
+ //continuous break only allowed if it is not the only section break
+ SectionPropertyMap* pLastContext = m_pImpl->GetLastSectionContext();
+ if ( nIntValue != NS_ooxml::LN_Value_ST_SectionMark_continuous || pLastContext || m_pImpl->GetParaSectpr() )
+ pSectionContext->SetBreakType( nIntValue );
+ }
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_titlePg:
+ {
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if(pSectionContext)
+ pSectionContext->SetTitlePage( nIntValue > 0 );//section has title page
+ }
+ break;
+ case 165:
+ {
+ //page height, rounded to default values, default: 0x3dc0 twip
+ sal_Int32 nHeight = ConversionHelper::convertTwipToMM100( nIntValue );
+ rContext->Insert( PROP_HEIGHT, uno::Any( PaperInfo::sloppyFitPageDimension( nHeight ) ) );
+ }
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_textDirection:
+ {
+ /* 0 HoriLR 1 Vert TR 2 Vert TR 3 Vert TT 4 HoriLT
+ only 0 and 1 can be imported correctly
+ */
+ text::WritingMode nDirection = text::WritingMode_LR_TB;
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_ST_TextDirection_lrTb:
+ case NS_ooxml::LN_Value_ST_TextDirection_lrTbV:
+ nDirection = text::WritingMode_LR_TB;
+ break;
+ case NS_ooxml::LN_Value_ST_TextDirection_tbRl:
+ case NS_ooxml::LN_Value_ST_TextDirection_btLr:
+ nDirection = text::WritingMode_TB_RL;
+ break;
+ default:;
+ }
+
+ PropertyMap * pTargetContext = rContext.get();
+
+ if (pSectionContext)
+ {
+ pTargetContext = pSectionContext;
+ }
+
+ pTargetContext->Insert(PROP_WRITING_MODE, uno::Any( sal_Int16(nDirection) ) );
+ }
+ break; // sprmSTextFlow
+ // the following are not part of the official documentation
+ case NS_ooxml::LN_CT_Tabs_tab:
+ resolveSprmProps(*this, rSprm);
+ m_pImpl->IncorporateTabStop(m_pImpl->m_aCurrentTabStop);
+ m_pImpl->m_aCurrentTabStop = DeletableTabStop();
+ break;
+ case NS_ooxml::LN_CT_PPrBase_tabs:
+ {
+ // Initialize tab stop vector from style sheet
+ // fdo#81033: for RTF, a tab stop is inherited from the style if it
+ // is also applied to the paragraph directly, and cleared if it is
+ // not applied to the paragraph directly => don't InitTabStopFromStyle
+ if ( !IsRTFImport() )
+ {
+ uno::Any aValue = m_pImpl->GetPropertyFromParaStyleSheet(PROP_PARA_TAB_STOPS);
+ uno::Sequence< style::TabStop > aStyleTabStops;
+ if(aValue >>= aStyleTabStops)
+ {
+ m_pImpl->InitTabStopFromStyle( aStyleTabStops );
+ }
+ }
+ resolveSprmProps(*this, rSprm);
+ rContext->Insert(PROP_PARA_TAB_STOPS, uno::Any( m_pImpl->GetCurrentTabStopAndClear()));
+ }
+ break;
+
+ case NS_ooxml::LN_CT_DocDefaults_pPrDefault:
+ case NS_ooxml::LN_CT_DocDefaults_rPrDefault:
+ GetStyleSheetTable()->sprm( rSprm );
+ break;
+ case NS_ooxml::LN_EG_RPrBase_bdr:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pBorderHandler = std::make_shared<BorderHandler>( true );
+ pProperties->resolve(*pBorderHandler);
+
+ rContext->Insert( PROP_CHAR_TOP_BORDER, uno::Any( pBorderHandler->getBorderLine()));
+ rContext->Insert( PROP_CHAR_BOTTOM_BORDER, uno::Any( pBorderHandler->getBorderLine()));
+ rContext->Insert( PROP_CHAR_LEFT_BORDER, uno::Any( pBorderHandler->getBorderLine()));
+ rContext->Insert( PROP_CHAR_RIGHT_BORDER, uno::Any( pBorderHandler->getBorderLine()));
+
+ rContext->Insert( PROP_CHAR_TOP_BORDER_DISTANCE, uno::Any( pBorderHandler->getLineDistance()));
+ rContext->Insert( PROP_CHAR_BOTTOM_BORDER_DISTANCE, uno::Any( pBorderHandler->getLineDistance()));
+ rContext->Insert( PROP_CHAR_LEFT_BORDER_DISTANCE, uno::Any( pBorderHandler->getLineDistance()));
+ rContext->Insert( PROP_CHAR_RIGHT_BORDER_DISTANCE, uno::Any( pBorderHandler->getLineDistance()));
+
+ table::ShadowFormat aFormat;
+ // Word only allows shadows on visible borders
+ if ( pBorderHandler->getShadow() && pBorderHandler->getBorderLine().LineStyle != table::BorderLineStyle::NONE )
+ aFormat = writerfilter::dmapper::PropertyMap::getShadowFromBorder(pBorderHandler->getBorderLine());
+ rContext->Insert(PROP_CHAR_SHADOW_FORMAT, uno::Any(aFormat));
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_PPr_sectPr:
+ case NS_ooxml::LN_EG_RPrBase_color:
+ case NS_ooxml::LN_EG_RPrBase_rFonts:
+ case NS_ooxml::LN_EG_RPrBase_eastAsianLayout:
+ case NS_ooxml::LN_EG_RPrBase_u:
+ case NS_ooxml::LN_EG_RPrBase_lang:
+ case NS_ooxml::LN_CT_PPrBase_spacing:
+ case NS_ooxml::LN_CT_PPrBase_ind:
+ case NS_ooxml::LN_CT_RPrDefault_rPr:
+ case NS_ooxml::LN_CT_PPrDefault_pPr:
+ case NS_ooxml::LN_CT_Style_pPr:
+ case NS_ooxml::LN_CT_Style_rPr:
+ case NS_ooxml::LN_CT_PPr_rPr:
+ case NS_ooxml::LN_CT_PPrBase_numPr:
+ {
+ bool bTempGrabBag = !m_pImpl->isInteropGrabBagEnabled();
+ if (nSprmId == NS_ooxml::LN_CT_PPr_sectPr)
+ m_pImpl->SetParaSectpr(true);
+ else if (nSprmId == NS_ooxml::LN_EG_RPrBase_color && bTempGrabBag)
+ // if DomainMapper grab bag is not enabled, enable it temporarily
+ m_pImpl->enableInteropGrabBag("TempColorPropsGrabBag");
+ resolveSprmProps(*this, rSprm);
+ if (nSprmId == NS_ooxml::LN_CT_PPrBase_spacing)
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "spacing", m_pImpl->m_aSubInteropGrabBag);
+ else if (nSprmId == NS_ooxml::LN_EG_RPrBase_rFonts)
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "rFonts", m_pImpl->m_aSubInteropGrabBag);
+ else if (nSprmId == NS_ooxml::LN_EG_RPrBase_lang)
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "lang", m_pImpl->m_aSubInteropGrabBag);
+ else if (nSprmId == NS_ooxml::LN_EG_RPrBase_color)
+ {
+ for (const auto& rItem : m_pImpl->m_aSubInteropGrabBag)
+ {
+ if (rItem.Name == "val")
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_ORIGINAL_COLOR, rItem.Value, true, CHAR_GRAB_BAG);
+ else if (rItem.Name == "themeColor")
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_COLOR, rItem.Value, true, CHAR_GRAB_BAG);
+ else if (rItem.Name == "themeShade")
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_COLOR_SHADE, rItem.Value, true, CHAR_GRAB_BAG);
+ else if (rItem.Name == "themeTint")
+ m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_COLOR_TINT, rItem.Value, true, CHAR_GRAB_BAG);
+ }
+ if (bTempGrabBag)
+ //disable and clear DomainMapper grab bag if it wasn't enabled before
+ m_pImpl->disableInteropGrabBag();
+
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "color", m_pImpl->m_aSubInteropGrabBag);
+ }
+ else if (nSprmId == NS_ooxml::LN_CT_PPrBase_ind)
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ind", m_pImpl->m_aSubInteropGrabBag);
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrBase_wordWrap:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "wordWrap", "");
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_footnotePr:
+ case NS_ooxml::LN_EG_SectPrContents_endnotePr:
+ m_pImpl->SetInFootnoteProperties( NS_ooxml::LN_EG_SectPrContents_footnotePr == nSprmId );
+ resolveSprmProps(*this, rSprm);
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_lnNumType:
+ {
+ resolveSprmProps(*this, rSprm);
+ LineNumberSettings aSettings = m_pImpl->GetLineNumberSettings();
+ m_pImpl->SetLineNumberSettings( aSettings );
+ //apply settings at XLineNumberingProperties
+ try
+ {
+ uno::Reference< text::XLineNumberingProperties > xLineNumberingProperties( m_pImpl->GetTextDocument(), uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xLineNumberingPropSet = xLineNumberingProperties->getLineNumberingProperties();
+ if( aSettings.nInterval == 0 )
+ xLineNumberingPropSet->setPropertyValue(getPropertyName( PROP_IS_ON ), uno::Any(false) );
+ else
+ {
+ xLineNumberingPropSet->setPropertyValue(getPropertyName( PROP_IS_ON ), uno::Any(true) );
+ if( aSettings.nInterval )
+ xLineNumberingPropSet->setPropertyValue(getPropertyName( PROP_INTERVAL ), uno::Any(static_cast<sal_Int16>(aSettings.nInterval)) );
+ if( aSettings.nDistance != -1 )
+ xLineNumberingPropSet->setPropertyValue(getPropertyName( PROP_DISTANCE ), uno::Any(aSettings.nDistance) );
+ else
+ {
+ // set Auto value (0.5 cm)
+ xLineNumberingPropSet->setPropertyValue(getPropertyName( PROP_DISTANCE ), uno::Any(static_cast<sal_Int32>(500)) );
+ if( pSectionContext )
+ pSectionContext->SetdxaLnn( static_cast<sal_Int32>(283) );
+ }
+ xLineNumberingPropSet->setPropertyValue(getPropertyName( PROP_RESTART_AT_EACH_PAGE ), uno::Any(aSettings.bRestartAtEachPage) );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrBase_framePr:
+ // Avoid frames if we're inside a structured document tag, would just cause outer tables fail to create.
+ if (!m_pImpl->GetSdt())
+ {
+ PropertyMapPtr pContext = m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH);
+ if( pContext )
+ {
+ // If there is a deferred page break applied to this framed paragraph,
+ // create a dummy paragraph without extra properties,
+ // so that the anchored frame will be on the correct page (similar to shapes).
+ if (pContext->isSet(PROP_BREAK_TYPE))
+ {
+ pContext->Erase(PROP_BREAK_TYPE);
+
+ lcl_startParagraphGroup();
+ m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_PAGE_BEFORE));
+ lcl_startCharacterGroup();
+ sal_uInt8 const sBreak[] = { 0xd };
+ lcl_text(sBreak, 1);
+ lcl_endCharacterGroup();
+ lcl_endParagraphGroup();
+ }
+
+ ParagraphPropertyMap* pParaContext = dynamic_cast< ParagraphPropertyMap* >( pContext.get() );
+ if (pParaContext)
+ pParaContext->SetFrameMode();
+
+ if (!IsInHeaderFooter())
+ m_pImpl->m_bIsActualParagraphFramed = true;
+ }
+ else
+ {
+ //TODO: What about style sheet import of frame properties
+ }
+ m_pImpl->NewFrameDirection();
+ resolveSprmProps(*this, rSprm);
+ }
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_pgSz:
+ {
+ PaperInfo aLetter(PAPER_LETTER);
+ CT_PageSz.w = aLetter.getWidth();
+ CT_PageSz.h = aLetter.getHeight();
+ }
+ CT_PageSz.orient = false;
+ resolveSprmProps(*this, rSprm);
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if(pSectionContext)
+ {
+ if (!m_pImpl->IsAltChunk())
+ {
+ pSectionContext->Insert(PROP_HEIGHT, uno::Any(CT_PageSz.h));
+ }
+ pSectionContext->Insert( PROP_IS_LANDSCAPE, uno::Any( CT_PageSz.orient ));
+ if (!m_pImpl->IsAltChunk())
+ {
+ pSectionContext->Insert(PROP_WIDTH, uno::Any(CT_PageSz.w));
+ }
+ }
+ break;
+
+ case NS_ooxml::LN_EG_SectPrContents_pgMar:
+ m_pImpl->InitPageMargins();
+ resolveSprmProps(*this, rSprm);
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if(pSectionContext)
+ {
+ const PageMar& rPageMar = m_pImpl->GetPageMargins();
+ pSectionContext->SetTopMargin( rPageMar.top );
+ pSectionContext->SetRightMargin( rPageMar.right );
+ pSectionContext->SetBottomMargin( rPageMar.bottom );
+ pSectionContext->SetLeftMargin( rPageMar.left );
+ pSectionContext->SetHeaderTop( rPageMar.header );
+ pSectionContext->SetHeaderBottom( rPageMar.footer );
+ pSectionContext->SetGutterMargin(rPageMar.gutter);
+ }
+ break;
+
+ case NS_ooxml::LN_EG_SectPrContents_cols:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+
+ tools::SvRef< SectionColumnHandler > pSectHdl( new SectionColumnHandler );
+ pProperties->resolve(*pSectHdl);
+ if(pSectionContext && !m_pImpl->isInIndexContext())
+ {
+ sal_Int16 nColumnCount = pSectHdl->GetNum() == 1 ? 0 : pSectHdl->GetNum();
+ if( pSectHdl->IsEqualWidth() )
+ {
+ pSectionContext->SetEvenlySpaced( true );
+ pSectionContext->SetColumnCount( nColumnCount );
+ pSectionContext->SetColumnDistance( pSectHdl->GetSpace() );
+ pSectionContext->SetSeparatorLine( pSectHdl->IsSeparator() );
+ }
+ else if( !pSectHdl->GetColumns().empty() )
+ {
+ pSectionContext->SetEvenlySpaced( false );
+ pSectionContext->SetColumnDistance( pSectHdl->GetSpace() );
+ nColumnCount = pSectHdl->GetColumns().size();
+ pSectionContext->SetColumnCount( nColumnCount == 1 ? 0 : nColumnCount );
+ std::vector<Column_>::const_iterator tmpIter = pSectHdl->GetColumns().begin();
+ for (; tmpIter != pSectHdl->GetColumns().end(); ++tmpIter)
+ {
+ pSectionContext->AppendColumnWidth( tmpIter->nWidth );
+ if ((tmpIter != pSectHdl->GetColumns().end() - 1) || (tmpIter->nSpace > 0))
+ pSectionContext->AppendColumnSpacing( tmpIter->nSpace );
+ }
+ pSectionContext->SetSeparatorLine( pSectHdl->IsSeparator() );
+ }
+ else if( nColumnCount )
+ {
+ pSectionContext->SetColumnCount( nColumnCount );
+ pSectionContext->SetColumnDistance( pSectHdl->GetSpace() );
+ pSectionContext->SetSeparatorLine( pSectHdl->IsSeparator() );
+ }
+ }
+
+ else if ( pSectionContext )
+ {
+ FieldContextPtr pContext = m_pImpl->GetTopFieldContext();
+ uno::Reference< beans::XPropertySet > xTOC = pContext->GetTOC();
+ if( xTOC.is() )
+ {
+ uno::Reference<text::XTextColumns> xTextColumns;
+ xTOC->getPropertyValue(getPropertyName( PROP_TEXT_COLUMNS )) >>= xTextColumns;
+ if (xTextColumns.is())
+ {
+ uno::Reference< beans::XPropertySet > xColumnPropSet( xTextColumns, uno::UNO_QUERY_THROW );
+ xColumnPropSet->setPropertyValue( getPropertyName( PROP_AUTOMATIC_DISTANCE ), uno::Any( pSectHdl->GetSpace() ));
+ xTOC->setPropertyValue( getPropertyName( PROP_TEXT_COLUMNS ), uno::Any( xTextColumns ) );
+ }
+ }
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_docGrid:
+ resolveSprmProps(*this, rSprm);
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_pgBorders:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties && pSectionContext )
+ {
+ tools::SvRef< PageBordersHandler > pHandler( new PageBordersHandler );
+ pProperties->resolve( *pHandler );
+
+ // Set the borders to the context and apply them to the styles
+ pHandler->SetBorders( pSectionContext );
+ }
+ }
+ break;
+
+ case NS_ooxml::LN_CT_PPrBase_snapToGrid:
+ if (!IsStyleSheetImport()||!m_pImpl->isInteropGrabBagEnabled())
+ {
+ rContext->Insert( PROP_SNAP_TO_GRID, uno::Any(bool(nIntValue)));
+ }
+ else
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "snapToGrid", OUString::number(nIntValue));
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrBase_pStyle:
+ {
+ StyleSheetTablePtr pStyleTable = m_pImpl->GetStyleSheetTable();
+ const OUString sConvertedStyleName = pStyleTable->ConvertStyleName( sStringValue, true );
+ m_pImpl->SetCurrentParaStyleName( sConvertedStyleName );
+ if (m_pImpl->GetTopContext() && m_pImpl->GetTopContextType() != CONTEXT_SECTION)
+ m_pImpl->GetTopContext()->Insert( PROP_PARA_STYLE_NAME, uno::Any( sConvertedStyleName ));
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_rStyle:
+ {
+ OUString sConvertedName( m_pImpl->GetStyleSheetTable()->ConvertStyleName( sStringValue, true ) );
+ if (m_pImpl->CheckFootnoteStyle() && m_pImpl->GetFootnoteContext())
+ m_pImpl->SetHasFootnoteStyle(m_pImpl->GetFootnoteContext()->GetFootnoteStyle() == sConvertedName);
+
+ // First check if the style exists in the document.
+ StyleSheetEntryPtr pEntry = m_pImpl->GetStyleSheetTable( )->FindStyleSheetByConvertedStyleName( sConvertedName );
+ bool bExists = pEntry && ( pEntry->nStyleTypeCode == STYLE_TYPE_CHAR );
+ // Add the property if the style exists, but do not add it elements in TOC:
+ // they will receive later another style references from TOC
+ if ( bExists && m_pImpl->GetTopContext() && !m_pImpl->IsInTOC())
+ m_pImpl->GetTopContext()->Insert( PROP_CHAR_STYLE_NAME, uno::Any( sConvertedName ) );
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblCellMar: //cell margins
+ {
+ resolveSprmProps(*this, rSprm);//contains LN_CT_TblCellMar_top, LN_CT_TblCellMar_left, LN_CT_TblCellMar_bottom, LN_CT_TblCellMar_right
+ }
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_top:
+ case NS_ooxml::LN_CT_TblCellMar_start:
+ case NS_ooxml::LN_CT_TblCellMar_left:
+ case NS_ooxml::LN_CT_TblCellMar_bottom:
+ case NS_ooxml::LN_CT_TblCellMar_end:
+ case NS_ooxml::LN_CT_TblCellMar_right:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ MeasureHandlerPtr pMeasureHandler( new MeasureHandler );
+ pProperties->resolve(*pMeasureHandler);
+ sal_Int32 nMeasureValue = pMeasureHandler->getMeasureValue();
+ PropertyIds eId = META_PROP_CELL_MAR_TOP;
+ bool rtl = false; // TODO
+ switch(nSprmId)
+ {
+ case NS_ooxml::LN_CT_TblCellMar_top:
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_start:
+ eId = rtl ? META_PROP_CELL_MAR_RIGHT : META_PROP_CELL_MAR_LEFT;
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_left:
+ eId = META_PROP_CELL_MAR_LEFT;
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_bottom:
+ eId = META_PROP_CELL_MAR_BOTTOM;
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_end:
+ eId = rtl ? META_PROP_CELL_MAR_LEFT : META_PROP_CELL_MAR_RIGHT;
+ break;
+ case NS_ooxml::LN_CT_TblCellMar_right:
+ eId = META_PROP_CELL_MAR_RIGHT;
+ break;
+ default:;
+ }
+ rContext->Insert( eId, uno::Any(nMeasureValue), false);
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_noProof: // no grammar and spell checking, unsupported
+ break;
+ case NS_ooxml::LN_anchor_anchor: // at_character drawing
+ case NS_ooxml::LN_inline_inline: // as_character drawing
+ {
+ if ( m_pImpl->IsDiscardHeaderFooter() )
+ break;
+ //tdf112342: Break before images as well, if there are page break
+ if (m_pImpl->isBreakDeferred(BreakType::PAGE_BREAK))
+ {
+ m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH)
+ ->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_PAGE_BEFORE));
+ m_pImpl->clearDeferredBreak(PAGE_BREAK);
+ }
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ GraphicImportType eGraphicType =
+ (NS_ooxml::LN_anchor_anchor ==
+ sal::static_int_cast<Id>(nSprmId)) ?
+ IMPORT_AS_DETECTED_ANCHOR :
+ IMPORT_AS_DETECTED_INLINE;
+ GraphicImportPtr pGraphicImport =
+ m_pImpl->GetGraphicImport(eGraphicType);
+ pProperties->resolve(*pGraphicImport);
+ m_pImpl->ImportGraphic(pProperties, eGraphicType);
+ if( !pGraphicImport->IsGraphic() )
+ {
+ m_pImpl->ResetGraphicImport();
+ // todo: It's a shape, now start shape import
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_vertAlign:
+ {
+ sal_Int16 nEscapement = 0;
+ sal_Int8 nProp = DFLT_ESC_PROP;
+ if ( sStringValue == "superscript" )
+ nEscapement = DFLT_ESC_AUTO_SUPER;
+ else if ( sStringValue == "subscript" )
+ nEscapement = DFLT_ESC_AUTO_SUB;
+ else
+ nProp = 100;
+
+ rContext->Insert(PROP_CHAR_ESCAPEMENT, uno::Any( nEscapement ) );
+ rContext->Insert(PROP_CHAR_ESCAPEMENT_HEIGHT, uno::Any( nProp ) );
+ }
+ break;
+ case NS_ooxml::LN_CT_FtnProps_pos:
+ //footnotes in word can be at page end or beneath text - writer supports only the first
+ //endnotes in word can be at section end or document end - writer supports only the latter
+ // -> so this property can be ignored
+ break;
+ case NS_ooxml::LN_CT_FtnProps_numFmt:
+ case NS_ooxml::LN_CT_EdnProps_numFmt:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_FtnEdnNumProps_numStart:
+ case NS_ooxml::LN_EG_FtnEdnNumProps_numRestart:
+ {
+ try
+ {
+ uno::Reference< beans::XPropertySet > xFtnEdnSettings;
+ if( m_pImpl->IsInFootnoteProperties() )
+ {
+ uno::Reference< text::XFootnotesSupplier> xFootnotesSupplier( m_pImpl->GetTextDocument(), uno::UNO_QUERY );
+ if (xFootnotesSupplier.is())
+ xFtnEdnSettings = xFootnotesSupplier->getFootnoteSettings();
+ }
+ else
+ {
+ uno::Reference< text::XEndnotesSupplier> xEndnotesSupplier( m_pImpl->GetTextDocument(), uno::UNO_QUERY );
+ if (xEndnotesSupplier.is())
+ xFtnEdnSettings = xEndnotesSupplier->getEndnoteSettings();
+ }
+ if( NS_ooxml::LN_EG_FtnEdnNumProps_numStart == nSprmId && xFtnEdnSettings.is())
+ {
+ xFtnEdnSettings->setPropertyValue(
+ getPropertyName( PROP_START_AT),
+ uno::Any( sal_Int16( nIntValue - 1 )));
+ }
+ else if( NS_ooxml::LN_EG_FtnEdnNumProps_numRestart == nSprmId && xFtnEdnSettings.is())
+ {
+ sal_Int16 nFootnoteCounting = 0;
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_RestartNumber_continuous: nFootnoteCounting = text::FootnoteNumbering::PER_DOCUMENT; break;
+ case NS_ooxml::LN_Value_ST_RestartNumber_eachPage: nFootnoteCounting = text::FootnoteNumbering::PER_PAGE; break;
+ case NS_ooxml::LN_Value_ST_RestartNumber_eachSect: nFootnoteCounting = text::FootnoteNumbering::PER_CHAPTER; break;
+ default: break;
+ }
+ xFtnEdnSettings->setPropertyValue(
+ getPropertyName( PROP_FOOTNOTE_COUNTING ),
+ uno::Any( nFootnoteCounting ));
+ }
+ else if (xFtnEdnSettings.is())
+ {
+ sal_Int16 nNumType = ConversionHelper::ConvertNumberingType( nIntValue );
+ xFtnEdnSettings->setPropertyValue(
+ getPropertyName( PROP_NUMBERING_TYPE),
+ uno::Any( nNumType ));
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_RangeMarkupElements_moveFromRangeStart:
+ m_pImpl->SetMoveBookmark(/*bIsFrom=*/true);
+ if (m_pImpl->hasTableManager())
+ m_pImpl->getTableManager().setMoved( getPropertyName(PROP_TABLE_ROW_DELETE) );
+ break;
+ case NS_ooxml::LN_EG_RangeMarkupElements_moveToRangeStart:
+ m_pImpl->SetMoveBookmark(/*bIsFrom=*/false);
+ if (m_pImpl->hasTableManager())
+ m_pImpl->getTableManager().setMoved( getPropertyName(PROP_TABLE_ROW_INSERT) );
+ break;
+ case NS_ooxml::LN_EG_RangeMarkupElements_moveFromRangeEnd:
+ case NS_ooxml::LN_EG_RangeMarkupElements_moveToRangeEnd:
+ if (m_pImpl->hasTableManager())
+ m_pImpl->getTableManager().setMoved( OUString() );
+ break;
+ case NS_ooxml::LN_CT_ParaRPr_moveFrom:
+ case NS_ooxml::LN_CT_ParaRPr_moveTo:
+ m_pImpl->StartParaMarkerMove( );
+ break;
+ case NS_ooxml::LN_paratrackchange:
+ m_pImpl->StartParaMarkerChange( );
+ [[fallthrough]];
+ case NS_ooxml::LN_CT_PPr_pPrChange:
+ case NS_ooxml::LN_CT_ParaRPr_rPrChange:
+ case NS_ooxml::LN_trackchange:
+ case NS_ooxml::LN_EG_RPrContent_rPrChange:
+ case NS_ooxml::LN_EG_RangeMarkupElements_customXmlDelRangeStart:
+ case NS_ooxml::LN_EG_RangeMarkupElements_customXmlDelRangeEnd:
+ case NS_ooxml::LN_EG_RangeMarkupElements_customXmlMoveFromRangeStart:
+ case NS_ooxml::LN_EG_RangeMarkupElements_customXmlMoveFromRangeEnd:
+ case NS_ooxml::LN_EG_RangeMarkupElements_customXmlMoveToRangeStart:
+ case NS_ooxml::LN_EG_RangeMarkupElements_customXmlMoveToRangeEnd:
+ {
+ HandleRedline( rSprm );
+ }
+ break;
+ case NS_ooxml::LN_endtrackchange:
+ m_pImpl->RemoveTopRedline();
+ break;
+ case NS_ooxml::LN_CT_RPrChange_rPr:
+ {
+ // Push all the current 'Character' properties to the stack, so that we don't store them
+ // as 'tracked changes' by mistake
+ m_pImpl->PushProperties(CONTEXT_CHARACTER);
+
+ // Resolve all the properties that are under the 'rPrChange'->'rPr' XML node
+ resolveSprmProps(*this, rSprm );
+
+ // Get all the properties that were processed in the 'rPrChange'->'rPr' XML node
+ uno::Sequence< beans::PropertyValue > currentRedlineRevertProperties = m_pImpl->GetTopContext()->GetPropertyValues();
+
+ // Pop back out the character properties that were on the run
+ m_pImpl->PopProperties(CONTEXT_CHARACTER);
+
+ // Store these properties in the current redline object (do it after the PopProperties() above, since
+ // otherwise it'd be stored in the content dropped there).
+ m_pImpl->SetCurrentRedlineRevertProperties( currentRedlineRevertProperties );
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrChange_pPr:
+ {
+ // Push all the current 'Paragraph' properties to the stack, so that we don't store them
+ // as 'tracked changes' by mistake
+ m_pImpl->PushProperties(CONTEXT_PARAGRAPH);
+
+ // Resolve all the properties that are under the 'pPrChange'->'pPr' XML node
+ resolveSprmProps(*this, rSprm );
+
+ // Get all the properties that were processed in the 'pPrChange'->'pPr' XML node
+ uno::Sequence< beans::PropertyValue > currentRedlineRevertProperties = m_pImpl->GetTopContext()->GetPropertyValues();
+
+ // Pop back out the character properties that were on the run
+ m_pImpl->PopProperties(CONTEXT_PARAGRAPH);
+
+ // Store these properties in the current redline object (do it after the PopProperties() above, since
+ // otherwise it'd be stored in the content dropped there).
+ m_pImpl->SetCurrentRedlineRevertProperties( currentRedlineRevertProperties );
+ }
+ break;
+ case NS_ooxml::LN_object:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pOLEHandler = std::make_shared<OLEHandler>(*this);
+ pProperties->resolve(*pOLEHandler);
+ if ( pOLEHandler->isOLEObject( ) )
+ {
+ OUString sStreamName = pOLEHandler->copyOLEOStream( m_pImpl->GetTextDocument() );
+ if( !sStreamName.isEmpty() )
+ {
+ m_pImpl->appendOLE( sStreamName, pOLEHandler );
+ }
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_HdrFtrReferences_headerReference: // header reference - not needed
+ case NS_ooxml::LN_EG_HdrFtrReferences_footerReference: // footer reference - not needed
+ break;
+ case NS_ooxml::LN_EG_RPrBase_snapToGrid: // "Use document grid settings for inter-paragraph spacing"
+ break;
+ case NS_ooxml::LN_CT_PPrBase_contextualSpacing:
+ rContext->Insert(PROP_PARA_CONTEXT_MARGIN, uno::Any( nIntValue != 0 ));
+ break;
+ case NS_ooxml::LN_CT_PPrBase_mirrorIndents: // mirrorIndents
+ rContext->Insert(PROP_MIRROR_INDENTS, uno::Any( nIntValue != 0 ), true, PARA_GRAB_BAG);
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_formProt: //section protection
+ {
+ if( pSectionContext )
+ pSectionContext->Insert( PROP_IS_PROTECTED, uno::Any( bool(nIntValue) ) );
+ }
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_vAlign:
+ {
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if( pSectionContext )
+ {
+ drawing::TextVerticalAdjust nVA = drawing::TextVerticalAdjust_TOP;
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_ST_VerticalJc_center: //92367
+ nVA = drawing::TextVerticalAdjust_CENTER;
+ break;
+ case NS_ooxml::LN_Value_ST_VerticalJc_both: //92368 - justify
+ nVA = drawing::TextVerticalAdjust_BLOCK;
+ break;
+ case NS_ooxml::LN_Value_ST_VerticalJc_bottom: //92369
+ nVA = drawing::TextVerticalAdjust_BOTTOM;
+ break;
+ default:
+ break;
+ }
+ pSectionContext->Insert( PROP_TEXT_VERTICAL_ADJUST, uno::Any( nVA ), true, PARA_GRAB_BAG );
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_fitText:
+ break;
+ case NS_ooxml::LN_ffdata:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ FFDataHandler::Pointer_t pFFDataHandler(new FFDataHandler());
+
+ pProperties->resolve(*pFFDataHandler);
+ m_pImpl->SetFieldFFData(pFFDataHandler);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtPr_dropDownList:
+ case NS_ooxml::LN_CT_SdtPr_comboBox:
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::dropDown);
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtDropDownList_listItem:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+
+ size_t nDropDownDisplayTexts = m_pImpl->m_pSdtHelper->getDropDownDisplayTexts().size();
+ size_t nDropDownItems = m_pImpl->m_pSdtHelper->getDropDownItems().size();
+
+ if (pProperties)
+ pProperties->resolve(*this);
+
+ if (m_pImpl->m_pSdtHelper->getDropDownDisplayTexts().size() != nDropDownDisplayTexts + 1)
+ {
+ // w:displayText="..." is optional, add empty value if it was not provided.
+ m_pImpl->m_pSdtHelper->getDropDownDisplayTexts().push_back(OUString());
+ }
+ if (m_pImpl->m_pSdtHelper->getDropDownItems().size() != nDropDownItems + 1)
+ {
+ // w:value="..." is optional, add empty value if it was not provided.
+ m_pImpl->m_pSdtHelper->getDropDownItems().push_back(OUString());
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtPr_placeholder:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ break;
+ case NS_ooxml::LN_CT_SdtPr_date:
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::datePicker);
+ resolveSprmProps(*this, rSprm);
+ m_pImpl->m_pSdtHelper->setDateFieldStartRange(GetCurrentTextRange()->getEnd());
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtDate_dateFormat:
+ {
+ m_pImpl->m_pSdtHelper->getDateFormat().append(sStringValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtDate_storeMappedDataAs:
+ {
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtDate_calendar:
+ {
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtDate_lid:
+ {
+ m_pImpl->m_pSdtHelper->getLocale().append(sStringValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtPr_text:
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::plainText);
+ enableInteropGrabBag("ooxml:CT_SdtPr_text");
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ m_pImpl->m_pSdtHelper->appendToInteropGrabBag(getInteropGrabBag());
+ m_pImpl->disableInteropGrabBag();
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtPr_showingPlcHdr:
+ {
+ m_pImpl->m_pSdtHelper->SetShowingPlcHdr();
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtPr_dataBinding:
+ case NS_ooxml::LN_CT_SdtPr_equation:
+ case NS_ooxml::LN_CT_SdtPr_checkbox:
+ case NS_ooxml::LN_CT_SdtPr_docPartObj:
+ case NS_ooxml::LN_CT_SdtPr_docPartList:
+ case NS_ooxml::LN_CT_SdtPr_picture:
+ case NS_ooxml::LN_CT_SdtPr_citation:
+ case NS_ooxml::LN_CT_SdtPr_group:
+ case NS_ooxml::LN_CT_SdtPr_id:
+ case NS_ooxml::LN_CT_SdtPr_alias:
+ case NS_ooxml::LN_CT_SdtPlaceholder_docPart:
+ case NS_ooxml::LN_CT_SdtPr_color:
+ {
+ if (!m_pImpl->GetSdtStarts().empty())
+ {
+ if (nSprmId == NS_ooxml::LN_CT_SdtPr_color)
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+ }
+ break;
+ }
+
+ if (nSprmId == NS_ooxml::LN_CT_SdtPr_checkbox)
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::checkBox);
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+ }
+ break;
+ }
+ else if (nSprmId == NS_ooxml::LN_CT_SdtPr_picture)
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::picture);
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+ }
+ break;
+ }
+ else if (nSprmId == NS_ooxml::LN_CT_SdtPr_date)
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::datePicker);
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+ }
+ break;
+ }
+ }
+
+ // this is an unsupported SDT property, create a grab bag for it
+ OUString sName;
+ switch (nSprmId)
+ {
+ case NS_ooxml::LN_CT_SdtPr_dataBinding: sName = "ooxml:CT_SdtPr_dataBinding"; break;
+ case NS_ooxml::LN_CT_SdtPr_equation: sName = "ooxml:CT_SdtPr_equation"; break;
+ case NS_ooxml::LN_CT_SdtPr_checkbox: sName = "ooxml:CT_SdtPr_checkbox"; break;
+ case NS_ooxml::LN_CT_SdtPr_docPartObj: sName = "ooxml:CT_SdtPr_docPartObj"; break;
+ case NS_ooxml::LN_CT_SdtPr_docPartList: sName = "ooxml:CT_SdtPr_docPartList"; break;
+ case NS_ooxml::LN_CT_SdtPr_picture: sName = "ooxml:CT_SdtPr_picture"; break;
+ case NS_ooxml::LN_CT_SdtPr_citation: sName = "ooxml:CT_SdtPr_citation"; break;
+ case NS_ooxml::LN_CT_SdtPr_group: sName = "ooxml:CT_SdtPr_group"; break;
+ case NS_ooxml::LN_CT_SdtPr_id: sName = "ooxml:CT_SdtPr_id"; break;
+ case NS_ooxml::LN_CT_SdtPr_alias: sName = "ooxml:CT_SdtPr_alias"; break;
+ case NS_ooxml::LN_CT_SdtPlaceholder_docPart: sName = "ooxml:CT_SdtPlaceholder_docPart"; break;
+ case NS_ooxml::LN_CT_SdtPr_color: sName = "ooxml:CT_SdtPr_color"; break;
+ default: assert(false);
+ };
+ if (
+ nSprmId == NS_ooxml::LN_CT_SdtPr_checkbox ||
+ nSprmId == NS_ooxml::LN_CT_SdtPr_docPartObj ||
+ nSprmId == NS_ooxml::LN_CT_SdtPr_docPartList ||
+ nSprmId == NS_ooxml::LN_CT_SdtPr_picture ||
+ nSprmId == NS_ooxml::LN_CT_SdtPr_citation)
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::unsupported);
+ }
+ enableInteropGrabBag(sName);
+
+ // process subitems
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+
+ if (nSprmId == NS_ooxml::LN_CT_SdtPr_alias)
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = sName;
+ aValue.Value <<= sStringValue;
+ m_pImpl->m_pSdtHelper->appendToInteropGrabBag(aValue);
+ }
+ else
+ m_pImpl->m_pSdtHelper->appendToInteropGrabBag(getInteropGrabBag());
+ m_pImpl->m_pSdtHelper->setOutsideAParagraph(m_pImpl->IsOutsideAParagraph());
+ m_pImpl->disableInteropGrabBag();
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtCheckbox_checked:
+ if (!m_pImpl->GetSdtStarts().empty())
+ {
+ // nIntValue is not just 0 or 1, because we're in the w14 namespace's ST_OnOff.
+ if (nIntValue == NS_ooxml::LN_ST_OnOff_true || nIntValue == NS_ooxml::LN_ST_OnOff_1)
+ {
+ m_pImpl->m_pSdtHelper->SetChecked();
+ }
+ }
+ else
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtCheckbox_checked",
+ TextEffectsHandler::getOnOffString(nIntValue));
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtCheckbox_checkedState:
+ if (!m_pImpl->GetSdtStarts().empty())
+ {
+ m_pImpl->m_pSdtHelper->SetCheckedState(OUString(sal_Unicode(sStringValue.toInt32(16))));
+ }
+ else
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtCheckbox_checkedState",
+ sStringValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtCheckbox_uncheckedState:
+ if (!m_pImpl->GetSdtStarts().empty())
+ {
+ m_pImpl->m_pSdtHelper->SetUncheckedState(
+ OUString(sal_Unicode(sStringValue.toInt32(16))));
+ }
+ else
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag,
+ "ooxml:CT_SdtCheckbox_uncheckedState", sStringValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtDocPart_docPartGallery:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtDocPart_docPartGallery", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_SdtDocPart_docPartCategory:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtDocPart_docPartCategory", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_SdtDocPart_docPartUnique:
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtDocPart_docPartUnique", sStringValue);
+ break;
+ case NS_ooxml::LN_EG_SectPrContents_pgNumType:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ pProperties->resolve(*this);
+ }
+ }
+ break;
+ case NS_ooxml::LN_tblStart:
+ {
+ if (m_pImpl->hasTableManager())
+ {
+ bool bTableStartsAtCellStart = m_pImpl->m_nTableDepth > 0 && m_pImpl->m_nTableCellDepth > m_pImpl->m_nLastTableCellParagraphDepth + 1;
+ m_pImpl->getTableManager().setTableStartsAtCellStart(bTableStartsAtCellStart);
+ }
+ /*
+ * Hack for Importing Section Properties
+ * LO is not able to import section properties if first element in the
+ * section is a table. So in case first element is a table add a dummy para
+ * and remove it again when lcl_endSectionGroup is called
+ */
+ if(m_pImpl->m_nTableDepth == 0 && m_pImpl->GetIsFirstParagraphInSection()
+ && !m_pImpl->GetIsDummyParaAddedForTableInSection() && !m_pImpl->GetIsTextFrameInserted()
+ && !IsInHeaderFooter())
+ {
+ m_pImpl->AddDummyParaForTableInSection();
+ }
+
+ // if first paragraph style in table has break-before-page, transfer that setting to the table itself.
+ if( m_pImpl->m_nTableDepth == 0 )
+ {
+ const uno::Any aBreakType(style::BreakType_PAGE_BEFORE);
+ const PropertyMapPtr pParagraphProps = m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH);
+ if( pParagraphProps && pParagraphProps->isSet(PROP_PARA_STYLE_NAME) )
+ {
+ StyleSheetEntryPtr pStyle;
+ OUString sStyleName;
+ pParagraphProps->getProperty(PROP_PARA_STYLE_NAME)->second >>= sStyleName;
+ if( !sStyleName.isEmpty() && GetStyleSheetTable() )
+ pStyle = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( sStyleName );
+
+ if( pStyle && pStyle->pProperties
+ && pStyle->pProperties->isSet(PROP_BREAK_TYPE)
+ && pStyle->pProperties->getProperty(PROP_BREAK_TYPE)->second == aBreakType )
+ {
+ pParagraphProps->Insert(PROP_BREAK_TYPE, aBreakType);
+ }
+ }
+ }
+
+ m_pImpl->m_nTableDepth++;
+ }
+ break;
+ case NS_ooxml::LN_tblEnd:
+ m_pImpl->m_nTableDepth--;
+ break;
+ case NS_ooxml::LN_tcStart:
+ m_pImpl->m_nTableCellDepth++;
+ break;
+ case NS_ooxml::LN_tcEnd:
+ m_pImpl->m_nTableCellDepth--;
+ m_pImpl->m_nLastTableCellParagraphDepth = 0;
+ break;
+ case NS_ooxml::LN_glow_glow:
+ case NS_ooxml::LN_shadow_shadow:
+ case NS_ooxml::LN_reflection_reflection:
+ case NS_ooxml::LN_textOutline_textOutline:
+ case NS_ooxml::LN_textFill_textFill:
+ case NS_ooxml::LN_scene3d_scene3d:
+ case NS_ooxml::LN_props3d_props3d:
+ case NS_ooxml::LN_ligatures_ligatures:
+ case NS_ooxml::LN_numForm_numForm:
+ case NS_ooxml::LN_numSpacing_numSpacing:
+ case NS_ooxml::LN_stylisticSets_stylisticSets:
+ case NS_ooxml::LN_cntxtAlts_cntxtAlts:
+ {
+ tools::SvRef<TextEffectsHandler> pTextEffectsHandlerPtr( new TextEffectsHandler(nSprmId) );
+ std::optional<PropertyIds> aPropertyId = pTextEffectsHandlerPtr->getGrabBagPropertyId();
+ if(aPropertyId)
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ pProperties->resolve(*pTextEffectsHandlerPtr);
+
+ beans::PropertyValue aGrabBag = pTextEffectsHandlerPtr->getInteropGrabBag();
+ rContext->Insert(*aPropertyId, uno::Any(aGrabBag), true, CHAR_GRAB_BAG);
+
+ sal_Int16 nTransparency = TextEffectsHandler::GetTextFillSolidFillAlpha(aGrabBag);
+ if (nTransparency != 0)
+ {
+ rContext->Insert(PROP_CHAR_TRANSPARENCE, uno::Any(nTransparency));
+ }
+ }
+ else if (nSprmId == NS_ooxml::LN_cntxtAlts_cntxtAlts)
+ {
+ pTextEffectsHandlerPtr->lcl_sprm(rSprm);
+ beans::PropertyValue aGrabBag = pTextEffectsHandlerPtr->getInteropGrabBag();
+ rContext->Insert(*aPropertyId, uno::Any(aGrabBag), true, CHAR_GRAB_BAG);
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_SdtPr_rPr:
+ {
+ // Make sure properties from a previous SDT are not merged with the current ones.
+ m_pImpl->m_pSdtHelper->getInteropGrabBagAndClear();
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblLook:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+ m_pImpl->getTableManager().finishTableLook();
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_cnfStyle:
+ {
+ m_pImpl->enableInteropGrabBag("cnfStyle");
+ resolveSprmProps(*this, rSprm);
+
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(PROP_ROW_CNF_STYLE, uno::Any(comphelper::containerToSequence(m_pImpl->m_aInteropGrabBag)), true, ROW_GRAB_BAG);
+ m_pImpl->getTableManager().insertRowProps(pPropMap);
+
+ m_pImpl->disableInteropGrabBag();
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_cnfStyle:
+ {
+ m_pImpl->enableInteropGrabBag("cnfStyle");
+ resolveSprmProps(*this, rSprm);
+
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(PROP_CELL_CNF_STYLE, uno::Any(comphelper::containerToSequence(m_pImpl->m_aInteropGrabBag)), true, CELL_GRAB_BAG);
+ m_pImpl->getTableManager().cellProps(pPropMap);
+
+ m_pImpl->disableInteropGrabBag();
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrBase_cnfStyle:
+ {
+ m_pImpl->enableInteropGrabBag("cnfStyle");
+ resolveSprmProps(*this, rSprm);
+ rContext->Insert(PROP_PARA_CNF_STYLE, uno::Any(comphelper::containerToSequence(m_pImpl->m_aInteropGrabBag)), true, PARA_GRAB_BAG);
+ m_pImpl->disableInteropGrabBag();
+ }
+ break;
+ case NS_ooxml::LN_EG_RunInnerContent_sym:
+ {
+ resolveSprmProps(*this, rSprm);
+ SymbolData aSymbolData = m_pImpl->GetSymbolData();
+ uno::Any aVal( aSymbolData.sFont );
+ auto xFootnote = rContext->GetFootnote();
+ if (!xFootnote.is() && m_pImpl->IsInCustomFootnote())
+ xFootnote = m_pImpl->GetFootnoteContext()->GetFootnote();
+ if (xFootnote.is())
+ {
+ // DOCX can have different labels for the footnote reference and the footnote area.
+ // This skips the one from the footnote area and just uses the reference one.
+ if (!m_pImpl->IsInFootOrEndnote())
+ {
+ auto xAnchorRange = xFootnote->getAnchor();
+ auto xAnchorCursor(xAnchorRange->getText()->createTextCursorByRange(xAnchorRange));
+
+ // append a dummy character, so the following properties will be set as
+ // as SwpHints::SwTextAttr instead of the SwAttrSet of the paragraph,
+ // which would be removed by SwXText::Impl::finishOrAppendParagraph
+ xAnchorCursor->collapseToEnd();
+ uno::Reference<text::XTextRange> xHackRange(xAnchorCursor, uno::UNO_QUERY);
+ xHackRange->setString("x");
+
+ uno::Reference<beans::XPropertySet> xAnchorProps(xAnchorRange, uno::UNO_QUERY);
+ xAnchorProps->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME), aVal);
+ xAnchorProps->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_ASIAN), aVal);
+ xAnchorProps->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_COMPLEX), aVal);
+ xAnchorProps->setPropertyValue(getPropertyName(PROP_CHAR_FONT_CHAR_SET), uno::Any(awt::CharSet::SYMBOL));
+
+ // remove the dummy char
+ xHackRange->setString("");
+
+ OUString sLabel = xFootnote->getLabel() + OUStringChar(aSymbolData.cSymbol);
+ xFootnote->setLabel(sLabel);
+ }
+ }
+ else //it's a _real_ symbol
+ {
+ rContext->Insert(PROP_CHAR_FONT_NAME, aVal);
+ rContext->Insert(PROP_CHAR_FONT_NAME_ASIAN, aVal);
+ rContext->Insert(PROP_CHAR_FONT_NAME_COMPLEX, aVal);
+ rContext->Insert(PROP_CHAR_FONT_CHAR_SET, uno::Any(awt::CharSet::SYMBOL));
+ utext( reinterpret_cast < const sal_uInt8 * >( &(aSymbolData.cSymbol) ), 1 );
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_RunInnerContent_ruby:
+ {
+ RubyInfo aInfo ;
+ m_pImpl->SetRubyInfo(aInfo);
+ }
+ break;
+ case NS_ooxml::LN_CT_RubyPr:
+ case NS_ooxml::LN_CT_Ruby_rt:
+ case NS_ooxml::LN_CT_Ruby_rubyBase:
+ {
+ m_pImpl->SetRubySprmId(nSprmId);
+ if (nSprmId == NS_ooxml::LN_CT_RubyPr)
+ {
+ resolveSprmProps(*this, rSprm);
+ }
+ }
+ break;
+ case NS_ooxml::LN_EG_RubyContent_r:
+ {
+ const RubyInfo & aInfo = m_pImpl->GetRubyInfo();
+ if (aInfo.nSprmId == NS_ooxml::LN_CT_Ruby_rubyBase)
+ {
+ rContext->Insert(PROP_RUBY_TEXT, uno::Any(aInfo.sRubyText));
+ rContext->Insert(PROP_RUBY_STYLE, uno::Any(aInfo.sRubyStyle));
+ rContext->Insert(PROP_RUBY_ADJUST, uno::Any(static_cast<sal_Int16>(ConversionHelper::convertRubyAlign(aInfo.nRubyAlign))));
+ if ( aInfo.nRubyAlign == NS_ooxml::LN_Value_ST_RubyAlign_rightVertical )
+ rContext->Insert(PROP_RUBY_POSITION, uno::Any(css::text::RubyPosition::INTER_CHARACTER));
+
+ m_pImpl->SetRubySprmId(0);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_RubyPr_rubyAlign:
+ case NS_ooxml::LN_CT_RubyPr_hps:
+ case NS_ooxml::LN_CT_RubyPr_hpsBaseText:
+ {
+ RubyInfo aInfo = m_pImpl->GetRubyInfo();
+ switch(nSprmId)
+ {
+ case NS_ooxml::LN_CT_RubyPr_rubyAlign:
+ aInfo.nRubyAlign = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_RubyPr_hps:
+ aInfo.nHps= nIntValue;
+ break;
+ case NS_ooxml::LN_CT_RubyPr_hpsBaseText:
+ aInfo.nHpsBaseText = nIntValue;
+ break;
+ }
+ m_pImpl->SetRubyInfo(aInfo);
+ }
+ break;
+ case NS_ooxml::LN_CT_SmartTagRun_smartTagPr:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties && m_pImpl->GetTopContextType() == CONTEXT_PARAGRAPH)
+ pProperties->resolve(m_pImpl->getSmartTagHandler());
+ }
+ break;
+ case NS_ooxml::LN_CT_DocPartPr_name:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_DocPartPr_category:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_DocPartCategory_gallery:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ default:
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("unhandled");
+ TagLogger::getInstance().attribute("id", nSprmId);
+ TagLogger::getInstance().attribute("name", rSprm.getName());
+ TagLogger::getInstance().endElement();
+#endif
+ }
+ }
+}
+
+void DomainMapper::processDeferredCharacterProperties( const std::map< sal_Int32, uno::Any >& deferredCharacterProperties )
+{
+ assert( m_pImpl->GetTopContextType() == CONTEXT_CHARACTER );
+ PropertyMapPtr rContext = m_pImpl->GetTopContext();
+ for( const auto& rProp : deferredCharacterProperties )
+ {
+ sal_Int32 Id = rProp.first;
+ sal_Int32 nIntValue = 0;
+ OUString sStringValue;
+ rProp.second >>= nIntValue;
+ rProp.second >>= sStringValue;
+ switch( Id )
+ {
+ case NS_ooxml::LN_EG_RPrBase_position:
+ {
+ double nEscapement = 0;
+ sal_Int8 nProp = 0;
+ if ( nIntValue )
+ {
+ nProp = 100;
+ double fFontSize = 0;
+ m_pImpl->GetAnyProperty(PROP_CHAR_HEIGHT, rContext) >>= fFontSize;
+ if ( fFontSize )
+ // nIntValue is in half-points, fontsize is in points, escapement is a percentage.
+ nEscapement = round( nIntValue/2.0 / fFontSize * 100 );
+ else
+ nEscapement = nIntValue > 0 ? DFLT_ESC_SUPER : DFLT_ESC_SUB;
+ }
+ if ( nEscapement > MAX_ESC_POS )
+ nEscapement = MAX_ESC_POS;
+ else if ( nEscapement < -MAX_ESC_POS )
+ nEscapement = -MAX_ESC_POS;
+
+ rContext->Insert(PROP_CHAR_ESCAPEMENT, uno::Any( sal_Int16(nEscapement) ) );
+ rContext->Insert(PROP_CHAR_ESCAPEMENT_HEIGHT, uno::Any( nProp ) );
+ }
+ break;
+ default:
+ SAL_WARN( "writerfilter", "Unhandled property in processDeferredCharacterProperty()" );
+ break;
+ }
+ }
+}
+
+void DomainMapper::lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref)
+{
+ ref->resolve(*this);
+}
+
+void DomainMapper::data(const sal_uInt8* /*buf*/, size_t /*len*/)
+{
+}
+
+void DomainMapper::lcl_startSectionGroup()
+{
+ if (!m_pImpl->isInIndexContext() && !m_pImpl->isInBibliographyContext())
+ {
+ m_pImpl->PushProperties(CONTEXT_SECTION);
+ }
+ m_pImpl->SetIsFirstParagraphInSection(true);
+ m_pImpl->SetIsFirstParagraphInSectionAfterRedline(true);
+}
+
+void DomainMapper::lcl_endSectionGroup()
+{
+ if (m_pImpl->isInIndexContext() || m_pImpl->isInBibliographyContext())
+ return;
+
+ m_pImpl->CheckUnregisteredFrameConversion();
+ m_pImpl->ExecuteFrameConversion();
+ // When pasting, it's fine to not have any paragraph inside the document at all.
+ if (m_pImpl->GetIsFirstParagraphInSection() && m_pImpl->IsNewDoc())
+ {
+ // This section has no paragraph at all (e.g. they are all actually in a frame).
+ // If this section has a page break, there would be nothing to apply to the page
+ // style, so force a dummy paragraph.
+ lcl_startParagraphGroup();
+ lcl_startCharacterGroup();
+ sal_uInt8 const sBreak[] = { 0xd };
+ lcl_text(sBreak, 1);
+ lcl_endCharacterGroup();
+ lcl_endParagraphGroup();
+ }
+ PropertyMapPtr pContext = m_pImpl->GetTopContextOfType(CONTEXT_SECTION);
+ SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
+ OSL_ENSURE(pSectionContext, "SectionContext unavailable!");
+ if(pSectionContext)
+ {
+ pSectionContext->CloseSectionGroup( *m_pImpl );
+ // Remove the dummy paragraph if added for
+ // handling the section properties if section starts with a table
+ if (m_pImpl->GetIsDummyParaAddedForTableInSection())
+ m_pImpl->RemoveDummyParaForTableInSection();
+ }
+ m_pImpl->SetIsTextFrameInserted( false );
+ m_pImpl->PopProperties(CONTEXT_SECTION);
+}
+
+void DomainMapper::lcl_startParagraphGroup()
+{
+ if (m_pImpl->hasTableManager())
+ m_pImpl->getTableManager().startParagraphGroup();
+ /*
+ * Add new para properties only if paragraph is not split
+ * or the top context is not of paragraph properties
+ * Set mbIsSplitPara to false as it has been handled
+ */
+ if (!mbIsSplitPara)
+ m_pImpl->PushProperties(CONTEXT_PARAGRAPH);
+ mbIsSplitPara = false;
+ if (m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH) != m_pImpl->GetTopContext())
+ m_pImpl->PushProperties(CONTEXT_PARAGRAPH);
+
+ if (m_pImpl->GetTopContext())
+ {
+ if (!m_pImpl->IsInShape())
+ {
+ const OUString& sDefaultParaStyle = m_pImpl->GetDefaultParaStyleName();
+ m_pImpl->GetTopContext()->Insert( PROP_PARA_STYLE_NAME, uno::Any( sDefaultParaStyle ) );
+ m_pImpl->SetCurrentParaStyleName( sDefaultParaStyle );
+
+ if (m_pImpl->isBreakDeferred(PAGE_BREAK))
+ m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_PAGE_BEFORE));
+ else if (m_pImpl->isBreakDeferred(COLUMN_BREAK))
+ m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_COLUMN_BEFORE));
+ mbWasShapeInPara = false;
+ }
+
+ if (m_pImpl->isParaSdtEndDeferred())
+ m_pImpl->GetTopContext()->Insert(PROP_PARA_SDT_END_BEFORE, uno::Any(true), true, PARA_GRAB_BAG);
+ }
+ m_pImpl->SetIsFirstRun(true);
+ m_pImpl->SetIsOutsideAParagraph(false);
+ if (!m_pImpl->IsInShape())
+ m_pImpl->clearDeferredBreaks();
+ m_pImpl->setParaSdtEndDeferred(false);
+}
+
+void DomainMapper::lcl_endParagraphGroup()
+{
+ if (m_pImpl->isBreakDeferred(LINE_BREAK))
+ {
+ if (m_pImpl->GetIsLastParagraphInSection())
+ m_pImpl->clearDeferredBreak(LINE_BREAK);
+
+ while (m_pImpl->isBreakDeferred(LINE_BREAK))
+ {
+ m_pImpl->clearDeferredBreak(LINE_BREAK);
+ m_pImpl->appendTextPortion("\n", m_pImpl->GetTopContext());
+ }
+ }
+
+ m_pImpl->PopProperties(CONTEXT_PARAGRAPH);
+ if (m_pImpl->hasTableManager())
+ m_pImpl->getTableManager().endParagraphGroup();
+ //frame conversion has to be executed after table conversion
+ m_pImpl->ExecuteFrameConversion();
+ m_pImpl->SetIsOutsideAParagraph(true);
+}
+
+void DomainMapper::markLastParagraphInSection( )
+{
+ m_pImpl->SetIsLastParagraphInSection( true );
+}
+
+void DomainMapper::markLastSectionGroup( )
+{
+ m_pImpl->SetIsLastSectionGroup( true );
+}
+
+void DomainMapper::lcl_startShape(uno::Reference<drawing::XShape> const& xShape)
+{
+ assert(xShape.is());
+
+ m_pImpl->AttachTextBoxContentToShape(xShape);
+ if (m_pImpl->GetTopContext())
+ {
+ // If there is a deferred page break, handle it now, so that the
+ // started shape will be on the correct page.
+ if (m_pImpl->isBreakDeferred(PAGE_BREAK))
+ {
+ m_pImpl->clearDeferredBreak(PAGE_BREAK);
+ lcl_startCharacterGroup();
+ sal_uInt8 const sBreak[] = { 0xd };
+ lcl_text(sBreak, 1);
+ lcl_endCharacterGroup();
+ lcl_endParagraphGroup();
+ lcl_startParagraphGroup();
+ m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_PAGE_BEFORE));
+ }
+ m_pImpl->PushShapeContext( xShape );
+ lcl_startParagraphGroup();
+ }
+ else
+ {
+ // No context? Then this image should not appear directly inside the
+ // document, just save it for later usage.
+ m_pImpl->PushPendingShape(xShape);
+ }
+
+ m_pImpl->SetIsFirstParagraphInShape(true);
+ mbWasShapeInPara = true;
+}
+
+void DomainMapper::lcl_endShape( )
+{
+ if (!m_pImpl->GetTopContext())
+ return;
+
+ // End the current table, if there are any. Otherwise the unavoidable
+ // empty paragraph at the end of the shape text will cause problems: if
+ // the shape text ends with a table, the extra paragraph will be
+ // handled as an additional row of the ending table.
+ if (m_pImpl->hasTableManager())
+ m_pImpl->getTableManager().endTable();
+
+ lcl_endParagraphGroup();
+ m_pImpl->PopShapeContext( );
+ // A shape is always inside a paragraph (anchored or inline).
+ m_pImpl->SetIsOutsideAParagraph(false);
+}
+
+void DomainMapper::lcl_startTextBoxContent()
+{
+ m_pImpl->PushTextBoxContent();
+}
+
+void DomainMapper::lcl_endTextBoxContent()
+{
+ m_pImpl->PopTextBoxContent();
+}
+
+void DomainMapper::PushStyleSheetProperties( const PropertyMapPtr& pStyleProperties, bool bAffectTableMngr )
+{
+ m_pImpl->PushStyleProperties( pStyleProperties );
+ if ( bAffectTableMngr )
+ m_pImpl->getTableManager( ).SetStyleProperties( pStyleProperties );
+}
+
+void DomainMapper::PopStyleSheetProperties( bool bAffectTableMngr )
+{
+ m_pImpl->PopProperties( CONTEXT_STYLESHEET );
+ if ( bAffectTableMngr )
+ {
+ PropertyMapPtr emptyPtr;
+ m_pImpl->getTableManager( ).SetStyleProperties( emptyPtr );
+ }
+}
+
+void DomainMapper::PushListProperties( const ::tools::SvRef<PropertyMap>& pListProperties )
+{
+ m_pImpl->PushListProperties( pListProperties );
+}
+
+void DomainMapper::PopListProperties()
+{
+ m_pImpl->PopProperties( CONTEXT_LIST );
+}
+
+void DomainMapper::lcl_startCharacterGroup()
+{
+ m_pImpl->PushProperties(CONTEXT_CHARACTER);
+ if (m_pImpl->isSdtEndDeferred())
+ {
+ // Fields have an empty character group before the real one, so don't
+ // call setSdtEndDeferred(false) here, that will happen only in lcl_utext().
+ m_pImpl->GetTopContext()->Insert(PROP_SDT_END_BEFORE, uno::Any(true), true, CHAR_GRAB_BAG);
+ }
+}
+
+void DomainMapper::lcl_endCharacterGroup()
+{
+ if (m_pImpl->CheckFootnoteStyle())
+ {
+ m_pImpl->SetCheckFootnoteStyle(m_pImpl->IsInCustomFootnote());
+ m_pImpl->SetHasFootnoteStyle(false);
+ }
+ m_pImpl->PopProperties(CONTEXT_CHARACTER);
+}
+
+void DomainMapper::lcl_text(const sal_uInt8 * data_, size_t len)
+{
+ //TODO: Determine the right text encoding (FIB?)
+ OUString sText( reinterpret_cast<const char*>(data_), len, RTL_TEXTENCODING_MS_1252 );
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("text");
+ TagLogger::getInstance().chars(sText);
+ TagLogger::getInstance().endElement();
+#endif
+
+ try
+ {
+ if(len == 1)
+ {
+ switch(*data_)
+ {
+ case 0x02: return; //footnote character
+ case 0x08: // Lock field if in field context
+ if (m_pImpl->IsOpenField())
+ m_pImpl->SetFieldLocked();
+ return;
+ case 0x0c: //page break
+ // page breaks aren't supported in footnotes and endnotes
+ if (!m_pImpl->IsInFootOrEndnote())
+ {
+ m_pImpl->deferBreak(PAGE_BREAK);
+ m_pImpl->SetIsDummyParaAddedForTableInSectionPage(false);
+ }
+ return;
+ case 0x0e: //column break
+ m_pImpl->deferBreak(COLUMN_BREAK);
+ return;
+ case 0x0a: //line break
+ if (m_pImpl->GetIsLastParagraphInSection())
+ {
+ m_pImpl->deferBreak(LINE_BREAK);
+ return;
+ }
+ break;
+ case 0x07:
+ m_pImpl->getTableManager().text(data_, len);
+ return;
+ case 0x0d:
+ {
+ PropertyMapPtr pContext = m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH);
+ if (pContext && m_pImpl->isBreakDeferred(COLUMN_BREAK))
+ {
+ pContext->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_COLUMN_BEFORE));
+ m_pImpl->clearDeferredBreak(COLUMN_BREAK);
+ }
+ finishParagraph();
+ return;
+ }
+ case cFieldStart:
+ m_pImpl->PushFieldContext();
+ return;
+ case cFieldSep:
+ // delimiter not necessarily available
+ // appears only if field contains further content
+ m_pImpl->CloseFieldCommand();
+ return;
+ case cFieldEnd:
+ m_pImpl->PopFieldContext();
+ return;
+ default:
+ break;
+ }
+ }
+
+ // GetTopContext() is changed by inserted breaks, but we want to keep the current context
+ PropertyMapPtr pContext = m_pImpl->GetTopContext();
+
+ while (m_pImpl->isBreakDeferred(LINE_BREAK))
+ {
+ m_pImpl->clearDeferredBreak(LINE_BREAK);
+ m_pImpl->appendTextPortion("\n", pContext);
+ }
+
+ if (!m_pImpl->GetFootnoteContext())
+ {
+ if (m_pImpl->isBreakDeferred(PAGE_BREAK))
+ m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_PAGE_BEFORE));
+ else if (m_pImpl->isBreakDeferred(COLUMN_BREAK))
+ m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_COLUMN_BEFORE));
+ m_pImpl->clearDeferredBreaks();
+ }
+
+ if (pContext && pContext->GetFootnote().is() && m_pImpl->IsInCustomFootnote())
+ {
+ pContext->GetFootnote()->setLabel(sText);
+ m_pImpl->EndCustomFootnote();
+ //otherwise ignore sText
+ }
+ else if (m_pImpl->IsOpenFieldCommand() && !m_pImpl->IsForceGenericFields())
+ {
+ m_pImpl->AppendFieldCommand(sText);
+ }
+ else if( m_pImpl->IsOpenField() && m_pImpl->IsFieldResultAsString())
+ /*depending on the success of the field insert operation this result will be
+ set at the field or directly inserted into the text*/
+ m_pImpl->AppendFieldResult(sText);
+ else
+ {
+ if (pContext == nullptr)
+ pContext = new PropertyMap();
+
+ if (sText == "\n")
+ {
+ m_pImpl->HandleLineBreak(pContext);
+ }
+ else
+ {
+ m_pImpl->appendTextPortion(sText, pContext);
+ }
+ }
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter", "");
+ }
+}
+
+void DomainMapper::lcl_positionOffset(const OUString& rText, bool bVertical)
+{
+ if (bVertical)
+ m_pImpl->m_aPositionOffsets.second = rText;
+ else
+ m_pImpl->m_aPositionOffsets.first = rText;
+}
+
+awt::Point DomainMapper::getPositionOffset()
+{
+ awt::Point aRet;
+ aRet.X = oox::drawingml::convertEmuToHmm(m_pImpl->m_aPositionOffsets.first.toInt32());
+ aRet.Y = oox::drawingml::convertEmuToHmm(m_pImpl->m_aPositionOffsets.second.toInt32());
+ return aRet;
+}
+
+void DomainMapper::lcl_align(const OUString& rText, bool bVertical)
+{
+ if (bVertical)
+ m_pImpl->m_aAligns.second = rText;
+ else
+ m_pImpl->m_aAligns.first = rText;
+}
+
+void DomainMapper::lcl_positivePercentage(const OUString& rText)
+{
+ m_pImpl->m_aPositivePercentages.push(rText);
+}
+
+void DomainMapper::lcl_checkId(const sal_Int32 nId)
+{
+ if (m_pImpl->IsInFootnote())
+ {
+ m_pImpl->m_aFootnoteIds.push_back(nId);
+ // keep only the first real footnote
+ if (m_pImpl->GetFootnoteCount() == -1 && m_pImpl->m_aFootnoteIds.size() == 2)
+ m_pImpl->m_aFootnoteIds.pop_front();
+ }
+ else
+ {
+ m_pImpl->m_aEndnoteIds.push_back(nId);
+ // keep only the first real endnote
+ if (m_pImpl->GetEndnoteCount() == -1 && m_pImpl->m_aEndnoteIds.size() == 2)
+ m_pImpl->m_aEndnoteIds.pop_front();
+ }
+}
+
+void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len)
+{
+ // All these fixed values are defined as static const sal_Unicode codepoints in the fast parser,
+ // like uFtnEdnRef = 0x2, uFtnEdnSep = 0x3, ... and have a len of 1, if they aren't valid unicode.
+
+ OUString sText(reinterpret_cast<const sal_Unicode *>(data_), len);
+ const RubyInfo & aInfo = m_pImpl->GetRubyInfo();
+ if (aInfo.nSprmId == NS_ooxml::LN_CT_Ruby_rt)
+ {
+ PropertyMapPtr pContext = m_pImpl->GetTopContext();
+ PropertyValueVector_t aProps = comphelper::sequenceToContainer< PropertyValueVector_t >(pContext->GetPropertyValues());
+ OUString sStyle = getOrCreateCharStyle(aProps, /*bAlwaysCreate=*/false);
+ m_pImpl->SetRubyText(sText,sStyle);
+ return;
+ }
+
+ if (len == 1)
+ {
+ // preload all footnotes in separated footnotes
+ if (sText[0] == 0x5)
+ {
+ if (m_pImpl->IsInFootnote())
+ {
+ if (m_pImpl->GetFootnoteCount() > -1)
+ {
+ m_pImpl->PopFootOrEndnote();
+ m_pImpl->PushFootOrEndnote(/*bIsFootnote=*/true);
+ }
+ m_pImpl->IncrementFootnoteCount();
+ }
+ else
+ {
+ if (m_pImpl->GetEndnoteCount() > -1)
+ {
+ m_pImpl->PopFootOrEndnote();
+ m_pImpl->PushFootOrEndnote(/*bIsFootnote=*/false);
+ }
+ m_pImpl->IncrementEndnoteCount();
+ }
+ }
+
+ // If the footnote contains a Footnote Reference Mark, it can't be a custom footnote
+ // ******
+ // This code block is wrong, as it should also be in m_pImpl->IsInFootOrEndnote().
+ // The main problem is that
+ //
+ // assert(len != 1 || sText[0] != 0x2)
+ //
+ // is triggered by the unit test SwLayoutWriter::testForcepoint75, so all these pseudo
+ // value handling is broken.
+ // But this is just a symptom, as I guess it's possible to generate broken DOCX documents,
+ // which might be problematic, triggering *funny* code paths left and right.
+ // ******
+ if (sText[0] == 0x2)
+ {
+ m_pImpl->EndCustomFootnote();
+ return;
+ }
+
+ if (m_pImpl->IsInCustomFootnote())
+ {
+ if (sText[0] != 0xd && sText[0] != 0x3)
+ {
+ // DOCX can have different labels for the footnote reference and the footnote area.
+ // This skips the one from the footnote area and just uses the reference one.
+ if (!m_pImpl->IsInFootOrEndnote())
+ {
+ if (PropertyMapPtr pFootnoteContext = m_pImpl->GetFootnoteContext())
+ {
+ auto xFootnote = pFootnoteContext->GetFootnote();
+ xFootnote->setLabel(xFootnote->getLabel() + sText);
+ }
+ }
+ return;
+ }
+ else
+ m_pImpl->SetHasFootnoteStyle(true);
+ }
+ }
+
+ if (m_pImpl->isSdtEndDeferred())
+ {
+ // In case we have a field context, then save the property there, so
+ // SDT's ending right before a field start are handled as well.
+ PropertyMapPtr pContext = m_pImpl->GetTopContext();
+ if (m_pImpl->IsOpenField())
+ pContext = m_pImpl->GetTopFieldContext()->getProperties();
+ pContext->Insert(PROP_SDT_END_BEFORE, uno::Any(true), true, CHAR_GRAB_BAG);
+ m_pImpl->setSdtEndDeferred(false);
+ }
+
+ bool bNewLine = len == 1 && (sText[0] == 0x0d || sText[0] == 0x07);
+ if (m_pImpl->GetSdtStarts().empty() && m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::dropDown)
+ {
+ // Block, cell or row SDT.
+ if (bNewLine)
+ // Dropdown control has single-line texts, so in case of newline, create the control.
+ m_pImpl->m_pSdtHelper->createDropDownControl();
+ else
+ {
+ m_pImpl->m_pSdtHelper->getSdtTexts().append(sText);
+ return;
+ }
+ }
+ else if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::datePicker)
+ {
+ if (IsInHeaderFooter() && m_pImpl->IsDiscardHeaderFooter())
+ {
+ m_pImpl->m_pSdtHelper->getDateFormat().truncate();
+ m_pImpl->m_pSdtHelper->getLocale().truncate();
+ return;
+ }
+ }
+ else if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::plainText)
+ {
+ m_pImpl->m_pSdtHelper->getSdtTexts().append(sText);
+ if (bNewLine)
+ {
+ m_pImpl->m_pSdtHelper->createPlainTextControl();
+ finishParagraph();
+ }
+ return;
+ }
+ else if (!m_pImpl->m_pSdtHelper->isInteropGrabBagEmpty())
+ {
+ // there are unsupported SDT properties in the document
+ // save them in the paragraph interop grab bag
+ if (m_pImpl->IsDiscardHeaderFooter())
+ {
+ // Unless we're supposed to ignore this header/footer.
+ m_pImpl->m_pSdtHelper->getInteropGrabBagAndClear();
+ return;
+ }
+ if((m_pImpl->m_pSdtHelper->containedInInteropGrabBag("ooxml:CT_SdtPr_checkbox") ||
+ m_pImpl->m_pSdtHelper->containedInInteropGrabBag("ooxml:CT_SdtPr_text") ||
+ m_pImpl->m_pSdtHelper->containedInInteropGrabBag("ooxml:CT_SdtPr_dataBinding") ||
+ m_pImpl->m_pSdtHelper->containedInInteropGrabBag("ooxml:CT_SdtPr_citation") ||
+ (m_pImpl->m_pSdtHelper->containedInInteropGrabBag("ooxml:CT_SdtPr_id") &&
+ m_pImpl->m_pSdtHelper->getInteropGrabBagSize() == 1)) && !m_pImpl->m_pSdtHelper->isOutsideAParagraph())
+ {
+ PropertyMapPtr pContext = m_pImpl->GetTopContextOfType(CONTEXT_CHARACTER);
+
+ if (m_pImpl->IsOpenField())
+ // We have a field, insert the SDT properties to the field's grab-bag, so they won't be lost.
+ pContext = m_pImpl->GetTopFieldContext()->getProperties();
+
+ uno::Sequence<beans::PropertyValue> aGrabBag = m_pImpl->m_pSdtHelper->getInteropGrabBagAndClear();
+ if (m_pImpl->GetSdtStarts().empty() || m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::dropDown)
+ {
+ pContext->Insert(PROP_SDTPR, uno::Any(aGrabBag), true, CHAR_GRAB_BAG);
+ }
+ }
+ else
+ {
+ uno::Sequence<beans::PropertyValue> aGrabBag = m_pImpl->m_pSdtHelper->getInteropGrabBagAndClear();
+ if (m_pImpl->GetSdtStarts().empty()
+ || (m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::dropDown && m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::richText))
+ {
+ m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH)->Insert(PROP_SDTPR,
+ uno::Any(aGrabBag), true, PARA_GRAB_BAG);
+ }
+ }
+ }
+ else if (len == 1 && sText[0] == 0x03)
+ {
+ // This is the uFtnEdnSep, remember that the document has a separator.
+ m_pImpl->m_bHasFtnSep = true;
+ return;
+ }
+ else if (len == 1 && sText[0] == '\r')
+ {
+ // Clear "last" one linebreak at end of section
+ if (m_pImpl->GetIsLastParagraphInSection() && m_pImpl->isBreakDeferred(LINE_BREAK))
+ m_pImpl->clearDeferredBreak(LINE_BREAK);
+ // And emit all other linebreaks
+ while (m_pImpl->isBreakDeferred(LINE_BREAK))
+ {
+ m_pImpl->clearDeferredBreak(LINE_BREAK);
+ m_pImpl->appendTextPortion("\n", m_pImpl->GetTopContext());
+ }
+ }
+ else if (len == 1 && sText[0] == '\t' )
+ {
+ if ( m_pImpl->m_bCheckFirstFootnoteTab && m_pImpl->IsInFootOrEndnote() )
+ {
+ // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent
+ m_pImpl->m_bCheckFirstFootnoteTab = false;
+ sal_Int32 nFirstLineIndent = 0;
+ m_pImpl->GetAnyProperty(PROP_PARA_FIRST_LINE_INDENT, m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH)) >>= nFirstLineIndent;
+ if ( nFirstLineIndent < 0 )
+ m_pImpl->m_bIgnoreNextTab = true;
+ }
+
+ if ( m_pImpl->m_bIgnoreNextTab )
+ {
+ m_pImpl->m_bIgnoreNextTab = false;
+ return;
+ }
+ }
+ if (!m_pImpl->hasTableManager())
+ return;
+
+ SkipFootnoteSeparator eSkip = m_pImpl->GetSkipFootnoteState();
+ if ( eSkip == SkipFootnoteSeparator::ON || eSkip == SkipFootnoteSeparator::SKIPPING )
+ {
+ m_pImpl->SetSkipFootnoteState( SkipFootnoteSeparator::SKIPPING );
+ return;
+ }
+
+ try
+ {
+ while (m_pImpl->isBreakDeferred(LINE_BREAK))
+ {
+ m_pImpl->clearDeferredBreak(LINE_BREAK);
+ m_pImpl->appendTextPortion("\n", m_pImpl->GetTopContext());
+ }
+
+ m_pImpl->getTableManager().utext(data_, len);
+
+ if (bNewLine)
+ {
+ const bool bSingleParagraph = m_pImpl->GetIsFirstParagraphInSection() && m_pImpl->GetIsLastParagraphInSection();
+ const bool bSingleParagraphAfterRedline = m_pImpl->GetIsFirstParagraphInSection(/*bAfterRedline=*/true) &&
+ m_pImpl->GetIsLastParagraphInSection();
+ PropertyMapPtr pContext = m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH);
+ if (!m_pImpl->GetFootnoteContext())
+ {
+ if (m_pImpl->isBreakDeferred(PAGE_BREAK))
+ {
+ if (m_pImpl->GetSettingsTable()->GetSplitPgBreakAndParaMark())
+ {
+ if ( m_pImpl->GetIsFirstParagraphInSection() || !m_pImpl->IsFirstRun() )
+ {
+ m_pImpl->m_bIsSplitPara = true;
+ finishParagraph();
+ lcl_startParagraphGroup();
+ }
+
+
+ pContext->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_PAGE_BEFORE));
+ m_pImpl->clearDeferredBreaks();
+ }
+ }
+ else if (m_pImpl->isBreakDeferred(COLUMN_BREAK))
+ {
+ if ( m_pImpl->GetIsFirstParagraphInSection() || !m_pImpl->IsFirstRun() )
+ {
+ mbIsSplitPara = true;
+ finishParagraph();
+ lcl_startParagraphGroup();
+ }
+
+ pContext->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_COLUMN_BEFORE));
+ m_pImpl->clearDeferredBreaks();
+ }
+ }
+
+ // If the paragraph contains only the section properties and it has
+ // no runs, we should not create a paragraph for it in Writer, unless that would remove the whole section.
+ // Also do not remove here column breaks: they are treated in a different way and place.
+ bool bIsColumnBreak = false;
+ if (pContext->isSet(PROP_BREAK_TYPE))
+ {
+ const uno::Any aBreakType = pContext->getProperty(PROP_BREAK_TYPE)->second;
+ bIsColumnBreak =
+ aBreakType == style::BreakType_COLUMN_BEFORE ||
+ aBreakType == style::BreakType_COLUMN_AFTER ||
+ aBreakType == style::BreakType_COLUMN_BOTH;
+ }
+
+ bool bRemove = (!m_pImpl->GetParaChanged() && m_pImpl->GetRemoveThisPara()) ||
+ (!m_pImpl->GetParaChanged() && m_pImpl->GetParaSectpr()
+ && !bSingleParagraphAfterRedline
+ && !bIsColumnBreak
+ && !m_pImpl->GetParaHadField()
+ && (!m_pImpl->GetIsDummyParaAddedForTableInSectionPage())
+ && !m_pImpl->GetIsPreviousParagraphFramed()
+ && !m_pImpl->HasTopAnchoredObjects()
+ && !m_pImpl->IsParaWithInlineObject());
+
+ const bool bNoNumbering = bRemove || (!m_pImpl->GetParaChanged() && m_pImpl->GetParaSectpr() && bSingleParagraph);
+ PropertyMapPtr xContext = bNoNumbering ? m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH) : PropertyMapPtr();
+ if (xContext)
+ {
+ // tdf#97417 delete numbering of the paragraph
+ // it will be deleted anyway, and the numbering would be copied
+ // to the next paragraph in sw SplitNode and then be applied to
+ // every following paragraph
+ xContext->Erase(PROP_NUMBERING_RULES);
+ static_cast<ParagraphPropertyMap*>(xContext.get())->SetListId(-1);;
+ xContext->Erase(PROP_NUMBERING_LEVEL);
+ }
+ finishParagraph(bRemove, bNoNumbering);
+ if (bRemove)
+ m_pImpl->RemoveLastParagraph();
+ m_pImpl->SetParaSectpr(false);
+ }
+ else
+ {
+ // GetTopContext() is changed by inserted breaks, but we want to keep the current context
+ PropertyMapPtr pContext = m_pImpl->GetTopContext();
+ if (!m_pImpl->GetFootnoteContext())
+ {
+ if (m_pImpl->isBreakDeferred(PAGE_BREAK))
+ {
+ /* If PAGEBREAK appears in first paragraph of the section or
+ * after first run of any paragraph then need to split paragraph
+ * to handle it properly.
+ */
+ if (m_pImpl->GetIsFirstParagraphInSection() || !m_pImpl->IsFirstRun())
+ {
+ m_pImpl->m_bIsSplitPara = true;
+ finishParagraph();
+ lcl_startParagraphGroup();
+ }
+ m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_PAGE_BEFORE));
+ }
+ else if (m_pImpl->isBreakDeferred(COLUMN_BREAK))
+ {
+ if (m_pImpl->GetIsFirstParagraphInSection() || !m_pImpl->IsFirstRun() || mbWasShapeInPara)
+ {
+ mbWasShapeInPara = false;
+ mbIsSplitPara = true;
+ finishParagraph();
+ lcl_startParagraphGroup();
+ }
+ m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, uno::Any(style::BreakType_COLUMN_BEFORE));
+ }
+ m_pImpl->clearDeferredBreaks();
+ }
+
+ if (pContext && pContext->GetFootnote().is())
+ {
+ pContext->GetFootnote()->setLabel( sText );
+ // tdf#141548 don't lose footnote/endnote text of the run with uFtnEdnRef
+ // (i.e. when footnoteRef/endnoteRef is followed by some text in the same run)
+ m_pImpl->appendTextPortion( sText, pContext );
+ }
+ else if (m_pImpl->IsOpenFieldCommand() && !m_pImpl->IsForceGenericFields())
+ {
+ m_pImpl->AppendFieldCommand(sText);
+ }
+ else if( m_pImpl->IsOpenField() && m_pImpl->IsFieldResultAsString())
+ /*depending on the success of the field insert operation this result will be
+ set at the field or directly inserted into the text*/
+ m_pImpl->AppendFieldResult(sText);
+ else
+ {
+ if (pContext == nullptr)
+ pContext = new PropertyMap();
+
+ m_pImpl->appendTextPortion( sText, pContext );
+ }
+
+ }
+ m_pImpl->SetIsFirstRun(false);
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+}
+
+void DomainMapper::lcl_props(writerfilter::Reference<Properties>::Pointer_t ref)
+{
+ ref->resolve(*this);
+}
+
+void DomainMapper::lcl_table(Id name, writerfilter::Reference<Table>::Pointer_t ref)
+{
+ m_pImpl->SetAnyTableImport(true);
+ switch(name)
+ {
+ case NS_ooxml::LN_FONTTABLE:
+
+ // create a font table object that listens to the attributes
+ // each entry call inserts a new font entry
+ ref->resolve( *m_pImpl->GetFontTable() );
+ break;
+ case NS_ooxml::LN_STYLESHEET:
+ //same as above to import style sheets
+ m_pImpl->SetStyleSheetImport( true );
+ ref->resolve( *m_pImpl->GetStyleSheetTable() );
+ m_pImpl->GetStyleSheetTable()->ApplyStyleSheets(m_pImpl->GetFontTable());
+ m_pImpl->SetStyleSheetImport( false );
+ break;
+ case NS_ooxml::LN_NUMBERING:
+ {
+ m_pImpl->SetNumberingImport(true);
+ //the same for list tables
+ ref->resolve( *m_pImpl->GetListTable() );
+ m_pImpl->GetListTable( )->CreateNumberingRules( );
+ m_pImpl->SetNumberingImport(false);
+ }
+ break;
+ case NS_ooxml::LN_THEMETABLE:
+ m_pImpl->GetThemeTable()->setThemeFontLangProperties(
+ m_pImpl->GetSettingsTable()->GetThemeFontLangProperties() );
+ ref->resolve ( *m_pImpl->GetThemeTable() );
+ break;
+ case NS_ooxml::LN_settings_settings:
+ ref->resolve ( *m_pImpl->GetSettingsTable() );
+ m_pImpl->ApplySettingsTable();
+ break;
+ default:
+ OSL_FAIL( "which table is to be filled here?");
+ }
+ m_pImpl->SetAnyTableImport(false);
+}
+
+void DomainMapper::lcl_substream(Id rName, ::writerfilter::Reference<Stream>::Pointer_t ref)
+{
+ m_pImpl->substream(rName, ref);
+}
+
+void DomainMapper::lcl_startGlossaryEntry()
+{
+ uno::Reference< text::XTextRange > xTextRange = GetCurrentTextRange();
+ m_pImpl->setGlossaryEntryStart(xTextRange);
+}
+
+void DomainMapper::lcl_endGlossaryEntry()
+{
+ m_pImpl->appendGlossaryEntry();
+}
+
+void DomainMapper::handleUnderlineType(const Id nId, const ::tools::SvRef<PropertyMap>& rContext)
+{
+ sal_Int16 nUnderline = awt::FontUnderline::NONE;
+
+ switch (nId)
+ {
+ case NS_ooxml::LN_Value_ST_Underline_none:
+ nUnderline = awt::FontUnderline::NONE;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_words:
+ rContext->Insert(PROP_CHAR_WORD_MODE, uno::Any(true));
+ [[fallthrough]];
+ case NS_ooxml::LN_Value_ST_Underline_single:
+ nUnderline = awt::FontUnderline::SINGLE;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_double:
+ nUnderline = awt::FontUnderline::DOUBLE;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dotted:
+ nUnderline = awt::FontUnderline::DOTTED;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dash:
+ nUnderline = awt::FontUnderline::DASH;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dotDash:
+ nUnderline = awt::FontUnderline::DASHDOT;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dotDotDash:
+ nUnderline = awt::FontUnderline::DASHDOTDOT;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_thick:
+ nUnderline = awt::FontUnderline::BOLD;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_wave:
+ nUnderline = awt::FontUnderline::WAVE;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dottedHeavy:
+ nUnderline = awt::FontUnderline::BOLDDOTTED;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dashedHeavy:
+ nUnderline = awt::FontUnderline::BOLDDASH;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dashLong:
+ nUnderline = awt::FontUnderline::LONGDASH;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dashLongHeavy:
+ nUnderline = awt::FontUnderline::BOLDLONGDASH;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dashDotHeavy:
+ nUnderline = awt::FontUnderline::BOLDDASHDOT;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_dashDotDotHeavy:
+ nUnderline = awt::FontUnderline::BOLDDASHDOTDOT;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_wavyHeavy:
+ nUnderline = awt::FontUnderline::BOLDWAVE;
+ break;
+ case NS_ooxml::LN_Value_ST_Underline_wavyDouble:
+ nUnderline = awt::FontUnderline::DOUBLEWAVE;
+ break;
+ }
+ rContext->Insert(PROP_CHAR_UNDERLINE, uno::Any(nUnderline));
+}
+
+void DomainMapper::handleParaJustification(const sal_Int32 nIntValue, const ::tools::SvRef<PropertyMap>& rContext, const bool bExchangeLeftRight)
+{
+ style::ParagraphAdjust nAdjust = style::ParagraphAdjust_LEFT;
+ style::ParagraphAdjust nLastLineAdjust = style::ParagraphAdjust_LEFT;
+ OUString aStringValue = "left";
+ switch(nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_Jc_center:
+ nAdjust = style::ParagraphAdjust_CENTER;
+ aStringValue = "center";
+ break;
+ case NS_ooxml::LN_Value_ST_Jc_right:
+ case NS_ooxml::LN_Value_ST_Jc_end:
+ nAdjust = bExchangeLeftRight ? style::ParagraphAdjust_LEFT : style::ParagraphAdjust_RIGHT;
+ aStringValue = "right";
+ break;
+ case NS_ooxml::LN_Value_ST_Jc_distribute:
+ nLastLineAdjust = style::ParagraphAdjust_BLOCK;
+ [[fallthrough]];
+ case NS_ooxml::LN_Value_ST_Jc_both:
+ nAdjust = style::ParagraphAdjust_BLOCK;
+ aStringValue = "both";
+ break;
+ case NS_ooxml::LN_Value_ST_Jc_left:
+ case NS_ooxml::LN_Value_ST_Jc_start:
+ default:
+ nAdjust = bExchangeLeftRight ? style::ParagraphAdjust_RIGHT : style::ParagraphAdjust_LEFT;
+ break;
+ }
+ rContext->Insert( PROP_PARA_ADJUST, uno::Any( nAdjust ) );
+ rContext->Insert( PROP_PARA_LAST_LINE_ADJUST, uno::Any( nLastLineAdjust ) );
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "jc", aStringValue);
+}
+
+bool DomainMapper::getColorFromId(const Id nId, sal_Int32 &nColor)
+{
+ nColor = 0;
+ if ((nId < NS_ooxml::LN_Value_ST_HighlightColor_black) || (nId > NS_ooxml::LN_Value_ST_HighlightColor_none))
+ return false;
+
+ switch (nId)
+ {
+ case NS_ooxml::LN_Value_ST_HighlightColor_black: nColor=0x000000; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_blue: nColor=0x0000ff; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_cyan: nColor=0x00ffff; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_green: nColor=0x00ff00; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_magenta: nColor=0xff00ff; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_red: nColor=0xff0000; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_yellow: nColor=0xffff00; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_white: nColor=0xffffff; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_darkBlue: nColor=0x000080; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_darkCyan: nColor=0x008080; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_darkGreen: nColor=0x008000; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_darkMagenta: nColor=0x800080; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_darkRed: nColor=0x800000; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_darkYellow: nColor=0x808000; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_darkGray: nColor=0x808080; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_lightGray: nColor=0xC0C0C0; break;
+ case NS_ooxml::LN_Value_ST_HighlightColor_none: nColor=0xFFFFFFFF; break; //COL_AUTO
+ default:
+ return false;
+ }
+ return true;
+}
+
+sal_Int16 DomainMapper::getEmphasisValue(const sal_Int32 nIntValue)
+{
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_Em_dot:
+ return text::FontEmphasis::DOT_ABOVE;
+ case NS_ooxml::LN_Value_ST_Em_comma:
+ return text::FontEmphasis::ACCENT_ABOVE;
+ case NS_ooxml::LN_Value_ST_Em_circle:
+ return text::FontEmphasis::CIRCLE_ABOVE;
+ case NS_ooxml::LN_Value_ST_Em_underDot:
+ return text::FontEmphasis::DOT_BELOW;
+ default:
+ return text::FontEmphasis::NONE;
+ }
+}
+
+OUString DomainMapper::getBracketStringFromEnum(const sal_Int32 nIntValue, const bool bIsPrefix)
+{
+ switch(nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_CombineBrackets_round:
+ if (bIsPrefix)
+ return "(";
+ return ")";
+
+ case NS_ooxml::LN_Value_ST_CombineBrackets_square:
+ if (bIsPrefix)
+ return "[";
+ return "]";
+
+ case NS_ooxml::LN_Value_ST_CombineBrackets_angle:
+ if (bIsPrefix)
+ return "<";
+ return ">";
+
+ case NS_ooxml::LN_Value_ST_CombineBrackets_curly:
+ if (bIsPrefix)
+ return "{";
+ return "}";
+
+ case NS_ooxml::LN_Value_ST_CombineBrackets_none:
+ default:
+ return OUString();
+ }
+}
+
+style::TabAlign DomainMapper::getTabAlignFromValue(const sal_Int32 nIntValue)
+{
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_TabJc_start:
+ case NS_ooxml::LN_Value_ST_TabJc_left:
+ case NS_ooxml::LN_Value_ST_TabJc_bar: // bar not supported
+ case NS_ooxml::LN_Value_ST_TabJc_num: // num not supported
+ return style::TabAlign_LEFT;
+ case NS_ooxml::LN_Value_ST_TabJc_center:
+ return style::TabAlign_CENTER;
+ case NS_ooxml::LN_Value_ST_TabJc_end:
+ case NS_ooxml::LN_Value_ST_TabJc_right:
+ return style::TabAlign_RIGHT;
+ case NS_ooxml::LN_Value_ST_TabJc_decimal:
+ return style::TabAlign_DECIMAL;
+ }
+ return style::TabAlign_LEFT;
+}
+
+sal_Unicode DomainMapper::getFillCharFromValue(const sal_Int32 nIntValue)
+{
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_TabTlc_dot:
+ return u'.';
+ case NS_ooxml::LN_Value_ST_TabTlc_hyphen:
+ return u'-';
+ case NS_ooxml::LN_Value_ST_TabTlc_underscore:
+ case NS_ooxml::LN_Value_ST_TabTlc_heavy: // FIXME ???
+ return u'_';
+ case NS_ooxml::LN_Value_ST_TabTlc_middleDot: // middleDot
+ return u'\x00b7';
+ case NS_ooxml::LN_Value_ST_TabTlc_none:
+ default:
+ return u' '; // blank space
+ }
+}
+
+bool DomainMapper::IsOOXMLImport() const
+{
+ return m_pImpl->IsOOXMLImport();
+}
+
+bool DomainMapper::IsRTFImport() const
+{
+ return m_pImpl->IsRTFImport();
+}
+
+uno::Reference < lang::XMultiServiceFactory > const & DomainMapper::GetTextFactory() const
+{
+ return m_pImpl->GetTextFactory();
+}
+
+uno::Reference< text::XTextRange > DomainMapper::GetCurrentTextRange()
+{
+ if (m_pImpl->HasTopText())
+ return m_pImpl->GetTopTextAppend()->getEnd();
+ return m_pImpl->m_xInsertTextRange;
+}
+
+OUString DomainMapper::getOrCreateCharStyle( PropertyValueVector_t& rCharProperties, bool bAlwaysCreate )
+{
+ StyleSheetTablePtr pStyleSheets = m_pImpl->GetStyleSheetTable();
+ return pStyleSheets->getOrCreateCharStyle( rCharProperties, bAlwaysCreate );
+}
+
+StyleSheetTablePtr const & DomainMapper::GetStyleSheetTable( )
+{
+ return m_pImpl->GetStyleSheetTable( );
+}
+
+SettingsTablePtr const & DomainMapper::GetSettingsTable()
+{
+ return m_pImpl->GetSettingsTable();
+}
+
+GraphicZOrderHelper* DomainMapper::graphicZOrderHelper()
+{
+ if (zOrderHelper == nullptr)
+ zOrderHelper.reset( new GraphicZOrderHelper );
+ return zOrderHelper.get();
+}
+
+GraphicNamingHelper& DomainMapper::GetGraphicNamingHelper()
+{
+ if (m_pGraphicNamingHelper == nullptr)
+ m_pGraphicNamingHelper.reset(new GraphicNamingHelper());
+ return *m_pGraphicNamingHelper;
+}
+
+uno::Reference<drawing::XShape> DomainMapper::PopPendingShape()
+{
+ return m_pImpl->PopPendingShape();
+}
+
+bool DomainMapper::IsInHeaderFooter() const
+{
+ return m_pImpl->IsInHeaderFooter();
+}
+
+bool DomainMapper::IsInShape() const { return m_pImpl->IsInShape(); }
+
+bool DomainMapper::IsInTable() const
+{
+ return m_pImpl->hasTableManager() && m_pImpl->getTableManager().isInCell();
+}
+
+OUString DomainMapper::GetListStyleName(sal_Int32 nListId) const
+{
+ return m_pImpl->GetListStyleName( nListId );
+}
+
+void DomainMapper::ValidateListLevel(const OUString& sStyleIdentifierD)
+{
+ m_pImpl->ValidateListLevel(sStyleIdentifierD);
+}
+
+void DomainMapper::SetDocDefaultsImport(bool bSet)
+{
+ m_pImpl->SetDocDefaultsImport(bSet);
+}
+
+bool DomainMapper::IsStyleSheetImport() const
+{
+ return m_pImpl->IsStyleSheetImport();
+}
+
+bool DomainMapper::IsNumberingImport() const
+{
+ return m_pImpl->IsNumberingImport();
+}
+
+void DomainMapper::enableInteropGrabBag(const OUString& aName)
+{
+ m_pImpl->m_aInteropGrabBagName = aName;
+}
+
+beans::PropertyValue DomainMapper::getInteropGrabBag()
+{
+ beans::PropertyValue aRet;
+ aRet.Name = m_pImpl->m_aInteropGrabBagName;
+ aRet.Value <<= comphelper::containerToSequence(m_pImpl->m_aInteropGrabBag);
+
+ m_pImpl->m_aInteropGrabBag.clear();
+ m_pImpl->m_aInteropGrabBagName.clear();
+ return aRet;
+}
+
+void DomainMapper::HandleRedline( Sprm& rSprm )
+{
+ sal_uInt32 nSprmId = rSprm.getId();
+
+ m_pImpl->AddNewRedline( nSprmId );
+
+ if (nSprmId == NS_ooxml::LN_CT_PPr_pPrChange)
+ {
+ m_pImpl->SetCurrentRedlineToken(XML_ParagraphFormat);
+ }
+ else if (nSprmId == NS_ooxml::LN_CT_TrPr_ins)
+ {
+ m_pImpl->SetCurrentRedlineToken(XML_tableRowInsert);
+ }
+ else if (nSprmId == NS_ooxml::LN_CT_TrPr_del)
+ {
+ m_pImpl->SetCurrentRedlineToken(XML_tableRowDelete);
+ }
+ else if (nSprmId == NS_ooxml::LN_CT_TcPrBase_cellIns)
+ {
+ m_pImpl->SetCurrentRedlineToken(XML_tableCellInsert);
+ }
+ else if (nSprmId == NS_ooxml::LN_CT_TcPrBase_cellDel)
+ {
+ m_pImpl->SetCurrentRedlineToken(XML_tableCellDelete);
+ }
+
+ resolveSprmProps(*this, rSprm );
+ // now the properties author, date and id should be available
+ sal_Int32 nToken = m_pImpl->GetCurrentRedlineToken();
+ switch( nToken & 0xffff )
+ {
+ case XML_mod:
+ case XML_ins:
+ case XML_del:
+ case XML_moveTo:
+ case XML_moveFrom:
+ case XML_ParagraphFormat:
+ case XML_tableRowInsert:
+ case XML_tableRowDelete:
+ case XML_tableCellInsert:
+ case XML_tableCellDelete:
+ break;
+ default: OSL_FAIL( "redline token other than mod, ins, del, moveTo, moveFrom or table row" ); break;
+ }
+ m_pImpl->EndParaMarkerChange( );
+ m_pImpl->SetCurrentRedlineIsRead();
+}
+
+void DomainMapper::finishParagraph(const bool bRemove, const bool bNoNumbering)
+{
+ if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::datePicker)
+ m_pImpl->m_pSdtHelper->createDateContentControl();
+ m_pImpl->finishParagraph(m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH), bRemove, bNoNumbering);
+}
+
+void DomainMapper::commentProps(const OUString& sId, const CommentProperties& rProps)
+{
+ m_pImpl->commentProps(sId, rProps);
+}
+
+css::uno::Reference<css::container::XNameContainer> const & DomainMapper::GetCharacterStyles()
+{
+ return m_pImpl->GetCharacterStyles();
+}
+
+OUString DomainMapper::GetUnusedCharacterStyleName()
+{
+ return m_pImpl->GetUnusedCharacterStyleName();
+}
+
+} //namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapper.hxx b/writerfilter/source/dmapper/DomainMapper.hxx
new file mode 100644
index 000000000..4096b2b5d
--- /dev/null
+++ b/writerfilter/source/dmapper/DomainMapper.hxx
@@ -0,0 +1,195 @@
+/* -*- 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 <dmapper/DomainMapperFactory.hxx>
+#include "LoggedResources.hxx"
+#include "PropertyMap.hxx"
+#include "SettingsTable.hxx"
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/style/TabAlign.hpp>
+
+#include <map>
+#include <vector>
+#include <memory>
+
+namespace com::sun::star{
+ namespace beans{
+ struct PropertyValue;
+ }
+ namespace io{
+ class XInputStream;
+ }
+ namespace uno{
+ class XComponentContext;
+ }
+ namespace lang{
+ class XMultiServiceFactory;
+ }
+ namespace text{
+ class XTextRange;
+ }
+}
+
+namespace utl
+{
+class MediaDescriptor;
+}
+
+typedef std::vector<css::beans::PropertyValue> PropertyValueVector_t;
+
+namespace writerfilter::dmapper
+{
+
+class PropertyMap;
+class DomainMapper_Impl;
+class ListsManager;
+class StyleSheetTable;
+class GraphicZOrderHelper;
+class GraphicNamingHelper;
+
+typedef tools::SvRef<StyleSheetTable> StyleSheetTablePtr;
+
+class DomainMapper : public LoggedProperties, public LoggedTable,
+ public BinaryObj, public LoggedStream
+{
+ std::unique_ptr<DomainMapper_Impl> m_pImpl;
+
+public:
+ DomainMapper(const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ css::uno::Reference<css::io::XInputStream> const& xInputStream,
+ css::uno::Reference<css::lang::XComponent> const& xModel,
+ bool bRepairStorage,
+ SourceDocumentType eDocumentType,
+ utl::MediaDescriptor const & rMediaDesc);
+ virtual ~DomainMapper() override;
+
+ virtual void setDocumentReference(writerfilter::ooxml::OOXMLDocument* pDocument) override;
+
+ // Stream
+ virtual void markLastParagraphInSection() override;
+ virtual void markLastSectionGroup() override;
+
+ // BinaryObj
+ virtual void data(const sal_uInt8* buf, size_t len) override;
+
+ void sprmWithProps( Sprm& sprm, const PropertyMapPtr& pContext );
+
+ void PushStyleSheetProperties( const PropertyMapPtr& pStyleProperties, bool bAffectTableMngr = false );
+ void PopStyleSheetProperties( bool bAffectTableMngr = false );
+
+ void PushListProperties( const ::tools::SvRef<PropertyMap>& pListProperties );
+ void PopListProperties();
+ OUString GetListStyleName(sal_Int32 nListId) const;
+ void ValidateListLevel(const OUString& sStyleIdentifierD);
+
+ bool IsOOXMLImport() const;
+ bool IsRTFImport() const;
+ css::uno::Reference<css::lang::XMultiServiceFactory> const & GetTextFactory() const;
+ css::uno::Reference<css::text::XTextRange> GetCurrentTextRange();
+
+ OUString getOrCreateCharStyle( PropertyValueVector_t& rCharProperties, bool bAlwaysCreate );
+ StyleSheetTablePtr const & GetStyleSheetTable( );
+ SettingsTablePtr const & GetSettingsTable();
+ GraphicZOrderHelper* graphicZOrderHelper();
+ GraphicNamingHelper& GetGraphicNamingHelper();
+
+ /// Return the first from the pending (not inserted to the document) shapes, if there are any.
+ css::uno::Reference<css::drawing::XShape> PopPendingShape();
+
+ bool IsInHeaderFooter() const;
+ bool IsInTable() const;
+ void SetDocDefaultsImport(bool bSet);
+ bool IsStyleSheetImport() const;
+ bool IsNumberingImport() const;
+ bool IsInShape() const;
+
+ void hasControls( const bool bSet ) { mbHasControls = bSet; }
+
+ /**
+ @see DomainMapper_Impl::processDeferredCharacterProperties()
+ */
+ void processDeferredCharacterProperties(const std::map<sal_Int32, css::uno::Any>& rDeferredCharacterProperties);
+
+ /// Enable storing of seen tokens in a named grab bag.
+ void enableInteropGrabBag(const OUString& aName);
+ /// Get the stored tokens and clear the internal storage.
+ css::beans::PropertyValue getInteropGrabBag();
+
+ void HandleRedline( Sprm& rSprm );
+
+ virtual void commentProps(const OUString& sId, const CommentProperties& rProps) override;
+
+ css::uno::Reference<css::container::XNameContainer> const & GetCharacterStyles();
+ OUString GetUnusedCharacterStyleName();
+
+private:
+ // Stream
+ virtual void lcl_startSectionGroup() override;
+ virtual void lcl_endSectionGroup() override;
+ virtual void lcl_startParagraphGroup() override;
+ virtual void lcl_endParagraphGroup() override;
+ virtual void lcl_startCharacterGroup() override;
+ virtual void lcl_endCharacterGroup() override;
+ virtual void lcl_startShape(css::uno::Reference<css::drawing::XShape> const& xShape) override;
+ virtual void lcl_endShape( ) override;
+ virtual void lcl_startTextBoxContent() override;
+ virtual void lcl_endTextBoxContent() override;
+ virtual void lcl_text(const sal_uInt8 * data, size_t len) override;
+ virtual void lcl_utext(const sal_uInt8 * data, size_t len) override;
+ virtual void lcl_positionOffset(const OUString& rText, bool bVertical) override;
+ virtual css::awt::Point getPositionOffset() override;
+ virtual void lcl_align(const OUString& rText, bool bVertical) override;
+ virtual void lcl_positivePercentage(const OUString& rText) override;
+ virtual void lcl_props(writerfilter::Reference<Properties>::Pointer_t ref) override;
+ virtual void lcl_table(Id name,
+ writerfilter::Reference<Table>::Pointer_t ref) override;
+ virtual void lcl_substream(Id name,
+ ::writerfilter::Reference<Stream>::Pointer_t ref) override;
+ virtual void lcl_startGlossaryEntry() override;
+ virtual void lcl_endGlossaryEntry() override;
+ virtual void lcl_checkId(const sal_Int32 nId) override;
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+ // Table
+ virtual void lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref) override;
+
+ void finishParagraph(const bool bRemove = false, const bool bNoNumbering = false);
+
+ static void handleUnderlineType(const Id nId, const ::tools::SvRef<PropertyMap>& rContext);
+ void handleParaJustification(const sal_Int32 nIntValue, const ::tools::SvRef<PropertyMap>& rContext, const bool bExchangeLeftRight);
+ static bool getColorFromId(const Id, sal_Int32 &nColor);
+ static sal_Int16 getEmphasisValue(const sal_Int32 nIntValue);
+ static OUString getBracketStringFromEnum(const sal_Int32 nIntValue, const bool bIsPrefix = true);
+ static css::style::TabAlign getTabAlignFromValue(const sal_Int32 nIntValue);
+ static sal_Unicode getFillCharFromValue(const sal_Int32 nIntValue);
+ bool mbIsSplitPara;
+ bool mbHasControls;
+ bool mbWasShapeInPara;
+ std::unique_ptr< GraphicZOrderHelper > zOrderHelper;
+ std::unique_ptr<GraphicNamingHelper> m_pGraphicNamingHelper;
+ OUString m_sGlossaryEntryName;
+};
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
new file mode 100644
index 000000000..bf170d9cc
--- /dev/null
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -0,0 +1,1700 @@
+/* -*- 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 "DomainMapperTableHandler.hxx"
+#include "DomainMapper_Impl.hxx"
+#include "StyleSheetTable.hxx"
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/table/TableBorderDistances.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/table/BorderLineStyle.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/XTextField.hpp>
+#include <com/sun/star/text/XTextRangeCompare.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include "TablePositionHandler.hxx"
+#include "TagLogger.hxx"
+#include "util.hxx"
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/sequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <boost/lexical_cast.hpp>
+
+#ifdef DBG_UTIL
+#include "PropertyMapHelper.hxx"
+#include <rtl/ustring.hxx>
+#endif
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+using namespace ::std;
+
+#define DEF_BORDER_DIST 190 //0,19cm
+#define CNF_FIRST_ROW 0x800
+#define CNF_LAST_ROW 0x400
+#define CNF_FIRST_COLUMN 0x200
+#define CNF_LAST_COLUMN 0x100
+#define CNF_ODD_VBAND 0x080
+#define CNF_EVEN_VBAND 0x040
+#define CNF_ODD_HBAND 0x020
+#define CNF_EVEN_HBAND 0x010
+#define CNF_FIRST_ROW_LAST_COLUMN 0x008
+#define CNF_FIRST_ROW_FIRST_COLUMN 0x004
+#define CNF_LAST_ROW_LAST_COLUMN 0x002
+#define CNF_LAST_ROW_FIRST_COLUMN 0x001
+#define CNF_ALL 0xFFF
+
+// total number of table columns
+#define MAXTABLECELLS 63
+
+DomainMapperTableHandler::DomainMapperTableHandler(
+ css::uno::Reference<css::text::XTextAppendAndConvert> const& xText,
+ DomainMapper_Impl& rDMapper_Impl)
+ : m_xText(xText),
+ m_rDMapper_Impl( rDMapper_Impl ),
+ m_bHadFootOrEndnote(false)
+{
+}
+
+DomainMapperTableHandler::~DomainMapperTableHandler()
+{
+}
+
+void DomainMapperTableHandler::startTable(const TablePropertyMapPtr& pProps)
+{
+ m_aTableProperties = pProps;
+ m_aTableRanges.clear();
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablehandler.table");
+
+ if (pProps)
+ pProps->dumpXml();
+#endif
+}
+
+static void lcl_mergeBorder( PropertyIds nId, const PropertyMapPtr& pOrig, const PropertyMapPtr& pDest )
+{
+ std::optional<PropertyMap::Property> pOrigVal = pOrig->getProperty(nId);
+
+ if ( pOrigVal )
+ {
+ pDest->Insert( nId, pOrigVal->second, false );
+ }
+}
+
+static void lcl_computeCellBorders( const PropertyMapPtr& pTableBorders, const PropertyMapPtr& pCellProps,
+ sal_uInt32 nCell, sal_uInt32 nFirstCell, sal_uInt32 nLastCell, sal_Int32 nRow, bool bIsEndRow, bool bMergedVertically )
+{
+ const bool bIsStartCol = nCell == nFirstCell;
+ const bool bIsEndCol = nCell == nLastCell;
+ std::optional<PropertyMap::Property> pVerticalVal = pCellProps->getProperty(META_PROP_VERTICAL_BORDER);
+ std::optional<PropertyMap::Property> pHorizontalVal = pCellProps->getProperty(META_PROP_HORIZONTAL_BORDER);
+
+ // Handle the vertical and horizontal borders
+ uno::Any aVertProp;
+ if ( !pVerticalVal)
+ {
+ pVerticalVal = pTableBorders->getProperty(META_PROP_VERTICAL_BORDER);
+ if ( pVerticalVal )
+ aVertProp = pVerticalVal->second;
+ }
+ else
+ {
+ aVertProp = pVerticalVal->second;
+ pCellProps->Erase( pVerticalVal->first );
+ }
+
+ uno::Any aHorizProp;
+ if ( !pHorizontalVal )
+ {
+ pHorizontalVal = pTableBorders->getProperty(META_PROP_HORIZONTAL_BORDER);
+ if ( pHorizontalVal )
+ aHorizProp = pHorizontalVal->second;
+ }
+ else
+ {
+ aHorizProp = pHorizontalVal->second;
+ pCellProps->Erase( pHorizontalVal->first );
+ }
+
+ if ( bIsStartCol )
+ lcl_mergeBorder( PROP_LEFT_BORDER, pTableBorders, pCellProps );
+
+ if ( bIsEndCol )
+ lcl_mergeBorder( PROP_RIGHT_BORDER, pTableBorders, pCellProps );
+
+ // <w:insideV> counts if there are multiple cells in this row.
+ if ( pVerticalVal )
+ {
+ if ( !bIsEndCol && nCell >= nFirstCell )
+ pCellProps->Insert( PROP_RIGHT_BORDER, aVertProp, false );
+ if ( !bIsStartCol && nCell <= nLastCell )
+ pCellProps->Insert( PROP_LEFT_BORDER, aVertProp, false );
+ }
+
+ if ( nRow == 0 )
+ {
+ lcl_mergeBorder( PROP_TOP_BORDER, pTableBorders, pCellProps );
+ if ( pHorizontalVal && !bMergedVertically )
+ pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
+ }
+
+ if ( bMergedVertically )
+ lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps );
+
+ if ( bIsEndRow )
+ {
+ lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps );
+ if ( pHorizontalVal )
+ pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
+ }
+
+ if ( nRow > 0 && !bIsEndRow )
+ {
+ if ( pHorizontalVal )
+ {
+ pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
+ pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
+ }
+ }
+}
+
+#ifdef DBG_UTIL
+
+static void lcl_debug_BorderLine(table::BorderLine const & rLine)
+{
+ TagLogger::getInstance().startElement("BorderLine");
+ TagLogger::getInstance().attribute("Color", rLine.Color);
+ TagLogger::getInstance().attribute("InnerLineWidth", rLine.InnerLineWidth);
+ TagLogger::getInstance().attribute("OuterLineWidth", rLine.OuterLineWidth);
+ TagLogger::getInstance().attribute("LineDistance", rLine.LineDistance);
+ TagLogger::getInstance().endElement();
+}
+
+static void lcl_debug_TableBorder(table::TableBorder const & rBorder)
+{
+ TagLogger::getInstance().startElement("TableBorder");
+ lcl_debug_BorderLine(rBorder.TopLine);
+ TagLogger::getInstance().attribute("IsTopLineValid", sal_uInt32(rBorder.IsTopLineValid));
+ lcl_debug_BorderLine(rBorder.BottomLine);
+ TagLogger::getInstance().attribute("IsBottomLineValid", sal_uInt32(rBorder.IsBottomLineValid));
+ lcl_debug_BorderLine(rBorder.LeftLine);
+ TagLogger::getInstance().attribute("IsLeftLineValid", sal_uInt32(rBorder.IsLeftLineValid));
+ lcl_debug_BorderLine(rBorder.RightLine);
+ TagLogger::getInstance().attribute("IsRightLineValid", sal_uInt32(rBorder.IsRightLineValid));
+ lcl_debug_BorderLine(rBorder.VerticalLine);
+ TagLogger::getInstance().attribute("IsVerticalLineValid", sal_uInt32(rBorder.IsVerticalLineValid));
+ lcl_debug_BorderLine(rBorder.HorizontalLine);
+ TagLogger::getInstance().attribute("IsHorizontalLineValid", sal_uInt32(rBorder.IsHorizontalLineValid));
+ TagLogger::getInstance().attribute("Distance", rBorder.Distance);
+ TagLogger::getInstance().attribute("IsDistanceValid", sal_uInt32(rBorder.IsDistanceValid));
+ TagLogger::getInstance().endElement();
+}
+#endif
+
+struct TableInfo
+{
+ sal_Int32 nLeftBorderDistance;
+ sal_Int32 nRightBorderDistance;
+ sal_Int32 nTopBorderDistance;
+ sal_Int32 nBottomBorderDistance;
+ sal_Int32 nTblLook;
+ sal_Int32 nNestLevel;
+ PropertyMapPtr pTableDefaults;
+ PropertyMapPtr pTableBorders;
+ TableStyleSheetEntry* pTableStyle;
+ css::beans::PropertyValues aTableProperties;
+ std::vector< PropertyIds > aTablePropertyIds;
+
+ TableInfo()
+ : nLeftBorderDistance(DEF_BORDER_DIST)
+ , nRightBorderDistance(DEF_BORDER_DIST)
+ , nTopBorderDistance(0)
+ , nBottomBorderDistance(0)
+ , nTblLook(0x4a0)
+ , nNestLevel(0)
+ , pTableDefaults(new PropertyMap)
+ , pTableBorders(new PropertyMap)
+ , pTableStyle(nullptr)
+ {
+ }
+
+};
+
+namespace
+{
+
+bool lcl_extractTableBorderProperty(const PropertyMapPtr& pTableProperties, const PropertyIds nId, TableInfo const & rInfo, table::BorderLine2& rLine)
+{
+ if (!pTableProperties)
+ return false;
+
+ const std::optional<PropertyMap::Property> aTblBorder = pTableProperties->getProperty(nId);
+ if( aTblBorder )
+ {
+ OSL_VERIFY(aTblBorder->second >>= rLine);
+
+ rInfo.pTableBorders->Insert( nId, uno::Any( rLine ) );
+ rInfo.pTableDefaults->Erase( nId );
+
+ return true;
+ }
+
+ return false;
+}
+
+void lcl_extractHoriOrient(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32& nHoriOrient)
+{
+ // Shifts the frame left by the given value.
+ for (const beans::PropertyValue & rFrameProperty : rFrameProperties)
+ {
+ if (rFrameProperty.Name == "HoriOrient")
+ {
+ sal_Int32 nValue = rFrameProperty.Value.get<sal_Int32>();
+ if (nValue != text::HoriOrientation::NONE)
+ nHoriOrient = nValue;
+ return;
+ }
+ }
+}
+
+void lcl_DecrementHoriOrientPosition(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32 nAmount)
+{
+ // Shifts the frame left by the given value.
+ for (beans::PropertyValue & rPropertyValue : rFrameProperties)
+ {
+ if (rPropertyValue.Name == "HoriOrientPosition")
+ {
+ sal_Int32 nValue = rPropertyValue.Value.get<sal_Int32>();
+ nValue -= nAmount;
+ rPropertyValue.Value <<= nValue;
+ return;
+ }
+ }
+}
+
+void lcl_adjustBorderDistance(TableInfo& rInfo, const table::BorderLine2& rLeftBorder,
+ const table::BorderLine2& rRightBorder)
+{
+ // MS Word appears to do these things to adjust the cell horizontal area:
+ //
+ // bll = left borderline width
+ // blr = right borderline width
+ // cea = cell's edit area rectangle
+ // cea_w = cea width
+ // cml = cell's left margin (padding) defined in cell settings
+ // cmr = cell's right margin (padding) defined in cell settings
+ // cw = cell width (distance between middles of left borderline and right borderline)
+ // pad_l = actual cea left padding = (its left pos relative to middle of bll)
+ // pad_r = actual cea right padding = abs (its right pos relative to middle of blr)
+ //
+ // pad_l = max(bll/2, cml) -> cea does not overlap left borderline
+ // cea_w = cw-max(pad_l+blr/2, cml+cmr) -> cea does not overlap right borderline
+ // pad_r = max(pad_l+blr/2, cml+cmr) - pad_l
+ //
+ // It means that e.g. for border widths of 6 pt (~2.12 mm), left margin 0 mm, and right margin
+ // 2 mm, actual left and right margins will (unexpectedly) coincide with inner edges of cell's
+ // borderlines - the right margin won't create spacing between right of edit rectangle and the
+ // inner edge of right borderline.
+
+ const sal_Int32 nActualL
+ = std::max<sal_Int32>(rLeftBorder.LineWidth / 2, rInfo.nLeftBorderDistance);
+ const sal_Int32 nActualR
+ = std::max<sal_Int32>(nActualL + rRightBorder.LineWidth / 2,
+ rInfo.nLeftBorderDistance + rInfo.nRightBorderDistance)
+ - nActualL;
+ rInfo.nLeftBorderDistance = nActualL;
+ rInfo.nRightBorderDistance = nActualR;
+}
+
+}
+
+TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo,
+ std::vector<beans::PropertyValue>& rFrameProperties,
+ bool bConvertToFloatingInFootnote)
+{
+ // will receive the table style if any
+ TableStyleSheetEntry* pTableStyle = nullptr;
+
+ if( m_aTableProperties )
+ {
+ //create properties from the table attributes
+ //...pPropMap->Insert( PROP_LEFT_MARGIN, uno::makeAny( m_nLeftMargin - m_nGapHalf ));
+ //pPropMap->Insert( PROP_HORI_ORIENT, uno::makeAny( text::HoriOrientation::RIGHT ));
+ sal_Int32 nGapHalf = 0;
+ sal_Int32 nLeftMargin = 0;
+
+ comphelper::SequenceAsHashMap aGrabBag;
+
+ if (nullptr != m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition())
+ {
+ TablePositionHandler *pTablePositions = m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition();
+
+ uno::Sequence< beans::PropertyValue > aGrabBagTS{
+ comphelper::makePropertyValue("bottomFromText", pTablePositions->getBottomFromText()),
+ comphelper::makePropertyValue("horzAnchor", pTablePositions->getHorzAnchor()),
+ comphelper::makePropertyValue("leftFromText", pTablePositions->getLeftFromText()),
+ comphelper::makePropertyValue("rightFromText", pTablePositions->getRightFromText()),
+ comphelper::makePropertyValue("tblpX", pTablePositions->getX()),
+ comphelper::makePropertyValue("tblpXSpec", pTablePositions->getXSpec()),
+ comphelper::makePropertyValue("tblpY", pTablePositions->getY()),
+ comphelper::makePropertyValue("tblpYSpec", pTablePositions->getYSpec()),
+ comphelper::makePropertyValue("topFromText", pTablePositions->getTopFromText()),
+ comphelper::makePropertyValue("vertAnchor", pTablePositions->getVertAnchor())
+ };
+
+ aGrabBag["TablePosition"] <<= aGrabBagTS;
+ }
+ else if (bConvertToFloatingInFootnote)
+ {
+ // define empty "TablePosition" to avoid export temporary floating
+ aGrabBag["TablePosition"] = uno::Any();
+ }
+
+ std::optional<PropertyMap::Property> aTableStyleVal = m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME);
+ if(aTableStyleVal)
+ {
+ // Apply table style properties recursively
+ OUString sTableStyleName;
+ aTableStyleVal->second >>= sTableStyleName;
+ StyleSheetTablePtr pStyleSheetTable = m_rDMapper_Impl.GetStyleSheetTable();
+ const StyleSheetEntryPtr pStyleSheet = pStyleSheetTable->FindStyleSheetByISTD( sTableStyleName );
+ pTableStyle = dynamic_cast<TableStyleSheetEntry*>( pStyleSheet.get( ) );
+ m_aTableProperties->Erase( aTableStyleVal->first );
+
+ aGrabBag["TableStyleName"] <<= sTableStyleName;
+
+ if( pStyleSheet )
+ {
+ // First get the style properties, then the table ones
+ PropertyMapPtr pTableProps( m_aTableProperties.get() );
+ TablePropertyMapPtr pEmptyProps( new TablePropertyMap );
+
+ m_aTableProperties = pEmptyProps;
+
+ PropertyMapPtr pMergedProperties = pStyleSheet->GetMergedInheritedProperties(pStyleSheetTable);
+
+ table::BorderLine2 aBorderLine;
+ TableInfo rStyleInfo;
+ if (lcl_extractTableBorderProperty(pMergedProperties, PROP_TOP_BORDER, rStyleInfo, aBorderLine))
+ {
+ aGrabBag["TableStyleTopBorder"] <<= aBorderLine;
+ }
+ if (lcl_extractTableBorderProperty(pMergedProperties, PROP_BOTTOM_BORDER, rStyleInfo, aBorderLine))
+ {
+ aGrabBag["TableStyleBottomBorder"] <<= aBorderLine;
+ }
+ if (lcl_extractTableBorderProperty(pMergedProperties, PROP_LEFT_BORDER, rStyleInfo, aBorderLine))
+ {
+ aGrabBag["TableStyleLeftBorder"] <<= aBorderLine;
+ }
+ if (lcl_extractTableBorderProperty(pMergedProperties, PROP_RIGHT_BORDER, rStyleInfo, aBorderLine))
+ {
+ aGrabBag["TableStyleRightBorder"] <<= aBorderLine;
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("mergedProps");
+ if (pMergedProperties)
+ pMergedProperties->dumpXml();
+ TagLogger::getInstance().endElement();
+#endif
+
+ m_aTableProperties->InsertProps(pMergedProperties);
+ m_aTableProperties->InsertProps(pTableProps);
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("TableProperties");
+ m_aTableProperties->dumpXml();
+ TagLogger::getInstance().endElement();
+#endif
+ if (pTableStyle)
+ {
+ // apply tblHeader setting of the table style
+ PropertyMapPtr pHeaderStyleProps = pTableStyle->GetProperties(CNF_FIRST_ROW);
+ if ( pHeaderStyleProps->getProperty(PROP_HEADER_ROW_COUNT) )
+ m_aTableProperties->Insert(PROP_HEADER_ROW_COUNT, uno::Any( sal_Int32(1)), false);
+ }
+ }
+ }
+
+ // This is the one preserving just all the table look attributes.
+ std::optional<PropertyMap::Property> oTableLook = m_aTableProperties->getProperty(META_PROP_TABLE_LOOK);
+ if (oTableLook)
+ {
+ aGrabBag["TableStyleLook"] = oTableLook->second;
+ m_aTableProperties->Erase(oTableLook->first);
+ }
+
+ // This is just the "val" attribute's numeric value.
+ const std::optional<PropertyMap::Property> aTblLook = m_aTableProperties->getProperty(PROP_TBL_LOOK);
+ if(aTblLook)
+ {
+ aTblLook->second >>= rInfo.nTblLook;
+ m_aTableProperties->Erase( aTblLook->first );
+ }
+
+ // apply cell margin settings of the table style
+ const std::optional<PropertyMap::Property> oLeftMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_LEFT);
+ if (oLeftMargin)
+ {
+ oLeftMargin->second >>= rInfo.nLeftBorderDistance;
+ m_aTableProperties->Erase(oLeftMargin->first);
+ }
+ const std::optional<PropertyMap::Property> oRightMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_RIGHT);
+ if (oRightMargin)
+ {
+ oRightMargin->second >>= rInfo.nRightBorderDistance;
+ m_aTableProperties->Erase(oRightMargin->first);
+ }
+ const std::optional<PropertyMap::Property> oTopMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_TOP);
+ if (oTopMargin)
+ {
+ oTopMargin->second >>= rInfo.nTopBorderDistance;
+ m_aTableProperties->Erase(oTopMargin->first);
+ }
+ const std::optional<PropertyMap::Property> oBottomMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_BOTTOM);
+ if (oBottomMargin)
+ {
+ oBottomMargin->second >>= rInfo.nBottomBorderDistance;
+ m_aTableProperties->Erase(oBottomMargin->first);
+ }
+
+ // Set the table default attributes for the cells
+ rInfo.pTableDefaults->InsertProps(m_aTableProperties.get());
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("TableDefaults");
+ rInfo.pTableDefaults->dumpXml();
+ TagLogger::getInstance().endElement();
+#endif
+
+ if (!aGrabBag.empty())
+ {
+ m_aTableProperties->Insert( PROP_TABLE_INTEROP_GRAB_BAG, uno::Any( aGrabBag.getAsConstPropertyValueList() ) );
+ }
+
+ m_aTableProperties->getValue( TablePropertyMap::GAP_HALF, nGapHalf );
+
+ std::optional<PropertyMap::Property> oLeftMarginFromStyle = m_aTableProperties->getProperty(PROP_LEFT_MARGIN);
+ if (oLeftMarginFromStyle)
+ {
+ oLeftMarginFromStyle->second >>= nLeftMargin;
+ // don't need to erase, we will push back the adjusted value
+ // of this (or the direct formatting, if that exists) later
+ }
+ m_aTableProperties->getValue( TablePropertyMap::LEFT_MARGIN, nLeftMargin );
+
+ m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_LEFT,
+ rInfo.nLeftBorderDistance );
+ m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_RIGHT,
+ rInfo.nRightBorderDistance );
+ m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_TOP,
+ rInfo.nTopBorderDistance );
+ m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_BOTTOM,
+ rInfo.nBottomBorderDistance );
+
+ table::TableBorderDistances aDistances;
+ aDistances.IsTopDistanceValid =
+ aDistances.IsBottomDistanceValid =
+ aDistances.IsLeftDistanceValid =
+ aDistances.IsRightDistanceValid = true;
+ aDistances.TopDistance = static_cast<sal_Int16>( rInfo.nTopBorderDistance );
+ aDistances.BottomDistance = static_cast<sal_Int16>( rInfo.nBottomBorderDistance );
+ aDistances.LeftDistance = static_cast<sal_Int16>( rInfo.nLeftBorderDistance );
+ aDistances.RightDistance = static_cast<sal_Int16>( rInfo.nRightBorderDistance );
+
+ m_aTableProperties->Insert( PROP_TABLE_BORDER_DISTANCES, uno::Any( aDistances ) );
+
+ if (!rFrameProperties.empty())
+ lcl_DecrementHoriOrientPosition(rFrameProperties, rInfo.nLeftBorderDistance);
+
+ // Set table above/bottom spacing to 0.
+ m_aTableProperties->Insert( PROP_TOP_MARGIN, uno::Any( sal_Int32( 0 ) ) );
+ m_aTableProperties->Insert( PROP_BOTTOM_MARGIN, uno::Any( sal_Int32( 0 ) ) );
+
+ //table border settings
+ table::TableBorder aTableBorder;
+ table::BorderLine2 aBorderLine, aLeftBorder, aRightBorder;
+
+ if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_TOP_BORDER, rInfo, aBorderLine))
+ {
+ aTableBorder.TopLine = aBorderLine;
+ aTableBorder.IsTopLineValid = true;
+ }
+ if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_BOTTOM_BORDER, rInfo, aBorderLine))
+ {
+ aTableBorder.BottomLine = aBorderLine;
+ aTableBorder.IsBottomLineValid = true;
+ }
+ if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_LEFT_BORDER, rInfo, aLeftBorder))
+ {
+ aTableBorder.LeftLine = aLeftBorder;
+ aTableBorder.IsLeftLineValid = true;
+ }
+ if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_RIGHT_BORDER, rInfo,
+ aRightBorder))
+ {
+ aTableBorder.RightLine = aRightBorder;
+ aTableBorder.IsRightLineValid = true;
+ }
+ if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_HORIZONTAL_BORDER, rInfo, aBorderLine))
+ {
+ aTableBorder.HorizontalLine = aBorderLine;
+ aTableBorder.IsHorizontalLineValid = true;
+ }
+ if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_VERTICAL_BORDER, rInfo, aBorderLine))
+ {
+ aTableBorder.VerticalLine = aBorderLine;
+ aTableBorder.IsVerticalLineValid = true;
+ }
+
+ aTableBorder.Distance = 0;
+ aTableBorder.IsDistanceValid = false;
+
+ m_aTableProperties->Insert( PROP_TABLE_BORDER, uno::Any( aTableBorder ) );
+
+#ifdef DBG_UTIL
+ lcl_debug_TableBorder(aTableBorder);
+#endif
+
+ // Table position in Office is computed in 2 different ways :
+ // - top level tables: the goal is to have in-cell text starting at table indent pos (tblInd),
+ // so table's position depends on table's cells margin
+ // - nested tables: the goal is to have left-most border starting at table_indent pos
+
+ // Only top level table position depends on border width of Column A.
+ if ( !m_aCellProperties.empty() && !m_aCellProperties[0].empty() )
+ {
+ // aLeftBorder already contains tblBorder; overwrite if cell is different.
+ std::optional<PropertyMap::Property> aCellBorder
+ = m_aCellProperties[0][0]->getProperty(PROP_LEFT_BORDER);
+ if ( aCellBorder )
+ aCellBorder->second >>= aLeftBorder;
+ aCellBorder = m_aCellProperties[0][0]->getProperty(PROP_RIGHT_BORDER);
+ if (aCellBorder)
+ aCellBorder->second >>= aRightBorder;
+ }
+ if (rInfo.nNestLevel == 1 && aLeftBorder.LineWidth && !rFrameProperties.empty())
+ {
+ lcl_DecrementHoriOrientPosition(rFrameProperties, aLeftBorder.LineWidth * 0.5);
+ }
+ lcl_adjustBorderDistance(rInfo, aLeftBorder, aRightBorder);
+
+ // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
+ // the default behavior when DOCX doesn't define "compatibilityMode" option is to add the cell spacing
+
+ // Undefined should not be possible any more for DOCX, but it is for RTF.
+ // In any case, continue to treat undefined as version 12 during import.
+ sal_Int32 nMode = m_rDMapper_Impl.GetSettingsTable()->GetWordCompatibilityMode();
+
+ if (((nMode < 0) || (0 < nMode && nMode <= 14)) && rInfo.nNestLevel == 1)
+ {
+ const sal_Int32 nAdjustedMargin = nLeftMargin - nGapHalf - rInfo.nLeftBorderDistance;
+ m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::Any( nAdjustedMargin ) );
+ }
+ else
+ {
+ // Writer starts a table in the middle of the border.
+ // Word starts a table at the left edge of the border,
+ // so emulate that by adding the half the width. (also see docxattributeoutput)
+ if ( rInfo.nNestLevel > 1 && nLeftMargin < 0 )
+ nLeftMargin = 0;
+ const sal_Int32 nAdjustedMargin = nLeftMargin - nGapHalf + (aLeftBorder.LineWidth / 2);
+ m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::Any( nAdjustedMargin ) );
+ }
+
+ sal_Int32 nTableWidth = 0;
+ sal_Int32 nTableWidthType = text::SizeType::FIX;
+ m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH, nTableWidth );
+ m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType );
+ if( nTableWidthType == text::SizeType::FIX )
+ {
+ if( nTableWidth > 0 )
+ m_aTableProperties->Insert( PROP_WIDTH, uno::Any( nTableWidth ));
+ else
+ {
+ // tdf#109524: If there is no width for the table, make it simply 100% by default.
+ // TODO: use cell contents to evaluate width (according to ECMA-376-1:2016 17.18.87)
+ nTableWidth = 100;
+ nTableWidthType = text::SizeType::VARIABLE;
+ }
+ }
+ if (nTableWidthType != text::SizeType::FIX)
+ {
+ m_aTableProperties->Insert( PROP_RELATIVE_WIDTH, uno::Any( sal_Int16( nTableWidth ) ) );
+ m_aTableProperties->Insert( PROP_IS_WIDTH_RELATIVE, uno::Any( true ) );
+ }
+
+ sal_Int32 nHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH;
+ // Fetch Horizontal Orientation in rFrameProperties if not set in m_aTableProperties
+ if ( !m_aTableProperties->getValue( TablePropertyMap::HORI_ORIENT, nHoriOrient ) )
+ lcl_extractHoriOrient( rFrameProperties, nHoriOrient );
+ m_aTableProperties->Insert( PROP_HORI_ORIENT, uno::Any( sal_Int16(nHoriOrient) ) );
+ //fill default value - if not available
+ m_aTableProperties->Insert( PROP_HEADER_ROW_COUNT, uno::Any( sal_Int32(0)), false);
+
+ // if table is only a single row, and row is set as don't split, set the same value for the whole table.
+ if( m_aRowProperties.size() == 1 && m_aRowProperties[0] )
+ {
+ std::optional<PropertyMap::Property> oSplitAllowed = m_aRowProperties[0]->getProperty(PROP_IS_SPLIT_ALLOWED);
+ if( oSplitAllowed )
+ {
+ bool bRowCanSplit = true;
+ oSplitAllowed->second >>= bRowCanSplit;
+ if( !bRowCanSplit )
+ m_aTableProperties->Insert( PROP_SPLIT, uno::Any(bRowCanSplit) );
+ }
+ }
+
+ rInfo.aTableProperties = m_aTableProperties->GetPropertyValues();
+ rInfo.aTablePropertyIds = m_aTableProperties->GetPropertyIds();
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("debug.tableprops");
+ m_aTableProperties->dumpXml();
+ TagLogger::getInstance().endElement();
+#endif
+
+ }
+
+ return pTableStyle;
+}
+
+CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("getCellProperties");
+#endif
+
+ CellPropertyValuesSeq_t aCellProperties( m_aCellProperties.size() );
+
+ if ( m_aCellProperties.empty() )
+ {
+ #ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+ #endif
+ return aCellProperties;
+ }
+ // std::vector< std::vector<PropertyMapPtr> > m_aCellProperties
+ PropertyMapVector2::const_iterator aRowOfCellsIterator = m_aCellProperties.begin();
+ PropertyMapVector2::const_iterator aRowOfCellsIteratorEnd = m_aCellProperties.end();
+ PropertyMapVector2::const_iterator aLastRowIterator = m_aCellProperties.end() - 1;
+ sal_Int32 nRow = 0;
+
+ css::uno::Sequence<css::beans::PropertyValues>* pCellProperties = aCellProperties.getArray();
+ PropertyMapVector1::const_iterator aRowIter = m_aRowProperties.begin();
+ while( aRowOfCellsIterator != aRowOfCellsIteratorEnd )
+ {
+ //aRowOfCellsIterator points to a vector of PropertyMapPtr
+ PropertyMapVector1::const_iterator aCellIterator = aRowOfCellsIterator->begin();
+ PropertyMapVector1::const_iterator aCellIteratorEnd = aRowOfCellsIterator->end();
+
+ sal_Int32 nRowStyleMask = 0;
+
+ if (aRowOfCellsIterator==m_aCellProperties.begin())
+ {
+ if(rInfo.nTblLook&0x20)
+ nRowStyleMask |= CNF_FIRST_ROW; // first row style used
+ }
+ else if (aRowOfCellsIterator==aLastRowIterator)
+ {
+ if(rInfo.nTblLook&0x40)
+ nRowStyleMask |= CNF_LAST_ROW; // last row style used
+ }
+ else if (*aRowIter && (*aRowIter)->isSet(PROP_TBL_HEADER))
+ nRowStyleMask |= CNF_FIRST_ROW; // table header implies first row
+ if(!nRowStyleMask) // if no row style used yet
+ {
+ // banding used only if not first and or last row style used
+ if(!(rInfo.nTblLook&0x200))
+ { // hbanding used
+ int n = nRow + 1;
+ if(rInfo.nTblLook&0x20)
+ n++;
+ if(n & 1)
+ nRowStyleMask = CNF_ODD_HBAND;
+ else
+ nRowStyleMask = CNF_EVEN_HBAND;
+ }
+ }
+
+ // Note that this is intentionally called "cell" and not "column".
+ // Don't make the mistake that all cell x's will be in the same column.
+ // Merged cells (grid span) in a row will affect the actual column. (fake cells were added to handle gridBefore/After)
+ sal_Int32 nCell = 0;
+ pCellProperties[nRow].realloc( aRowOfCellsIterator->size() );
+ beans::PropertyValues* pSingleCellProperties = pCellProperties[nRow].getArray();
+
+ while( aCellIterator != aCellIteratorEnd )
+ {
+ PropertyMapPtr pAllCellProps( new PropertyMap );
+
+ PropertyMapVector1::const_iterator aLastCellIterator = aRowOfCellsIterator->end() - 1;
+ bool bIsEndCol = aCellIterator == aLastCellIterator;
+ bool bIsEndRow = aRowOfCellsIterator == aLastRowIterator;
+
+ //aCellIterator points to a PropertyMapPtr;
+ if( *aCellIterator )
+ {
+ // remove directly applied insideV/H borders since they are meaningless without a context (tdf#82177)
+ (*aCellIterator)->Erase(META_PROP_VERTICAL_BORDER);
+ (*aCellIterator)->Erase(META_PROP_HORIZONTAL_BORDER);
+
+ pAllCellProps->InsertProps(rInfo.pTableDefaults);
+
+ sal_Int32 nCellStyleMask = 0;
+ if (aCellIterator==aRowOfCellsIterator->begin())
+ {
+ if(rInfo.nTblLook&0x80)
+ nCellStyleMask = CNF_FIRST_COLUMN; // first col style used
+ }
+ else if (bIsEndCol)
+ {
+ if(rInfo.nTblLook&0x100)
+ nCellStyleMask = CNF_LAST_COLUMN; // last col style used
+ }
+ if(!nCellStyleMask) // if no cell style is used yet
+ {
+ if(!(rInfo.nTblLook&0x400))
+ { // vbanding used
+ int n = nCell + 1;
+ if(rInfo.nTblLook&0x80)
+ n++;
+ if(n & 1)
+ nCellStyleMask = CNF_ODD_VBAND;
+ else
+ nCellStyleMask = CNF_EVEN_VBAND;
+ }
+ }
+ sal_Int32 nCnfStyleMask = nCellStyleMask + nRowStyleMask;
+ if(nCnfStyleMask == CNF_FIRST_COLUMN + CNF_FIRST_ROW)
+ nCnfStyleMask |= CNF_FIRST_ROW_FIRST_COLUMN;
+ else if(nCnfStyleMask == CNF_FIRST_COLUMN + CNF_LAST_ROW)
+ nCnfStyleMask |= CNF_LAST_ROW_FIRST_COLUMN;
+ else if(nCnfStyleMask == CNF_LAST_COLUMN + CNF_FIRST_ROW)
+ nCnfStyleMask |= CNF_FIRST_ROW_LAST_COLUMN;
+ else if(nCnfStyleMask == CNF_LAST_COLUMN + CNF_LAST_ROW)
+ nCnfStyleMask |= CNF_LAST_ROW_LAST_COLUMN;
+
+ if ( rInfo.pTableStyle )
+ {
+ PropertyMapPtr pStyleProps = rInfo.pTableStyle->GetProperties( nCnfStyleMask );
+
+ // Check if we need to clean up some empty border definitions to match what Word does.
+ static const PropertyIds pBorders[] =
+ {
+ PROP_TOP_BORDER, PROP_LEFT_BORDER, PROP_BOTTOM_BORDER, PROP_RIGHT_BORDER
+ };
+ for (const PropertyIds& rBorder : pBorders)
+ {
+ std::optional<PropertyMap::Property> oStyleCellBorder = pStyleProps->getProperty(rBorder);
+ std::optional<PropertyMap::Property> oDirectCellBorder = (*aCellIterator)->getProperty(rBorder);
+ if (oStyleCellBorder && oDirectCellBorder)
+ {
+ // We have a cell border from the table style and as direct formatting as well.
+ table::BorderLine2 aStyleCellBorder = oStyleCellBorder->second.get<table::BorderLine2>();
+ table::BorderLine2 aDirectCellBorder = oDirectCellBorder->second.get<table::BorderLine2>();
+ if (aStyleCellBorder.LineStyle != table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE)
+ {
+ // The style one would be visible, but then cleared away as direct formatting.
+ // Delete both, so that table formatting can become visible.
+ pStyleProps->Erase(rBorder);
+ (*aCellIterator)->Erase(rBorder);
+ }
+ else
+ {
+ std::optional<PropertyMap::Property> oTableBorder = rInfo.pTableBorders->getProperty(rBorder);
+ if (oTableBorder)
+ {
+ table::BorderLine2 aTableBorder = oTableBorder->second.get<table::BorderLine2>();
+ // Both style and direct formatting says that the cell has no border.
+ bool bNoCellBorder = aStyleCellBorder.LineStyle == table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE;
+ if (aTableBorder.LineStyle != table::BorderLineStyle::NONE && bNoCellBorder)
+ {
+ // But at a table-level, there is a border, then again delete both cell properties.
+ pStyleProps->Erase(rBorder);
+ (*aCellIterator)->Erase(rBorder);
+ }
+ }
+ }
+ }
+ }
+
+ pAllCellProps->InsertProps( pStyleProps );
+ }
+
+ // Remove properties from style/row that aren't allowed in cells
+ pAllCellProps->Erase( PROP_HEADER_ROW_COUNT );
+ pAllCellProps->Erase( PROP_TBL_HEADER );
+
+ // Then add the cell properties
+ pAllCellProps->InsertProps(*aCellIterator);
+ std::swap(*(*aCellIterator), *pAllCellProps );
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("cell");
+ TagLogger::getInstance().attribute("cell", nCell);
+ TagLogger::getInstance().attribute("row", nRow);
+#endif
+
+ // Do not apply horizontal and vertical borders to a one cell table.
+ if (m_aCellProperties.size() <= 1 && aRowOfCellsIterator->size() <= 1)
+ {
+ rInfo.pTableBorders->Erase(META_PROP_HORIZONTAL_BORDER);
+ rInfo.pTableBorders->Erase(META_PROP_VERTICAL_BORDER);
+ }
+ // Do not apply vertical borders to a one column table.
+ else if (m_aCellProperties.size() > 1 && aRowOfCellsIterator->size() <= 1)
+ {
+ bool isOneCol = true;
+ for (size_t i = nRow; i < m_aCellProperties.size(); i++)
+ {
+ if (m_aCellProperties[i].size() > 1)
+ {
+ isOneCol = false;
+ break;
+ }
+ }
+ if (isOneCol)
+ rInfo.pTableBorders->Erase(META_PROP_VERTICAL_BORDER);
+ }
+ // Do not apply horizontal borders to a one row table.
+ else if (m_aCellProperties.size() == 1 && aRowOfCellsIterator->size() > 1)
+ {
+ rInfo.pTableBorders->Erase(META_PROP_HORIZONTAL_BORDER);
+ }
+
+ // tdf#129452 Checking if current cell is vertically merged with all the other cells below to the bottom.
+ // This must be done in order to apply the bottom border of the table to the first cell in a vertical merge.
+ std::optional<PropertyMap::Property> oProp = m_aCellProperties[nRow][nCell]->getProperty(PROP_VERTICAL_MERGE);
+ bool bMergedVertically = oProp && oProp->second.get<bool>(); // starting cell
+ if ( bMergedVertically )
+ {
+ const sal_uInt32 nColumn = m_rDMapper_Impl.getTableManager().findColumn(nRow, nCell);
+ sal_Int32 nLastMergedRow = 0;
+ for (size_t i = nRow + 1; bMergedVertically && i < m_aCellProperties.size(); i++)
+ {
+ const sal_uInt32 nColumnCell = m_rDMapper_Impl.getTableManager().findColumnCell(i, nColumn);
+ if ( m_aCellProperties[i].size() > sal::static_int_cast<std::size_t>(nColumnCell) )
+ {
+ oProp = m_aCellProperties[i][nColumnCell]->getProperty(PROP_VERTICAL_MERGE);
+ bMergedVertically = oProp && !oProp->second.get<bool>(); //continuing cell
+ if ( bMergedVertically )
+ nLastMergedRow = i;
+ }
+ else
+ bMergedVertically = false;
+ }
+
+ // Only consider the bottom border setting from the last merged cell.
+ // Note: in MSO, left/right apply per-unmerged-row. Can't do that in LO, so just using the top cell's borders should be fine.
+ if ( nRow < nLastMergedRow )
+ {
+ (*aCellIterator)->Erase(PROP_BOTTOM_BORDER);
+ const sal_uInt32 nColumnCell = m_rDMapper_Impl.getTableManager().findColumnCell(nLastMergedRow, nColumn);
+ lcl_mergeBorder( PROP_BOTTOM_BORDER, m_aCellProperties[nLastMergedRow][nColumnCell], *aCellIterator );
+ }
+ }
+
+ const sal_uInt32 nFirstCell = m_rDMapper_Impl.getTableManager().getGridBefore(nRow);
+ const sal_uInt32 nLastCell = m_aCellProperties[nRow].size() - m_rDMapper_Impl.getTableManager().getGridAfter(nRow) - 1;
+ lcl_computeCellBorders( rInfo.pTableBorders, *aCellIterator, nCell, nFirstCell, nLastCell, nRow, bIsEndRow, bMergedVertically );
+
+ //now set the default left+right border distance TODO: there's an sprm containing the default distance!
+ aCellIterator->get()->Insert( PROP_LEFT_BORDER_DISTANCE,
+ uno::Any(rInfo.nLeftBorderDistance ), false);
+ aCellIterator->get()->Insert( PROP_RIGHT_BORDER_DISTANCE,
+ uno::Any(rInfo.nRightBorderDistance ), false);
+ aCellIterator->get()->Insert( PROP_TOP_BORDER_DISTANCE,
+ uno::Any(rInfo.nTopBorderDistance ), false);
+ aCellIterator->get()->Insert( PROP_BOTTOM_BORDER_DISTANCE,
+ uno::Any(rInfo.nBottomBorderDistance ), false);
+
+ // Horizontal merge is not a UNO property, extract that info here to rMerges, and then remove it from the map.
+ const std::optional<PropertyMap::Property> aHorizontalMergeVal = (*aCellIterator)->getProperty(PROP_HORIZONTAL_MERGE);
+ if (aHorizontalMergeVal)
+ {
+ if (aHorizontalMergeVal->second.get<bool>())
+ {
+ // first cell in a merge
+ HorizontallyMergedCell aMerge(nRow, nCell);
+ rMerges.push_back(aMerge);
+ }
+ else if (!rMerges.empty())
+ {
+ // resuming an earlier merge
+ HorizontallyMergedCell& rMerge = rMerges.back();
+ rMerge.m_nLastRow = nRow;
+ rMerge.m_nLastCol = nCell;
+ }
+ (*aCellIterator)->Erase(PROP_HORIZONTAL_MERGE);
+ }
+ pSingleCellProperties[nCell] = (*aCellIterator)->GetPropertyValues();
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+ }
+ ++nCell;
+ ++aCellIterator;
+ }
+ ++nRow;
+ ++aRowOfCellsIterator;
+ ++aRowIter;
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+
+ return aCellProperties;
+}
+
+/// Do all cells in this row have a CellHideMark property?
+static bool lcl_hideMarks(PropertyMapVector1& rCellProperties)
+{
+ for (const PropertyMapPtr & p : rCellProperties)
+ {
+ // if anything is vertically merged, the row must not be set to fixed
+ // as Writer's layout doesn't handle that well
+ if (!p->isSet(PROP_CELL_HIDE_MARK) || p->isSet(PROP_VERTICAL_MERGE))
+ return false;
+ }
+ return true;
+}
+
+/// Are all cells in this row empty?
+static bool lcl_emptyRow(std::vector<RowSequence_t>& rTableRanges, sal_Int32 nRow)
+{
+ if (nRow >= static_cast<sal_Int32>(rTableRanges.size()))
+ {
+ SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
+ return false;
+ }
+
+ const RowSequence_t rRowSeq = rTableRanges[nRow];
+ if (!rRowSeq.hasElements())
+ {
+ SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
+ return false;
+ }
+
+ if (!rRowSeq[0][0].is())
+ {
+ // This can happen when we can't import the table, e.g. we're inside a
+ // comment.
+ SAL_WARN("writerfilter.dmapper", "rRowSeq[0][0] is an empty reference");
+ return false;
+ }
+
+ uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rRowSeq[0][0]->getText(), uno::UNO_QUERY);
+ try
+ {
+ // See SwXText::Impl::ConvertCell(), we need to compare the start of
+ // the start and the end of the end. However for our text ranges, only
+ // the starts are set, so compareRegionStarts() does what we need.
+ bool bRangesAreNotEqual = std::any_of(rRowSeq.begin(), rRowSeq.end(),
+ [&xTextRangeCompare](const CellSequence_t& rCellSeq) {
+ return xTextRangeCompare->compareRegionStarts(rCellSeq[0], rCellSeq[1]) != 0; });
+ if (bRangesAreNotEqual)
+ return false;
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "compareRegionStarts() failed");
+ return false;
+ }
+ return true;
+}
+
+css::uno::Sequence<css::beans::PropertyValues> DomainMapperTableHandler::endTableGetRowProperties()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("getRowProperties");
+#endif
+
+ css::uno::Sequence<css::beans::PropertyValues> aRowProperties( m_aRowProperties.size() );
+ auto aRowPropertiesRange = asNonConstRange(aRowProperties);
+ sal_Int32 nRow = 0;
+ for( const auto& rRow : m_aRowProperties )
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("rowProps.row");
+#endif
+ if (rRow)
+ {
+ //set default to 'break across pages"
+ rRow->Insert( PROP_IS_SPLIT_ALLOWED, uno::Any(true ), false );
+ // tblHeader is only our property, remove before the property map hits UNO
+ rRow->Erase(PROP_TBL_HEADER);
+
+ if (lcl_hideMarks(m_aCellProperties[nRow]) && lcl_emptyRow(m_aTableRanges, nRow))
+ {
+ // We have CellHideMark on all cells, and also all cells are empty:
+ // Force the row height to be exactly as specified, and not just as the minimum suggestion.
+ rRow->Insert(PROP_SIZE_TYPE, uno::Any(text::SizeType::FIX));
+ }
+
+ aRowPropertiesRange[nRow] = rRow->GetPropertyValues();
+#ifdef DBG_UTIL
+ rRow->dumpXml();
+ lcl_DumpPropertyValues(aRowProperties[nRow]);
+#endif
+ }
+ ++nRow;
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+
+ return aRowProperties;
+}
+
+// table style has got bigger precedence than docDefault style,
+// but lower precedence than the paragraph styles and direct paragraph formatting
+void DomainMapperTableHandler::ApplyParagraphPropertiesFromTableStyle(TableParagraph rParaProp, std::vector< PropertyIds > aAllTableParaProperties, const css::beans::PropertyValues rCellProperties)
+{
+ for( auto const& eId : aAllTableParaProperties )
+ {
+ // apply paragraph and character properties of the table style on table paragraphs
+ // if there is no direct paragraph formatting
+ bool bIsParaLevel = rParaProp.m_pPropertyMap->isSet(eId);
+ if ( !bIsParaLevel || isCharacterProperty(eId) )
+ {
+ if ( (eId == PROP_PARA_LEFT_MARGIN || eId == PROP_PARA_FIRST_LINE_INDENT) &&
+ rParaProp.m_pPropertyMap->isSet(PROP_NUMBERING_RULES) )
+ {
+ // indentation of direct numbering has bigger precedence, than table style
+ continue;
+ }
+
+ OUString sPropertyName = getPropertyName(eId);
+
+ auto pCellProp = std::find_if(rCellProperties.begin(), rCellProperties.end(),
+ [&](const beans::PropertyValue& rProp) { return rProp.Name == sPropertyName; });
+ // this cell applies the table style property
+ if (pCellProp != rCellProperties.end())
+ {
+ bool bDocDefault;
+ // handle paragraph background color defined in CellColorHandler
+ if (eId == PROP_FILL_COLOR)
+ {
+ // table style defines paragraph background color, use the correct property name
+ auto pFillStyleProp = std::find_if(rCellProperties.begin(), rCellProperties.end(),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "FillStyle"; });
+ if ( pFillStyleProp != rCellProperties.end() &&
+ pFillStyleProp->Value == uno::Any(drawing::FillStyle_SOLID) )
+ {
+ sPropertyName = "ParaBackColor";
+ }
+ else
+ {
+ // FillStyle_NONE, skip table style usage for paragraph background color
+ continue;
+ }
+ }
+ OUString sParaStyleName;
+ rParaProp.m_rPropertySet->getPropertyValue("ParaStyleName") >>= sParaStyleName;
+ StyleSheetEntryPtr pEntry = m_rDMapper_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sParaStyleName);
+ uno::Any aParaStyle = m_rDMapper_Impl.GetPropertyFromStyleSheet(eId, pEntry, true, true, &bDocDefault);
+ // A very strange compatibility rule says that the DEFAULT style's specified fontsize of 11 or 12
+ // or a specified left justify will always be overridden by the table-style.
+ // Normally this rule is applied, so always do this unless a compatSetting indicates otherwise.
+ bool bCompatOverride = false;
+ if ( (eId == PROP_CHAR_HEIGHT || eId == PROP_PARA_ADJUST) && sParaStyleName == m_rDMapper_Impl.GetDefaultParaStyleName() )
+ {
+ if ( eId == PROP_CHAR_HEIGHT )
+ bCompatOverride = aParaStyle == uno::Any(double(11)) || aParaStyle == uno::Any(double(12));
+ else if ( eId == PROP_PARA_ADJUST )
+ {
+ style::ParagraphAdjust eAdjust(style::ParagraphAdjust_CENTER);
+ aParaStyle >>= eAdjust;
+ bCompatOverride = eAdjust == style::ParagraphAdjust_LEFT;
+ }
+
+ // The wording is confusing here. Normally, the paragraph style DOES override the table-style.
+ // But for these two special situations, do not override the table-style. So the default is false.
+ // If false, then "CompatOverride" the normal behaviour, and apply the table-style's value.
+ bCompatOverride &= !m_rDMapper_Impl.GetSettingsTable()->GetCompatSettingValue(u"overrideTableStyleFontSizeAndJustification");
+ }
+
+ // use table style when no paragraph style setting or a docDefault value is applied instead of it
+ if ( aParaStyle == uno::Any() || bDocDefault || bCompatOverride ) try
+ {
+ // check property state of paragraph
+ uno::Reference<text::XParagraphCursor> xParagraph(
+ rParaProp.m_rEndParagraph->getText()->createTextCursorByRange(rParaProp.m_rEndParagraph), uno::UNO_QUERY_THROW );
+ // select paragraph
+ xParagraph->gotoStartOfParagraph( true );
+ uno::Reference< beans::XPropertyState > xParaProperties( xParagraph, uno::UNO_QUERY_THROW );
+ if ( xParaProperties->getPropertyState(sPropertyName) == css::beans::PropertyState_DEFAULT_VALUE )
+ {
+ // don't overwrite empty paragraph with table style, if it has a direct paragraph formatting
+ if ( bIsParaLevel && xParagraph->getString().getLength() == 0 )
+ continue;
+
+ if ( eId != PROP_FILL_COLOR )
+ {
+ // apply style setting when the paragraph doesn't modify it
+ rParaProp.m_rPropertySet->setPropertyValue( sPropertyName, pCellProp->Value );
+ }
+ else
+ {
+ // we need this for complete import of table-style based paragraph background color
+ rParaProp.m_rPropertySet->setPropertyValue( "FillColor", pCellProp->Value );
+ rParaProp.m_rPropertySet->setPropertyValue( "FillStyle", uno::Any(drawing::FillStyle_SOLID) );
+ }
+ }
+ else
+ {
+ // apply style setting only on text portions without direct modification of it
+ uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xParagraph, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
+ uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
+ while ( xRunEnum->hasMoreElements() )
+ {
+ uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
+ uno::Reference< beans::XPropertyState > xRunProperties( xRun, uno::UNO_QUERY_THROW );
+ if ( xRunProperties->getPropertyState(sPropertyName) == css::beans::PropertyState_DEFAULT_VALUE )
+ {
+ uno::Reference< beans::XPropertySet > xRunPropertySet( xRun, uno::UNO_QUERY_THROW );
+ xRunPropertySet->setPropertyValue( sPropertyName, pCellProp->Value );
+ }
+ }
+ }
+ }
+ catch ( const uno::Exception & )
+ {
+ TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table style correction");
+ }
+ }
+ }
+ }
+}
+
+// convert formula range identifier ABOVE, BELOW, LEFT and RIGHT
+static void lcl_convertFormulaRanges(const uno::Reference<text::XTextTable> & xTable)
+{
+ uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW);
+ uno::Reference<container::XIndexAccess> xTableRows(xTable->getRows(), uno::UNO_QUERY_THROW);
+ sal_Int32 nRows = xTableRows->getCount();
+ for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
+ {
+ for (sal_Int16 nCol = 0; nCol < MAXTABLECELLS; ++nCol)
+ {
+ try
+ {
+ uno::Reference<beans::XPropertySet> xCellProperties(xCellRange->getCellByPosition(nCol, nRow), uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aCellGrabBag;
+ xCellProperties->getPropertyValue("CellInteropGrabBag") >>= aCellGrabBag;
+ OUString sFormula;
+ bool bReplace = false;
+ for (const auto& rProp : std::as_const(aCellGrabBag))
+ {
+ if ( rProp.Name == "CellFormulaConverted" )
+ {
+ rProp.Value >>= sFormula;
+ struct RangeDirection
+ {
+ OUString m_sName;
+ sal_Int16 m_nCol;
+ sal_Int16 m_nRow;
+ };
+ static const RangeDirection pDirections[] =
+ {
+ { OUString(" LEFT "), -1, 0},
+ { OUString(" RIGHT "), 1, 0},
+ { OUString(" ABOVE "), 0, -1},
+ { OUString(" BELOW "), 0, 1 }
+ };
+ for (const RangeDirection& rRange : pDirections)
+ {
+ if ( sFormula.indexOf(rRange.m_sName) > -1 )
+ {
+ // range starts at the first cell above/below/left/right, but ends at the
+ // table border or at the first non-value cell after a value cell
+ bool bFoundFirst = false;
+ OUString sNextCell;
+ OUString sLastCell;
+ OUString sLastValueCell;
+ // walk through the cells of the range
+ try
+ {
+ sal_Int32 nCell = 0;
+ while (++nCell)
+ {
+ uno::Reference<beans::XPropertySet> xCell(
+ xCellRange->getCellByPosition(nCol + nCell * rRange.m_nCol, nRow + nCell * rRange.m_nRow),
+ uno::UNO_QUERY_THROW);
+ // empty cell or cell with text content is end of the range
+ uno::Reference<text::XText> xText(xCell, uno::UNO_QUERY_THROW);
+ sLastCell = xCell->getPropertyValue("CellName").get<OUString>();
+ if (sNextCell.isEmpty())
+ sNextCell = sLastCell;
+ try
+ {
+ // accept numbers with comma and percent
+ OUString sCellText = xText->getString().replace(',', '.');
+ if (sCellText.endsWith("%"))
+ sCellText = sCellText.copy(0, sCellText.getLength()-1);
+ boost::lexical_cast<double>(sCellText);
+ }
+ catch( boost::bad_lexical_cast const& )
+ {
+ if ( !bFoundFirst )
+ {
+ // still search value cells
+ continue;
+ }
+ else
+ {
+ // end of range
+ break;
+ }
+ }
+ sLastValueCell = sLastCell;
+ bFoundFirst = true;
+ }
+ }
+ catch ( const lang::IndexOutOfBoundsException & )
+ {
+ }
+
+ if ( !sNextCell.isEmpty() )
+ {
+ OUString sRange = "<" + sNextCell + ":" +
+ ( sLastValueCell.isEmpty() ? sLastCell : sLastValueCell ) + ">";
+ sFormula = sFormula.replaceAll(rRange.m_sName, sRange);
+ bReplace = true;
+ }
+ }
+ }
+
+ // update formula field
+ if (bReplace)
+ {
+ uno::Reference<text::XText> xCell(xCellRange->getCellByPosition(nCol, nRow), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xCell, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
+ uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
+ while ( xRunEnum->hasMoreElements() )
+ {
+ uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
+ uno::Reference< beans::XPropertySet > xRunProperties( xRun, uno::UNO_QUERY_THROW );
+ if ( xRunProperties->getPropertyValue("TextPortionType") == uno::Any(OUString("TextField")) )
+ {
+ uno::Reference<text::XTextField> const xField(xRunProperties->getPropertyValue("TextField").get<uno::Reference<text::XTextField>>());
+ uno::Reference< beans::XPropertySet > xFieldProperties( xField, uno::UNO_QUERY_THROW );
+ // cell can contain multiple text fields, but only one is handled now (~formula cell)
+ if ( rProp.Value != xFieldProperties->getPropertyValue("Content") )
+ continue;
+ xFieldProperties->setPropertyValue("Content", uno::Any(sFormula));
+ // update grab bag
+ auto aGrabBag = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aCellGrabBag);
+ beans::PropertyValue aValue;
+ aValue.Name = "CellFormulaConverted";
+ aValue.Value <<= sFormula;
+ aGrabBag.push_back(aValue);
+ xCellProperties->setPropertyValue("CellInteropGrabBag", uno::Any(comphelper::containerToSequence(aGrabBag)));
+ }
+ }
+ }
+ }
+ }
+ }
+ catch ( const lang::IndexOutOfBoundsException & )
+ {
+ // jump to next table row
+ break;
+ }
+ }
+ }
+}
+
+void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablehandler.endTable");
+#endif
+
+ // If we want to make this table a floating one.
+ std::vector<beans::PropertyValue> aFrameProperties = comphelper::sequenceToContainer<std::vector<beans::PropertyValue> >
+ (m_rDMapper_Impl.getTableManager().getCurrentTablePosition());
+ TableInfo aTableInfo;
+ aTableInfo.nNestLevel = nestedTableLevel;
+
+ // non-floating tables need floating in footnotes and endnotes, because
+ // Writer core cannot handle (i.e. save in ODT, copy, edit etc.) them otherwise
+ bool bConvertToFloating = aFrameProperties.empty() &&
+ nestedTableLevel <= 1 &&
+ m_rDMapper_Impl.IsInFootOrEndnote();
+ bool bFloating = !aFrameProperties.empty() || bConvertToFloating;
+
+ aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties, bConvertToFloating);
+ // expands to uno::Sequence< Sequence< beans::PropertyValues > >
+
+ std::vector<HorizontallyMergedCell> aMerges;
+ CellPropertyValuesSeq_t aCellProperties = endTableGetCellProperties(aTableInfo, aMerges);
+
+ css::uno::Sequence<css::beans::PropertyValues> aRowProperties = endTableGetRowProperties();
+
+#ifdef DBG_UTIL
+ lcl_DumpPropertyValueSeq(aRowProperties);
+#endif
+
+ if (!m_aTableRanges.empty())
+ {
+ uno::Reference<text::XTextRange> xStart;
+ uno::Reference<text::XTextRange> xEnd;
+
+ // fill empty frame properties to create an invisible frame around the table:
+ // hide frame borders and zero inner and outer frame margins
+ if (bConvertToFloating)
+ DomainMapper_Impl::fillEmptyFrameProperties(aFrameProperties, true);
+
+ // OOXML table style may contain paragraph properties, apply these on cell paragraphs
+ if ( m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements() )
+ {
+ // collect all paragraph properties used in table styles
+ PropertyMapPtr pAllTableProps( new PropertyMap );
+ pAllTableProps->InsertProps(aTableInfo.pTableDefaults);
+ if ( aTableInfo.pTableStyle )
+ pAllTableProps->InsertProps(aTableInfo.pTableStyle->GetProperties( CNF_ALL ));
+ for (const auto& eId : pAllTableProps->GetPropertyIds())
+ {
+ if ( !isParagraphProperty(eId) && !isCharacterProperty(eId) )
+ pAllTableProps->Erase(eId);
+ }
+ std::vector< PropertyIds > aAllTableParaProperties = pAllTableProps->GetPropertyIds();
+
+ if ( !aAllTableParaProperties.empty() )
+ {
+ TableParagraphVectorPtr pTableParagraphs = m_rDMapper_Impl.getTableManager().getCurrentParagraphs();
+ for (size_t nRow = 0; nRow < m_aTableRanges.size(); ++nRow)
+ {
+ // Note that this is "cell" since you must not treat it as "column".
+ for (size_t nCell = 0; nCell < m_aTableRanges[nRow].size(); ++nCell)
+ {
+ auto rStartPara = m_aTableRanges[nRow][nCell][0];
+ if (!rStartPara.is())
+ continue;
+ auto rEndPara = m_aTableRanges[nRow][nCell][1];
+ uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rStartPara->getText(), uno::UNO_QUERY);
+ bool bApply = false;
+ // search paragraphs of the cell
+ std::vector<TableParagraph>::iterator aIt = pTableParagraphs->begin();
+ while ( aIt != pTableParagraphs->end() ) try
+ {
+ if (!bApply && xTextRangeCompare->compareRegionStarts(rStartPara, aIt->m_rStartParagraph) == 0)
+ bApply = true;
+ if (bApply)
+ {
+ bool bEndOfApply = (xTextRangeCompare->compareRegionEnds(rEndPara, aIt->m_rEndParagraph) == 0);
+ // tdf#153891 handle missing cell properties (exception in style handling?)
+ if ( nCell < sal::static_int_cast<std::size_t>(aCellProperties[nRow].getLength()) )
+ ApplyParagraphPropertiesFromTableStyle(*aIt, aAllTableParaProperties, aCellProperties[nRow][nCell]);
+ // erase processed paragraph from list of pending paragraphs
+ aIt = pTableParagraphs->erase(aIt);
+ if (bEndOfApply)
+ break;
+ }
+ else
+ ++aIt;
+ }
+ catch( const lang::IllegalArgumentException & )
+ {
+ // skip compareRegion with nested tables
+ ++aIt;
+ }
+ }
+ }
+ }
+ }
+
+ // Additional checks: if we can do this.
+ if (bFloating && m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements())
+ {
+ xStart = m_aTableRanges[0][0][0];
+ uno::Sequence< uno::Sequence< uno::Reference<text::XTextRange> > >& rLastRow = m_aTableRanges[m_aTableRanges.size() - 1];
+ if (rLastRow.hasElements())
+ {
+ const uno::Sequence< uno::Reference<text::XTextRange> >& rLastCell = rLastRow[rLastRow.getLength() - 1];
+ xEnd = rLastCell[1];
+ }
+ }
+ uno::Reference<text::XTextTable> xTable;
+ try
+ {
+ if (m_xText.is())
+ {
+ xTable = m_xText->convertToTable(comphelper::containerToSequence(m_aTableRanges), aCellProperties, aRowProperties, aTableInfo.aTableProperties);
+
+ if (xTable.is())
+ {
+ if (!aMerges.empty())
+ {
+ static const std::vector<std::u16string_view> aBorderNames
+ = { u"TopBorder", u"LeftBorder", u"BottomBorder", u"RightBorder" };
+
+ // Perform horizontal merges in reverse order, so the fact that merging changes the position of cells won't cause a problem for us.
+ for (std::vector<HorizontallyMergedCell>::reverse_iterator it = aMerges.rbegin(); it != aMerges.rend(); ++it)
+ {
+ uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xFirstCell(
+ xCellRange->getCellByPosition(it->m_nFirstCol, it->m_nFirstRow),
+ uno::UNO_QUERY_THROW);
+ OUString aFirst
+ = xFirstCell->getPropertyValue("CellName").get<OUString>();
+ // tdf#105852: Only try to merge if m_nLastCol is set (i.e. there were some merge continuation cells)
+ if (it->m_nLastCol != -1)
+ {
+ // Save border properties of the first cell
+ // before merge.
+ table::BorderLine2 aBorderValues[4];
+ for (size_t i = 0; i < aBorderNames.size(); ++i)
+ xFirstCell->getPropertyValue(OUString(aBorderNames[i]))
+ >>= aBorderValues[i];
+
+ uno::Reference<beans::XPropertySet> xLastCell(
+ xCellRange->getCellByPosition(it->m_nLastCol, it->m_nLastRow),
+ uno::UNO_QUERY_THROW);
+ OUString aLast
+ = xLastCell->getPropertyValue("CellName").get<OUString>();
+
+ uno::Reference<text::XTextTableCursor> xCursor = xTable->createCursorByCellName(aFirst);
+ xCursor->gotoCellByName(aLast, true);
+
+ xCursor->mergeRange();
+
+ // Handle conflicting properties: mergeRange()
+ // takes the last cell, Word takes the first
+ // cell.
+ for (size_t i = 0; i < aBorderNames.size(); ++i)
+ {
+ if (aBorderValues[i].LineStyle != table::BorderLineStyle::NONE)
+ xFirstCell->setPropertyValue(
+ OUString(aBorderNames[i]), uno::Any(aBorderValues[i]));
+ }
+ }
+ }
+ }
+
+ // convert special range IDs ABOVE, BELOW, LEFT and RIGHT
+ lcl_convertFormulaRanges(xTable);
+ }
+ }
+ }
+ catch ( const lang::IllegalArgumentException & )
+ {
+ TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Conversion to table error");
+#ifdef DBG_UTIL
+ TagLogger::getInstance().chars(std::string("failed to import table!"));
+#endif
+ }
+ catch ( const uno::Exception & )
+ {
+ TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table creation");
+ }
+
+ // If we have a table with a start and an end position, we should make it a floating one.
+ // Unless the table had a foot or endnote, as Writer doesn't support those in TextFrames.
+ if (xTable.is() && xStart.is() && xEnd.is() && !m_bHadFootOrEndnote)
+ {
+ uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
+ bool bIsRelative = false;
+ xTableProperties->getPropertyValue("IsWidthRelative") >>= bIsRelative;
+ if (!bIsRelative)
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "Width";
+ aValue.Value = xTableProperties->getPropertyValue("Width");
+ aFrameProperties.push_back(aValue);
+ }
+ else
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "FrameWidthPercent";
+ aValue.Value = xTableProperties->getPropertyValue("RelativeWidth");
+ aFrameProperties.push_back(aValue);
+
+ // Applying the relative width to the frame, needs to have the table width to be 100% of the frame width
+ xTableProperties->setPropertyValue("RelativeWidth", uno::Any(sal_Int16(100)));
+ }
+
+ // A non-zero left margin would move the table out of the frame, move the frame itself instead.
+ xTableProperties->setPropertyValue("LeftMargin", uno::Any(sal_Int32(0)));
+
+ if (nestedTableLevel >= 2)
+ {
+ // Floating tables inside a table always stay inside the cell.
+ aFrameProperties.push_back(
+ comphelper::makePropertyValue("IsFollowingTextFlow", true));
+ }
+
+ // In case the document ends with a table, we're called after
+ // SectionPropertyMap::CloseSectionGroup(), so we'll have no idea
+ // about the text area width, nor can fix this by delaying the text
+ // frame conversion: just do it here.
+ // Also, when the anchor is within a table, then do it here as well,
+ // as xStart/xEnd would not point to the start/end at conversion
+ // time anyway.
+ // Next exception: it's pointless to delay the conversion if the
+ // table is not in the body text.
+ sal_Int32 nTableWidth = 0;
+ m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
+ sal_Int32 nTableWidthType = text::SizeType::FIX;
+ m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
+ if (m_rDMapper_Impl.GetSectionContext() && nestedTableLevel <= 1 && !m_rDMapper_Impl.IsInHeaderFooter())
+ {
+ m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd,
+ comphelper::containerToSequence(aFrameProperties),
+ nTableWidth, nTableWidthType, bConvertToFloating);
+ }
+ else
+ {
+ // m_xText points to the body text, get the current xText from m_rDMapper_Impl, in case e.g. we would be in a header.
+ uno::Reference<text::XTextAppendAndConvert> xTextAppendAndConvert(m_rDMapper_Impl.GetTopTextAppend(), uno::UNO_QUERY);
+ // Only execute the conversion if the table is not anchored at
+ // the start of an outer table cell, that's not yet
+ // implemented.
+ if (xTextAppendAndConvert.is() && !bTableStartsAtCellStart)
+ xTextAppendAndConvert->convertToTextFrame(xStart, xEnd, comphelper::containerToSequence(aFrameProperties));
+ }
+ }
+
+ // We're right after a table conversion.
+ m_rDMapper_Impl.m_bConvertedTable = true;
+ }
+
+ m_aTableProperties.clear();
+ m_aCellProperties.clear();
+ m_aRowProperties.clear();
+ m_bHadFootOrEndnote = false;
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void DomainMapperTableHandler::startRow(const TablePropertyMapPtr& pProps)
+{
+ m_aRowProperties.push_back( pProps.get() );
+ m_aCellProperties.emplace_back( );
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("table.row");
+ if (pProps != nullptr)
+ pProps->dumpXml();
+#endif
+
+ m_aRowRanges.clear();
+}
+
+void DomainMapperTableHandler::endRow()
+{
+ m_aTableRanges.push_back(comphelper::containerToSequence(m_aRowRanges));
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void DomainMapperTableHandler::startCell(const css::uno::Reference< css::text::XTextRange > & start,
+ const TablePropertyMapPtr& pProps )
+{
+ sal_uInt32 nRow = m_aRowProperties.size();
+ if ( pProps )
+ m_aCellProperties[nRow - 1].push_back( pProps.get() );
+ else
+ {
+ // Adding an empty cell properties map to be able to get
+ // the table defaults properties
+ TablePropertyMapPtr pEmptyProps( new TablePropertyMap( ) );
+ m_aCellProperties[nRow - 1].push_back( pEmptyProps.get() );
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("table.cell");
+ TagLogger::getInstance().startElement("table.cell.start");
+ TagLogger::getInstance().chars(XTextRangeToString(start));
+ TagLogger::getInstance().endElement();
+ if (pProps)
+ pProps->printProperties();
+#endif
+
+ //add a new 'row' of properties
+ m_aCellRange.clear();
+ uno::Reference<text::XTextRange> xStart;
+ if (start)
+ xStart = start->getStart();
+ m_aCellRange.push_back(xStart);
+}
+
+void DomainMapperTableHandler::endCell(const css::uno::Reference< css::text::XTextRange > & end)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("table.cell.end");
+ TagLogger::getInstance().chars(XTextRangeToString(end));
+ TagLogger::getInstance().endElement();
+ TagLogger::getInstance().endElement();
+#endif
+
+ uno::Reference<text::XTextRange> xEnd;
+ if (end)
+ xEnd = end->getEnd();
+ m_aCellRange.push_back(xEnd);
+ m_aRowRanges.push_back(comphelper::containerToSequence(m_aCellRange));
+}
+
+void DomainMapperTableHandler::setHadFootOrEndnote(bool bHadFootOrEndnote)
+{
+ m_bHadFootOrEndnote = bHadFootOrEndnote;
+}
+
+DomainMapper_Impl& DomainMapperTableHandler::getDomainMapperImpl()
+{
+ return m_rDMapper_Impl;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
new file mode 100644
index 000000000..4e396b6b2
--- /dev/null
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
@@ -0,0 +1,126 @@
+/* -*- 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 "PropertyMap.hxx"
+#include <vector>
+
+#include <com/sun/star/text/XTextAppendAndConvert.hpp>
+
+namespace writerfilter::dmapper {
+
+typedef css::uno::Sequence< css::uno::Reference< css::text::XTextRange > > CellSequence_t;
+typedef css::uno::Sequence<CellSequence_t> RowSequence_t;
+
+typedef css::uno::Sequence< css::uno::Sequence<css::beans::PropertyValues> > CellPropertyValuesSeq_t;
+
+typedef std::vector<PropertyMapPtr> PropertyMapVector1;
+typedef std::vector<PropertyMapVector1> PropertyMapVector2;
+
+class DomainMapper_Impl;
+class TableStyleSheetEntry;
+struct TableInfo;
+
+/// A horizontally merged cell is in fact a range of cells till its merge is performed.
+struct HorizontallyMergedCell
+{
+ sal_Int32 m_nFirstRow;
+ sal_Int32 m_nFirstCol;
+ sal_Int32 m_nLastRow;
+ sal_Int32 m_nLastCol;
+ HorizontallyMergedCell(sal_Int32 nFirstRow, sal_Int32 nFirstCol)
+ : m_nFirstRow(nFirstRow)
+ , m_nFirstCol(nFirstCol)
+ , m_nLastRow(nFirstRow)
+ , m_nLastCol(-1)
+ {
+ }
+};
+
+/// Class to handle events generated by TableManager::resolveCurrentTable().
+class DomainMapperTableHandler final : public virtual SvRefBase
+{
+ css::uno::Reference<css::text::XTextAppendAndConvert> m_xText;
+ DomainMapper_Impl& m_rDMapper_Impl;
+ std::vector< css::uno::Reference<css::text::XTextRange> > m_aCellRange;
+ std::vector<CellSequence_t> m_aRowRanges;
+ std::vector<RowSequence_t> m_aTableRanges;
+
+ // properties
+ PropertyMapVector2 m_aCellProperties;
+ PropertyMapVector1 m_aRowProperties;
+ TablePropertyMapPtr m_aTableProperties;
+
+ /// Did we have a foot or endnote in this table?
+ bool m_bHadFootOrEndnote;
+
+ TableStyleSheetEntry * endTableGetTableStyle(TableInfo & rInfo,
+ std::vector<css::beans::PropertyValue>& rFrameProperties,
+ bool bConvertToFloating);
+ CellPropertyValuesSeq_t endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges);
+ css::uno::Sequence<css::beans::PropertyValues> endTableGetRowProperties();
+
+public:
+ typedef tools::SvRef<DomainMapperTableHandler> Pointer_t;
+
+ DomainMapperTableHandler(css::uno::Reference<css::text::XTextAppendAndConvert> const& xText,
+ DomainMapper_Impl& rDMapper_Impl);
+ ~DomainMapperTableHandler() override;
+
+ /**
+ Handle start of table.
+
+ @param pProps properties of the table
+ */
+ void startTable(const TablePropertyMapPtr& pProps);
+
+ void ApplyParagraphPropertiesFromTableStyle(TableParagraph rParaProp, std::vector< PropertyIds > aAllTableProperties, const css::beans::PropertyValues rCellProperties);
+
+ /// Handle end of table.
+ void endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart);
+ /**
+ Handle start of row.
+
+ @param pProps properties of the row
+ */
+ void startRow(const TablePropertyMapPtr& pProps);
+ /// Handle end of row.
+ void endRow();
+ /**
+ Handle start of cell.
+
+ @param rT start handle of the cell
+ @param pProps properties of the cell
+ */
+ void startCell(const css::uno::Reference< css::text::XTextRange > & start, const TablePropertyMapPtr& pProps);
+ /**
+ Handle end of cell.
+
+ @param rT end handle of cell
+ */
+ void endCell(const css::uno::Reference< css::text::XTextRange > & end);
+
+ void setHadFootOrEndnote(bool bHadFootOrEndnote);
+
+ DomainMapper_Impl& getDomainMapperImpl();
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapperTableManager.cxx b/writerfilter/source/dmapper/DomainMapperTableManager.cxx
new file mode 100644
index 000000000..004f34971
--- /dev/null
+++ b/writerfilter/source/dmapper/DomainMapperTableManager.cxx
@@ -0,0 +1,859 @@
+/* -*- 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 <optional>
+#include "DomainMapperTableManager.hxx"
+#include "ConversionHelper.hxx"
+#include "MeasureHandler.hxx"
+#include "TagLogger.hxx"
+#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/TableColumnSeparator.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <o3tl/numeric.hxx>
+#include <o3tl/safeint.hxx>
+#include <ooxml/resourceids.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+#include <numeric>
+#include "TrackChangesHandler.hxx"
+#include <oox/token/tokens.hxx>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+using namespace ::std;
+
+DomainMapperTableManager::DomainMapperTableManager() :
+ m_nRow(0),
+ m_nGridSpan(1),
+ m_nHeaderRepeat(0),
+ m_nTableWidth(0),
+ m_bIsInShape(false),
+ m_bPushCurrentWidth(false),
+ m_bTableSizeTypeInserted(false),
+ m_nLayoutType(0),
+ m_pTablePropsHandler(new TablePropertiesHandler())
+{
+ m_pTablePropsHandler->SetTableManager( this );
+}
+
+
+DomainMapperTableManager::~DomainMapperTableManager()
+{
+}
+
+bool DomainMapperTableManager::attribute(Id nName, Value const & rValue)
+{
+ bool bRet = true;
+
+ switch (nName)
+ {
+ case NS_ooxml::LN_CT_TblLook_val:
+ {
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(PROP_TBL_LOOK, uno::Any(sal_Int32(rValue.getInt())));
+ insertTableProps(pPropMap);
+ m_aTableLook["val"] <<= static_cast<sal_Int32>(rValue.getInt());
+ }
+ break;
+ case NS_ooxml::LN_CT_TblLook_noVBand:
+ m_aTableLook["noVBand"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_noHBand:
+ m_aTableLook["noHBand"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_lastColumn:
+ m_aTableLook["lastColumn"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_lastRow:
+ m_aTableLook["lastRow"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_firstColumn:
+ m_aTableLook["firstColumn"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_firstRow:
+ m_aTableLook["firstRow"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ default:
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+void DomainMapperTableManager::finishTableLook()
+{
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(META_PROP_TABLE_LOOK, uno::Any(m_aTableLook.getAsConstPropertyValueList()));
+ m_aTableLook.clear();
+ insertTableProps(pPropMap);
+}
+
+bool DomainMapperTableManager::sprm(Sprm & rSprm)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.sprm");
+ string sSprm = rSprm.toString();
+ TagLogger::getInstance().chars(sSprm);
+ TagLogger::getInstance().endElement();
+#endif
+ bool bRet = TableManager::sprm(rSprm);
+ if( !bRet )
+ {
+ bRet = m_pTablePropsHandler->sprm( rSprm );
+ }
+
+ if ( !bRet )
+ {
+ bRet = true;
+ sal_uInt32 nSprmId = rSprm.getId();
+ Value::Pointer_t pValue = rSprm.getValue();
+ sal_Int32 nIntValue = (pValue ? pValue->getInt() : 0);
+ switch ( nSprmId )
+ {
+ case NS_ooxml::LN_CT_TblPrBase_tblW:
+ case NS_ooxml::LN_CT_TblPrBase_tblInd:
+ {
+ //contains unit and value
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ MeasureHandlerPtr pMeasureHandler( new MeasureHandler );
+ pProperties->resolve(*pMeasureHandler);
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ if (nSprmId == sal_uInt32(NS_ooxml::LN_CT_TblPrBase_tblInd))
+ {
+ pPropMap->setValue( TablePropertyMap::LEFT_MARGIN, pMeasureHandler->getMeasureValue() );
+ }
+ else
+ {
+ m_nTableWidth = pMeasureHandler->getMeasureValue();
+ if( m_nTableWidth )
+ {
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::FIX );
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, m_nTableWidth );
+ m_bTableSizeTypeInserted = true;
+ }
+ else if( sal::static_int_cast<Id>(pMeasureHandler->getUnit()) == NS_ooxml::LN_Value_ST_TblWidth_pct )
+ {
+ sal_Int32 nPercent = pMeasureHandler->getValue() / 50;
+ if(nPercent > 100)
+ nPercent = 100;
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::VARIABLE );
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, nPercent );
+ m_bTableSizeTypeInserted = true;
+ }
+ else if( sal::static_int_cast<Id>(pMeasureHandler->getUnit()) == NS_ooxml::LN_Value_ST_TblWidth_auto )
+ {
+ /*
+ This attribute specifies the width type of table. This is used as part of the table layout
+ algorithm specified by the tblLayout element.(See 17.4.64 and 17.4.65 of the ISO/IEC 29500-1:2011.)
+ If this value is 'auto', the table layout has to use the preferred widths on the table items to generate
+ the final sizing of the table, but then must use the contents of each cell to determine final column widths.
+ (See 17.18.87 of the ISO/IEC 29500-1:2011.)
+ */
+ IntVectorPtr pCellWidths = getCurrentCellWidths();
+ // Check whether all cells have fixed widths in the given row of table.
+ bool bFixed = std::find(pCellWidths->begin(), pCellWidths->end(), -1) == pCellWidths->end();
+ if (!bFixed)
+ {
+ // Set the width type of table with 'Auto' and set the width value to 0 (as per grid values)
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::VARIABLE );
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, 0 );
+ m_bTableSizeTypeInserted = true;
+ }
+ else if (getTableDepth() > 1)
+ {
+ // tdf#131819 limiting the fix for nested tables temporarily
+ // TODO revert the fix for tdf#104876 and reopen it
+ m_bTableSizeTypeInserted = true;
+ }
+ }
+ }
+#ifdef DBG_UTIL
+ pPropMap->dumpXml();
+#endif
+ insertTableProps(pPropMap);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_tblHeader:
+ // if nIntValue == 1 then the row is a repeated header line
+ // to prevent later rows from increasing the repeating m_nHeaderRepeat is set to NULL when repeating stops
+ if( nIntValue > 0 && m_nHeaderRepeat == static_cast<int>(m_nRow) )
+ {
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+
+ // FIXME: DOCX tables with more than 10 repeating header lines imported
+ // without repeating header lines to mimic an MSO workaround for its usability bug.
+ // Explanation: it's very hard to set and modify repeating header rows in Word,
+ // often resulting tables with a special workaround: setting all table rows as
+ // repeating header, because exceeding the pages by "unlimited" header rows turns off the
+ // table headers automatically in MSO. 10-row limit is a reasonable temporary limit
+ // to handle DOCX tables with "unlimited" repeating header, till the same "turn off
+ // exceeding header" feature is ready (see tdf#88496).
+#define HEADER_ROW_LIMIT_FOR_MSO_WORKAROUND 10
+ if ( m_nHeaderRepeat == HEADER_ROW_LIMIT_FOR_MSO_WORKAROUND )
+ {
+ m_nHeaderRepeat = -1;
+ pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::Any(sal_Int32(0)));
+ }
+ else
+ {
+ ++m_nHeaderRepeat;
+ pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::Any( m_nHeaderRepeat ));
+ }
+ insertTableProps(pPropMap);
+ }
+ else
+ {
+ if ( nIntValue == 0 && m_nRow == 0 )
+ {
+ // explicit tblHeader=0 in the first row must overwrite table style
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::Any(sal_Int32(0)));
+ insertTableProps(pPropMap);
+ }
+ m_nHeaderRepeat = -1;
+ }
+ if (nIntValue)
+ {
+ // Store the info that this is a header, we'll need that when we apply table styles.
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( PROP_TBL_HEADER, uno::Any(nIntValue));
+ insertRowProps(pPropMap);
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblStyle: //table style name
+ {
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( META_PROP_TABLE_STYLE_NAME, uno::Any( pValue->getString() ));
+ insertTableProps(pPropMap);
+ }
+ break;
+ case NS_ooxml::LN_CT_TblGridBase_gridCol:
+ {
+ if (nIntValue == -1)
+ getCurrentGrid()->clear();
+ else
+ getCurrentGrid()->push_back( nIntValue );
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_vMerge : //vertical merge
+ {
+ // values can be: LN_Value_ST_Merge_restart, LN_Value_ST_Merge_continue, in reality the second one is a 0
+ TablePropertyMapPtr pMergeProps( new TablePropertyMap );
+ pMergeProps->Insert( PROP_VERTICAL_MERGE, uno::Any( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_Merge_restart ) );
+ cellProps( pMergeProps);
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_hMerge:
+ {
+ // values can be: LN_Value_ST_Merge_restart, LN_Value_ST_Merge_continue, in reality the second one is a 0
+ TablePropertyMapPtr pMergeProps(new TablePropertyMap());
+ pMergeProps->Insert(PROP_HORIZONTAL_MERGE, uno::Any( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_Merge_restart ));
+ cellProps(pMergeProps);
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_gridSpan: //number of grid positions spanned by this cell
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.GridSpan");
+ TagLogger::getInstance().attribute("gridSpan", nIntValue);
+ TagLogger::getInstance().endElement();
+#endif
+ m_nGridSpan = nIntValue;
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_textDirection:
+ {
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ bool bInsertCellProps = true;
+ switch ( nIntValue )
+ {
+ case NS_ooxml::LN_Value_ST_TextDirection_tbRl:
+ // Binary filter takes BiDirection into account ( but I have no idea about that here )
+ // or even what it is. But... here's where to handle it if it becomes an issue
+ pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::TB_RL ));
+ SAL_INFO( "writerfilter", "Have inserted textDirection " << nIntValue );
+ break;
+ case NS_ooxml::LN_Value_ST_TextDirection_btLr:
+ pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::BT_LR ));
+ break;
+ case NS_ooxml::LN_Value_ST_TextDirection_lrTbV:
+ pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::LR_TB ));
+ break;
+ case NS_ooxml::LN_Value_ST_TextDirection_tbRlV:
+ pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::TB_RL ));
+ break;
+ case NS_ooxml::LN_Value_ST_TextDirection_lrTb:
+ case NS_ooxml::LN_Value_ST_TextDirection_tbLrV:
+ default:
+ // Ignore - we can't handle these
+ bInsertCellProps = false;
+ break;
+ }
+ if ( bInsertCellProps )
+ cellProps( pPropMap );
+ break;
+ }
+ case NS_ooxml::LN_CT_TcPrBase_tcW:
+ {
+ // Contains unit and value, but unit is not interesting for
+ // us, later we'll just distribute these values in a
+ // 0..10000 scale.
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ MeasureHandlerPtr pMeasureHandler(new MeasureHandler());
+ pProperties->resolve(*pMeasureHandler);
+ if (sal::static_int_cast<Id>(pMeasureHandler->getUnit()) == NS_ooxml::LN_Value_ST_TblWidth_auto)
+ getCurrentCellWidths()->push_back(sal_Int32(-1));
+ else
+ // store the original value to limit rounding mistakes, if it's there in a recognized measure (twip)
+ getCurrentCellWidths()->push_back(pMeasureHandler->getMeasureValue() ? pMeasureHandler->getValue() : sal_Int32(0));
+ if (getTableDepthDifference())
+ m_bPushCurrentWidth = true;
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblpPr:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ // Ignore <w:tblpPr> in shape text, those tables should be always non-floating ones.
+ if (!m_bIsInShape && pProperties)
+ {
+ TablePositionHandlerPtr pHandler = m_aTmpPosition.back();
+ if ( !pHandler )
+ {
+ m_aTmpPosition.pop_back();
+ pHandler = new TablePositionHandler;
+ m_aTmpPosition.push_back( pHandler );
+ }
+ pProperties->resolve(*m_aTmpPosition.back());
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_gridBefore:
+ setCurrentGridBefore( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_gridAfter:
+ setCurrentGridAfter( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblCaption:
+ // To-Do: Not yet preserved
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblDescription:
+ // To-Do: Not yet preserved
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_tblCellSpacing:
+ // To-Do: Not yet preserved
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblCellSpacing:
+ // To-Do: Not yet preserved
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_bidiVisual:
+ {
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(PROP_WRITING_MODE, uno::Any(sal_Int16(nIntValue ? text::WritingMode2::RL_TB : text::WritingMode2::LR_TB)));
+ insertTableProps(pPropMap);
+ break;
+ }
+ default:
+ bRet = false;
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ }
+ return bRet;
+}
+
+DomainMapperTableManager::IntVectorPtr const & DomainMapperTableManager::getCurrentGrid( )
+{
+ if (m_aTableGrid.empty())
+ throw std::out_of_range("no current grid");
+ return m_aTableGrid.back( );
+}
+
+DomainMapperTableManager::IntVectorPtr const & DomainMapperTableManager::getCurrentCellWidths( )
+{
+ return m_aCellWidths.back( );
+}
+
+uno::Sequence<beans::PropertyValue> DomainMapperTableManager::getCurrentTablePosition( )
+{
+ if ( !m_aTablePositions.empty( ) && m_aTablePositions.back() )
+ return m_aTablePositions.back( )->getTablePosition();
+ else
+ return uno::Sequence< beans::PropertyValue >();
+}
+
+TablePositionHandler* DomainMapperTableManager::getCurrentTableRealPosition()
+{
+ if ( !m_aTablePositions.empty( ) && m_aTablePositions.back() )
+ return m_aTablePositions.back().get();
+ else
+ return nullptr;
+}
+
+const TableParagraphVectorPtr & DomainMapperTableManager::getCurrentParagraphs( )
+{
+ return m_aParagraphsToEndTable.top( );
+}
+
+void DomainMapperTableManager::setIsInShape(bool bIsInShape)
+{
+ m_bIsInShape = bIsInShape;
+}
+
+void DomainMapperTableManager::startLevel( )
+{
+ TableManager::startLevel( );
+
+ // If requested, pop the value that was pushed too early.
+ std::optional<sal_Int32> oCurrentWidth;
+ if (m_bPushCurrentWidth && !m_aCellWidths.empty() && !m_aCellWidths.back()->empty())
+ {
+ oCurrentWidth = m_aCellWidths.back()->back();
+ m_aCellWidths.back()->pop_back();
+ }
+ std::optional<TableParagraph> oParagraph;
+ if (getTableDepthDifference() > 0 && !m_aParagraphsToEndTable.empty() && !m_aParagraphsToEndTable.top()->empty())
+ {
+ oParagraph = m_aParagraphsToEndTable.top()->back();
+ m_aParagraphsToEndTable.top()->pop_back();
+ }
+
+ IntVectorPtr pNewGrid = std::make_shared<vector<sal_Int32>>();
+ IntVectorPtr pNewCellWidths = std::make_shared<vector<sal_Int32>>();
+ TablePositionHandlerPtr pNewPositionHandler;
+ m_aTableGrid.push_back( pNewGrid );
+ m_aCellWidths.push_back( pNewCellWidths );
+ m_aTablePositions.push_back( pNewPositionHandler );
+ // empty name will be replaced by the table style name, if it exists
+ m_aTableStyleNames.push_back( OUString() );
+ m_aMoved.push_back( OUString() );
+
+ TablePositionHandlerPtr pTmpPosition;
+ TablePropertyMapPtr pTmpProperties( new TablePropertyMap( ) );
+ m_aTmpPosition.push_back( pTmpPosition );
+ m_aTmpTableProperties.push_back( pTmpProperties );
+ m_nCell.push_back( 0 );
+ m_nTableWidth = 0;
+ m_nLayoutType = 0;
+ TableParagraphVectorPtr pNewParagraphs = std::make_shared<vector<TableParagraph>>();
+ m_aParagraphsToEndTable.push( pNewParagraphs );
+
+ // And push it back to the right level.
+ if (oCurrentWidth)
+ m_aCellWidths.back()->push_back(*oCurrentWidth);
+ if (oParagraph)
+ m_aParagraphsToEndTable.top()->push_back(*oParagraph);
+}
+
+void DomainMapperTableManager::endLevel( )
+{
+ if (m_aTableGrid.empty())
+ {
+ SAL_WARN("writerfilter.dmapper", "Table stack is empty");
+ return;
+ }
+
+ m_aTableGrid.pop_back( );
+
+ // Do the same trick as in startLevel(): pop the value that was pushed too early.
+ std::optional<sal_Int32> oCurrentWidth;
+ if (m_bPushCurrentWidth && !m_aCellWidths.empty() && !m_aCellWidths.back()->empty())
+ oCurrentWidth = m_aCellWidths.back()->back();
+ m_aCellWidths.pop_back( );
+ // And push it back to the right level.
+ if (oCurrentWidth && !m_aCellWidths.empty() && !m_aCellWidths.back()->empty())
+ m_aCellWidths.back()->push_back(*oCurrentWidth);
+
+ m_nCell.pop_back( );
+ m_nTableWidth = 0;
+ m_nLayoutType = 0;
+
+ m_aTmpPosition.pop_back( );
+ m_aTmpTableProperties.pop_back( );
+
+ TableManager::endLevel( );
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("dmappertablemanager.endLevel");
+ PropertyMapPtr pProps = getTableProps().get();
+ if (pProps)
+ getTableProps()->dumpXml();
+
+ TagLogger::getInstance().endElement();
+#endif
+
+ // Pop back the table position after endLevel as it's used
+ // in the endTable method called in endLevel.
+ m_aTablePositions.pop_back();
+ m_aTableStyleNames.pop_back();
+ m_aMoved.pop_back( );
+
+ std::optional<TableParagraph> oParagraph;
+ if (getTableDepthDifference() < 0 && !m_aParagraphsToEndTable.top()->empty())
+ oParagraph = m_aParagraphsToEndTable.top()->back();
+ m_aParagraphsToEndTable.pop();
+ if (oParagraph && m_aParagraphsToEndTable.size())
+ m_aParagraphsToEndTable.top()->push_back(*oParagraph);
+}
+
+void DomainMapperTableManager::endOfCellAction()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("endOFCellAction");
+#endif
+
+ if ( !isInTable() )
+ throw std::out_of_range("cell without a table");
+ if ( m_nGridSpan > 1 )
+ setCurrentGridSpan( m_nGridSpan );
+ m_nGridSpan = 1;
+ ++m_nCell.back( );
+}
+
+bool DomainMapperTableManager::shouldInsertRow(IntVectorPtr pCellWidths, IntVectorPtr pTableGrid, size_t nGrids, bool& rIsIncompleteGrid)
+{
+ if (pCellWidths->empty())
+ return false;
+ if (m_nLayoutType == NS_ooxml::LN_Value_doc_ST_TblLayout_fixed)
+ return true;
+ if (pCellWidths->size() == nGrids)
+ return true;
+ rIsIncompleteGrid = true;
+ return nGrids > pTableGrid->size();
+}
+
+void DomainMapperTableManager::endOfRowAction()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("endOfRowAction");
+#endif
+
+ // Compare the table position and style with the previous ones. We may need to split
+ // into two tables if those are different. We surely don't want to do anything
+ // if we don't have any row yet.
+ if (m_aTmpPosition.empty())
+ throw std::out_of_range("row without a position");
+ TablePositionHandlerPtr pTmpPosition = m_aTmpPosition.back();
+ TablePropertyMapPtr pTablePropMap = m_aTmpTableProperties.back( );
+ TablePositionHandlerPtr pCurrentPosition = m_aTablePositions.back();
+ bool bSamePosition = ( pTmpPosition == pCurrentPosition ) ||
+ ( pTmpPosition && pCurrentPosition && *pTmpPosition == *pCurrentPosition );
+ bool bIsSetTableStyle = pTablePropMap->isSet(META_PROP_TABLE_STYLE_NAME);
+ OUString sTableStyleName;
+ bool bSameTableStyle = ( !bIsSetTableStyle && m_aTableStyleNames.back().isEmpty()) ||
+ ( bIsSetTableStyle &&
+ (pTablePropMap->getProperty(META_PROP_TABLE_STYLE_NAME)->second >>= sTableStyleName) &&
+ sTableStyleName == m_aTableStyleNames.back() );
+ if ( (!bSamePosition || !bSameTableStyle) && m_nRow > 0 )
+ {
+ // Save the grid infos to have them survive the end/start level
+ IntVectorPtr pTmpTableGrid = m_aTableGrid.back();
+ IntVectorPtr pTmpCellWidths = m_aCellWidths.back();
+ sal_uInt32 nTmpCell = m_nCell.back();
+ TableParagraphVectorPtr pTableParagraphs = getCurrentParagraphs();
+
+ // endLevel and startLevel are taking care of the non finished row
+ // to carry it over to the next table
+ setKeepUnfinishedRow( true );
+ endLevel();
+ setKeepUnfinishedRow( false );
+ startLevel();
+
+ m_aTableGrid.pop_back();
+ m_aCellWidths.pop_back();
+ m_nCell.pop_back();
+ m_aTableGrid.push_back(pTmpTableGrid);
+ m_aCellWidths.push_back(pTmpCellWidths);
+ m_nCell.push_back(nTmpCell);
+ m_aParagraphsToEndTable.pop( );
+ m_aParagraphsToEndTable.push( pTableParagraphs );
+ }
+ // save table style in the first row for comparison
+ if ( m_nRow == 0 && pTablePropMap->isSet(META_PROP_TABLE_STYLE_NAME) )
+ {
+ pTablePropMap->getProperty(META_PROP_TABLE_STYLE_NAME)->second >>= sTableStyleName;
+ m_aTableStyleNames.pop_back();
+ m_aTableStyleNames.push_back( sTableStyleName );
+ }
+
+ // Push the tmp position now that we compared it
+ m_aTablePositions.pop_back();
+ m_aTablePositions.push_back( pTmpPosition );
+ m_aTmpPosition.back().clear( );
+
+
+ IntVectorPtr pTableGrid = getCurrentGrid( );
+ IntVectorPtr pCellWidths = getCurrentCellWidths( );
+ if(!m_nTableWidth && !pTableGrid->empty())
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tableWidth");
+#endif
+
+ for( const auto& rCell : *pTableGrid )
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("col");
+ TagLogger::getInstance().attribute("width", rCell);
+ TagLogger::getInstance().endElement();
+#endif
+
+ m_nTableWidth = o3tl::saturating_add(m_nTableWidth, rCell);
+ }
+ if (m_nTableWidth)
+ // convert sum of grid twip values to 1/100 mm with rounding up to avoid table width loss
+ m_nTableWidth = static_cast<sal_Int32>(ceil(ConversionHelper::convertTwipToMM100Double(m_nTableWidth)));
+
+ if (m_nTableWidth > 0 && !m_bTableSizeTypeInserted)
+ {
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, m_nTableWidth );
+ insertTableProps(pPropMap);
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+ }
+
+ std::vector<sal_uInt32> rCurrentSpans = getCurrentGridSpans();
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("gridSpans");
+ {
+ for (const auto& rGridSpan : rCurrentSpans)
+ {
+ TagLogger::getInstance().startElement("gridSpan");
+ TagLogger::getInstance().attribute("span", rGridSpan);
+ TagLogger::getInstance().endElement();
+ }
+ }
+ TagLogger::getInstance().endElement();
+#endif
+
+ //calculate number of used grids - it has to match the size of m_aTableGrid
+ size_t nGrids = std::accumulate(rCurrentSpans.begin(), rCurrentSpans.end(), sal::static_int_cast<size_t>(0));
+
+ // sj: the grid is having no units... it is containing only relative values.
+ // a table with a grid of "1:2:1" looks identical as if the table is having
+ // a grid of "20:40:20" and it doesn't have to do something with the tableWidth
+ // -> so we have get the sum of each grid entry for the fullWidthRelative:
+ int nFullWidthRelative = 0;
+ for (int i : (*pTableGrid))
+ nFullWidthRelative = o3tl::saturating_add(nFullWidthRelative, i);
+
+ bool bIsIncompleteGrid = false;
+ if( pTableGrid->size() == nGrids && m_nCell.back( ) > 0 )
+ {
+ /*
+ * If table width property set earlier is smaller than the current table width,
+ * then replace the TABLE_WIDTH property, set earlier.
+ */
+ sal_Int32 nTableWidth(0);
+ sal_Int32 nTableWidthType(text::SizeType::VARIABLE);
+ pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
+ pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
+ if ((nTableWidthType == text::SizeType::FIX) && (nTableWidth < m_nTableWidth))
+ {
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, m_nTableWidth);
+ }
+ if (nTableWidthType == text::SizeType::VARIABLE )
+ {
+ if(nTableWidth > 100 || nTableWidth <= 0)
+ {
+ if(getTableDepth() > 1 && !m_bTableSizeTypeInserted)
+ {
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, sal_Int32(100));
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::VARIABLE);
+ }
+ else
+ {
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, m_nTableWidth);
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::FIX);
+ }
+ }
+ }
+ uno::Sequence< text::TableColumnSeparator > aSeparators( getCurrentGridBefore() + m_nCell.back() - 1 );
+ text::TableColumnSeparator* pSeparators = aSeparators.getArray();
+ double nLastRelPos = 0.0;
+ sal_uInt32 nBorderGridIndex = 0;
+
+ size_t nWidthsBound = getCurrentGridBefore() + m_nCell.back() - 1;
+ if (nWidthsBound)
+ {
+ ::std::vector< sal_uInt32 >::const_iterator aSpansIter = rCurrentSpans.begin();
+ for( size_t nBorder = 0; nBorder < nWidthsBound; ++nBorder )
+ {
+ double nRelPos, fGridWidth = 0.;
+ for ( sal_Int32 nGridCount = *aSpansIter; nGridCount > 0; --nGridCount )
+ fGridWidth += (*pTableGrid)[nBorderGridIndex++];
+
+ if (fGridWidth == 0.)
+ {
+ // allow nFullWidthRelative here, with a sane 0.0 result
+ nRelPos = 0.;
+ }
+ else
+ {
+ if (nFullWidthRelative == 0)
+ throw o3tl::divide_by_zero();
+
+ nRelPos = (fGridWidth * 10000) / nFullWidthRelative;
+ }
+
+ pSeparators[nBorder].Position = rtl::math::round(nRelPos + nLastRelPos);
+ pSeparators[nBorder].IsVisible = true;
+ nLastRelPos = nLastRelPos + nRelPos;
+ ++aSpansIter;
+ }
+ }
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( PROP_TABLE_COLUMN_SEPARATORS, uno::Any( aSeparators ) );
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("rowProperties");
+ pPropMap->dumpXml();
+ TagLogger::getInstance().endElement();
+#endif
+
+ // set row insertion/deletion at tracked drag & drop of tables
+ OUString aMoved = getMoved();
+ if ( !aMoved.isEmpty() )
+ {
+ auto pTrackChangesHandler = std::make_shared<TrackChangesHandler>(
+ aMoved == getPropertyName( PROP_TABLE_ROW_DELETE )
+ ? oox::XML_tableRowDelete
+ : oox::XML_tableRowInsert );
+ uno::Sequence<beans::PropertyValue> aTableRedlineProperties = pTrackChangesHandler->getRedlineProperties();
+ pPropMap->Insert( PROP_TABLE_REDLINE_PARAMS , uno::Any( aTableRedlineProperties ));
+ }
+
+ insertRowProps(pPropMap);
+ }
+ else if (shouldInsertRow(pCellWidths, pTableGrid, nGrids, bIsIncompleteGrid))
+ {
+ // If we're here, then the number of cells does not equal to the amount
+ // defined by the grid, even after taking care of
+ // gridSpan/gridBefore/gridAfter. Handle this by ignoring the grid and
+ // providing the separators based on the provided cell widths, as long
+ // as we have a fixed layout;
+ // On the other hand even if the layout is not fixed, but the cell widths
+ // provided equal the total number of cells, and there are no after/before cells
+ // then use the cell widths to calculate the column separators.
+ // Also handle autofit tables with incomplete grids, when rows can have
+ // different widths and last cells can be wider, than their values.
+ uno::Sequence< text::TableColumnSeparator > aSeparators(pCellWidths->size() - 1);
+ text::TableColumnSeparator* pSeparators = aSeparators.getArray();
+ sal_Int16 nSum = 0;
+ sal_uInt32 nPos = 0;
+
+ if (bIsIncompleteGrid)
+ nFullWidthRelative = 0;
+
+ // Avoid divide by zero (if there's no grid, position using cell widths).
+ if( nFullWidthRelative == 0 )
+ for (size_t i = 0; i < pCellWidths->size(); ++i)
+ nFullWidthRelative += (*pCellWidths)[i];
+
+ if (bIsIncompleteGrid)
+ {
+ /*
+ * If table width property set earlier is smaller than the current table row width,
+ * then replace the TABLE_WIDTH property, set earlier.
+ */
+ sal_Int32 nFullWidth = static_cast<sal_Int32>(ceil(ConversionHelper::convertTwipToMM100Double(nFullWidthRelative)));
+ sal_Int32 nTableWidth(0);
+ sal_Int32 nTableWidthType(text::SizeType::VARIABLE);
+ pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
+ pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
+ if (nTableWidth < nFullWidth)
+ {
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, nFullWidth);
+ }
+ }
+
+ size_t nWidthsBound = pCellWidths->size() - 1;
+ if (nWidthsBound)
+ {
+ // At incomplete table grids, last cell width can be smaller, than its final width.
+ // Correct it based on the last but one column width and their span values.
+ if ( bIsIncompleteGrid && rCurrentSpans.size()-1 == nWidthsBound )
+ {
+ auto aSpansIter = std::next(rCurrentSpans.begin(), nWidthsBound - 1);
+ sal_Int32 nFixLastCellWidth = (*pCellWidths)[nWidthsBound-1] / *aSpansIter * *std::next(aSpansIter);
+ if (nFixLastCellWidth > (*pCellWidths)[nWidthsBound])
+ nFullWidthRelative += nFixLastCellWidth - (*pCellWidths)[nWidthsBound];
+ }
+
+ // tdf#131203 handle missing w:tblGrid
+ if (nFullWidthRelative > 0)
+ {
+ for (size_t i = 0; i < nWidthsBound; ++i)
+ {
+ nSum += (*pCellWidths)[i];
+ pSeparators[nPos].Position = (nSum * 10000) / nFullWidthRelative; // Relative position
+ pSeparators[nPos].IsVisible = true;
+ nPos++;
+ }
+ }
+ }
+
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( PROP_TABLE_COLUMN_SEPARATORS, uno::Any( aSeparators ) );
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("rowProperties");
+ pPropMap->dumpXml();
+ TagLogger::getInstance().endElement();
+#endif
+ insertRowProps(pPropMap);
+ }
+
+ // Now that potentially opened table is closed, save the table properties
+ TableManager::insertTableProps(pTablePropMap);
+
+ m_aTmpTableProperties.pop_back();
+ TablePropertyMapPtr pEmptyTableProps( new TablePropertyMap() );
+ m_aTmpTableProperties.push_back( pEmptyTableProps );
+
+ ++m_nRow;
+ m_nCell.back( ) = 0;
+ getCurrentGrid()->clear();
+ pCellWidths->clear();
+
+ m_bTableSizeTypeInserted = false;
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void DomainMapperTableManager::clearData()
+{
+ m_nRow = m_nHeaderRepeat = m_nTableWidth = m_nLayoutType = 0;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapperTableManager.hxx b/writerfilter/source/dmapper/DomainMapperTableManager.hxx
new file mode 100644
index 000000000..a2e492936
--- /dev/null
+++ b/writerfilter/source/dmapper/DomainMapperTableManager.hxx
@@ -0,0 +1,172 @@
+/* -*- 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 "TablePropertiesHandler.hxx"
+#include "TablePositionHandler.hxx"
+
+#include "TableManager.hxx"
+#include "PropertyMap.hxx"
+#include <vector>
+#include <memory>
+#include <comphelper/sequenceashashmap.hxx>
+
+namespace writerfilter::dmapper {
+
+class DomainMapper;
+
+class DomainMapperTableManager : public TableManager
+{
+ typedef std::shared_ptr< std::vector<sal_Int32> > IntVectorPtr;
+
+ sal_uInt32 m_nRow;
+ ::std::vector< sal_uInt32 > m_nCell;
+ sal_uInt32 m_nGridSpan;
+ sal_Int32 m_nHeaderRepeat; //counter of repeated headers - if == -1 then the repeating stops
+ sal_Int32 m_nTableWidth; //might be set directly or has to be calculated from the column positions
+ /// Are we in a shape (text append stack is not empty) or in the body document?
+ bool m_bIsInShape;
+ std::vector< OUString > m_aTableStyleNames;
+ /// Moved table (in moveRangeFromStart...moveRangeFromEnd or moveRangeToStart...moveRangeToEnd)
+ std::vector< OUString > m_aMoved;
+ /// Grab-bag of table look attributes for preserving.
+ comphelper::SequenceAsHashMap m_aTableLook;
+ std::vector< TablePositionHandlerPtr > m_aTablePositions;
+ std::vector< TablePositionHandlerPtr > m_aTmpPosition; ///< Temporarily stores the position to compare it later
+ std::vector< TablePropertyMapPtr > m_aTmpTableProperties; ///< Temporarily stores the table properties until end of row
+
+ ::std::vector< IntVectorPtr > m_aTableGrid;
+ /// If this is true, then we pushed a width before the next level started, and that should be carried over when starting the next level.
+ bool m_bPushCurrentWidth;
+ /// Individual table cell width values, used only in case the number of cells doesn't match the table grid.
+ ::std::vector< IntVectorPtr > m_aCellWidths;
+ /// Remember if table width was already set, when we lack a w:tblW, it should be set manually at the end.
+ bool m_bTableSizeTypeInserted;
+ /// Table layout algorithm, IOW if we should consider fixed column width or not.
+ sal_uInt32 m_nLayoutType;
+ /// Collected table paragraphs for table style handling
+ std::stack< TableParagraphVectorPtr > m_aParagraphsToEndTable;
+
+ std::unique_ptr<TablePropertiesHandler> m_pTablePropsHandler;
+ PropertyMapPtr m_pStyleProps;
+
+ bool shouldInsertRow(IntVectorPtr pCellWidths, IntVectorPtr pTableGrid, size_t nGrids, bool& rIsIncompleteGrid);
+
+ virtual void clearData() override;
+
+public:
+
+ DomainMapperTableManager();
+ virtual ~DomainMapperTableManager() override;
+
+ // use this method to avoid adding the properties for the table
+ // but in the provided properties map.
+ void SetStyleProperties( PropertyMapPtr pProperties ) { m_pStyleProps = pProperties; };
+
+ virtual bool sprm(Sprm & rSprm) override;
+ bool attribute(Id nName, Value const & val);
+
+ virtual void startLevel( ) override;
+ virtual void endLevel( ) override;
+
+ virtual void endOfCellAction() override;
+ virtual void endOfRowAction() override;
+
+ IntVectorPtr const & getCurrentGrid( );
+ IntVectorPtr const & getCurrentCellWidths( );
+ const TableParagraphVectorPtr & getCurrentParagraphs( );
+
+ /// Turn the attributes collected so far in m_aTableLook into a property and clear the container.
+ void finishTableLook();
+ css::uno::Sequence<css::beans::PropertyValue> getCurrentTablePosition();
+ TablePositionHandler* getCurrentTableRealPosition();
+
+ virtual void cellProps(const TablePropertyMapPtr& pProps) override
+ {
+ if ( m_pStyleProps )
+ m_pStyleProps->InsertProps(pProps.get());
+ else
+ TableManager::cellProps( pProps );
+ };
+
+ virtual void insertRowProps(const TablePropertyMapPtr& pProps) override
+ {
+ if ( m_pStyleProps )
+ m_pStyleProps->InsertProps(pProps.get());
+ else
+ TableManager::insertRowProps( pProps );
+ };
+
+ virtual void insertTableProps(const TablePropertyMapPtr& pProps) override
+ {
+ if ( m_pStyleProps )
+ m_pStyleProps->InsertProps(pProps.get());
+ else
+ m_aTmpTableProperties.back()->InsertProps(pProps.get());
+ };
+
+ void SetLayoutType(sal_uInt32 nLayoutType)
+ {
+ m_nLayoutType = nLayoutType;
+ }
+
+ using TableManager::isInCell;
+
+ void setIsInShape(bool bIsInShape);
+
+ // moveFromRangeStart and moveToRangeStart are there
+ // in the first paragraph in the first cell of the
+ // table moved by drag & drop with track changes, but
+ // moveFromRangeEnd and moveToRangeEnd follow the
+ // table element w:tbl in the same level (not in paragraph).
+ // (Special indexing is related to the load of the tables:
+ // first-level tables handled by two levels during the
+ // import, to support table join etc. In the first cell,
+ // setMoved() writes the first level from these two levels
+ // i.e. second startLevel() hasn't been called, yet.)
+ // TODO: check drag & drop of only a part of the tables.
+ void setMoved(OUString sMoved)
+ {
+ if ( m_aMoved.empty() )
+ return;
+
+ if ( !sMoved.isEmpty() )
+ m_aMoved[m_aMoved.size() - 1] = sMoved;
+ else if ( m_aMoved.size() >= 2 )
+ // next table rows weren't moved
+ m_aMoved[m_aMoved.size() - 2] = "";
+ else
+ m_aMoved[m_aMoved.size() - 1] = "";
+ }
+
+ OUString getMoved() const
+ {
+ if ( m_aMoved.size() >= 2 && !m_aMoved[m_aMoved.size() - 2].isEmpty() )
+ return m_aMoved[m_aMoved.size() - 2];
+ else if ( !m_aMoved.empty() )
+ return m_aMoved[m_aMoved.size() -1 ];
+
+ return OUString();
+ }
+
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
new file mode 100644
index 000000000..ae0a8a27d
--- /dev/null
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -0,0 +1,8792 @@
+/* -*- 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/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <ooxml/resourceids.hxx>
+#include "DomainMapper_Impl.hxx"
+#include "ConversionHelper.hxx"
+#include "SdtHelper.hxx"
+#include "DomainMapperTableHandler.hxx"
+#include "TagLogger.hxx"
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/document/PrinterIndependentLayout.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/i18n/NumberFormatMapper.hpp>
+#include <com/sun/star/i18n/NumberFormatIndex.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/style/LineNumberPosition.hpp>
+#include <com/sun/star/style/LineSpacing.hpp>
+#include <com/sun/star/style/LineSpacingMode.hpp>
+#include <com/sun/star/text/ChapterFormat.hpp>
+#include <com/sun/star/text/FilenameDisplayFormat.hpp>
+#include <com/sun/star/text/SetVariableType.hpp>
+#include <com/sun/star/text/XDocumentIndex.hpp>
+#include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
+#include <com/sun/star/text/XFootnote.hpp>
+#include <com/sun/star/text/XEndnotesSupplier.hpp>
+#include <com/sun/star/text/XFootnotesSupplier.hpp>
+#include <com/sun/star/text/XLineNumberingProperties.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/text/PageNumberType.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/ReferenceFieldPart.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/text/ReferenceFieldSource.hpp>
+#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/XDependentTextField.hpp>
+#include <com/sun/star/text/XParagraphCursor.hpp>
+#include <com/sun/star/text/XRedline.hpp>
+#include <com/sun/star/text/XTextFieldsSupplier.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
+#include <com/sun/star/text/RubyPosition.hpp>
+#include <com/sun/star/text/XTextRangeCompare.hpp>
+#include <com/sun/star/style/DropCapFormat.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/XNumberFormatter.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/text/ControlCharacter.hpp>
+#include <com/sun/star/text/XTextColumns.hpp>
+#include <com/sun/star/awt/CharSet.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/embed/XHierarchicalStorageAccess.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/document/XImporter.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/unotext.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/temporary.hxx>
+#include <oox/mathml/import.hxx>
+#include <xmloff/odffields.hxx>
+#include <rtl/uri.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <comphelper/string.hxx>
+
+#include <dmapper/GraphicZOrderHelper.hxx>
+
+#include <oox/token/tokens.hxx>
+
+#include <cmath>
+#include <optional>
+#include <map>
+#include <tuple>
+#include <unordered_map>
+#include <regex>
+#include <algorithm>
+
+#include <officecfg/Office/Common.hxx>
+#include <filter/msfilter/util.hxx>
+#include <filter/msfilter/ww8fields.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/drawing/FillStyle.hpp>
+
+#include <unicode/errorcode.h>
+#include <unicode/regex.h>
+
+using namespace ::com::sun::star;
+using namespace oox;
+namespace writerfilter::dmapper{
+
+//line numbering for header/footer
+static void lcl_linenumberingHeaderFooter( const uno::Reference<container::XNameContainer>& xStyles, const OUString& rname, DomainMapper_Impl* dmapper )
+{
+ const StyleSheetEntryPtr pEntry = dmapper->GetStyleSheetTable()->FindStyleSheetByISTD( rname );
+ if (!pEntry)
+ return;
+ const StyleSheetPropertyMap* pStyleSheetProperties = pEntry->pProperties.get();
+ if ( !pStyleSheetProperties )
+ return;
+ sal_Int32 nListId = pStyleSheetProperties->GetListId();
+ if( xStyles.is() )
+ {
+ if( xStyles->hasByName( rname ) )
+ {
+ uno::Reference< style::XStyle > xStyle;
+ xStyles->getByName( rname ) >>= xStyle;
+ if( !xStyle.is() )
+ return;
+ uno::Reference<beans::XPropertySet> xPropertySet( xStyle, uno::UNO_QUERY );
+ xPropertySet->setPropertyValue( getPropertyName( PROP_PARA_LINE_NUMBER_COUNT ), uno::Any( nListId >= 0 ) );
+ }
+ }
+}
+
+// Populate Dropdown Field properties from FFData structure
+static void lcl_handleDropdownField( const uno::Reference< beans::XPropertySet >& rxFieldProps, const FFDataHandler::Pointer_t& pFFDataHandler )
+{
+ if ( !rxFieldProps.is() )
+ return;
+
+ if ( !pFFDataHandler->getName().isEmpty() )
+ rxFieldProps->setPropertyValue( "Name", uno::Any( pFFDataHandler->getName() ) );
+
+ const FFDataHandler::DropDownEntries_t& rEntries = pFFDataHandler->getDropDownEntries();
+ uno::Sequence< OUString > sItems( rEntries.size() );
+ ::std::copy( rEntries.begin(), rEntries.end(), sItems.getArray());
+ if ( sItems.hasElements() )
+ rxFieldProps->setPropertyValue( "Items", uno::Any( sItems ) );
+
+ sal_Int32 nResult = pFFDataHandler->getDropDownResult().toInt32();
+ if (nResult > 0 && o3tl::make_unsigned(nResult) < sItems.size())
+ rxFieldProps->setPropertyValue( "SelectedItem", uno::Any( std::as_const(sItems)[ nResult ] ) );
+ if ( !pFFDataHandler->getHelpText().isEmpty() )
+ rxFieldProps->setPropertyValue( "Help", uno::Any( pFFDataHandler->getHelpText() ) );
+}
+
+static void lcl_handleTextField( const uno::Reference< beans::XPropertySet >& rxFieldProps, const FFDataHandler::Pointer_t& pFFDataHandler )
+{
+ if ( rxFieldProps.is() && pFFDataHandler )
+ {
+ rxFieldProps->setPropertyValue
+ (getPropertyName(PROP_HINT),
+ uno::Any(pFFDataHandler->getStatusText()));
+ rxFieldProps->setPropertyValue
+ (getPropertyName(PROP_HELP),
+ uno::Any(pFFDataHandler->getHelpText()));
+ rxFieldProps->setPropertyValue
+ (getPropertyName(PROP_CONTENT),
+ uno::Any(pFFDataHandler->getTextDefault()));
+ }
+}
+
+/**
+ Very similar to DomainMapper_Impl::GetPropertyFromStyleSheet
+ It is focused on paragraph properties search in current & parent stylesheet entries.
+ But it will not take into account properties with listid: these are "list paragraph styles" and
+ not used in some cases.
+*/
+static uno::Any lcl_GetPropertyFromParaStyleSheetNoNum(PropertyIds eId, StyleSheetEntryPtr pEntry, const StyleSheetTablePtr& rStyleSheet)
+{
+ while (pEntry)
+ {
+ if (pEntry->pProperties)
+ {
+ std::optional<PropertyMap::Property> aProperty =
+ pEntry->pProperties->getProperty(eId);
+ if (aProperty)
+ {
+ if (pEntry->pProperties->GetListId())
+ // It is a paragraph style with list. Paragraph list styles are not taken into account
+ return uno::Any();
+ else
+ return aProperty->second;
+ }
+ }
+ //search until the property is set or no parent is available
+ StyleSheetEntryPtr pNewEntry;
+ if (!pEntry->sBaseStyleIdentifier.isEmpty())
+ pNewEntry = rStyleSheet->FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier);
+
+ SAL_WARN_IF(pEntry == pNewEntry, "writerfilter.dmapper", "circular loop in style hierarchy?");
+
+ if (pEntry == pNewEntry) //fdo#49587
+ break;
+
+ pEntry = pNewEntry;
+ }
+ return uno::Any();
+}
+
+
+namespace {
+
+struct FieldConversion
+{
+ const char* cFieldServiceName;
+ FieldId eFieldId;
+};
+
+}
+
+typedef std::unordered_map<OUString, FieldConversion> FieldConversionMap_t;
+
+/// Gives access to the parent field context of the topmost one, if there is any.
+static FieldContextPtr GetParentFieldContext(const std::deque<FieldContextPtr>& rFieldStack)
+{
+ if (rFieldStack.size() < 2)
+ {
+ return nullptr;
+ }
+
+ return rFieldStack[rFieldStack.size() - 2];
+}
+
+/// Decides if the pInner field inside pOuter is allowed in Writer core, depending on their type.
+static bool IsFieldNestingAllowed(const FieldContextPtr& pOuter, const FieldContextPtr& pInner)
+{
+ std::optional<FieldId> oOuterFieldId = pOuter->GetFieldId();
+ OUString aCommand = pOuter->GetCommand();
+
+ // Ignore leading space before the field name, but don't accept IFF when we check for IF.
+ if (!aCommand.isEmpty() && aCommand[0] == ' ')
+ {
+ aCommand = aCommand.subView(1);
+ }
+
+ if (!oOuterFieldId && aCommand.startsWith("IF "))
+ {
+ // This will be FIELD_IF once the command is closed.
+ oOuterFieldId = FIELD_IF;
+ }
+
+ if (!oOuterFieldId)
+ {
+ return true;
+ }
+
+ if (!pInner->GetFieldId())
+ {
+ return true;
+ }
+
+ switch (*oOuterFieldId)
+ {
+ case FIELD_IF:
+ {
+ switch (*pInner->GetFieldId())
+ {
+ case FIELD_DOCVARIABLE:
+ case FIELD_FORMULA:
+ case FIELD_IF:
+ case FIELD_MERGEFIELD:
+ {
+ return false;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+
+uno::Any FloatingTableInfo::getPropertyValue(std::u16string_view propertyName)
+{
+ for( beans::PropertyValue const & propVal : std::as_const(m_aFrameProperties) )
+ if( propVal.Name == propertyName )
+ return propVal.Value ;
+ return uno::Any() ;
+}
+
+DomainMapper_Impl::DomainMapper_Impl(
+ DomainMapper& rDMapper,
+ uno::Reference<uno::XComponentContext> const& xContext,
+ uno::Reference<lang::XComponent> const& xModel,
+ SourceDocumentType eDocumentType,
+ utl::MediaDescriptor const & rMediaDesc) :
+ m_eDocumentType( eDocumentType ),
+ m_rDMapper( rDMapper ),
+ m_pOOXMLDocument(nullptr),
+ m_xTextDocument( xModel, uno::UNO_QUERY ),
+ m_xTextFactory( xModel, uno::UNO_QUERY ),
+ m_xComponentContext( xContext ),
+ m_bForceGenericFields(!utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::get()),
+ m_bIsDecimalComma( false ),
+ m_bSetUserFieldContent( false ),
+ m_bSetCitation( false ),
+ m_bSetDateValue( false ),
+ m_bIsFirstSection( true ),
+ m_bIsColumnBreakDeferred( false ),
+ m_bIsPageBreakDeferred( false ),
+ m_nLineBreaksDeferred( 0 ),
+ m_bSdtEndDeferred(false),
+ m_bParaSdtEndDeferred(false),
+ m_bStartTOC(false),
+ m_bStartTOCHeaderFooter(false),
+ m_bStartedTOC(false),
+ m_bStartIndex(false),
+ m_bStartBibliography(false),
+ m_nStartGenericField(0),
+ m_bTextInserted(false),
+ m_sCurrentPermId(0),
+ m_bFrameDirectionSet(false),
+ m_bInDocDefaultsImport(false),
+ m_bInStyleSheetImport( false ),
+ m_bInNumberingImport(false),
+ m_bInAnyTableImport( false ),
+ m_eInHeaderFooterImport( HeaderFooterImportState::none ),
+ m_bDiscardHeaderFooter( false ),
+ m_bInFootOrEndnote(false),
+ m_bInFootnote(false),
+ m_bHasFootnoteStyle(false),
+ m_bCheckFootnoteStyle(false),
+ m_eSkipFootnoteState(SkipFootnoteSeparator::OFF),
+ m_nFootnotes(-1),
+ m_nEndnotes(-1),
+ m_nFirstFootnoteIndex(-1),
+ m_nFirstEndnoteIndex(-1),
+ m_bLineNumberingSet( false ),
+ m_bIsInFootnoteProperties( false ),
+ m_bIsParaMarkerChange( false ),
+ m_bIsParaMarkerMove( false ),
+ m_bRedlineImageInPreviousRun( false ),
+ m_bParaChanged( false ),
+ m_bIsFirstParaInSection( true ),
+ m_bIsFirstParaInSectionAfterRedline( true ),
+ m_bDummyParaAddedForTableInSection( false ),
+ m_bDummyParaAddedForTableInSectionPage( false ),
+ m_bTextFrameInserted(false),
+ m_bIsPreviousParagraphFramed( false ),
+ m_bIsLastParaInSection( false ),
+ m_bIsLastSectionGroup( false ),
+ m_bIsInComments( false ),
+ m_bParaSectpr( false ),
+ m_bUsingEnhancedFields( false ),
+ m_bSdt(false),
+ m_bIsFirstRun(false),
+ m_bIsOutsideAParagraph(true),
+ m_nAnnotationId( -1 ),
+ m_aSmartTagHandler(m_xComponentContext, m_xTextDocument),
+ m_xInsertTextRange(rMediaDesc.getUnpackedValueOrDefault("TextInsertModeRange", uno::Reference<text::XTextRange>())),
+ m_xAltChunkStartingRange(rMediaDesc.getUnpackedValueOrDefault("AltChunkStartingRange", uno::Reference<text::XTextRange>())),
+ m_bIsInTextBox(false),
+ m_bIsNewDoc(!rMediaDesc.getUnpackedValueOrDefault("InsertMode", false)),
+ m_bIsAltChunk(rMediaDesc.getUnpackedValueOrDefault("AltChunkMode", false)),
+ m_bIsReadGlossaries(rMediaDesc.getUnpackedValueOrDefault("ReadGlossaries", false)),
+ m_nTableDepth(0),
+ m_nTableCellDepth(0),
+ m_nLastTableCellParagraphDepth(0),
+ m_bHasFtn(false),
+ m_bHasFtnSep(false),
+ m_bCheckFirstFootnoteTab(false),
+ m_bIgnoreNextTab(false),
+ m_bIsSplitPara(false),
+ m_bIsActualParagraphFramed( false ),
+ m_bParaHadField(false),
+ m_bSaveParaHadField(false),
+ m_bParaAutoBefore(false),
+ m_bFirstParagraphInCell(true),
+ m_bSaveFirstParagraphInCell(false),
+ m_bParaWithInlineObject(false),
+ m_bSaxError(false)
+{
+ m_aBaseUrl = rMediaDesc.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_DOCUMENTBASEURL, OUString());
+ if (m_aBaseUrl.isEmpty()) {
+ m_aBaseUrl = rMediaDesc.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_URL, OUString());
+ }
+
+ appendTableManager( );
+ GetBodyText();
+ if (!m_bIsNewDoc && !m_xBodyText)
+ {
+ throw uno::Exception("failed to find body text of the insert position", nullptr);
+ }
+
+ uno::Reference< text::XTextAppend > xBodyTextAppend( m_xBodyText, uno::UNO_QUERY );
+ m_aTextAppendStack.push(TextAppendContext(xBodyTextAppend,
+ m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : m_xBodyText->createTextCursorByRange(m_xInsertTextRange)));
+
+ //todo: does it makes sense to set the body text as static text interface?
+ uno::Reference< text::XTextAppendAndConvert > xBodyTextAppendAndConvert( m_xBodyText, uno::UNO_QUERY );
+ m_pTableHandler = new DomainMapperTableHandler(xBodyTextAppendAndConvert, *this);
+ getTableManager( ).setHandler(m_pTableHandler);
+
+ getTableManager( ).startLevel();
+ m_bUsingEnhancedFields = !utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ImportWWFieldsAsEnhancedFields::get();
+
+ m_pSdtHelper = new SdtHelper(*this, m_xComponentContext);
+
+ m_aRedlines.push(std::vector<RedlineParamsPtr>());
+
+ if (m_bIsAltChunk)
+ {
+ m_bIsFirstSection = false;
+ }
+}
+
+
+DomainMapper_Impl::~DomainMapper_Impl()
+{
+ ChainTextFrames();
+ // Don't remove last paragraph when pasting, sw expects that empty paragraph.
+ if (m_bIsNewDoc)
+ RemoveLastParagraph();
+ if (hasTableManager())
+ {
+ getTableManager().endLevel();
+ popTableManager();
+ }
+}
+
+writerfilter::ooxml::OOXMLDocument* DomainMapper_Impl::getDocumentReference() const
+{
+ return m_pOOXMLDocument;
+}
+
+uno::Reference< container::XNameContainer > const & DomainMapper_Impl::GetPageStyles()
+{
+ if(!m_xPageStyles1.is())
+ {
+ uno::Reference< style::XStyleFamiliesSupplier > xSupplier( m_xTextDocument, uno::UNO_QUERY );
+ if (xSupplier.is())
+ xSupplier->getStyleFamilies()->getByName("PageStyles") >>= m_xPageStyles1;
+ }
+ return m_xPageStyles1;
+}
+
+OUString DomainMapper_Impl::GetUnusedPageStyleName()
+{
+ static const char DEFAULT_STYLE[] = "Converted";
+ if (!m_xNextUnusedPageStyleNo)
+ {
+ const uno::Sequence< OUString > aPageStyleNames = GetPageStyles()->getElementNames();
+ sal_Int32 nMaxIndex = 0;
+ // find the highest number x in each style with the name "DEFAULT_STYLE+x" and
+ // return an incremented name
+
+ for ( const auto& rStyleName : aPageStyleNames )
+ {
+ if ( rStyleName.startsWith( DEFAULT_STYLE ) )
+ {
+ sal_Int32 nIndex = o3tl::toInt32(rStyleName.subView( strlen( DEFAULT_STYLE ) ));
+ if ( nIndex > nMaxIndex )
+ nMaxIndex = nIndex;
+ }
+ }
+ m_xNextUnusedPageStyleNo = nMaxIndex + 1;
+ }
+
+ OUString sPageStyleName = DEFAULT_STYLE + OUString::number( *m_xNextUnusedPageStyleNo );
+ *m_xNextUnusedPageStyleNo = *m_xNextUnusedPageStyleNo + 1;
+ return sPageStyleName;
+}
+
+uno::Reference< container::XNameContainer > const & DomainMapper_Impl::GetCharacterStyles()
+{
+ if(!m_xCharacterStyles.is())
+ {
+ uno::Reference< style::XStyleFamiliesSupplier > xSupplier( m_xTextDocument, uno::UNO_QUERY );
+ if (xSupplier.is())
+ xSupplier->getStyleFamilies()->getByName("CharacterStyles") >>= m_xCharacterStyles;
+ }
+ return m_xCharacterStyles;
+}
+
+OUString DomainMapper_Impl::GetUnusedCharacterStyleName()
+{
+ static const char cListLabel[] = "ListLabel ";
+ if (!m_xNextUnusedCharacterStyleNo)
+ {
+ //search for all character styles with the name sListLabel + <index>
+ const uno::Sequence< OUString > aCharacterStyleNames = GetCharacterStyles()->getElementNames();
+ sal_Int32 nMaxIndex = 0;
+ for ( const auto& rStyleName : aCharacterStyleNames )
+ {
+ OUString sSuffix;
+ if ( rStyleName.startsWith( cListLabel, &sSuffix ) )
+ {
+ sal_Int32 nSuffix = sSuffix.toInt32();
+ if( nSuffix > 0 && nSuffix > nMaxIndex )
+ nMaxIndex = nSuffix;
+ }
+ }
+ m_xNextUnusedCharacterStyleNo = nMaxIndex + 1;
+ }
+
+ OUString sPageStyleName = cListLabel + OUString::number( *m_xNextUnusedCharacterStyleNo );
+ *m_xNextUnusedCharacterStyleNo = *m_xNextUnusedCharacterStyleNo + 1;
+ return sPageStyleName;
+}
+
+uno::Reference< text::XText > const & DomainMapper_Impl::GetBodyText()
+{
+ if(!m_xBodyText.is())
+ {
+ if (m_xInsertTextRange.is())
+ m_xBodyText = m_xInsertTextRange->getText();
+ else if (m_xTextDocument.is())
+ m_xBodyText = m_xTextDocument->getText();
+ }
+ return m_xBodyText;
+}
+
+
+uno::Reference< beans::XPropertySet > const & DomainMapper_Impl::GetDocumentSettings()
+{
+ if( !m_xDocumentSettings.is() && m_xTextFactory.is())
+ {
+ m_xDocumentSettings.set( m_xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY );
+ }
+ return m_xDocumentSettings;
+}
+
+
+void DomainMapper_Impl::SetDocumentSettingsProperty( const OUString& rPropName, const uno::Any& rValue )
+{
+ uno::Reference< beans::XPropertySet > xSettings = GetDocumentSettings();
+ if( xSettings.is() )
+ {
+ try
+ {
+ xSettings->setPropertyValue( rPropName, rValue );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+}
+void DomainMapper_Impl::RemoveDummyParaForTableInSection()
+{
+ SetIsDummyParaAddedForTableInSection(false);
+ PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_SECTION);
+ SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
+ if (!pSectionContext)
+ return;
+
+ if (m_aTextAppendStack.empty())
+ return;
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (!xTextAppend.is())
+ return;
+
+ uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursorByRange(pSectionContext->GetStartingRange());
+
+ // Remove the extra NumPicBullets from the document,
+ // which get attached to the first paragraph in the
+ // document
+ ListsManager::Pointer pListTable = GetListTable();
+ pListTable->DisposeNumPicBullets();
+
+ uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCursor, uno::UNO_QUERY);
+ if (xEnumerationAccess.is() && m_aTextAppendStack.size() == 1 )
+ {
+ uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
+ uno::Reference<lang::XComponent> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
+ xParagraph->dispose();
+ }
+}
+void DomainMapper_Impl::AddDummyParaForTableInSection()
+{
+ // Shapes and textboxes can't have sections.
+ if (IsInShape() || m_bIsInTextBox)
+ return;
+
+ if (!m_aTextAppendStack.empty())
+ {
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (xTextAppend.is())
+ {
+ xTextAppend->finishParagraph( uno::Sequence< beans::PropertyValue >() );
+ SetIsDummyParaAddedForTableInSection(true);
+ }
+ }
+}
+
+ static OUString lcl_FindLastBookmark(const uno::Reference<text::XTextCursor>& xCursor)
+ {
+ OUString sName;
+ if (!xCursor.is())
+ return sName;
+
+ // Select 1 previous element
+ xCursor->goLeft(1, true);
+ uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xCursor, uno::UNO_QUERY);
+ if (!xParaEnumAccess.is())
+ {
+ xCursor->goRight(1, true);
+ return sName;
+ }
+
+ // Iterate through selection paragraphs
+ uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
+ if (!xParaEnum->hasMoreElements())
+ {
+ xCursor->goRight(1, true);
+ return sName;
+ }
+
+ // Iterate through first para portions
+ uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(),
+ uno::UNO_QUERY_THROW);
+ uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
+ while (xRunEnum->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xProps(xRunEnum->nextElement(), uno::UNO_QUERY_THROW);
+ uno::Any aType(xProps->getPropertyValue("TextPortionType"));
+ OUString sType;
+ aType >>= sType;
+ if (sType == "Bookmark")
+ {
+ uno::Reference<container::XNamed> xBookmark(xProps->getPropertyValue("Bookmark"),
+ uno::UNO_QUERY_THROW);
+ sName = xBookmark->getName();
+ // Do not stop the scan here. Maybe there are 2 bookmarks?
+ }
+ }
+
+ xCursor->goRight(1, true);
+ return sName;
+ }
+
+void DomainMapper_Impl::RemoveLastParagraph( )
+{
+ if (m_bDiscardHeaderFooter)
+ return;
+
+ if (m_aTextAppendStack.empty())
+ return;
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (!xTextAppend.is())
+ return;
+ try
+ {
+ uno::Reference< text::XTextCursor > xCursor;
+ if (m_bIsNewDoc)
+ {
+ xCursor = xTextAppend->createTextCursor();
+ xCursor->gotoEnd(false);
+ }
+ else
+ xCursor = m_aTextAppendStack.top().xCursor;
+ uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCursor, uno::UNO_QUERY);
+ // Keep the character properties of the last but one paragraph, even if
+ // it's empty. This works for headers/footers, and maybe in other cases
+ // as well, but surely not in textboxes.
+ // fdo#58327: also do this at the end of the document: when pasting,
+ // a table before the cursor position would be deleted
+ // (but only for paste/insert, not load; otherwise it can happen that
+ // flys anchored at the disposed paragraph are deleted (fdo47036.rtf))
+ bool const bEndOfDocument(m_aTextAppendStack.size() == 1);
+
+ // Try to find and remember last bookmark in document: it potentially
+ // can be deleted by xCursor->setString() but not by xParagraph->dispose()
+ OUString sLastBookmarkName;
+ if (bEndOfDocument)
+ sLastBookmarkName = lcl_FindLastBookmark(xCursor);
+
+ if ((IsInHeaderFooter() || (bEndOfDocument && !m_bIsNewDoc))
+ && xEnumerationAccess.is())
+ {
+ uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
+ uno::Reference<lang::XComponent> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
+ xParagraph->dispose();
+ }
+ else if (xCursor.is())
+ {
+ xCursor->goLeft( 1, true );
+ // If this is a text on a shape, possibly the text has the trailing
+ // newline removed already.
+ if (xCursor->getString() == SAL_NEWLINE_STRING ||
+ // tdf#105444 comments need an exception, if SAL_NEWLINE_STRING defined as "\r\n"
+ (sizeof(SAL_NEWLINE_STRING)-1 == 2 && xCursor->getString() == "\n"))
+ {
+ uno::Reference<beans::XPropertySet> xDocProps(GetTextDocument(), uno::UNO_QUERY);
+ static const OUStringLiteral aRecordChanges(u"RecordChanges");
+ uno::Any aPreviousValue(xDocProps->getPropertyValue(aRecordChanges));
+
+ // disable redlining for this operation, otherwise we might
+ // end up with an unwanted recorded deletion
+ xDocProps->setPropertyValue(aRecordChanges, uno::Any(false));
+
+ // delete
+ xCursor->setString(OUString());
+
+ // While removing paragraphs that contain section properties, reset list
+ // related attributes to prevent them leaking into the following section's lists
+ if (GetParaSectpr())
+ {
+ uno::Reference<beans::XPropertySet> XCursorProps(xCursor, uno::UNO_QUERY);
+ XCursorProps->setPropertyValue("ResetParagraphListAttributes", uno::Any());
+ }
+
+ // call to xCursor->setString possibly did remove final bookmark
+ // from previous paragraph. We need to restore it, if there was any.
+ if (sLastBookmarkName.getLength())
+ {
+ OUString sBookmarkNameAfterRemoval = lcl_FindLastBookmark(xCursor);
+ if (sBookmarkNameAfterRemoval.isEmpty())
+ {
+ // Yes, it was removed. Restore
+ uno::Reference<text::XTextContent> xBookmark(
+ m_xTextFactory->createInstance("com.sun.star.text.Bookmark"),
+ uno::UNO_QUERY_THROW);
+
+ uno::Reference<container::XNamed> xBkmNamed(xBookmark,
+ uno::UNO_QUERY_THROW);
+ xBkmNamed->setName(sLastBookmarkName);
+ xTextAppend->insertTextContent(
+ uno::Reference<text::XTextRange>(xCursor, uno::UNO_QUERY_THROW),
+ xBookmark, !xCursor->isCollapsed());
+ }
+ }
+ // restore redline options again
+ xDocProps->setPropertyValue(aRecordChanges, aPreviousValue);
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+
+void DomainMapper_Impl::SetIsLastSectionGroup( bool bIsLast )
+{
+ m_bIsLastSectionGroup = bIsLast;
+}
+
+void DomainMapper_Impl::SetIsLastParagraphInSection( bool bIsLast )
+{
+ m_bIsLastParaInSection = bIsLast;
+}
+
+
+void DomainMapper_Impl::SetIsFirstParagraphInSection( bool bIsFirst )
+{
+ m_bIsFirstParaInSection = bIsFirst;
+}
+
+void DomainMapper_Impl::SetIsFirstParagraphInSectionAfterRedline( bool bIsFirstAfterRedline )
+{
+ m_bIsFirstParaInSectionAfterRedline = bIsFirstAfterRedline;
+}
+
+bool DomainMapper_Impl::GetIsFirstParagraphInSection( bool bAfterRedline ) const
+{
+ // Anchored objects may include multiple paragraphs,
+ // and none of them should be considered the first para in section.
+ return ( bAfterRedline ? m_bIsFirstParaInSectionAfterRedline : m_bIsFirstParaInSection )
+ && !IsInShape()
+ && !m_bIsInComments
+ && !IsInFootOrEndnote();
+}
+
+void DomainMapper_Impl::SetIsFirstParagraphInShape(bool bIsFirst)
+{
+ m_bIsFirstParaInShape = bIsFirst;
+}
+
+void DomainMapper_Impl::SetIsDummyParaAddedForTableInSection( bool bIsAdded )
+{
+ m_bDummyParaAddedForTableInSection = bIsAdded;
+ m_bDummyParaAddedForTableInSectionPage = bIsAdded;
+}
+
+void DomainMapper_Impl::SetIsDummyParaAddedForTableInSectionPage( bool bIsAdded )
+{
+ m_bDummyParaAddedForTableInSectionPage = bIsAdded;
+}
+
+
+void DomainMapper_Impl::SetIsTextFrameInserted( bool bIsInserted )
+{
+ m_bTextFrameInserted = bIsInserted;
+}
+
+
+void DomainMapper_Impl::SetParaSectpr(bool bParaSectpr)
+{
+ m_bParaSectpr = bParaSectpr;
+}
+
+
+void DomainMapper_Impl::SetSdt(bool bSdt)
+{
+ m_bSdt = bSdt;
+
+ if (m_bSdt && !m_aTextAppendStack.empty())
+ {
+ m_xSdtEntryStart = GetTopTextAppend()->getEnd();
+ }
+ else
+ {
+ m_xSdtEntryStart.clear();
+ }
+}
+
+void DomainMapper_Impl::PushSdt()
+{
+ if (m_aTextAppendStack.empty())
+ {
+ return;
+ }
+
+ uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ uno::Reference<text::XTextCursor> xCursor
+ = xTextAppend->getText()->createTextCursorByRange(xTextAppend->getEnd());
+ // Offset so the cursor is not adjusted as we import the SDT's content.
+ bool bStart = !xCursor->goLeft(1, /*bExpand=*/false);
+ m_xSdtStarts.push({bStart, OUString(), xCursor->getStart()});
+}
+
+const std::stack<BookmarkInsertPosition>& DomainMapper_Impl::GetSdtStarts() const
+{
+ return m_xSdtStarts;
+}
+
+void DomainMapper_Impl::PopSdt()
+{
+ if (m_xSdtStarts.empty())
+ {
+ return;
+ }
+
+ BookmarkInsertPosition aPosition = m_xSdtStarts.top();
+ m_xSdtStarts.pop();
+ uno::Reference<text::XTextRange> xStart = aPosition.m_xTextRange;
+ uno::Reference<text::XTextRange> xEnd = GetTopTextAppend()->getEnd();
+ uno::Reference<text::XText> xText = xEnd->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursorByRange(xStart);
+ if (!xCursor)
+ {
+ SAL_WARN("writerfilter.dmapper", "DomainMapper_Impl::PopSdt: no start position");
+ return;
+ }
+
+ if (aPosition.m_bIsStartOfText)
+ {
+ // Go to the start of the end's paragraph. This helps in case
+ // DomainMapper_Impl::AddDummyParaForTableInSection() would make our range multi-paragraph,
+ // while the intention is to keep start/end inside the same paragraph for run SDTs.
+ uno::Reference<text::XParagraphCursor> xParagraphCursor(xCursor, uno::UNO_QUERY);
+ if (xParagraphCursor.is())
+ {
+ xCursor->gotoRange(xEnd, /*bExpand=*/false);
+ xParagraphCursor->gotoStartOfParagraph(/*bExpand=*/false);
+ }
+ }
+ else
+ {
+ // Undo the goLeft() in DomainMapper_Impl::PushSdt();
+ xCursor->goRight(1, /*bExpand=*/false);
+ }
+ xCursor->gotoRange(xEnd, /*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ m_xTextFactory->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ if (m_pSdtHelper->GetShowingPlcHdr())
+ {
+ xContentControlProps->setPropertyValue("ShowingPlaceHolder",
+ uno::Any(m_pSdtHelper->GetShowingPlcHdr()));
+ }
+
+ if (!m_pSdtHelper->GetPlaceholderDocPart().isEmpty())
+ {
+ xContentControlProps->setPropertyValue("PlaceholderDocPart",
+ uno::Any(m_pSdtHelper->GetPlaceholderDocPart()));
+ }
+
+ if (!m_pSdtHelper->GetDataBindingPrefixMapping().isEmpty())
+ {
+ xContentControlProps->setPropertyValue("DataBindingPrefixMappings",
+ uno::Any(m_pSdtHelper->GetDataBindingPrefixMapping()));
+ }
+ if (!m_pSdtHelper->GetDataBindingXPath().isEmpty())
+ {
+ xContentControlProps->setPropertyValue("DataBindingXpath",
+ uno::Any(m_pSdtHelper->GetDataBindingXPath()));
+ }
+ if (!m_pSdtHelper->GetDataBindingStoreItemID().isEmpty())
+ {
+ xContentControlProps->setPropertyValue("DataBindingStoreItemID",
+ uno::Any(m_pSdtHelper->GetDataBindingStoreItemID()));
+ }
+
+ if (!m_pSdtHelper->GetColor().isEmpty())
+ {
+ xContentControlProps->setPropertyValue("Color",
+ uno::Any(m_pSdtHelper->GetColor()));
+ }
+
+ if (m_pSdtHelper->getControlType() == SdtControlType::checkBox)
+ {
+ xContentControlProps->setPropertyValue("Checkbox", uno::Any(true));
+
+ xContentControlProps->setPropertyValue("Checked", uno::Any(m_pSdtHelper->GetChecked()));
+
+ xContentControlProps->setPropertyValue("CheckedState",
+ uno::Any(m_pSdtHelper->GetCheckedState()));
+
+ xContentControlProps->setPropertyValue("UncheckedState",
+ uno::Any(m_pSdtHelper->GetUncheckedState()));
+ }
+
+ if (m_pSdtHelper->getControlType() == SdtControlType::dropDown)
+ {
+ std::vector<OUString>& rDisplayTexts = m_pSdtHelper->getDropDownDisplayTexts();
+ std::vector<OUString>& rValues = m_pSdtHelper->getDropDownItems();
+ if (rDisplayTexts.size() == rValues.size())
+ {
+ uno::Sequence<beans::PropertyValues> aItems(rValues.size());
+ beans::PropertyValues* pItems = aItems.getArray();
+ for (size_t i = 0; i < rValues.size(); ++i)
+ {
+ uno::Sequence<beans::PropertyValue> aItem = {
+ comphelper::makePropertyValue("DisplayText", rDisplayTexts[i]),
+ comphelper::makePropertyValue("Value", rValues[i]),
+ };
+ pItems[i] = aItem;
+ }
+ xContentControlProps->setPropertyValue("ListItems", uno::Any(aItems));
+ }
+ }
+
+ if (m_pSdtHelper->getControlType() == SdtControlType::picture)
+ {
+ xContentControlProps->setPropertyValue("Picture", uno::Any(true));
+ }
+
+ if (m_pSdtHelper->getControlType() == SdtControlType::datePicker)
+ {
+ xContentControlProps->setPropertyValue("Date", uno::Any(true));
+ OUString aDateFormat = m_pSdtHelper->getDateFormat().makeStringAndClear();
+ xContentControlProps->setPropertyValue("DateFormat",
+ uno::Any(aDateFormat.replaceAll("'", "\"")));
+ xContentControlProps->setPropertyValue("DateLanguage",
+ uno::Any(m_pSdtHelper->getLocale().makeStringAndClear()));
+ xContentControlProps->setPropertyValue("CurrentDate",
+ uno::Any(m_pSdtHelper->getDate().makeStringAndClear()));
+ }
+
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+ m_pSdtHelper->clear();
+}
+
+void DomainMapper_Impl::PushProperties(ContextType eId)
+{
+ PropertyMapPtr pInsert(eId == CONTEXT_SECTION ?
+ (new SectionPropertyMap( m_bIsFirstSection )) :
+ eId == CONTEXT_PARAGRAPH ? new ParagraphPropertyMap : new PropertyMap);
+ if(eId == CONTEXT_SECTION)
+ {
+ if( m_bIsFirstSection )
+ m_bIsFirstSection = false;
+ // beginning with the second section group a section has to be inserted
+ // into the document
+ SectionPropertyMap* pSectionContext_ = dynamic_cast< SectionPropertyMap* >( pInsert.get() );
+ if (!m_aTextAppendStack.empty())
+ {
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (xTextAppend.is() && pSectionContext_)
+ pSectionContext_->SetStart( xTextAppend->getEnd() );
+ }
+ }
+ if(eId == CONTEXT_PARAGRAPH && m_bIsSplitPara)
+ {
+ m_aPropertyStacks[eId].push( GetTopContextOfType(eId));
+ m_bIsSplitPara = false;
+ }
+ else
+ {
+ m_aPropertyStacks[eId].push( pInsert );
+ }
+ m_aContextStack.push(eId);
+
+ m_pTopContext = m_aPropertyStacks[eId].top();
+}
+
+
+void DomainMapper_Impl::PushStyleProperties( const PropertyMapPtr& pStyleProperties )
+{
+ m_aPropertyStacks[CONTEXT_STYLESHEET].push( pStyleProperties );
+ m_aContextStack.push(CONTEXT_STYLESHEET);
+
+ m_pTopContext = m_aPropertyStacks[CONTEXT_STYLESHEET].top();
+}
+
+
+void DomainMapper_Impl::PushListProperties(const PropertyMapPtr& pListProperties)
+{
+ m_aPropertyStacks[CONTEXT_LIST].push( pListProperties );
+ m_aContextStack.push(CONTEXT_LIST);
+ m_pTopContext = m_aPropertyStacks[CONTEXT_LIST].top();
+}
+
+
+void DomainMapper_Impl::PopProperties(ContextType eId)
+{
+ OSL_ENSURE(!m_aPropertyStacks[eId].empty(), "section stack already empty");
+ if ( m_aPropertyStacks[eId].empty() )
+ return;
+
+ if ( eId == CONTEXT_SECTION )
+ {
+ if (m_aPropertyStacks[eId].size() == 1) // tdf#112202 only top level !!!
+ {
+ m_pLastSectionContext = m_aPropertyStacks[eId].top();
+ }
+ }
+ else if (eId == CONTEXT_CHARACTER)
+ {
+ m_pLastCharacterContext = m_aPropertyStacks[eId].top();
+ // Sadly an assert about deferredCharacterProperties being empty is not possible
+ // here, because appendTextPortion() may not be called for every character section.
+ deferredCharacterProperties.clear();
+ }
+
+ if (!IsInFootOrEndnote() && IsInCustomFootnote() && !m_aPropertyStacks[eId].empty())
+ {
+ PropertyMapPtr pRet = m_aPropertyStacks[eId].top();
+ if (pRet->GetFootnote().is() && m_pFootnoteContext.is())
+ EndCustomFootnote();
+ }
+
+ m_aPropertyStacks[eId].pop();
+ m_aContextStack.pop();
+ if(!m_aContextStack.empty() && !m_aPropertyStacks[m_aContextStack.top()].empty())
+
+ m_pTopContext = m_aPropertyStacks[m_aContextStack.top()].top();
+ else
+ {
+ // OSL_ENSURE(eId == CONTEXT_SECTION, "this should happen at a section context end");
+ m_pTopContext.clear();
+ }
+}
+
+
+PropertyMapPtr DomainMapper_Impl::GetTopContextOfType(ContextType eId)
+{
+ PropertyMapPtr pRet;
+ if(!m_aPropertyStacks[eId].empty())
+ pRet = m_aPropertyStacks[eId].top();
+ return pRet;
+}
+
+bool DomainMapper_Impl::HasTopText() const
+{
+ return !m_aTextAppendStack.empty();
+}
+
+uno::Reference< text::XTextAppend > const & DomainMapper_Impl::GetTopTextAppend()
+{
+ OSL_ENSURE(!m_aTextAppendStack.empty(), "text append stack is empty" );
+ return m_aTextAppendStack.top().xTextAppend;
+}
+
+FieldContextPtr const & DomainMapper_Impl::GetTopFieldContext()
+{
+ SAL_WARN_IF(m_aFieldStack.empty(), "writerfilter.dmapper", "Field stack is empty");
+ return m_aFieldStack.back();
+}
+
+bool DomainMapper_Impl::HasTopAnchoredObjects() const
+{
+ return !m_aTextAppendStack.empty() && !m_aTextAppendStack.top().m_aAnchoredObjects.empty();
+}
+
+void DomainMapper_Impl::InitTabStopFromStyle( const uno::Sequence< style::TabStop >& rInitTabStops )
+{
+ OSL_ENSURE(m_aCurrentTabStops.empty(), "tab stops already initialized");
+ for( const auto& rTabStop : rInitTabStops)
+ {
+ m_aCurrentTabStops.emplace_back(rTabStop);
+ }
+}
+
+void DomainMapper_Impl::IncorporateTabStop( const DeletableTabStop & rTabStop )
+{
+ sal_Int32 nConverted = rTabStop.Position;
+ auto aIt = std::find_if(m_aCurrentTabStops.begin(), m_aCurrentTabStops.end(),
+ [&nConverted](const DeletableTabStop& rCurrentTabStop) { return rCurrentTabStop.Position == nConverted; });
+ if( aIt != m_aCurrentTabStops.end() )
+ {
+ if( rTabStop.bDeleted )
+ m_aCurrentTabStops.erase( aIt );
+ else
+ *aIt = rTabStop;
+ }
+ else
+ m_aCurrentTabStops.push_back( rTabStop );
+}
+
+
+uno::Sequence< style::TabStop > DomainMapper_Impl::GetCurrentTabStopAndClear()
+{
+ std::vector<style::TabStop> aRet;
+ for (const DeletableTabStop& rStop : m_aCurrentTabStops)
+ {
+ if (!rStop.bDeleted)
+ aRet.push_back(rStop);
+ }
+ m_aCurrentTabStops.clear();
+ return comphelper::containerToSequence(aRet);
+}
+
+OUString DomainMapper_Impl::GetCurrentParaStyleName()
+{
+ OUString sName;
+ // use saved currParaStyleName as a fallback, in case no particular para style name applied.
+ // tdf#134784 except in the case of first paragraph of shapes to avoid bad fallback.
+ // TODO fix this "highly inaccurate" m_sCurrentParaStyleName
+ if ( !IsInShape() )
+ sName = m_sCurrentParaStyleName;
+
+ PropertyMapPtr pParaContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
+ if ( pParaContext && pParaContext->isSet(PROP_PARA_STYLE_NAME) )
+ pParaContext->getProperty(PROP_PARA_STYLE_NAME)->second >>= sName;
+
+ // In rare situations the name might still be blank, so use the default style,
+ // despite documentation that states, "If this attribute is not specified for any style,
+ // then no properties shall be applied to objects of the specified type."
+ // Word, however, assigns "Normal" style even in these situations.
+ if ( !m_bInStyleSheetImport && sName.isEmpty() )
+ sName = GetDefaultParaStyleName();
+
+ return sName;
+}
+
+OUString DomainMapper_Impl::GetDefaultParaStyleName()
+{
+ // After import the default style won't change and is frequently requested: cache the LO style name.
+ // TODO assert !InStyleSheetImport? This function really only makes sense once import is finished anyway.
+ if ( m_sDefaultParaStyleName.isEmpty() )
+ {
+ const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindDefaultParaStyle();
+ if ( pEntry && !pEntry->sConvertedStyleName.isEmpty() )
+ {
+ if ( !m_bInStyleSheetImport )
+ m_sDefaultParaStyleName = pEntry->sConvertedStyleName;
+ return pEntry->sConvertedStyleName;
+ }
+ else
+ return "Standard";
+ }
+ return m_sDefaultParaStyleName;
+}
+
+uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, bool* pIsDocDefault)
+{
+ while(pEntry)
+ {
+ if(pEntry->pProperties)
+ {
+ std::optional<PropertyMap::Property> aProperty =
+ pEntry->pProperties->getProperty(eId);
+ if( aProperty )
+ {
+ if (pIsDocDefault)
+ *pIsDocDefault = pEntry->pProperties->isDocDefault(eId);
+
+ return aProperty->second;
+ }
+ }
+ //search until the property is set or no parent is available
+ StyleSheetEntryPtr pNewEntry;
+ if ( !pEntry->sBaseStyleIdentifier.isEmpty() )
+ pNewEntry = GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier);
+
+ SAL_WARN_IF( pEntry == pNewEntry, "writerfilter.dmapper", "circular loop in style hierarchy?");
+
+ if (pEntry == pNewEntry) //fdo#49587
+ break;
+
+ pEntry = pNewEntry;
+ }
+ // not found in style, try the document's DocDefault properties
+ if ( bDocDefaults && bPara )
+ {
+ const PropertyMapPtr& pDefaultParaProps = GetStyleSheetTable()->GetDefaultParaProps();
+ if ( pDefaultParaProps )
+ {
+ std::optional<PropertyMap::Property> aProperty = pDefaultParaProps->getProperty(eId);
+ if ( aProperty )
+ {
+ if (pIsDocDefault)
+ *pIsDocDefault = true;
+
+ return aProperty->second;
+ }
+ }
+ }
+ if ( bDocDefaults && isCharacterProperty(eId) )
+ {
+ const PropertyMapPtr& pDefaultCharProps = GetStyleSheetTable()->GetDefaultCharProps();
+ if ( pDefaultCharProps )
+ {
+ std::optional<PropertyMap::Property> aProperty = pDefaultCharProps->getProperty(eId);
+ if ( aProperty )
+ {
+ if (pIsDocDefault)
+ *pIsDocDefault = true;
+
+ return aProperty->second;
+ }
+ }
+ }
+
+ if (pIsDocDefault)
+ *pIsDocDefault = false;
+
+ return uno::Any();
+}
+
+uno::Any DomainMapper_Impl::GetPropertyFromParaStyleSheet(PropertyIds eId)
+{
+ StyleSheetEntryPtr pEntry;
+ if ( m_bInStyleSheetImport )
+ pEntry = GetStyleSheetTable()->GetCurrentEntry();
+ else
+ pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(GetCurrentParaStyleName());
+ return GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/true, /*bPara=*/true);
+}
+
+uno::Any DomainMapper_Impl::GetPropertyFromCharStyleSheet(PropertyIds eId, const PropertyMapPtr& rContext)
+{
+ if ( m_bInStyleSheetImport || eId == PROP_CHAR_STYLE_NAME || !isCharacterProperty(eId) )
+ return uno::Any();
+
+ StyleSheetEntryPtr pEntry;
+ OUString sCharStyleName;
+ if ( GetAnyProperty(PROP_CHAR_STYLE_NAME, rContext) >>= sCharStyleName )
+ pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sCharStyleName);
+ return GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/false, /*bPara=*/false);
+}
+
+uno::Any DomainMapper_Impl::GetAnyProperty(PropertyIds eId, const PropertyMapPtr& rContext)
+{
+ // first look in directly applied attributes
+ if ( rContext )
+ {
+ std::optional<PropertyMap::Property> aProperty = rContext->getProperty(eId);
+ if ( aProperty )
+ return aProperty->second;
+ }
+
+ // then look whether it was directly applied as a paragraph property
+ PropertyMapPtr pParaContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
+ if (pParaContext && rContext != pParaContext)
+ {
+ std::optional<PropertyMap::Property> aProperty = pParaContext->getProperty(eId);
+ if (aProperty)
+ return aProperty->second;
+ }
+
+ // then look whether it was inherited from a directly applied character style
+ if ( eId != PROP_CHAR_STYLE_NAME && isCharacterProperty(eId) )
+ {
+ uno::Any aRet = GetPropertyFromCharStyleSheet(eId, rContext);
+ if ( aRet.hasValue() )
+ return aRet;
+ }
+
+ // then look in current paragraph style, and docDefaults
+ return GetPropertyFromParaStyleSheet(eId);
+}
+
+OUString DomainMapper_Impl::GetListStyleName(sal_Int32 nListId)
+{
+ auto const pList(GetListTable()->GetList( nListId ));
+ return pList ? pList->GetStyleName() : OUString();
+}
+
+ListsManager::Pointer const & DomainMapper_Impl::GetListTable()
+{
+ if(!m_pListTable)
+ m_pListTable =
+ new ListsManager( m_rDMapper, m_xTextFactory );
+ return m_pListTable;
+}
+
+
+void DomainMapper_Impl::deferBreak( BreakType deferredBreakType)
+{
+ switch (deferredBreakType)
+ {
+ case LINE_BREAK:
+ m_nLineBreaksDeferred++;
+ break;
+ case COLUMN_BREAK:
+ m_bIsColumnBreakDeferred = true;
+ break;
+ case PAGE_BREAK:
+ // See SwWW8ImplReader::HandlePageBreakChar(), page break should be
+ // ignored inside tables.
+ if (m_nTableDepth > 0)
+ return;
+
+ m_bIsPageBreakDeferred = true;
+ break;
+ default:
+ return;
+ }
+}
+
+bool DomainMapper_Impl::isBreakDeferred( BreakType deferredBreakType )
+{
+ switch (deferredBreakType)
+ {
+ case LINE_BREAK:
+ return m_nLineBreaksDeferred > 0;
+ case COLUMN_BREAK:
+ return m_bIsColumnBreakDeferred;
+ case PAGE_BREAK:
+ return m_bIsPageBreakDeferred;
+ default:
+ return false;
+ }
+}
+
+void DomainMapper_Impl::clearDeferredBreak(BreakType deferredBreakType)
+{
+ switch (deferredBreakType)
+ {
+ case LINE_BREAK:
+ assert(m_nLineBreaksDeferred > 0);
+ m_nLineBreaksDeferred--;
+ break;
+ case COLUMN_BREAK:
+ m_bIsColumnBreakDeferred = false;
+ break;
+ case PAGE_BREAK:
+ m_bIsPageBreakDeferred = false;
+ break;
+ default:
+ break;
+ }
+}
+
+void DomainMapper_Impl::clearDeferredBreaks()
+{
+ m_nLineBreaksDeferred = 0;
+ m_bIsColumnBreakDeferred = false;
+ m_bIsPageBreakDeferred = false;
+}
+
+void DomainMapper_Impl::setSdtEndDeferred(bool bSdtEndDeferred)
+{
+ m_bSdtEndDeferred = bSdtEndDeferred;
+}
+
+bool DomainMapper_Impl::isSdtEndDeferred() const
+{
+ return m_bSdtEndDeferred;
+}
+
+void DomainMapper_Impl::setParaSdtEndDeferred(bool bParaSdtEndDeferred)
+{
+ m_bParaSdtEndDeferred = bParaSdtEndDeferred;
+}
+
+bool DomainMapper_Impl::isParaSdtEndDeferred() const
+{
+ return m_bParaSdtEndDeferred;
+}
+
+static void lcl_MoveBorderPropertiesToFrame(std::vector<beans::PropertyValue>& rFrameProperties,
+ uno::Reference<text::XTextRange> const& xStartTextRange,
+ uno::Reference<text::XTextRange> const& xEndTextRange )
+{
+ try
+ {
+ if (!xStartTextRange.is()) //rhbz#1077780
+ return;
+ uno::Reference<text::XTextCursor> xRangeCursor = xStartTextRange->getText()->createTextCursorByRange( xStartTextRange );
+ xRangeCursor->gotoRange( xEndTextRange, true );
+
+ uno::Reference<beans::XPropertySet> xTextRangeProperties(xRangeCursor, uno::UNO_QUERY);
+ if(!xTextRangeProperties.is())
+ return ;
+
+ static PropertyIds const aBorderProperties[] =
+ {
+ PROP_LEFT_BORDER,
+ PROP_RIGHT_BORDER,
+ PROP_TOP_BORDER,
+ PROP_BOTTOM_BORDER,
+ PROP_LEFT_BORDER_DISTANCE,
+ PROP_RIGHT_BORDER_DISTANCE,
+ PROP_TOP_BORDER_DISTANCE,
+ PROP_BOTTOM_BORDER_DISTANCE
+ };
+
+ for( size_t nProperty = 0; nProperty < SAL_N_ELEMENTS( aBorderProperties ); ++nProperty)
+ {
+ OUString sPropertyName = getPropertyName(aBorderProperties[nProperty]);
+ beans::PropertyValue aValue;
+ aValue.Name = sPropertyName;
+ aValue.Value = xTextRangeProperties->getPropertyValue(sPropertyName);
+ rFrameProperties.push_back(aValue);
+ if( nProperty < 4 )
+ xTextRangeProperties->setPropertyValue( sPropertyName, uno::Any(table::BorderLine2()));
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+
+static void lcl_AddRangeAndStyle(
+ ParagraphPropertiesPtr const & pToBeSavedProperties,
+ uno::Reference< text::XTextAppend > const& xTextAppend,
+ const PropertyMapPtr& pPropertyMap,
+ TextAppendContext const & rAppendContext)
+{
+ uno::Reference<text::XParagraphCursor> xParaCursor(
+ xTextAppend->createTextCursorByRange( rAppendContext.xInsertPosition.is() ? rAppendContext.xInsertPosition : xTextAppend->getEnd()), uno::UNO_QUERY_THROW );
+ pToBeSavedProperties->SetEndingRange(xParaCursor->getStart());
+ xParaCursor->gotoStartOfParagraph( false );
+
+ pToBeSavedProperties->SetStartingRange(xParaCursor->getStart());
+ if(pPropertyMap)
+ {
+ std::optional<PropertyMap::Property> aParaStyle = pPropertyMap->getProperty(PROP_PARA_STYLE_NAME);
+ if( aParaStyle )
+ {
+ OUString sName;
+ aParaStyle->second >>= sName;
+ pToBeSavedProperties->SetParaStyleName(sName);
+ }
+ }
+}
+
+
+//define some default frame width - 0cm ATM: this allow the frame to be wrapped around the text
+constexpr sal_Int32 DEFAULT_FRAME_MIN_WIDTH = 0;
+constexpr sal_Int32 DEFAULT_FRAME_MIN_HEIGHT = 0;
+constexpr sal_Int32 DEFAULT_VALUE = 0;
+
+void DomainMapper_Impl::CheckUnregisteredFrameConversion( )
+{
+ if (m_aTextAppendStack.empty())
+ return;
+ TextAppendContext& rAppendContext = m_aTextAppendStack.top();
+ // n#779642: ignore fly frame inside table as it could lead to messy situations
+ if (!rAppendContext.pLastParagraphProperties)
+ return;
+ if (!rAppendContext.pLastParagraphProperties->IsFrameMode())
+ return;
+ if (!hasTableManager())
+ return;
+ if (getTableManager().isInTable())
+ return;
+ try
+ {
+ StyleSheetEntryPtr pParaStyle =
+ GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(rAppendContext.pLastParagraphProperties->GetParaStyleName());
+
+ std::vector<beans::PropertyValue> aFrameProperties;
+
+ if ( pParaStyle )
+ {
+ const StyleSheetPropertyMap* pStyleProperties = pParaStyle->pProperties.get();
+ if (!pStyleProperties)
+ return;
+ sal_Int32 nWidth =
+ rAppendContext.pLastParagraphProperties->Getw() > 0 ?
+ rAppendContext.pLastParagraphProperties->Getw() :
+ pStyleProperties->Getw();
+ bool bAutoWidth = nWidth < 1;
+ if( bAutoWidth )
+ nWidth = DEFAULT_FRAME_MIN_WIDTH;
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH), nWidth));
+
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEIGHT),
+ rAppendContext.pLastParagraphProperties->Geth() > 0 ?
+ rAppendContext.pLastParagraphProperties->Geth() :
+ pStyleProperties->Geth() > 0 ? pStyleProperties->Geth() : DEFAULT_FRAME_MIN_HEIGHT));
+
+ sal_Int16 nhRule = sal_Int16(
+ rAppendContext.pLastParagraphProperties->GethRule() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GethRule() :
+ pStyleProperties->GethRule());
+ if ( nhRule < 0 )
+ {
+ if ( rAppendContext.pLastParagraphProperties->Geth() >= 0 ||
+ pStyleProperties->GethRule() >= 0 )
+ {
+ // [MS-OE376] Word uses a default value of "atLeast" for
+ // this attribute when the value of the h attribute is not 0.
+ nhRule = text::SizeType::MIN;
+ }
+ else
+ {
+ nhRule = text::SizeType::VARIABLE;
+ }
+ }
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE), nhRule));
+
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE), bAutoWidth ? text::SizeType::MIN : text::SizeType::FIX));
+
+ if (const std::optional<sal_Int16> nDirection = PopFrameDirection())
+ {
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_FRM_DIRECTION), *nDirection));
+ }
+
+ sal_Int16 nHoriOrient = sal_Int16(
+ rAppendContext.pLastParagraphProperties->GetxAlign() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GetxAlign() :
+ pStyleProperties->GetxAlign() >= 0 ? pStyleProperties->GetxAlign() : text::HoriOrientation::NONE );
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT), nHoriOrient));
+
+ //set a non negative default value
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_POSITION),
+ rAppendContext.pLastParagraphProperties->IsxValid() ?
+ rAppendContext.pLastParagraphProperties->Getx() :
+ pStyleProperties->IsxValid() ? pStyleProperties->Getx() : DEFAULT_VALUE));
+
+ //Default the anchor in case FramePr_hAnchor is missing ECMA 17.3.1.11
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_RELATION), sal_Int16(
+ rAppendContext.pLastParagraphProperties->GethAnchor() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GethAnchor() :
+ pStyleProperties->GethAnchor() >=0 ? pStyleProperties->GethAnchor() : text::RelOrientation::FRAME )));
+
+ sal_Int16 nVertOrient = sal_Int16(
+ rAppendContext.pLastParagraphProperties->GetyAlign() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GetyAlign() :
+ pStyleProperties->GetyAlign() >= 0 ? pStyleProperties->GetyAlign() : text::VertOrientation::NONE );
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT), nVertOrient));
+
+ //set a non negative default value
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_POSITION),
+ rAppendContext.pLastParagraphProperties->IsyValid() ?
+ rAppendContext.pLastParagraphProperties->Gety() :
+ pStyleProperties->IsyValid() ? pStyleProperties->Gety() : DEFAULT_VALUE));
+
+ //Default the anchor in case FramePr_vAnchor is missing ECMA 17.3.1.11
+ if (rAppendContext.pLastParagraphProperties->GetWrap() == text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE &&
+ pStyleProperties->GetWrap() == text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE)
+ {
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_RELATION), sal_Int16(
+ rAppendContext.pLastParagraphProperties->GetvAnchor() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GetvAnchor() :
+ pStyleProperties->GetvAnchor() >= 0 ? pStyleProperties->GetvAnchor() : text::RelOrientation::FRAME)));
+ }
+ else
+ {
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_RELATION), sal_Int16(
+ rAppendContext.pLastParagraphProperties->GetvAnchor() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GetvAnchor() :
+ pStyleProperties->GetvAnchor() >= 0 ? pStyleProperties->GetvAnchor() : text::RelOrientation::PAGE_PRINT_AREA)));
+ }
+
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SURROUND),
+ rAppendContext.pLastParagraphProperties->GetWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
+ ? rAppendContext.pLastParagraphProperties->GetWrap()
+ : pStyleProperties->GetWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
+ ? pStyleProperties->GetWrap()
+ : text::WrapTextMode_NONE ));
+
+ /** FDO#73546 : distL & distR should be unsigned integers <Ecma 20.4.3.6>
+ Swapped the array elements 11,12 & 13,14 since 11 & 12 are
+ LEFT & RIGHT margins and 13,14 are TOP and BOTTOM margins respectively.
+ */
+ sal_Int32 nRightDist;
+ sal_Int32 nLeftDist = nRightDist =
+ rAppendContext.pLastParagraphProperties->GethSpace() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GethSpace() :
+ pStyleProperties->GethSpace() >= 0 ? pStyleProperties->GethSpace() : 0;
+
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LEFT_MARGIN), nHoriOrient == text::HoriOrientation::LEFT ? 0 : nLeftDist));
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_RIGHT_MARGIN), nHoriOrient == text::HoriOrientation::RIGHT ? 0 : nRightDist));
+
+ sal_Int32 nBottomDist;
+ sal_Int32 nTopDist = nBottomDist =
+ rAppendContext.pLastParagraphProperties->GetvSpace() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GetvSpace() :
+ pStyleProperties->GetvSpace() >= 0 ? pStyleProperties->GetvSpace() : 0;
+
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_TOP_MARGIN), nVertOrient == text::VertOrientation::TOP ? 0 : nTopDist));
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BOTTOM_MARGIN), nVertOrient == text::VertOrientation::BOTTOM ? 0 : nBottomDist));
+ // If there is no fill, the Word default is 100% transparency.
+ // Otherwise CellColorHandler has priority, and this setting
+ // will be ignored.
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BACK_COLOR_TRANSPARENCY), sal_Int32(100)));
+
+ uno::Sequence<beans::PropertyValue> aGrabBag( comphelper::InitPropertySequence({
+ { "ParaFrameProperties", uno::Any(rAppendContext.pLastParagraphProperties->IsFrameMode()) }
+ }));
+ aFrameProperties.push_back(comphelper::makePropertyValue("FrameInteropGrabBag", aGrabBag));
+
+ lcl_MoveBorderPropertiesToFrame(aFrameProperties,
+ rAppendContext.pLastParagraphProperties->GetStartingRange(),
+ rAppendContext.pLastParagraphProperties->GetEndingRange());
+ }
+ else
+ {
+ sal_Int32 nWidth = rAppendContext.pLastParagraphProperties->Getw();
+ bool bAutoWidth = nWidth < 1;
+ if( bAutoWidth )
+ nWidth = DEFAULT_FRAME_MIN_WIDTH;
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH), nWidth));
+
+ sal_Int16 nhRule = sal_Int16(rAppendContext.pLastParagraphProperties->GethRule());
+ if ( nhRule < 0 )
+ {
+ if ( rAppendContext.pLastParagraphProperties->Geth() >= 0 )
+ {
+ // [MS-OE376] Word uses a default value of atLeast for
+ // this attribute when the value of the h attribute is not 0.
+ nhRule = text::SizeType::MIN;
+ }
+ else
+ {
+ nhRule = text::SizeType::VARIABLE;
+ }
+ }
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE), nhRule));
+
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE), bAutoWidth ? text::SizeType::MIN : text::SizeType::FIX));
+
+ sal_Int16 nHoriOrient = sal_Int16(
+ rAppendContext.pLastParagraphProperties->GetxAlign() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GetxAlign() :
+ text::HoriOrientation::NONE );
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT), nHoriOrient));
+
+ sal_Int16 nVertOrient = sal_Int16(
+ rAppendContext.pLastParagraphProperties->GetyAlign() >= 0 ?
+ rAppendContext.pLastParagraphProperties->GetyAlign() :
+ text::VertOrientation::NONE );
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT), nVertOrient));
+
+ sal_Int32 nVertDist = rAppendContext.pLastParagraphProperties->GethSpace();
+ if( nVertDist < 0 )
+ nVertDist = 0;
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LEFT_MARGIN), nVertOrient == text::VertOrientation::TOP ? 0 : nVertDist));
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_RIGHT_MARGIN), nVertOrient == text::VertOrientation::BOTTOM ? 0 : nVertDist));
+
+ sal_Int32 nHoriDist = rAppendContext.pLastParagraphProperties->GetvSpace();
+ if( nHoriDist < 0 )
+ nHoriDist = 0;
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_TOP_MARGIN), nHoriOrient == text::HoriOrientation::LEFT ? 0 : nHoriDist));
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BOTTOM_MARGIN), nHoriOrient == text::HoriOrientation::RIGHT ? 0 : nHoriDist));
+
+ if( rAppendContext.pLastParagraphProperties->Geth() > 0 )
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEIGHT), rAppendContext.pLastParagraphProperties->Geth()));
+
+ if( rAppendContext.pLastParagraphProperties->IsxValid() )
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_POSITION), rAppendContext.pLastParagraphProperties->Getx()));
+
+ if( rAppendContext.pLastParagraphProperties->GethAnchor() >= 0 )
+ aFrameProperties.push_back(comphelper::makePropertyValue("HoriOrientRelation", sal_Int16(rAppendContext.pLastParagraphProperties->GethAnchor())));
+
+ if( rAppendContext.pLastParagraphProperties->IsyValid() )
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_POSITION), rAppendContext.pLastParagraphProperties->Gety()));
+
+ if( rAppendContext.pLastParagraphProperties->GetvAnchor() >= 0 )
+ aFrameProperties.push_back(comphelper::makePropertyValue("VertOrientRelation", sal_Int16(rAppendContext.pLastParagraphProperties->GetvAnchor())));
+
+ if( rAppendContext.pLastParagraphProperties->GetWrap() >= text::WrapTextMode_NONE )
+ aFrameProperties.push_back(comphelper::makePropertyValue("Surround", rAppendContext.pLastParagraphProperties->GetWrap()));
+
+ lcl_MoveBorderPropertiesToFrame(aFrameProperties,
+ rAppendContext.pLastParagraphProperties->GetStartingRange(),
+ rAppendContext.pLastParagraphProperties->GetEndingRange());
+ }
+
+ //frame conversion has to be executed after table conversion
+ RegisterFrameConversion(
+ rAppendContext.pLastParagraphProperties->GetStartingRange(),
+ rAppendContext.pLastParagraphProperties->GetEndingRange(),
+ std::move(aFrameProperties) );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+/// Check if the style or its parent has a list id, recursively.
+static sal_Int32 lcl_getListId(const StyleSheetEntryPtr& rEntry, const StyleSheetTablePtr& rStyleTable, bool & rNumberingFromBaseStyle)
+{
+ const StyleSheetPropertyMap* pEntryProperties = rEntry->pProperties.get();
+ if (!pEntryProperties)
+ return -1;
+
+ sal_Int32 nListId = pEntryProperties->GetListId();
+ // The style itself has a list id.
+ if (nListId >= 0)
+ return nListId;
+
+ // The style has no parent.
+ if (rEntry->sBaseStyleIdentifier.isEmpty())
+ return -1;
+
+ const StyleSheetEntryPtr pParent = rStyleTable->FindStyleSheetByISTD(rEntry->sBaseStyleIdentifier);
+ // No such parent style or loop in the style hierarchy.
+ if (!pParent || pParent == rEntry)
+ return -1;
+
+ rNumberingFromBaseStyle = true;
+
+ return lcl_getListId(pParent, rStyleTable, rNumberingFromBaseStyle);
+}
+
+/// Return the paragraph's list level (from styles, unless pParacontext is provided).
+/// -1 indicates the level is not set anywhere. [In that case, with a numId, use 0 (level 1)]
+/// 9 indicates that numbering should be at body level (aka disabled) - rarely used by MSWord.
+/// 0-8 are the nine valid numbering levels.
+sal_Int16 DomainMapper_Impl::GetListLevel(const StyleSheetEntryPtr& pEntry,
+ const PropertyMapPtr& pParaContext)
+{
+ sal_Int16 nListLevel = -1;
+ if (pParaContext)
+ {
+ // Deliberately ignore inherited PROP_NUMBERING_LEVEL. Only trust StyleSheetEntry for that.
+ std::optional<PropertyMap::Property> aLvl = pParaContext->getProperty(PROP_NUMBERING_LEVEL);
+ if (aLvl)
+ aLvl->second >>= nListLevel;
+
+ if (nListLevel != -1)
+ return nListLevel;
+ }
+
+ if (!pEntry)
+ return -1;
+
+ const StyleSheetPropertyMap* pEntryProperties = pEntry->pProperties.get();
+ if (!pEntryProperties)
+ return -1;
+
+ nListLevel = pEntryProperties->GetListLevel();
+ // The style itself has a list level.
+ if (nListLevel >= 0)
+ return nListLevel;
+
+ // The style has no parent.
+ if (pEntry->sBaseStyleIdentifier.isEmpty())
+ return -1;
+
+ const StyleSheetEntryPtr pParent = GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier);
+ // No such parent style or loop in the style hierarchy.
+ if (!pParent || pParent == pEntry)
+ return -1;
+
+ return GetListLevel(pParent);
+}
+
+void DomainMapper_Impl::ValidateListLevel(const OUString& sStyleIdentifierD)
+{
+ StyleSheetEntryPtr pMyStyle = GetStyleSheetTable()->FindStyleSheetByISTD(sStyleIdentifierD);
+ if (!pMyStyle)
+ return;
+
+ sal_Int8 nListLevel = GetListLevel(pMyStyle);
+ if (nListLevel < 0 || nListLevel >= WW_OUTLINE_MAX)
+ return;
+
+ bool bDummy = false;
+ sal_Int16 nListId = lcl_getListId(pMyStyle, GetStyleSheetTable(), bDummy);
+ if (nListId < 1)
+ return;
+
+ auto const pList(GetListTable()->GetList(nListId));
+ if (!pList)
+ return;
+
+ auto pLevel = pList->GetLevel(nListLevel);
+ if (!pLevel && pList->GetAbstractDefinition())
+ pLevel = pList->GetAbstractDefinition()->GetLevel(nListLevel);
+ if (!pLevel)
+ return;
+
+ if (!pLevel->GetParaStyle())
+ {
+ // First come, first served, and it hasn't been claimed yet, so claim it now.
+ pLevel->SetParaStyle(pMyStyle);
+ }
+ else if (pLevel->GetParaStyle() != pMyStyle)
+ {
+ // This level is already used by another style, so prevent numbering via this style
+ // by setting to body level (9).
+ pMyStyle->pProperties->SetListLevel(WW_OUTLINE_MAX);
+ // WARNING: PROP_NUMBERING_LEVEL is now out of sync with GetListLevel()
+ }
+}
+
+void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, const bool bRemove, const bool bNoNumbering )
+{
+ if (m_bDiscardHeaderFooter)
+ return;
+
+ if (!m_aFieldStack.empty())
+ {
+ FieldContextPtr pFieldContext = m_aFieldStack.back();
+ if (pFieldContext && !pFieldContext->IsCommandCompleted())
+ {
+ std::vector<OUString> aCommandParts = pFieldContext->GetCommandParts();
+ if (!aCommandParts.empty() && aCommandParts[0] == "IF")
+ {
+ // Conditional text field conditions don't support linebreaks in Writer.
+ return;
+ }
+ }
+
+ if (pFieldContext && pFieldContext->IsCommandCompleted())
+ {
+ if (pFieldContext->GetFieldId() == FIELD_IF)
+ {
+ // Conditional text fields can't contain newlines, finish the paragraph later.
+ FieldParagraph aFinish{pPropertyMap, bRemove};
+ pFieldContext->GetParagraphsToFinish().push_back(aFinish);
+ return;
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("finishParagraph");
+#endif
+
+ m_nLastTableCellParagraphDepth = m_nTableCellDepth;
+ ParagraphPropertyMap* pParaContext = dynamic_cast< ParagraphPropertyMap* >( pPropertyMap.get() );
+ if (m_aTextAppendStack.empty())
+ return;
+ TextAppendContext& rAppendContext = m_aTextAppendStack.top();
+ uno::Reference< text::XTextAppend > xTextAppend(rAppendContext.xTextAppend);
+#ifdef DBG_UTIL
+ TagLogger::getInstance().attribute("isTextAppend", sal_uInt32(xTextAppend.is()));
+#endif
+
+ const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetCurrentParaStyleName() );
+ OSL_ENSURE( pEntry, "no style sheet found" );
+ const StyleSheetPropertyMap* pStyleSheetProperties = pEntry ? pEntry->pProperties.get() : nullptr;
+ sal_Int32 nListId = pParaContext ? pParaContext->GetListId() : -1;
+ bool isNumberingViaStyle(false);
+ bool isNumberingViaRule = nListId > -1;
+ if ( !bRemove && pStyleSheetProperties && pParaContext )
+ {
+ bool bNumberingFromBaseStyle = false;
+ if (!isNumberingViaRule)
+ nListId = lcl_getListId(pEntry, GetStyleSheetTable(), bNumberingFromBaseStyle);
+
+ //apply numbering level/style to paragraph if it was set at the style, but only if the paragraph itself
+ //does not specify the numbering
+ sal_Int16 nListLevel = GetListLevel(pEntry, pParaContext);
+ // Undefined listLevel with a valid numId is treated as a first level numbering.
+ if (nListLevel == -1 && nListId > (IsOOXMLImport() ? 0 : -1))
+ nListLevel = 0;
+
+ if (!bNoNumbering && nListLevel >= 0 && nListLevel < 9)
+ pParaContext->Insert( PROP_NUMBERING_LEVEL, uno::Any(nListLevel), false );
+
+ auto const pList(GetListTable()->GetList(nListId));
+ if (pList && !pParaContext->isSet(PROP_NUMBERING_STYLE_NAME))
+ {
+ // ListLevel 9 means Body Level/no numbering.
+ if (bNoNumbering || nListLevel == 9)
+ {
+ pParaContext->Insert(PROP_NUMBERING_STYLE_NAME, uno::Any(OUString()), true);
+ pParaContext->Erase(PROP_NUMBERING_LEVEL);
+ }
+ else if ( !isNumberingViaRule )
+ {
+ isNumberingViaStyle = true;
+ // Since LO7.0/tdf#131321 fixed the loss of numbering in styles, this OUGHT to be obsolete,
+ // but now other new/critical LO7.0 code expects it, and perhaps some corner cases still need it as well.
+ pParaContext->Insert(PROP_NUMBERING_STYLE_NAME, uno::Any(pList->GetStyleName()), true);
+ }
+ else
+ {
+ // we have direct numbering, as well as paragraph-style numbering.
+ // Apply the style if it uses the same list as the direct numbering,
+ // otherwise the directly-applied-to-paragraph status will be lost,
+ // and the priority of the numbering-style-indents will be lowered. tdf#133000
+ bool bDummy;
+ if (nListId == lcl_getListId(pEntry, GetStyleSheetTable(), bDummy))
+ pParaContext->Insert( PROP_NUMBERING_STYLE_NAME, uno::Any(pList->GetStyleName()), true );
+ }
+ }
+
+ if ( isNumberingViaStyle )
+ {
+ // When numbering is defined by the paragraph style, then the para-style indents have priority.
+ // But since import has just copied para-style's PROP_NUMBERING_STYLE_NAME directly onto the paragraph,
+ // the numbering indents now have the priority.
+ // So now import must also copy the para-style indents directly onto the paragraph to compensate.
+ std::optional<PropertyMap::Property> oProperty;
+ const StyleSheetEntryPtr pParent = (!pEntry->sBaseStyleIdentifier.isEmpty()) ? GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier) : nullptr;
+ const StyleSheetPropertyMap* pParentProperties = pParent ? pParent->pProperties.get() : nullptr;
+ if (!pEntry->sBaseStyleIdentifier.isEmpty())
+ {
+ oProperty = pStyleSheetProperties->getProperty(PROP_PARA_FIRST_LINE_INDENT);
+ if ( oProperty
+ // If the numbering comes from a base style, indent of the base style has also priority.
+ || (bNumberingFromBaseStyle && pParentProperties && (oProperty = pParentProperties->getProperty(PROP_PARA_FIRST_LINE_INDENT))) )
+ pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, oProperty->second, /*bOverwrite=*/false);
+ }
+ oProperty = pStyleSheetProperties->getProperty(PROP_PARA_LEFT_MARGIN);
+ if ( oProperty
+ || (bNumberingFromBaseStyle && pParentProperties && (oProperty = pParentProperties->getProperty(PROP_PARA_LEFT_MARGIN))) )
+ pParaContext->Insert(PROP_PARA_LEFT_MARGIN, oProperty->second, /*bOverwrite=*/false);
+
+ // We're inheriting properties from a numbering style. Make sure a possible right margin is inherited from the base style.
+ sal_Int32 nParaRightMargin;
+ if ( pParentProperties && (oProperty = pParentProperties->getProperty(PROP_PARA_RIGHT_MARGIN)) && (nParaRightMargin = oProperty->second.get<sal_Int32>()) != 0 )
+ {
+ // If we're setting the right margin, we should set the first / left margin as well from the numbering style.
+ const sal_Int32 nFirstLineIndent = getNumberingProperty(nListId, nListLevel, "FirstLineIndent");
+ const sal_Int32 nParaLeftMargin = getNumberingProperty(nListId, nListLevel, "IndentAt");
+ if (nFirstLineIndent != 0)
+ pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::Any(nFirstLineIndent), /*bOverwrite=*/false);
+ if (nParaLeftMargin != 0)
+ pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::Any(nParaLeftMargin), /*bOverwrite=*/false);
+
+ // Override right margin value with value from current style, if any
+ if (pStyleSheetProperties && pStyleSheetProperties->isSet(PROP_PARA_RIGHT_MARGIN))
+ nParaRightMargin = pStyleSheetProperties->getProperty(PROP_PARA_RIGHT_MARGIN)->second.get<sal_Int32>();
+
+ pParaContext->Insert(PROP_PARA_RIGHT_MARGIN, uno::Any(nParaRightMargin), /*bOverwrite=*/false);
+ }
+ }
+ // Paragraph style based right paragraph indentation affects not paragraph style based lists in DOCX.
+ // Apply it as direct formatting, also left and first line indentation of numbering to keep them.
+ else if (isNumberingViaRule)
+ {
+ uno::Any aRightMargin = GetPropertyFromParaStyleSheet(PROP_PARA_RIGHT_MARGIN);
+ if ( aRightMargin != uno::Any() )
+ {
+ pParaContext->Insert(PROP_PARA_RIGHT_MARGIN, aRightMargin, /*bOverwrite=*/false);
+
+ const sal_Int32 nFirstLineIndent = getNumberingProperty(nListId, nListLevel, "FirstLineIndent");
+ const sal_Int32 nParaLeftMargin = getNumberingProperty(nListId, nListLevel, "IndentAt");
+ if (nFirstLineIndent != 0)
+ pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::Any(nFirstLineIndent), /*bOverwrite=*/false);
+ if (nParaLeftMargin != 0)
+ pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::Any(nParaLeftMargin), /*bOverwrite=*/false);
+ }
+ }
+
+ if (nListId == 0 && !pList)
+ {
+ // listid = 0 and no list definition is used in DOCX to stop numbering
+ // defined somewhere in parent styles
+ // And here we should explicitly set left margin and first-line margin.
+ // They can be taken from referred style, but not from styles with listid!
+ uno::Any aProp = lcl_GetPropertyFromParaStyleSheetNoNum(PROP_PARA_FIRST_LINE_INDENT, pEntry, m_pStyleSheetTable);
+ if (aProp.hasValue())
+ pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, aProp, false);
+ else
+ pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::Any(sal_uInt32(0)), false);
+
+ aProp = lcl_GetPropertyFromParaStyleSheetNoNum(PROP_PARA_LEFT_MARGIN, pEntry, m_pStyleSheetTable);
+ if (aProp.hasValue())
+ pParaContext->Insert(PROP_PARA_LEFT_MARGIN, aProp, false);
+ else
+ pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::Any(sal_uInt32(0)), false);
+ }
+ }
+
+ // apply AutoSpacing: it has priority over all other margin settings
+ // (note that numbering with autoSpacing is handled separately later on)
+ const bool bAllowAdjustments = !GetSettingsTable()->GetDoNotUseHTMLParagraphAutoSpacing();
+ sal_Int32 nBeforeAutospacing = -1;
+ bool bIsAutoSet = pParaContext && pParaContext->isSet(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING);
+ const bool bNoTopmargin = pParaContext && !pParaContext->isSet(PROP_PARA_TOP_MARGIN);
+ // apply INHERITED autospacing only if top margin is not set
+ if ( bIsAutoSet || bNoTopmargin )
+ {
+ GetAnyProperty(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING, pPropertyMap) >>= nBeforeAutospacing;
+ // tdf#137655 only w:beforeAutospacing=0 was specified, but not PARA_TOP_MARGIN
+ // (see default_spacing = -1 in processing of LN_CT_Spacing_beforeAutospacing)
+ if ( bNoTopmargin && nBeforeAutospacing == ConversionHelper::convertTwipToMM100(-1) )
+ {
+ sal_Int32 nStyleAuto = -1;
+ GetPropertyFromParaStyleSheet(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING) >>= nStyleAuto;
+ if (nStyleAuto > 0)
+ nBeforeAutospacing = 0;
+ }
+ }
+ if ( nBeforeAutospacing > -1 && pParaContext )
+ {
+ if (bAllowAdjustments)
+ {
+ if ( GetIsFirstParagraphInShape() ||
+ (GetIsFirstParagraphInSection() && GetSectionContext() && GetSectionContext()->IsFirstSection()) ||
+ (m_bFirstParagraphInCell && m_nTableDepth > 0 && m_nTableDepth == m_nTableCellDepth) )
+ {
+ // export requires grabbag to match top_margin, so keep them in sync
+ if (nBeforeAutospacing && bIsAutoSet)
+ pParaContext->Insert( PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING, uno::Any( sal_Int32(0) ),true, PARA_GRAB_BAG );
+ nBeforeAutospacing = 0;
+ }
+ }
+ pParaContext->Insert(PROP_PARA_TOP_MARGIN, uno::Any(nBeforeAutospacing));
+ }
+
+ sal_Int32 nAfterAutospacing = -1;
+ bIsAutoSet = pParaContext && pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING);
+ const bool bNoBottomMargin = pParaContext && !pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN);
+ bool bAppliedBottomAutospacing = false;
+ if (bIsAutoSet || bNoBottomMargin)
+ {
+ GetAnyProperty(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING, pPropertyMap) >>= nAfterAutospacing;
+ if (bNoBottomMargin && nAfterAutospacing == ConversionHelper::convertTwipToMM100(-1))
+ {
+ sal_Int32 nStyleAuto = -1;
+ GetPropertyFromParaStyleSheet(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING) >>= nStyleAuto;
+ if (nStyleAuto > 0)
+ nAfterAutospacing = 0;
+ }
+ }
+ if ( nAfterAutospacing > -1 && pParaContext )
+ {
+ pParaContext->Insert(PROP_PARA_BOTTOM_MARGIN, uno::Any(nAfterAutospacing));
+ bAppliedBottomAutospacing = bAllowAdjustments;
+ }
+
+ // tell TableManager to reset the bottom margin if it determines that this is the cell's last paragraph.
+ if ( hasTableManager() && getTableManager().isInCell() )
+ getTableManager().setCellLastParaAfterAutospacing(bAppliedBottomAutospacing);
+
+ if (xTextAppend.is() && pParaContext && hasTableManager() && !getTableManager().isIgnore())
+ {
+ try
+ {
+ /*the following combinations of previous and current frame settings can occur:
+ (1) - no old frame and no current frame -> no special action
+ (2) - no old frame and current DropCap -> save DropCap for later use, don't call finishParagraph
+ remove character properties of the DropCap?
+ (3) - no old frame and current Frame -> save Frame for later use
+ (4) - old DropCap and no current frame -> add DropCap to the properties of the finished paragraph, delete previous setting
+ (5) - old DropCap and current frame -> add DropCap to the properties of the finished paragraph, save current frame settings
+ (6) - old Frame and new DropCap -> add old Frame, save DropCap for later use
+ (7) - old Frame and new same Frame -> continue
+ (8) - old Frame and new different Frame -> add old Frame, save new Frame for later use
+ (9) - old Frame and no current frame -> add old Frame, delete previous settings
+
+ old _and_ new DropCap must not occur
+ */
+
+ bool bIsDropCap =
+ pParaContext->IsFrameMode() &&
+ sal::static_int_cast<Id>(pParaContext->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none;
+
+ style::DropCapFormat aDrop;
+ ParagraphPropertiesPtr pToBeSavedProperties;
+ bool bKeepLastParagraphProperties = false;
+ if( bIsDropCap )
+ {
+ uno::Reference<text::XParagraphCursor> xParaCursor(
+ xTextAppend->createTextCursorByRange(xTextAppend->getEnd()), uno::UNO_QUERY_THROW);
+ //select paragraph
+ xParaCursor->gotoStartOfParagraph( true );
+ uno::Reference< beans::XPropertyState > xParaProperties( xParaCursor, uno::UNO_QUERY_THROW );
+ xParaProperties->setPropertyToDefault(getPropertyName(PROP_CHAR_ESCAPEMENT));
+ xParaProperties->setPropertyToDefault(getPropertyName(PROP_CHAR_HEIGHT));
+ //handles (2) and part of (6)
+ pToBeSavedProperties = new ParagraphProperties(*pParaContext);
+ sal_Int32 nCount = xParaCursor->getString().getLength();
+ pToBeSavedProperties->SetDropCapLength(nCount > 0 && nCount < 255 ? static_cast<sal_Int8>(nCount) : 1);
+ }
+ if( rAppendContext.pLastParagraphProperties )
+ {
+ if( sal::static_int_cast<Id>(rAppendContext.pLastParagraphProperties->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none)
+ {
+ //handles (4) and part of (5)
+ //create a DropCap property, add it to the property sequence of finishParagraph
+ sal_Int32 nLines = rAppendContext.pLastParagraphProperties->GetLines();
+ aDrop.Lines = nLines > 0 && nLines < SAL_MAX_INT8 ? static_cast<sal_Int8>(nLines) : 2;
+ aDrop.Count = rAppendContext.pLastParagraphProperties->GetDropCapLength();
+ sal_Int32 nHSpace = rAppendContext.pLastParagraphProperties->GethSpace();
+ aDrop.Distance = nHSpace > 0 && nHSpace < SAL_MAX_INT16 ? static_cast<sal_Int16>(nHSpace) : 0;
+ //completes (5)
+ if( pParaContext->IsFrameMode() )
+ pToBeSavedProperties = new ParagraphProperties(*pParaContext);
+ }
+ else if(*rAppendContext.pLastParagraphProperties == *pParaContext )
+ {
+ //handles (7)
+ rAppendContext.pLastParagraphProperties->SetEndingRange(rAppendContext.xInsertPosition.is() ? rAppendContext.xInsertPosition : xTextAppend->getEnd());
+ bKeepLastParagraphProperties = true;
+ }
+ else
+ {
+ //handles (8)(9) and completes (6)
+ CheckUnregisteredFrameConversion( );
+
+ // If different frame properties are set on this paragraph, keep them.
+ if ( !bIsDropCap && pParaContext->IsFrameMode() )
+ {
+ pToBeSavedProperties = new ParagraphProperties(*pParaContext);
+ lcl_AddRangeAndStyle(pToBeSavedProperties, xTextAppend, pPropertyMap, rAppendContext);
+ }
+ }
+ }
+ else
+ {
+ // (1) doesn't need handling
+
+ if( !bIsDropCap && pParaContext->IsFrameMode() )
+ {
+ pToBeSavedProperties = new ParagraphProperties(*pParaContext);
+ lcl_AddRangeAndStyle(pToBeSavedProperties, xTextAppend, pPropertyMap, rAppendContext);
+ }
+ }
+ std::vector<beans::PropertyValue> aProperties;
+ if (pPropertyMap)
+ {
+ aProperties = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(pPropertyMap->GetPropertyValues());
+ }
+ if (pPropertyMap)
+ {
+ // tdf#64222 filter out the "paragraph marker" formatting and
+ // set it as a separate paragraph property, not a empty hint at
+ // end of paragraph
+ std::vector<beans::NamedValue> charProperties;
+ for (auto it = aProperties.begin(); it != aProperties.end(); )
+ {
+ // this condition isn't ideal but as it happens all
+ // RES_CHRATR_* have names that start with "Char"
+ if (it->Name.startsWith("Char"))
+ {
+ charProperties.emplace_back(it->Name, it->Value);
+ // as testN793262 demonstrates, font size in rPr must
+ // affect the paragraph size => also insert empty hint!
+// it = aProperties.erase(it);
+ }
+ ++it;
+ }
+ if (!charProperties.empty())
+ {
+ aProperties.push_back(beans::PropertyValue("ListAutoFormat",
+ 0, uno::Any(comphelper::containerToSequence(charProperties)), beans::PropertyState_DIRECT_VALUE));
+ }
+ }
+ if( !bIsDropCap )
+ {
+ if( aDrop.Lines > 1 )
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = getPropertyName(PROP_DROP_CAP_FORMAT);
+ aValue.Value <<= aDrop;
+ aProperties.push_back(aValue);
+ }
+ uno::Reference< text::XTextRange > xTextRange;
+ if (rAppendContext.xInsertPosition.is())
+ {
+ xTextRange = xTextAppend->finishParagraphInsert( comphelper::containerToSequence(aProperties), rAppendContext.xInsertPosition );
+ rAppendContext.xCursor->gotoNextParagraph(false);
+ if (rAppendContext.pLastParagraphProperties)
+ rAppendContext.pLastParagraphProperties->SetEndingRange(xTextRange->getEnd());
+ }
+ else
+ {
+ uno::Reference<text::XTextCursor> xCursor;
+ if (m_bParaHadField && !m_bIsInComments && !xTOCMarkerCursor.is())
+ {
+ // Workaround to make sure char props of the field are not lost.
+ // Not relevant for editeng-based comments.
+ // Not relevant for fields inside a TOC field.
+ xCursor = xTextAppend->getText()->createTextCursor();
+ if (xCursor.is())
+ xCursor->gotoEnd(false);
+ PropertyMapPtr pEmpty(new PropertyMap());
+ appendTextPortion("X", pEmpty);
+ }
+
+ // Check if top / bottom margin has to be updated, now that we know the numbering status of both the previous and
+ // the current text node.
+ auto itNumberingRules = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
+ {
+ return rValue.Name == "NumberingRules";
+ });
+
+ assert( isNumberingViaRule == (itNumberingRules != aProperties.end()) );
+ isNumberingViaRule = (itNumberingRules != aProperties.end());
+ if (m_xPreviousParagraph.is() && (isNumberingViaRule || isNumberingViaStyle))
+ {
+ // This textnode has numbering. Look up the numbering style name of the current and previous paragraph.
+ OUString aCurrentNumberingName;
+ OUString aPreviousNumberingName;
+ if (isNumberingViaRule)
+ {
+ assert(itNumberingRules != aProperties.end() && "by definition itNumberingRules is valid if isNumberingViaRule is true");
+ uno::Reference<container::XNamed> xCurrentNumberingRules(itNumberingRules->Value, uno::UNO_QUERY);
+ if (xCurrentNumberingRules.is())
+ aCurrentNumberingName = xCurrentNumberingRules->getName();
+ if (m_xPreviousParagraph.is())
+ {
+ uno::Reference<container::XNamed> xPreviousNumberingRules(m_xPreviousParagraph->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
+ if (xPreviousNumberingRules.is())
+ aPreviousNumberingName = xPreviousNumberingRules->getName();
+ }
+ }
+ else if ( m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("NumberingStyleName") &&
+ // don't update before tables
+ (m_nTableDepth == 0 || !m_bFirstParagraphInCell))
+ {
+ aCurrentNumberingName = GetListStyleName(nListId);
+ m_xPreviousParagraph->getPropertyValue("NumberingStyleName") >>= aPreviousNumberingName;
+ }
+
+ if (!aPreviousNumberingName.isEmpty() && aCurrentNumberingName == aPreviousNumberingName)
+ {
+ uno::Sequence<beans::PropertyValue> aPrevPropertiesSeq;
+ m_xPreviousParagraph->getPropertyValue("ParaInteropGrabBag") >>= aPrevPropertiesSeq;
+ const auto & rPrevProperties = aPrevPropertiesSeq;
+ bool bParaAutoBefore = m_bParaAutoBefore || std::any_of(rPrevProperties.begin(), rPrevProperties.end(), [](const beans::PropertyValue& rValue)
+ {
+ return rValue.Name == "ParaTopMarginBeforeAutoSpacing";
+ });
+ // if style based spacing was set to auto in the previous paragraph, style of the actual paragraph must be the same
+ if (bParaAutoBefore && !m_bParaAutoBefore && m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("ParaStyleName"))
+ {
+ auto itParaStyle = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
+ {
+ return rValue.Name == "ParaStyleName";
+ });
+ bParaAutoBefore = itParaStyle != aProperties.end() &&
+ m_xPreviousParagraph->getPropertyValue("ParaStyleName") == itParaStyle->Value;
+ }
+ // There was a previous textnode and it had the same numbering.
+ if (bParaAutoBefore)
+ {
+ // This before spacing is set to auto, set before space to 0.
+ auto itParaTopMargin = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
+ {
+ return rValue.Name == "ParaTopMargin";
+ });
+ if (itParaTopMargin != aProperties.end())
+ itParaTopMargin->Value <<= static_cast<sal_Int32>(0);
+ else
+ aProperties.push_back(comphelper::makePropertyValue("ParaTopMargin", static_cast<sal_Int32>(0)));
+ }
+
+ bool bPrevParaAutoAfter = std::any_of(rPrevProperties.begin(), rPrevProperties.end(), [](const beans::PropertyValue& rValue)
+ {
+ return rValue.Name == "ParaBottomMarginAfterAutoSpacing";
+ });
+ if (bPrevParaAutoAfter)
+ {
+ // Previous after spacing is set to auto, set previous after space to 0.
+ m_xPreviousParagraph->setPropertyValue("ParaBottomMargin", uno::Any(static_cast<sal_Int32>(0)));
+ }
+ }
+ }
+
+ // apply redlines for inline images
+ if (IsParaWithInlineObject())
+ {
+ for (const auto& rAnchored : rAppendContext.m_aAnchoredObjects)
+ {
+ // process only inline objects with redlining
+ if (!rAnchored.m_xRedlineForInline)
+ continue;
+
+ // select the inline image and set its redline
+ auto xAnchorRange = rAnchored.m_xAnchoredObject->getAnchor();
+ uno::Reference< text::XTextCursor > xCursorOnImage =
+ xAnchorRange->getText()->createTextCursorByRange(xAnchorRange);
+ xCursorOnImage->goRight(1, true);
+ CreateRedline( xCursorOnImage, rAnchored.m_xRedlineForInline );
+ }
+ }
+
+ xTextRange = xTextAppend->finishParagraph( comphelper::containerToSequence(aProperties) );
+ m_xPreviousParagraph.set(xTextRange, uno::UNO_QUERY);
+
+ if (m_xPreviousParagraph.is() && // null for SvxUnoTextBase
+ (isNumberingViaStyle || isNumberingViaRule))
+ {
+ assert(pParaContext);
+ if (ListDef::Pointer const& pList = m_pListTable->GetList(nListId))
+ { // styles could refer to non-existing lists...
+ AbstractListDef::Pointer const& pAbsList =
+ pList->GetAbstractDefinition();
+ if (pAbsList &&
+ // SvxUnoTextRange doesn't have ListId
+ m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("ListId"))
+ {
+ OUString paraId;
+ m_xPreviousParagraph->getPropertyValue("ListId") >>= paraId;
+ if (!paraId.isEmpty()) // must be on some list?
+ {
+ OUString const listId = pAbsList->MapListId(paraId);
+ if (listId != paraId)
+ {
+ m_xPreviousParagraph->setPropertyValue("ListId", uno::Any(listId));
+ }
+ }
+ }
+
+ sal_Int16 nCurrentLevel = GetListLevel(pEntry, pPropertyMap);
+ if (nCurrentLevel == -1)
+ nCurrentLevel = 0;
+
+ const ListLevel::Pointer pListLevel = pList->GetLevel(nCurrentLevel);
+ if (pListLevel)
+ {
+ sal_Int16 nOverrideLevel = pListLevel->GetStartOverride();
+ if (nOverrideLevel != -1 && m_aListOverrideApplied.find(nListId) == m_aListOverrideApplied.end())
+ {
+ // Apply override: we have override instruction for this level
+ // And this was not done for this list before: we can do this only once on first occurrence
+ // of list with override
+ // TODO: Not tested variant with different levels override in different lists.
+ // Probably m_aListOverrideApplied as a set of overridden listids is not sufficient
+ // and we need to register level overrides separately.
+ m_xPreviousParagraph->setPropertyValue("ParaIsNumberingRestart", uno::Any(true));
+ m_xPreviousParagraph->setPropertyValue("NumberingStartValue", uno::Any(nOverrideLevel));
+ m_aListOverrideApplied.insert(nListId);
+ }
+ }
+ }
+ }
+
+ if (!rAppendContext.m_aAnchoredObjects.empty() && !IsInHeaderFooter())
+ {
+ // Remember what objects are anchored to this paragraph.
+ // That list is only used for Word compat purposes, and
+ // it is only relevant for body text.
+ AnchoredObjectsInfo aInfo;
+ aInfo.m_xParagraph = xTextRange;
+ aInfo.m_aAnchoredObjects = rAppendContext.m_aAnchoredObjects;
+ m_aAnchoredObjectAnchors.push_back(aInfo);
+ rAppendContext.m_aAnchoredObjects.clear();
+ }
+
+ // We're no longer right after a table conversion.
+ m_bConvertedTable = false;
+
+ if (xCursor.is())
+ {
+ xCursor->goLeft(1, true);
+ xCursor->setString(OUString());
+ }
+ }
+ getTableManager( ).handle(xTextRange);
+ m_aSmartTagHandler.handle(xTextRange);
+
+ if (xTextRange.is())
+ {
+ // Get the end of paragraph character inserted
+ uno::Reference< text::XTextCursor > xCur = xTextRange->getText( )->createTextCursor( );
+ if (rAppendContext.xInsertPosition.is())
+ xCur->gotoRange( rAppendContext.xInsertPosition, false );
+ else
+ xCur->gotoEnd( false );
+
+ // tdf#77417 trim right white spaces in table cells in 2010 compatibility mode
+ sal_Int32 nMode = GetSettingsTable()->GetWordCompatibilityMode();
+ if ( m_nTableDepth > 0 && nMode > 0 && nMode <= 14 )
+ {
+ // skip new line
+ xCur->goLeft(1, false);
+ while ( xCur->goLeft(1, true) )
+ {
+ OUString sChar = xCur->getString();
+ if ( sChar == " " || sChar == "\t" || sChar == OUStringChar(u'\x00A0') )
+ xCur->setString("");
+ else
+ break;
+ }
+
+ if (rAppendContext.xInsertPosition.is())
+ xCur->gotoRange(rAppendContext.xInsertPosition, false);
+ else
+ xCur->gotoEnd(false);
+ }
+
+ xCur->goLeft( 1 , true );
+ // Extend the redline ranges for empty paragraphs
+ if ( !m_bParaChanged && m_previousRedline )
+ CreateRedline( xCur, m_previousRedline );
+ CheckParaMarkerRedline( xCur );
+ }
+
+ css::uno::Reference<css::beans::XPropertySet> xParaProps(xTextRange, uno::UNO_QUERY);
+
+ // table style precedence and not hidden shapes anchored to hidden empty table paragraphs
+ if (xParaProps && (m_nTableDepth > 0 || !m_aAnchoredObjectAnchors.empty()) )
+ {
+ // table style has got bigger precedence than docDefault style
+ // collect these pending paragraph properties to process in endTable()
+ uno::Reference<text::XTextCursor> xCur = xTextRange->getText( )->createTextCursor( );
+ xCur->gotoEnd(false);
+ xCur->goLeft(1, false);
+ uno::Reference<text::XTextCursor> xCur2 = xTextRange->getText()->createTextCursorByRange(xCur);
+ uno::Reference<text::XParagraphCursor> xParaCursor(xCur2, uno::UNO_QUERY_THROW);
+ xParaCursor->gotoStartOfParagraph(false);
+ if (m_nTableDepth > 0)
+ {
+ TableParagraph aPending{xParaCursor, xCur, pParaContext, xParaProps};
+ getTableManager().getCurrentParagraphs()->push_back(aPending);
+ }
+
+ // hidden empty paragraph with a not hidden shape, set as not hidden
+ std::optional<PropertyMap::Property> pHidden;
+ if ( !m_aAnchoredObjectAnchors.empty() && (pHidden = pParaContext->getProperty(PROP_CHAR_HIDDEN)) )
+ {
+ bool bIsHidden = {}; // -Werror=maybe-uninitialized
+ pHidden->second >>= bIsHidden;
+ if (bIsHidden)
+ {
+ bIsHidden = false;
+ pHidden = GetTopContext()->getProperty(PROP_CHAR_HIDDEN);
+ if (pHidden)
+ pHidden->second >>= bIsHidden;
+ if (!bIsHidden)
+ {
+ uno::Reference<text::XTextCursor> xCur3 = xTextRange->getText()->createTextCursorByRange(xParaCursor);
+ xCur3->goRight(1, true);
+ if (xCur3->getString() == SAL_NEWLINE_STRING)
+ {
+ uno::Reference< beans::XPropertySet > xProp( xCur3, uno::UNO_QUERY );
+ xProp->setPropertyValue(getPropertyName(PROP_CHAR_HIDDEN), uno::Any(false));
+ }
+ }
+ }
+ }
+ }
+
+ // tdf#118521 set paragraph top or bottom margin based on the paragraph style
+ // if we already set the other margin with direct formatting
+ if (xParaProps)
+ {
+ const bool bTopSet = pParaContext->isSet(PROP_PARA_TOP_MARGIN);
+ const bool bBottomSet = pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN);
+ const bool bContextSet = pParaContext->isSet(PROP_PARA_CONTEXT_MARGIN);
+ if ( bTopSet != bBottomSet || bBottomSet != bContextSet )
+ {
+
+ if ( !bTopSet )
+ {
+ uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_TOP_MARGIN);
+ if ( aMargin != uno::Any() )
+ xParaProps->setPropertyValue("ParaTopMargin", aMargin);
+ }
+ if ( !bBottomSet )
+ {
+ uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_BOTTOM_MARGIN);
+ if ( aMargin != uno::Any() )
+ xParaProps->setPropertyValue("ParaBottomMargin", aMargin);
+ }
+ if ( !bContextSet )
+ {
+ uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_CONTEXT_MARGIN);
+ if ( aMargin != uno::Any() )
+ xParaProps->setPropertyValue("ParaContextMargin", aMargin);
+ }
+ }
+ }
+
+ // Left, Right, and Hanging settings are also grouped. Ensure that all or none are set.
+ if (xParaProps)
+ {
+ const bool bLeftSet = pParaContext->isSet(PROP_PARA_LEFT_MARGIN);
+ const bool bRightSet = pParaContext->isSet(PROP_PARA_RIGHT_MARGIN);
+ const bool bFirstSet = pParaContext->isSet(PROP_PARA_FIRST_LINE_INDENT);
+ if (bLeftSet != bRightSet || bRightSet != bFirstSet)
+ {
+ if ( !bLeftSet )
+ {
+ uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_LEFT_MARGIN);
+ if ( aMargin != uno::Any() )
+ xParaProps->setPropertyValue("ParaLeftMargin", aMargin);
+ else if (isNumberingViaStyle)
+ {
+ const sal_Int32 nParaLeftMargin = getNumberingProperty(nListId, GetListLevel(pEntry, pPropertyMap), "IndentAt");
+ if (nParaLeftMargin != 0)
+ xParaProps->setPropertyValue("ParaLeftMargin", uno::Any(nParaLeftMargin));
+ }
+ }
+ if ( !bRightSet )
+ {
+ uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_RIGHT_MARGIN);
+ if ( aMargin != uno::Any() )
+ xParaProps->setPropertyValue("ParaRightMargin", aMargin);
+ }
+ if ( !bFirstSet )
+ {
+ uno::Any aMargin = GetPropertyFromParaStyleSheet(PROP_PARA_FIRST_LINE_INDENT);
+ if ( aMargin != uno::Any() )
+ xParaProps->setPropertyValue("ParaFirstLineIndent", aMargin);
+ else if (isNumberingViaStyle)
+ {
+ const sal_Int32 nFirstLineIndent = getNumberingProperty(nListId, GetListLevel(pEntry, pPropertyMap), "FirstLineIndent");
+ if (nFirstLineIndent != 0)
+ xParaProps->setPropertyValue("ParaFirstLineIndent", uno::Any(nFirstLineIndent));
+ }
+ }
+ }
+ }
+
+ // fix table paragraph properties
+ if ( xTextRange.is() && xParaProps && m_nTableDepth > 0 )
+ {
+ // tdf#128959 table paragraphs haven't got window and orphan controls
+ uno::Any aAny(static_cast<sal_Int8>(0));
+ xParaProps->setPropertyValue("ParaOrphans", aAny);
+ xParaProps->setPropertyValue("ParaWidows", aAny);
+ }
+ }
+ if( !bKeepLastParagraphProperties )
+ rAppendContext.pLastParagraphProperties = pToBeSavedProperties;
+ }
+ catch(const lang::IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "DomainMapper_Impl::finishParagraph" );
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "finishParagraph()" );
+ }
+
+ }
+
+ bool bIgnoreFrameState = IsInHeaderFooter();
+ if( (!bIgnoreFrameState && pParaContext && pParaContext->IsFrameMode()) || (bIgnoreFrameState && GetIsPreviousParagraphFramed()) )
+ SetIsPreviousParagraphFramed(true);
+ else
+ SetIsPreviousParagraphFramed(false);
+
+ m_bRemoveThisParagraph = false;
+ if( !IsInHeaderFooter() && !IsInShape() && (!pParaContext || !pParaContext->IsFrameMode()) )
+ { // If the paragraph is in a frame, shape or header/footer, it's not a paragraph of the section itself.
+ SetIsFirstParagraphInSection(false);
+ // don't count an empty deleted paragraph as first paragraph in section to avoid of
+ // the deletion of the next empty paragraph later, resulting loss of the associated page break
+ if (!m_previousRedline || m_bParaChanged)
+ {
+ SetIsFirstParagraphInSectionAfterRedline(false);
+ SetIsLastParagraphInSection(false);
+ }
+ }
+ m_previousRedline.clear();
+ m_bParaChanged = false;
+
+ if (m_bIsInComments && pParaContext)
+ {
+ if (const OUString sParaId = pParaContext->GetParaId(); !sParaId.isEmpty())
+ {
+ if (const auto& item = m_aCommentProps.find(sParaId); item != m_aCommentProps.end())
+ {
+ m_bAnnotationResolved = item->second.bDone;
+ }
+ }
+ }
+
+ if (m_bIsFirstParaInShape)
+ m_bIsFirstParaInShape = false;
+
+ if (pParaContext)
+ {
+ // Reset the frame properties for the next paragraph
+ pParaContext->ResetFrameProperties();
+ }
+
+ SetIsOutsideAParagraph(true);
+ m_bParaHadField = false;
+
+ // don't overwrite m_bFirstParagraphInCell in table separator nodes
+ // and in text boxes anchored to the first paragraph of table cells
+ if (m_nTableDepth > 0 && m_nTableDepth == m_nTableCellDepth && !IsInShape())
+ m_bFirstParagraphInCell = false;
+
+ m_bParaAutoBefore = false;
+ m_bParaWithInlineObject = false;
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+
+}
+
+void DomainMapper_Impl::appendTextPortion( const OUString& rString, const PropertyMapPtr& pPropertyMap )
+{
+ if (m_bDiscardHeaderFooter)
+ return;
+
+ if (m_aTextAppendStack.empty())
+ return;
+ // Before placing call to processDeferredCharacterProperties(), TopContextType should be CONTEXT_CHARACTER
+ // processDeferredCharacterProperties() invokes only if character inserted
+ if( pPropertyMap == m_pTopContext && !deferredCharacterProperties.empty() && (GetTopContextType() == CONTEXT_CHARACTER) )
+ processDeferredCharacterProperties();
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (!xTextAppend.is() || !hasTableManager() || getTableManager().isIgnore())
+ return;
+
+ try
+ {
+ // If we are in comments, then disable CharGrabBag, comment text doesn't support that.
+ uno::Sequence< beans::PropertyValue > aValues = pPropertyMap->GetPropertyValues(/*bCharGrabBag=*/!m_bIsInComments);
+
+ if (m_bStartTOC || m_bStartIndex || m_bStartBibliography)
+ for( auto& rValue : asNonConstRange(aValues) )
+ {
+ if (rValue.Name == "CharHidden")
+ rValue.Value <<= false;
+ }
+
+ // remove workaround for change tracked images, if they are part of a redline,
+ // i.e. if the next run is a tracked change with the same type, author and date,
+ // as in the change tracking of the image.
+ if ( m_bRedlineImageInPreviousRun )
+ {
+ auto pCurrentRedline = m_aRedlines.top().size() > 0
+ ? m_aRedlines.top().back()
+ : GetTopContextOfType(CONTEXT_CHARACTER) &&
+ GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().size() > 0
+ ? GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().back()
+ : nullptr;
+ if ( m_previousRedline && pCurrentRedline &&
+ // same redline
+ (m_previousRedline->m_nToken & 0xffff) == (pCurrentRedline->m_nToken & 0xffff) &&
+ m_previousRedline->m_sAuthor == pCurrentRedline->m_sAuthor &&
+ m_previousRedline->m_sDate == pCurrentRedline->m_sDate )
+ {
+ uno::Reference< text::XTextCursor > xCursor = xTextAppend->getEnd()->getText( )->createTextCursor( );
+ assert(xCursor.is());
+ xCursor->gotoEnd(false);
+ xCursor->goLeft(2, true);
+ if ( xCursor->getString() == u"​​" )
+ {
+ xCursor->goRight(1, true);
+ xCursor->setString("");
+ xCursor->gotoEnd(false);
+ xCursor->goLeft(1, true);
+ xCursor->setString("");
+ }
+ }
+
+ m_bRedlineImageInPreviousRun = false;
+ }
+
+ uno::Reference< text::XTextRange > xTextRange;
+ if (m_aTextAppendStack.top().xInsertPosition.is())
+ {
+ xTextRange = xTextAppend->insertTextPortion(rString, aValues, m_aTextAppendStack.top().xInsertPosition);
+ m_aTextAppendStack.top().xCursor->gotoRange(xTextRange->getEnd(), true);
+ }
+ else
+ {
+ if (m_bStartTOC || m_bStartIndex || m_bStartBibliography || m_nStartGenericField != 0)
+ {
+ if (IsInHeaderFooter() && !m_bStartTOCHeaderFooter)
+ {
+ xTextRange = xTextAppend->appendTextPortion(rString, aValues);
+ }
+ else
+ {
+ m_bStartedTOC = true;
+ uno::Reference< text::XTextCursor > xTOCTextCursor = xTextAppend->getEnd()->getText( )->createTextCursor( );
+ assert(xTOCTextCursor.is());
+ xTOCTextCursor->gotoEnd(false);
+ if (m_nStartGenericField != 0)
+ {
+ xTOCTextCursor->goLeft(1, false);
+ }
+ xTextRange = xTextAppend->insertTextPortion(rString, aValues, xTOCTextCursor);
+ SAL_WARN_IF(!xTextRange.is(), "writerfilter.dmapper", "insertTextPortion failed");
+ if (!xTextRange.is())
+ throw uno::Exception("insertTextPortion failed", nullptr);
+ m_bTextInserted = true;
+ xTOCTextCursor->gotoRange(xTextRange->getEnd(), true);
+ if (m_nStartGenericField == 0)
+ {
+ m_aTextAppendStack.push(TextAppendContext(xTextAppend, xTOCTextCursor));
+ }
+ }
+ }
+ else
+ {
+#if !defined(MACOSX) // TODO: check layout differences and support all platforms, if needed
+ sal_Int32 nPos = 0;
+ OUString sFontName;
+ OUString sDoubleSpace(" ");
+ PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_CHARACTER);
+ // tdf#123703 workaround for longer space sequences of the old or compatible RTF documents
+ if (GetSettingsTable()->GetLongerSpaceSequence() && !IsOpenFieldCommand() && (nPos = rString.indexOf(sDoubleSpace)) != -1 &&
+ // monospaced fonts have no longer space sequences, regardless of \fprq2 (not monospaced) font setting
+ // fix for the base monospaced font Courier
+ (!pContext || !pContext->isSet(PROP_CHAR_FONT_NAME) ||
+ ((pContext->getProperty(PROP_CHAR_FONT_NAME)->second >>= sFontName) && sFontName.indexOf("Courier") == -1)))
+ {
+ // an RTF space character is longer by an extra six-em-space in an old-style RTF space sequence,
+ // insert them to keep RTF document layout formatted by consecutive spaces
+ const sal_Unicode aExtraSpace[5] = { 0x2006, 0x20, 0x2006, 0x20, 0 };
+ const sal_Unicode aExtraSpace2[4] = { 0x20, 0x2006, 0x20, 0 };
+ xTextRange = xTextAppend->appendTextPortion(rString.replaceAll(sDoubleSpace, aExtraSpace, nPos)
+ .replaceAll(sDoubleSpace, aExtraSpace2, nPos), aValues);
+ }
+ else
+#endif
+ xTextRange = xTextAppend->appendTextPortion(rString, aValues);
+ }
+ }
+
+ // reset moveFrom/moveTo data of non-terminating runs of the paragraph
+ if ( m_pParaMarkerRedlineMove )
+ {
+ m_pParaMarkerRedlineMove.clear();
+ }
+ CheckRedline( xTextRange );
+ m_bParaChanged = true;
+
+ //getTableManager( ).handle(xTextRange);
+ }
+ catch(const lang::IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "DomainMapper_Impl::appendTextPortion" );
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "DomainMapper_Impl::appendTextPortion" );
+ }
+}
+
+void DomainMapper_Impl::appendTextContent(
+ const uno::Reference< text::XTextContent >& xContent,
+ const uno::Sequence< beans::PropertyValue >& xPropertyValues
+ )
+{
+ SAL_WARN_IF(m_aTextAppendStack.empty(), "writerfilter.dmapper", "no text append stack");
+ if (m_aTextAppendStack.empty())
+ return;
+ uno::Reference< text::XTextAppendAndConvert > xTextAppendAndConvert( m_aTextAppendStack.top().xTextAppend, uno::UNO_QUERY );
+ OSL_ENSURE( xTextAppendAndConvert.is(), "trying to append a text content without XTextAppendAndConvert" );
+ if (!xTextAppendAndConvert.is() || !hasTableManager() || getTableManager().isIgnore())
+ return;
+
+ try
+ {
+ if (m_aTextAppendStack.top().xInsertPosition.is())
+ xTextAppendAndConvert->insertTextContentWithProperties( xContent, xPropertyValues, m_aTextAppendStack.top().xInsertPosition );
+ else
+ xTextAppendAndConvert->appendTextContent( xContent, xPropertyValues );
+ }
+ catch(const lang::IllegalArgumentException&)
+ {
+ }
+ catch(const uno::Exception&)
+ {
+ }
+}
+
+void DomainMapper_Impl::appendOLE( const OUString& rStreamName, const std::shared_ptr<OLEHandler>& pOLEHandler )
+{
+ try
+ {
+ uno::Reference< text::XTextContent > xOLE( m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xOLEProperties(xOLE, uno::UNO_QUERY_THROW);
+
+ OUString aCLSID = pOLEHandler->getCLSID();
+ if (aCLSID.isEmpty())
+ xOLEProperties->setPropertyValue(getPropertyName( PROP_STREAM_NAME ),
+ uno::Any( rStreamName ));
+ else
+ xOLEProperties->setPropertyValue("CLSID", uno::Any(aCLSID));
+
+ OUString aDrawAspect = pOLEHandler->GetDrawAspect();
+ if(!aDrawAspect.isEmpty())
+ xOLEProperties->setPropertyValue("DrawAspect", uno::Any(aDrawAspect));
+
+ awt::Size aSize = pOLEHandler->getSize();
+ if( !aSize.Width )
+ aSize.Width = 1000;
+ if( !aSize.Height )
+ aSize.Height = 1000;
+ xOLEProperties->setPropertyValue(getPropertyName( PROP_WIDTH ),
+ uno::Any(aSize.Width));
+ xOLEProperties->setPropertyValue(getPropertyName( PROP_HEIGHT ),
+ uno::Any(aSize.Height));
+
+ OUString aVisAreaWidth = pOLEHandler->GetVisAreaWidth();
+ if(!aVisAreaWidth.isEmpty())
+ xOLEProperties->setPropertyValue("VisibleAreaWidth", uno::Any(aVisAreaWidth));
+
+ OUString aVisAreaHeight = pOLEHandler->GetVisAreaHeight();
+ if(!aVisAreaHeight.isEmpty())
+ xOLEProperties->setPropertyValue("VisibleAreaHeight", uno::Any(aVisAreaHeight));
+
+ uno::Reference< graphic::XGraphic > xGraphic = pOLEHandler->getReplacement();
+ xOLEProperties->setPropertyValue(getPropertyName( PROP_GRAPHIC ),
+ uno::Any(xGraphic));
+ uno::Reference<beans::XPropertySet> xReplacementProperties(pOLEHandler->getShape(), uno::UNO_QUERY);
+ if (xReplacementProperties.is())
+ {
+ table::BorderLine2 aBorderProps;
+ xReplacementProperties->getPropertyValue("LineColor") >>= aBorderProps.Color;
+ xReplacementProperties->getPropertyValue("LineWidth") >>= aBorderProps.LineWidth;
+ xReplacementProperties->getPropertyValue("LineStyle") >>= aBorderProps.LineStyle;
+
+ if (aBorderProps.LineStyle) // Set line props only if LineStyle is set
+ {
+ xOLEProperties->setPropertyValue("RightBorder", uno::Any(aBorderProps));
+ xOLEProperties->setPropertyValue("TopBorder", uno::Any(aBorderProps));
+ xOLEProperties->setPropertyValue("LeftBorder", uno::Any(aBorderProps));
+ xOLEProperties->setPropertyValue("BottomBorder", uno::Any(aBorderProps));
+ }
+ OUString pProperties[] = {
+ "AnchorType",
+ "Surround",
+ "SurroundContour",
+ "HoriOrient",
+ "HoriOrientPosition",
+ "VertOrient",
+ "VertOrientPosition",
+ "VertOrientRelation",
+ "HoriOrientRelation",
+ "LeftMargin",
+ "RightMargin",
+ "TopMargin",
+ "BottomMargin"
+ };
+ for (const OUString& s : pProperties)
+ {
+ const uno::Any aVal = xReplacementProperties->getPropertyValue(s);
+ xOLEProperties->setPropertyValue(s, aVal);
+ }
+
+ if (xReplacementProperties->getPropertyValue("FillStyle").get<css::drawing::FillStyle>()
+ != css::drawing::FillStyle::FillStyle_NONE) // Apply fill props if style is set
+ {
+ xOLEProperties->setPropertyValue(
+ "FillStyle", xReplacementProperties->getPropertyValue("FillStyle"));
+ xOLEProperties->setPropertyValue(
+ "FillColor", xReplacementProperties->getPropertyValue("FillColor"));
+ xOLEProperties->setPropertyValue(
+ "FillColor2", xReplacementProperties->getPropertyValue("FillColor2"));
+ }
+ }
+ else
+ // mimic the treatment of graphics here... it seems anchoring as character
+ // gives a better ( visually ) result
+ xOLEProperties->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE ), uno::Any( text::TextContentAnchorType_AS_CHARACTER ) );
+ // remove ( if valid ) associated shape ( used for graphic replacement )
+ SAL_WARN_IF(m_aAnchoredStack.empty(), "writerfilter.dmapper", "no anchor stack");
+ if (!m_aAnchoredStack.empty())
+ m_aAnchoredStack.top( ).bToRemove = true;
+ RemoveLastParagraph();
+ SAL_WARN_IF(m_aTextAppendStack.empty(), "writerfilter.dmapper", "no text stack");
+ if (!m_aTextAppendStack.empty())
+ m_aTextAppendStack.pop();
+
+ appendTextContent( xOLE, uno::Sequence< beans::PropertyValue >() );
+
+ if (!aCLSID.isEmpty())
+ pOLEHandler->importStream(m_xComponentContext, GetTextDocument(), xOLE);
+
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "in creation of OLE object" );
+ }
+
+}
+
+void DomainMapper_Impl::appendStarMath( const Value& val )
+{
+ uno::Reference< embed::XEmbeddedObject > formula;
+ val.getAny() >>= formula;
+ if( !formula.is() )
+ return;
+
+ try
+ {
+ uno::Reference< text::XTextContent > xStarMath( m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xStarMathProperties(xStarMath, uno::UNO_QUERY_THROW);
+
+ xStarMathProperties->setPropertyValue(getPropertyName( PROP_EMBEDDED_OBJECT ),
+ val.getAny());
+ // tdf#66405: set zero margins for embedded object
+ xStarMathProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ),
+ uno::Any(sal_Int32(0)));
+ xStarMathProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ),
+ uno::Any(sal_Int32(0)));
+ xStarMathProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ),
+ uno::Any(sal_Int32(0)));
+ xStarMathProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ),
+ uno::Any(sal_Int32(0)));
+
+ uno::Reference< uno::XInterface > xInterface( formula->getComponent(), uno::UNO_QUERY );
+ // set zero margins for object's component
+ uno::Reference< beans::XPropertySet > xComponentProperties( xInterface, uno::UNO_QUERY_THROW );
+ xComponentProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ),
+ uno::Any(sal_Int32(0)));
+ xComponentProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ),
+ uno::Any(sal_Int32(0)));
+ xComponentProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ),
+ uno::Any(sal_Int32(0)));
+ xComponentProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ),
+ uno::Any(sal_Int32(0)));
+ Size size( 1000, 1000 );
+ if( oox::FormulaImportBase* formulaimport = dynamic_cast< oox::FormulaImportBase* >( xInterface.get()))
+ size = formulaimport->getFormulaSize();
+ xStarMathProperties->setPropertyValue(getPropertyName( PROP_WIDTH ),
+ uno::Any( sal_Int32(size.Width())));
+ xStarMathProperties->setPropertyValue(getPropertyName( PROP_HEIGHT ),
+ uno::Any( sal_Int32(size.Height())));
+ xStarMathProperties->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE),
+ uno::Any(text::TextContentAnchorType_AS_CHARACTER));
+ // mimic the treatment of graphics here... it seems anchoring as character
+ // gives a better ( visually ) result
+ appendTextContent(xStarMath, uno::Sequence<beans::PropertyValue>());
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "in creation of StarMath object" );
+ }
+}
+
+void DomainMapper_Impl::adjustLastPara(sal_Int8 nAlign)
+{
+ PropertyMapPtr pLastPara = GetTopContextOfType(dmapper::CONTEXT_PARAGRAPH);
+ pLastPara->Insert(PROP_PARA_ADJUST, uno::Any(nAlign), true);
+}
+
+uno::Reference< beans::XPropertySet > DomainMapper_Impl::appendTextSectionAfter(
+ uno::Reference< text::XTextRange > const & xBefore )
+{
+ uno::Reference< beans::XPropertySet > xRet;
+ if (m_aTextAppendStack.empty())
+ return xRet;
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if(xTextAppend.is())
+ {
+ try
+ {
+ uno::Reference< text::XParagraphCursor > xCursor(
+ xTextAppend->createTextCursorByRange( xBefore ), uno::UNO_QUERY_THROW);
+ //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
+ xCursor->gotoStartOfParagraph( false );
+ if (m_aTextAppendStack.top().xInsertPosition.is())
+ xCursor->gotoRange( m_aTextAppendStack.top().xInsertPosition, true );
+ else
+ xCursor->gotoEnd( true );
+ //the paragraph after this new section is already inserted
+ xCursor->goLeft(1, true);
+ css::uno::Reference<css::text::XTextRange> xTextRange(xCursor, css::uno::UNO_QUERY_THROW);
+
+ if (css::uno::Reference<css::text::XDocumentIndexesSupplier> xIndexSupplier{
+ GetTextDocument(), css::uno::UNO_QUERY })
+ {
+ css::uno::Reference<css::text::XTextRangeCompare> xCompare(
+ xTextAppend, css::uno::UNO_QUERY);
+ const auto xIndexAccess = xIndexSupplier->getDocumentIndexes();
+ for (sal_Int32 i = xIndexAccess->getCount(); i > 0; --i)
+ {
+ if (css::uno::Reference<css::text::XDocumentIndex> xIndex{
+ xIndexAccess->getByIndex(i - 1), css::uno::UNO_QUERY })
+ {
+ const auto xIndexTextRange = xIndex->getAnchor();
+ if (xCompare->compareRegionStarts(xTextRange, xIndexTextRange) == 0
+ && xCompare->compareRegionEnds(xTextRange, xIndexTextRange) == 0)
+ {
+ // The boundaries coincide with an index: trying to attach a section
+ // to the range will insert the section inside the index. goRight will
+ // extend the range outside of the index, so that created section will
+ // be around it. Alternatively we could return index section itself
+ // instead : xRet.set(xIndex, uno::UNO_QUERY) - to set its properties,
+ // like columns/fill.
+ xCursor->goRight(1, true);
+ break;
+ }
+ }
+ }
+ }
+
+ uno::Reference< text::XTextContent > xSection( m_xTextFactory->createInstance("com.sun.star.text.TextSection"), uno::UNO_QUERY_THROW );
+ xSection->attach( xTextRange );
+ xRet.set(xSection, uno::UNO_QUERY );
+ }
+ catch(const uno::Exception&)
+ {
+ }
+
+ }
+
+ return xRet;
+}
+
+void DomainMapper_Impl::appendGlossaryEntry()
+{
+ appendTextSectionAfter(m_xGlossaryEntryStart);
+}
+
+void DomainMapper_Impl::fillEmptyFrameProperties(std::vector<beans::PropertyValue>& rFrameProperties, bool bSetAnchorToChar)
+{
+ if (bSetAnchorToChar)
+ rFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_ANCHOR_TYPE), text::TextContentAnchorType_AS_CHARACTER));
+
+ uno::Any aEmptyBorder{table::BorderLine2()};
+ static const std::vector<PropertyIds> aBorderIds
+ = { PROP_BOTTOM_BORDER, PROP_LEFT_BORDER, PROP_RIGHT_BORDER, PROP_TOP_BORDER };
+ for (size_t i = 0; i < aBorderIds.size(); ++i)
+ rFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(aBorderIds[i]), aEmptyBorder));
+
+ static const std::vector<PropertyIds> aMarginIds
+ = { PROP_BOTTOM_MARGIN, PROP_BOTTOM_BORDER_DISTANCE,
+ PROP_LEFT_MARGIN, PROP_LEFT_BORDER_DISTANCE,
+ PROP_RIGHT_MARGIN, PROP_RIGHT_BORDER_DISTANCE,
+ PROP_TOP_MARGIN, PROP_TOP_BORDER_DISTANCE };
+ for (size_t i = 0; i < aMarginIds.size(); ++i)
+ rFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(aMarginIds[i]), static_cast<sal_Int32>(0)));
+}
+
+void DomainMapper_Impl::ConvertHeaderFooterToTextFrame(bool bDynamicHeightTop, bool bDynamicHeightBottom)
+{
+ while (!m_aHeaderFooterTextAppendStack.empty())
+ {
+ auto aFooterHeader = m_aHeaderFooterTextAppendStack.top();
+ if ((aFooterHeader.second && !bDynamicHeightTop) || (!aFooterHeader.second && !bDynamicHeightBottom))
+ {
+ uno::Reference< text::XTextAppend > xTextAppend = aFooterHeader.first.xTextAppend;
+ uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursor();
+ uno::Reference< text::XTextRange > xRangeStart, xRangeEnd;
+
+ xRangeStart = xCursor->getStart();
+ xCursor->gotoEnd(false);
+ xRangeEnd = xCursor->getStart();
+
+ std::vector<beans::PropertyValue> aFrameProperties
+ {
+ comphelper::makePropertyValue("TextWrap", css::text::WrapTextMode_THROUGH),
+ comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT), text::HoriOrientation::LEFT),
+ comphelper::makePropertyValue(getPropertyName(PROP_OPAQUE), false),
+ comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE), text::SizeType::MIN),
+ comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE), text::SizeType::MIN),
+ // tdf#143384 If the header/footer started with a table, convertToTextFrame could not
+ // convert the table, because it used createTextCursor() -which ignore tables-
+ // to set the conversion range.
+ // This dummy property is set to make convertToTextFrame to use another CreateTextCursor
+ // method that can be parameterized to not ignore tables.
+ comphelper::makePropertyValue(getPropertyName(PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF), true)
+ };
+
+ fillEmptyFrameProperties(aFrameProperties, false);
+
+ // If it is a footer, then orient the frame to the bottom
+ if (!aFooterHeader.second)
+ aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT), text::VertOrientation::BOTTOM));
+
+ uno::Reference<text::XTextAppendAndConvert> xBodyText(
+ xRangeStart->getText(), uno::UNO_QUERY);
+ xBodyText->convertToTextFrame(xTextAppend, xRangeEnd,
+ comphelper::containerToSequence(aFrameProperties));
+ }
+ m_aHeaderFooterTextAppendStack.pop();
+ }
+}
+
+void DomainMapper_Impl::PushPageHeaderFooter(bool bHeader, SectionPropertyMap::PageType eType)
+{
+ m_bSaveParaHadField = m_bParaHadField;
+ m_aHeaderFooterStack.push(HeaderFooterContext(m_bTextInserted, m_nTableDepth));
+ m_bTextInserted = false;
+ m_nTableDepth = 0;
+
+ const PropertyIds ePropIsOn = bHeader? PROP_HEADER_IS_ON: PROP_FOOTER_IS_ON;
+ const PropertyIds ePropShared = bHeader? PROP_HEADER_IS_SHARED: PROP_FOOTER_IS_SHARED;
+ const PropertyIds ePropTextLeft = bHeader? PROP_HEADER_TEXT_LEFT: PROP_FOOTER_TEXT_LEFT;
+ const PropertyIds ePropText = bHeader? PROP_HEADER_TEXT: PROP_FOOTER_TEXT;
+
+ m_bDiscardHeaderFooter = true;
+ m_eInHeaderFooterImport
+ = bHeader ? HeaderFooterImportState::header : HeaderFooterImportState::footer;
+
+ //get the section context
+ PropertyMapPtr pContext = DomainMapper_Impl::GetTopContextOfType(CONTEXT_SECTION);
+ //ask for the header/footer name of the given type
+ SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
+ if(!pSectionContext)
+ return;
+
+ // clear the "Link To Previous" flag so that the header/footer
+ // content is not copied from the previous section
+ pSectionContext->ClearHeaderFooterLinkToPrevious(bHeader, eType);
+
+ if (!m_bIsNewDoc)
+ {
+ return; // TODO sw cannot Undo insert header/footer without crashing
+ }
+
+ uno::Reference< beans::XPropertySet > xPageStyle =
+ pSectionContext->GetPageStyle(
+ *this,
+ eType == SectionPropertyMap::PAGE_FIRST );
+ if (!xPageStyle.is())
+ return;
+ try
+ {
+ bool bLeft = eType == SectionPropertyMap::PAGE_LEFT;
+ bool bFirst = eType == SectionPropertyMap::PAGE_FIRST;
+ if (!bLeft || GetSettingsTable()->GetEvenAndOddHeaders())
+ {
+ //switch on header/footer use
+ xPageStyle->setPropertyValue(
+ getPropertyName(ePropIsOn),
+ uno::Any(true));
+
+ // If the 'Different Even & Odd Pages' flag is turned on - do not ignore it
+ // Even if the 'Even' header/footer is blank - the flag should be imported (so it would look in LO like in Word)
+ if (!bFirst && GetSettingsTable()->GetEvenAndOddHeaders())
+ xPageStyle->setPropertyValue(getPropertyName(ePropShared), uno::Any(false));
+
+ //set the interface
+ uno::Reference< text::XText > xText;
+ xPageStyle->getPropertyValue(getPropertyName(bLeft? ePropTextLeft: ePropText)) >>= xText;
+
+ m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >(xText, uno::UNO_QUERY_THROW),
+ m_bIsNewDoc
+ ? uno::Reference<text::XTextCursor>()
+ : xText->createTextCursorByRange(xText->getStart())));
+ m_aHeaderFooterTextAppendStack.push(std::make_pair(TextAppendContext(uno::Reference< text::XTextAppend >(xText, uno::UNO_QUERY_THROW),
+ m_bIsNewDoc
+ ? uno::Reference<text::XTextCursor>()
+ : xText->createTextCursorByRange(xText->getStart())),
+ bHeader));
+ }
+ // If we have *hidden* header footer
+ else
+ {
+ bool bIsShared = false;
+ // Turn on the headers
+ xPageStyle->setPropertyValue(getPropertyName(ePropIsOn), uno::Any(true));
+ // Store the state of the previous state of shared prop
+ xPageStyle->getPropertyValue(getPropertyName(ePropShared)) >>= bIsShared;
+ // Turn on the shared prop in order to save the headers/footers in time
+ xPageStyle->setPropertyValue(getPropertyName(ePropShared), uno::Any(false));
+ // Add the content of the headers footers to the doc
+ uno::Reference<text::XText> xText;
+ xPageStyle->getPropertyValue(getPropertyName(bLeft ? ePropTextLeft : ePropText))
+ >>= xText;
+
+ m_aTextAppendStack.push(
+ TextAppendContext(uno::Reference<text::XTextAppend>(xText, uno::UNO_QUERY_THROW),
+ m_bIsNewDoc ? uno::Reference<text::XTextCursor>()
+ : xText->createTextCursorByRange(xText->getStart())));
+ // Restore the original state of the shared prop after we stored the necessary values.
+ xPageStyle->setPropertyValue(getPropertyName(ePropShared), uno::Any(bIsShared));
+ }
+ m_bDiscardHeaderFooter = false; // set only on success!
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
+ }
+}
+
+void DomainMapper_Impl::PushPageHeader(SectionPropertyMap::PageType eType)
+{
+ PushPageHeaderFooter(/* bHeader = */ true, eType);
+}
+
+void DomainMapper_Impl::PushPageFooter(SectionPropertyMap::PageType eType)
+{
+ PushPageHeaderFooter(/* bHeader = */ false, eType);
+}
+
+void DomainMapper_Impl::PopPageHeaderFooter()
+{
+ //header and footer always have an empty paragraph at the end
+ //this has to be removed
+ RemoveLastParagraph( );
+
+ if (!m_aTextAppendStack.empty())
+ {
+ if (!m_bDiscardHeaderFooter)
+ {
+ m_aTextAppendStack.pop();
+ }
+ m_bDiscardHeaderFooter = false;
+ }
+ m_eInHeaderFooterImport = HeaderFooterImportState::none;
+
+ if (!m_aHeaderFooterStack.empty())
+ {
+ m_bTextInserted = m_aHeaderFooterStack.top().getTextInserted();
+ m_nTableDepth = m_aHeaderFooterStack.top().getTableDepth();
+ m_aHeaderFooterStack.pop();
+ }
+
+ m_bParaHadField = m_bSaveParaHadField;
+}
+
+void DomainMapper_Impl::PushFootOrEndnote( bool bIsFootnote )
+{
+ SAL_WARN_IF(m_bInFootOrEndnote, "writerfilter.dmapper", "PushFootOrEndnote() is called from another foot or endnote");
+ m_bInFootOrEndnote = true;
+ m_bInFootnote = bIsFootnote;
+ m_bCheckFirstFootnoteTab = true;
+ m_bSaveFirstParagraphInCell = m_bFirstParagraphInCell;
+ try
+ {
+ // Redlines outside the footnote should not affect footnote content
+ m_aRedlines.push(std::vector< RedlineParamsPtr >());
+
+ // IMHO character styles from footnote labels should be ignored in the edit view of Writer.
+ // This adds a hack on top of the following hack to save the style name in the context.
+ PropertyMapPtr pTopContext = GetTopContext();
+ OUString sFootnoteCharStyleName;
+ std::optional< PropertyMap::Property > aProp = pTopContext->getProperty(PROP_CHAR_STYLE_NAME);
+ if (aProp)
+ aProp->second >>= sFootnoteCharStyleName;
+
+ // Remove style reference, if any. This reference did appear here as a side effect of tdf#43017
+ // Seems it is not required by LO, but causes side effects during editing. So remove it
+ // for footnotes/endnotes to restore original LO behavior here.
+ pTopContext->Erase(PROP_CHAR_STYLE_NAME);
+
+ uno::Reference< text::XText > xFootnoteText;
+ if (GetTextFactory().is())
+ xFootnoteText.set( GetTextFactory()->createInstance(
+ bIsFootnote ?
+ OUString( "com.sun.star.text.Footnote" ) : OUString( "com.sun.star.text.Endnote" )),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< text::XFootnote > xFootnote( xFootnoteText, uno::UNO_QUERY_THROW );
+ pTopContext->SetFootnote(xFootnote, sFootnoteCharStyleName);
+ uno::Sequence< beans::PropertyValue > aFontProperties;
+ if (GetTopContextOfType(CONTEXT_CHARACTER))
+ aFontProperties = GetTopContextOfType(CONTEXT_CHARACTER)->GetPropertyValues();
+ appendTextContent( uno::Reference< text::XTextContent >( xFootnoteText, uno::UNO_QUERY_THROW ), aFontProperties );
+ m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >( xFootnoteText, uno::UNO_QUERY_THROW ),
+ xFootnoteText->createTextCursorByRange(xFootnoteText->getStart())));
+
+ // Redlines for the footnote anchor in the main text content
+ std::vector< RedlineParamsPtr > aFootnoteRedline = std::move(m_aRedlines.top());
+ m_aRedlines.pop();
+ CheckRedline( xFootnote->getAnchor( ) );
+ m_aRedlines.push( aFootnoteRedline );
+
+ // Try scanning for custom footnote labels
+ if (!sFootnoteCharStyleName.isEmpty())
+ StartCustomFootnote(pTopContext);
+ else
+ EndCustomFootnote();
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "PushFootOrEndnote");
+ }
+}
+
+void DomainMapper_Impl::CreateRedline(uno::Reference<text::XTextRange> const& xRange,
+ const RedlineParamsPtr& pRedline)
+{
+ if ( !pRedline )
+ return;
+
+ bool bRedlineMoved = false;
+ try
+ {
+ OUString sType;
+ switch ( pRedline->m_nToken & 0xffff )
+ {
+ case XML_mod:
+ sType = getPropertyName( PROP_FORMAT );
+ break;
+ case XML_moveTo:
+ bRedlineMoved = true;
+ m_pParaMarkerRedlineMove = pRedline.get();
+ [[fallthrough]];
+ case XML_ins:
+ sType = getPropertyName( PROP_INSERT );
+ break;
+ case XML_moveFrom:
+ bRedlineMoved = true;
+ m_pParaMarkerRedlineMove = pRedline.get();
+ [[fallthrough]];
+ case XML_del:
+ sType = getPropertyName( PROP_DELETE );
+ break;
+ case XML_ParagraphFormat:
+ sType = getPropertyName( PROP_PARAGRAPH_FORMAT );
+ break;
+ default:
+ throw lang::IllegalArgumentException("illegal redline token type", nullptr, 0);
+ }
+ beans::PropertyValues aRedlineProperties( 4 );
+ beans::PropertyValue * pRedlineProperties = aRedlineProperties.getArray( );
+ pRedlineProperties[0].Name = getPropertyName( PROP_REDLINE_AUTHOR );
+ pRedlineProperties[0].Value <<= pRedline->m_sAuthor;
+ pRedlineProperties[1].Name = getPropertyName( PROP_REDLINE_DATE_TIME );
+ util::DateTime aDateTime = ConversionHelper::ConvertDateStringToDateTime( pRedline->m_sDate );
+ // tdf#146171 import not specified w:date (or specified as zero date "0-00-00")
+ // as Epoch time to avoid of losing change tracking data during ODF roundtrip
+ if ( aDateTime.Year == 0 && aDateTime.Month == 0 && aDateTime.Day == 0 )
+ {
+ aDateTime.Year = 1970;
+ aDateTime.Month = 1;
+ aDateTime.Day = 1;
+ }
+ pRedlineProperties[1].Value <<= aDateTime;
+ pRedlineProperties[2].Name = getPropertyName( PROP_REDLINE_REVERT_PROPERTIES );
+ pRedlineProperties[2].Value <<= pRedline->m_aRevertProperties;
+ pRedlineProperties[3].Name = "RedlineMoved";
+ pRedlineProperties[3].Value <<= bRedlineMoved;
+
+ if (!m_bIsActualParagraphFramed)
+ {
+ uno::Reference < text::XRedline > xRedline( xRange, uno::UNO_QUERY_THROW );
+ xRedline->makeRedline( sType, aRedlineProperties );
+ }
+ // store frame and (possible floating) table redline data for restoring them after frame conversion
+ enum StoredRedlines eType;
+ if (m_bIsActualParagraphFramed || m_nTableDepth > 0)
+ eType = StoredRedlines::FRAME;
+ else if (IsInFootOrEndnote())
+ eType = IsInFootnote() ? StoredRedlines::FOOTNOTE : StoredRedlines::ENDNOTE;
+ else
+ eType = StoredRedlines::NONE;
+
+ if (eType != StoredRedlines::NONE)
+ {
+ m_aStoredRedlines[eType].push_back( uno::Any(xRange) );
+ m_aStoredRedlines[eType].push_back( uno::Any(sType) );
+ m_aStoredRedlines[eType].push_back( uno::Any(aRedlineProperties) );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "in makeRedline" );
+ }
+}
+
+void DomainMapper_Impl::CheckParaMarkerRedline( uno::Reference< text::XTextRange > const& xRange )
+{
+ if ( m_pParaMarkerRedline )
+ {
+ CreateRedline( xRange, m_pParaMarkerRedline );
+ if ( m_pParaMarkerRedline )
+ {
+ m_pParaMarkerRedline.clear();
+ m_currentRedline.clear();
+ }
+ }
+ else if ( m_pParaMarkerRedlineMove && m_bIsParaMarkerMove )
+ {
+ // terminating moveFrom/moveTo redline removes also the paragraph mark
+ CreateRedline( xRange, m_pParaMarkerRedlineMove );
+ }
+ if ( m_pParaMarkerRedlineMove )
+ {
+ m_pParaMarkerRedlineMove.clear();
+ EndParaMarkerMove();
+ }
+}
+
+void DomainMapper_Impl::CheckRedline( uno::Reference< text::XTextRange > const& xRange )
+{
+ // Writer core "officially" does not like overlapping redlines, and its UNO interface is stupid enough
+ // to not prevent that. However, in practice in fact everything appears to work fine (except for the debug warnings
+ // about redline table corruption, which may possibly be harmless in reality). So leave this as it is, since this
+ // is a better representation of how the changes happened. If this will ever become a problem, overlapping redlines
+ // will need to be merged into one, just like doing the changes in the UI does, which will lose some information
+ // (and so if that happens, it may be better to fix Writer).
+ // Create the redlines here from lowest (formats) to highest (inserts/removals) priority, since the last one is
+ // what Writer presents graphically, so this will show deletes as deleted text and not as just formatted text being there.
+ bool bUsedRange = m_aRedlines.top().size() > 0 || (GetTopContextOfType(CONTEXT_CHARACTER) &&
+ GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().size() > 0);
+
+ // only export ParagraphFormat, when there is no other redline in the same text portion to avoid missing redline compression,
+ // but always export the first ParagraphFormat redline in a paragraph to keep the paragraph style change data for rejection
+ if( (!bUsedRange || !m_bParaChanged) && GetTopContextOfType(CONTEXT_PARAGRAPH) )
+ {
+ std::vector<RedlineParamsPtr>& avRedLines = GetTopContextOfType(CONTEXT_PARAGRAPH)->Redlines();
+ for( const auto& rRedline : avRedLines )
+ CreateRedline( xRange, rRedline );
+ }
+ if( GetTopContextOfType(CONTEXT_CHARACTER) )
+ {
+ std::vector<RedlineParamsPtr>& avRedLines = GetTopContextOfType(CONTEXT_CHARACTER)->Redlines();
+ for( const auto& rRedline : avRedLines )
+ CreateRedline( xRange, rRedline );
+ }
+ for (const auto& rRedline : m_aRedlines.top() )
+ CreateRedline( xRange, rRedline );
+}
+
+void DomainMapper_Impl::StartParaMarkerChange( )
+{
+ m_bIsParaMarkerChange = true;
+}
+
+void DomainMapper_Impl::EndParaMarkerChange( )
+{
+ m_bIsParaMarkerChange = false;
+ m_previousRedline = m_currentRedline;
+ m_currentRedline.clear();
+}
+
+void DomainMapper_Impl::StartParaMarkerMove( )
+{
+ m_bIsParaMarkerMove = true;
+}
+
+void DomainMapper_Impl::EndParaMarkerMove( )
+{
+ m_bIsParaMarkerMove = false;
+}
+
+void DomainMapper_Impl::StartCustomFootnote(const PropertyMapPtr pContext)
+{
+ if (pContext == m_pFootnoteContext)
+ return;
+
+ assert(pContext->GetFootnote().is());
+ m_bHasFootnoteStyle = true;
+ m_bCheckFootnoteStyle = !pContext->GetFootnoteStyle().isEmpty();
+ m_pFootnoteContext = pContext;
+}
+
+void DomainMapper_Impl::EndCustomFootnote()
+{
+ m_bHasFootnoteStyle = false;
+ m_bCheckFootnoteStyle = false;
+}
+
+void DomainMapper_Impl::PushAnnotation()
+{
+ try
+ {
+ m_bIsInComments = true;
+ if (!GetTextFactory().is())
+ return;
+ m_xAnnotationField.set( GetTextFactory()->createInstance( "com.sun.star.text.TextField.Annotation" ),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< text::XText > xAnnotationText;
+ m_xAnnotationField->getPropertyValue("TextRange") >>= xAnnotationText;
+ m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >( xAnnotationText, uno::UNO_QUERY_THROW ),
+ m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : xAnnotationText->createTextCursorByRange(xAnnotationText->getStart())));
+ }
+ catch( const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
+ }
+}
+
+static void lcl_CopyRedlines(
+ uno::Reference< text::XText > const& xSrc,
+ std::deque<css::uno::Any>& rRedlines,
+ std::vector<sal_Int32>& redPos,
+ std::vector<sal_Int32>& redLen,
+ sal_Int32& redIdx)
+{
+ redIdx = -1;
+ for( size_t i = 0; i < rRedlines.size(); i+=3)
+ {
+ uno::Reference< text::XTextRange > xRange;
+ rRedlines[i] >>= xRange;
+
+ // is this a redline of the temporary footnote?
+ uno::Reference<text::XTextCursor> xRangeCursor;
+ try
+ {
+ xRangeCursor = xSrc->createTextCursorByRange( xRange );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ if (xRangeCursor.is())
+ {
+ redIdx = i;
+ sal_Int32 nLen = xRange->getString().getLength();
+ redLen.push_back(nLen);
+ xRangeCursor->gotoRange(xSrc->getStart(), true);
+ redPos.push_back(xRangeCursor->getString().getLength() - nLen);
+ }
+ else
+ {
+ // we have already found all redlines of the footnote,
+ // skip checking the redlines of the other footnotes
+ if (redIdx > -1)
+ break;
+ // failed createTextCursorByRange(), for example, table inside the frame
+ redLen.push_back(-1);
+ redPos.push_back(-1);
+ }
+ }
+}
+
+static void lcl_PasteRedlines(
+ uno::Reference< text::XText > const& xDest,
+ std::deque<css::uno::Any>& rRedlines,
+ std::vector<sal_Int32>& redPos,
+ std::vector<sal_Int32>& redLen,
+ sal_Int32 redIdx)
+{
+ // create redlines in the copied footnote
+ for( size_t i = 0; redIdx > -1 && i <= sal::static_int_cast<size_t>(redIdx); i+=3)
+ {
+ OUString sType;
+ beans::PropertyValues aRedlineProperties( 3 );
+ // skip failed createTextCursorByRange()
+ if (redPos[i/3] == -1)
+ continue;
+ rRedlines[i+1] >>= sType;
+ rRedlines[i+2] >>= aRedlineProperties;
+ uno::Reference< text::XTextCursor > xCrsr = xDest->getText()->createTextCursor();
+ xCrsr->goRight(redPos[i/3], false);
+ xCrsr->goRight(redLen[i/3], true);
+ uno::Reference < text::XRedline > xRedline( xCrsr, uno::UNO_QUERY_THROW );
+ try {
+ xRedline->makeRedline( sType, aRedlineProperties );
+ }
+ catch(const uno::Exception&)
+ {
+ // ignore (footnotes of tracked deletions)
+ }
+ }
+}
+
+bool DomainMapper_Impl::CopyTemporaryNotes(
+ uno::Reference< text::XFootnote > xNoteSrc,
+ uno::Reference< text::XFootnote > xNoteDest )
+{
+ if (!m_bSaxError && xNoteSrc != xNoteDest)
+ {
+ uno::Reference< text::XText > xSrc( xNoteSrc, uno::UNO_QUERY_THROW );
+ uno::Reference< text::XText > xDest( xNoteDest, uno::UNO_QUERY_THROW );
+ uno::Reference< text::XTextCopy > xTxt, xTxt2;
+ xTxt.set( xSrc, uno::UNO_QUERY_THROW );
+ xTxt2.set( xDest, uno::UNO_QUERY_THROW );
+ xTxt2->copyText( xTxt );
+
+ // copy its redlines
+ std::vector<sal_Int32> redPos, redLen;
+ sal_Int32 redIdx;
+ enum StoredRedlines eType = IsInFootnote() ? StoredRedlines::FOOTNOTE : StoredRedlines::ENDNOTE;
+ lcl_CopyRedlines(xSrc, m_aStoredRedlines[eType], redPos, redLen, redIdx);
+ lcl_PasteRedlines(xDest, m_aStoredRedlines[eType], redPos, redLen, redIdx);
+
+ // remove processed redlines
+ for( size_t i = 0; redIdx > -1 && i <= sal::static_int_cast<size_t>(redIdx) + 2; i++)
+ m_aStoredRedlines[eType].pop_front();
+
+ return true;
+ }
+
+ return false;
+}
+
+void DomainMapper_Impl::RemoveTemporaryFootOrEndnotes()
+{
+ uno::Reference< text::XFootnotesSupplier> xFootnotesSupplier( GetTextDocument(), uno::UNO_QUERY );
+ uno::Reference< text::XEndnotesSupplier> xEndnotesSupplier( GetTextDocument(), uno::UNO_QUERY );
+ uno::Reference< text::XFootnote > xNote;
+ if (GetFootnoteCount() > 0)
+ {
+ auto xFootnotes = xFootnotesSupplier->getFootnotes();
+ if ( m_nFirstFootnoteIndex > 0 )
+ {
+ uno::Reference< text::XFootnote > xFirstNote;
+ xFootnotes->getByIndex(0) >>= xFirstNote;
+ uno::Reference< text::XText > xText( xFirstNote, uno::UNO_QUERY_THROW );
+ xText->setString("");
+ xFootnotes->getByIndex(m_nFirstFootnoteIndex) >>= xNote;
+ CopyTemporaryNotes(xNote, xFirstNote);
+ }
+ for (sal_Int32 i = GetFootnoteCount(); i > 0; --i)
+ {
+ xFootnotes->getByIndex(i) >>= xNote;
+ xNote->getAnchor()->setString("");
+ }
+ }
+ if (GetEndnoteCount() > 0)
+ {
+ auto xEndnotes = xEndnotesSupplier->getEndnotes();
+ if ( m_nFirstEndnoteIndex > 0 )
+ {
+ uno::Reference< text::XFootnote > xFirstNote;
+ xEndnotes->getByIndex(0) >>= xFirstNote;
+ uno::Reference< text::XText > xText( xFirstNote, uno::UNO_QUERY_THROW );
+ xText->setString("");
+ xEndnotes->getByIndex(m_nFirstEndnoteIndex) >>= xNote;
+ CopyTemporaryNotes(xNote, xFirstNote);
+ }
+ for (sal_Int32 i = GetEndnoteCount(); i > 0; --i)
+ {
+ xEndnotes->getByIndex(i) >>= xNote;
+ xNote->getAnchor()->setString("");
+ }
+ }
+}
+
+static void lcl_convertToNoteIndices(std::deque<sal_Int32>& rNoteIds, sal_Int32& rFirstNoteIndex)
+{
+ // rNoteIds contains XML footnote identifiers in the loaded order of the footnotes
+ // (the same order as in footnotes.xml), i.e. it maps temporary footnote positions to the
+ // identifiers. For example: Ids[0] = 100; Ids[1] = -1, Ids[2] = 5.
+ // To copy the footnotes in their final place, create an array, which map the (normalized)
+ // footnote identifiers to the temporary footnote positions. Using the previous example,
+ // Pos[0] = 1; Pos[1] = 2; Pos[2] = 0 (where [0], [1], [2] are the normalized
+ // -1, 5 and 100 identifiers).
+ std::deque<sal_Int32> aSortedIds = rNoteIds;
+ std::sort(aSortedIds.begin(), aSortedIds.end());
+ std::map<sal_Int32, size_t> aMapIds;
+ // normalize footnote identifiers to 0, 1, 2 ...
+ for (size_t i = 0; i < aSortedIds.size(); ++i)
+ aMapIds[aSortedIds[i]] = i;
+ // reusing rNoteIds, create the Pos array to map normalized identifiers to the loaded positions
+ std::deque<sal_Int32> aOrigNoteIds = rNoteIds;
+ for (size_t i = 0; i < rNoteIds.size(); ++i)
+ rNoteIds[aMapIds[aOrigNoteIds[i]]] = i;
+ rFirstNoteIndex = rNoteIds.front();
+ rNoteIds.pop_front();
+}
+
+void DomainMapper_Impl::PopFootOrEndnote()
+{
+ // content of the footnotes were inserted after the first footnote in temporary footnotes,
+ // restore the content of the actual footnote by copying its content from the first
+ // (remaining) temporary footnote and remove the temporary footnote.
+ uno::Reference< text::XFootnotesSupplier> xFootnotesSupplier( GetTextDocument(), uno::UNO_QUERY );
+ uno::Reference< text::XEndnotesSupplier> xEndnotesSupplier( GetTextDocument(), uno::UNO_QUERY );
+ bool bCopied = false;
+ if ( IsInFootOrEndnote() && ( ( IsInFootnote() && GetFootnoteCount() > -1 && xFootnotesSupplier.is() ) ||
+ ( !IsInFootnote() && GetEndnoteCount() > -1 && xEndnotesSupplier.is() ) ) )
+ {
+ uno::Reference< text::XFootnote > xNoteFirst, xNoteLast;
+ auto xFootnotes = xFootnotesSupplier->getFootnotes();
+ auto xEndnotes = xEndnotesSupplier->getEndnotes();
+ if ( ( ( IsInFootnote() && xFootnotes->getCount() > 1 &&
+ ( xFootnotes->getByIndex(xFootnotes->getCount()-1) >>= xNoteLast ) ) ||
+ ( !IsInFootnote() && xEndnotes->getCount() > 1 &&
+ ( xEndnotes->getByIndex(xEndnotes->getCount()-1) >>= xNoteLast ) )
+ ) && xNoteLast->getLabel().isEmpty() )
+ {
+ // copy content of the next temporary footnote
+ try
+ {
+ if ( IsInFootnote() && !m_aFootnoteIds.empty() )
+ {
+ if ( m_nFirstFootnoteIndex == -1 )
+ lcl_convertToNoteIndices(m_aFootnoteIds, m_nFirstFootnoteIndex);
+ if (m_aFootnoteIds.empty()) // lcl_convertToNoteIndices pops m_aFootnoteIds
+ m_bSaxError = true;
+ else
+ {
+ xFootnotes->getByIndex(m_aFootnoteIds.front()) >>= xNoteFirst;
+ m_aFootnoteIds.pop_front();
+ }
+ }
+ else if ( !IsInFootnote() && !m_aEndnoteIds.empty() )
+ {
+ if ( m_nFirstEndnoteIndex == -1 )
+ lcl_convertToNoteIndices(m_aEndnoteIds, m_nFirstEndnoteIndex);
+ if (m_aEndnoteIds.empty()) // lcl_convertToNoteIndices pops m_aEndnoteIds
+ m_bSaxError = true;
+ else
+ {
+ xEndnotes->getByIndex(m_aEndnoteIds.front()) >>= xNoteFirst;
+ m_aEndnoteIds.pop_front();
+ }
+ }
+ else
+ m_bSaxError = true;
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Cannot insert footnote/endnote");
+ m_bSaxError = true;
+ }
+
+ bCopied = CopyTemporaryNotes(xNoteFirst, xNoteLast);
+ }
+ }
+
+ if (!IsRTFImport() && !bCopied)
+ RemoveLastParagraph();
+
+ // In case the foot or endnote did not contain a tab.
+ m_bIgnoreNextTab = false;
+
+ if (!m_aTextAppendStack.empty())
+ m_aTextAppendStack.pop();
+
+ if (m_aRedlines.size() == 1)
+ {
+ SAL_WARN("writerfilter.dmapper", "PopFootOrEndnote() is called without PushFootOrEndnote()?");
+ return;
+ }
+ m_aRedlines.pop();
+ m_eSkipFootnoteState = SkipFootnoteSeparator::OFF;
+ m_bInFootOrEndnote = false;
+ m_pFootnoteContext = nullptr;
+ m_bFirstParagraphInCell = m_bSaveFirstParagraphInCell;
+}
+
+void DomainMapper_Impl::PopAnnotation()
+{
+ RemoveLastParagraph();
+
+ m_bIsInComments = false;
+ m_aTextAppendStack.pop();
+
+ try
+ {
+ if (m_bAnnotationResolved)
+ m_xAnnotationField->setPropertyValue("Resolved", css::uno::Any(true));
+
+ // See if the annotation will be a single position or a range.
+ if (m_nAnnotationId == -1 || !m_aAnnotationPositions[m_nAnnotationId].m_xStart.is() || !m_aAnnotationPositions[m_nAnnotationId].m_xEnd.is())
+ {
+ uno::Sequence< beans::PropertyValue > aEmptyProperties;
+ uno::Reference< text::XTextContent > xContent( m_xAnnotationField, uno::UNO_QUERY_THROW );
+ appendTextContent( xContent, aEmptyProperties );
+ CheckRedline( xContent->getAnchor( ) );
+ }
+ else
+ {
+ AnnotationPosition& aAnnotationPosition = m_aAnnotationPositions[m_nAnnotationId];
+ // Create a range that points to the annotation start/end.
+ uno::Reference<text::XText> const xText = aAnnotationPosition.m_xStart->getText();
+ uno::Reference<text::XTextCursor> const xCursor = xText->createTextCursorByRange(aAnnotationPosition.m_xStart);
+
+ bool bMarker = false;
+ uno::Reference<text::XTextRangeCompare> xTextRangeCompare(xText, uno::UNO_QUERY);
+ if (xTextRangeCompare->compareRegionStarts(aAnnotationPosition.m_xStart, aAnnotationPosition.m_xEnd) == 0)
+ {
+ // Insert a marker so that comment around an anchored image is not collapsed during
+ // insertion.
+ xText->insertString(xCursor, "x", false);
+ bMarker = true;
+ }
+
+ xCursor->gotoRange(aAnnotationPosition.m_xEnd, true);
+ uno::Reference<text::XTextRange> const xTextRange(xCursor, uno::UNO_QUERY_THROW);
+
+ // Attach the annotation to the range.
+ uno::Reference<text::XTextAppend> const xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ xTextAppend->insertTextContent(xTextRange, uno::Reference<text::XTextContent>(m_xAnnotationField, uno::UNO_QUERY_THROW), !xCursor->isCollapsed());
+
+ if (bMarker)
+ {
+ // Remove the marker.
+ xCursor->goLeft(1, true);
+ xCursor->setString(OUString());
+ }
+ }
+ m_aAnnotationPositions.erase( m_nAnnotationId );
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Cannot insert annotation field");
+ }
+
+ m_xAnnotationField.clear();
+ m_nAnnotationId = -1;
+ m_bAnnotationResolved = false;
+}
+
+void DomainMapper_Impl::PushPendingShape( const uno::Reference< drawing::XShape > & xShape )
+{
+ m_aPendingShapes.push_back(xShape);
+}
+
+uno::Reference<drawing::XShape> DomainMapper_Impl::PopPendingShape()
+{
+ uno::Reference<drawing::XShape> xRet;
+ if (!m_aPendingShapes.empty())
+ {
+ xRet = m_aPendingShapes.front();
+ m_aPendingShapes.pop_front();
+ }
+ return xRet;
+}
+
+void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape > & xShape )
+{
+ // Append these early, so the context and the table manager stack will be
+ // in sync, even if the text append stack is empty.
+ appendTableManager();
+ appendTableHandler();
+ getTableManager().startLevel();
+
+ if (m_aTextAppendStack.empty())
+ return;
+ uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
+
+ try
+ {
+ uno::Reference< lang::XServiceInfo > xSInfo( xShape, uno::UNO_QUERY_THROW );
+ if (xSInfo->supportsService("com.sun.star.drawing.GroupShape"))
+ {
+ // Textboxes in shapes do not support styles, so check saved style information and apply properties directly to the child shapes.
+ const uno::Reference<drawing::XShapes> xShapes(xShape, uno::UNO_QUERY);
+ const sal_uInt32 nShapeCount = xShapes.is() ? xShapes->getCount() : 0;
+ for ( sal_uInt32 i = 0; i < nShapeCount; ++i )
+ {
+ try
+ {
+ uno::Reference<text::XTextRange> xFrame(xShapes->getByIndex(i), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xFramePropertySet;
+ if (xFrame)
+ xFramePropertySet.set(xFrame, uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xShapePropertySet(xShapes->getByIndex(i), uno::UNO_QUERY_THROW);
+
+ comphelper::SequenceAsHashMap aGrabBag( xShapePropertySet->getPropertyValue("CharInteropGrabBag") );
+
+ // only VML import has checked for style. Don't apply default parastyle properties to other imported shapes
+ // - except for fontsize - to maintain compatibility with previous versions of LibreOffice.
+ const bool bOnlyApplyCharHeight = !aGrabBag["mso-pStyle"].hasValue();
+
+ OUString sStyleName;
+ aGrabBag["mso-pStyle"] >>= sStyleName;
+ StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindStyleSheetByISTD( sStyleName );
+ if ( !pEntry )
+ {
+ // Use default style even in ambiguous cases (where multiple styles were defined) since MOST styles inherit
+ // MOST of their properties from the default style. In the ambiguous case, we have to accept some kind of compromise
+ // and the default paragraph style ought to be the safest one... (compared to DocDefaults or program defaults)
+ pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetDefaultParaStyleName() );
+ }
+ if ( pEntry )
+ {
+ // The Ids here come from oox/source/vml/vmltextbox.cxx.
+ // It probably could safely expand to all Ids that shapes support.
+ const PropertyIds eIds[] = {
+ PROP_CHAR_HEIGHT,
+ PROP_CHAR_FONT_NAME,
+ PROP_CHAR_WEIGHT,
+ PROP_CHAR_CHAR_KERNING,
+ PROP_CHAR_COLOR,
+ PROP_PARA_ADJUST
+ };
+ const uno::Reference<beans::XPropertyState> xShapePropertyState(xShapePropertySet, uno::UNO_QUERY_THROW);
+ for ( const auto& eId : eIds )
+ {
+ try
+ {
+ if ( bOnlyApplyCharHeight && eId != PROP_CHAR_HEIGHT )
+ continue;
+
+ const OUString sPropName = getPropertyName(eId);
+ if ( beans::PropertyState_DEFAULT_VALUE == xShapePropertyState->getPropertyState(sPropName) )
+ {
+ const uno::Any aProp = GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/true, /*bPara=*/true);
+ if (aProp.hasValue())
+ {
+ if (xFrame)
+ xFramePropertySet->setPropertyValue(sPropName, aProp);
+ else
+ xShapePropertySet->setPropertyValue(sPropName, aProp);
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "PushShapeContext() text stylesheet property exception" );
+ }
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "PushShapeContext()" );
+ }
+ }
+
+ // A GroupShape doesn't implement text::XTextRange, but appending
+ // an empty reference to the stacks still makes sense, because this
+ // way bToRemove can be set, and we won't end up with duplicated
+ // shapes for OLE objects.
+ m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xShape, uno::UNO_QUERY), uno::Reference<text::XTextCursor>()));
+ uno::Reference<text::XTextContent> xTxtContent(xShape, uno::UNO_QUERY);
+ m_aAnchoredStack.push(AnchoredContext(xTxtContent));
+ }
+ else if (xSInfo->supportsService("com.sun.star.drawing.OLE2Shape"))
+ {
+ // OLE2Shape from oox should be converted to a TextEmbeddedObject for sw.
+ m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xShape, uno::UNO_QUERY), uno::Reference<text::XTextCursor>()));
+ uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY);
+ m_aAnchoredStack.push(AnchoredContext(xTextContent));
+ uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
+
+ m_xEmbedded.set(m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xEmbeddedProperties(m_xEmbedded, uno::UNO_QUERY_THROW);
+ xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT), xShapePropertySet->getPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT)));
+ xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE), uno::Any(text::TextContentAnchorType_AS_CHARACTER));
+ // So that the original bitmap-only shape will be replaced by the embedded object.
+ m_aAnchoredStack.top().bToRemove = true;
+ m_aTextAppendStack.pop();
+ appendTextContent(m_xEmbedded, uno::Sequence<beans::PropertyValue>());
+ }
+ else
+ {
+ uno::Reference<text::XTextRange> xShapeTextRange(xShape, uno::UNO_QUERY_THROW);
+ // Add the shape to the text append stack
+ uno::Reference<text::XTextAppend> xShapeTextAppend(xShape, uno::UNO_QUERY_THROW);
+ uno::Reference<text::XTextCursor> xTextCursor;
+ if (!m_bIsNewDoc)
+ {
+ xTextCursor = xShapeTextRange->getText()->createTextCursorByRange(
+ xShapeTextRange->getStart());
+ }
+ TextAppendContext aContext(xShapeTextAppend, xTextCursor);
+ m_aTextAppendStack.push(aContext);
+
+ // Add the shape to the anchored objects stack
+ uno::Reference< text::XTextContent > xTxtContent( xShape, uno::UNO_QUERY_THROW );
+ m_aAnchoredStack.push( AnchoredContext(xTxtContent) );
+
+ uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY_THROW );
+#ifdef DBG_UTIL
+ TagLogger::getInstance().unoPropertySet(xProps);
+#endif
+ text::TextContentAnchorType nAnchorType(text::TextContentAnchorType_AT_PARAGRAPH);
+ xProps->getPropertyValue(getPropertyName( PROP_ANCHOR_TYPE )) >>= nAnchorType;
+ bool checkZOrderStatus = false;
+ if (xSInfo->supportsService("com.sun.star.text.TextFrame"))
+ {
+ SetIsTextFrameInserted(true);
+ // Extract the special "btLr text frame" mode, requested by oox, if needed.
+ // Extract vml ZOrder from FrameInteropGrabBag
+ uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ xShapePropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
+
+ for (const auto& rProp : std::as_const(aGrabBag))
+ {
+ if (rProp.Name == "VML-Z-ORDER")
+ {
+ GraphicZOrderHelper* pZOrderHelper = m_rDMapper.graphicZOrderHelper();
+ sal_Int32 zOrder(0);
+ rProp.Value >>= zOrder;
+ xShapePropertySet->setPropertyValue( "ZOrder", uno::Any(pZOrderHelper->findZOrder(zOrder)));
+ pZOrderHelper->addItem(xShapePropertySet, zOrder);
+ xShapePropertySet->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::Any( zOrder >= 0 ) );
+ checkZOrderStatus = true;
+ }
+ else if ( rProp.Name == "TxbxHasLink" )
+ {
+ //Chaining of textboxes will happen in ~DomainMapper_Impl
+ //i.e when all the textboxes are read and all its attributes
+ //have been set ( basically the Name/LinkedDisplayName )
+ //which is set in Graphic Import.
+ m_vTextFramesForChaining.push_back(xShape);
+ }
+ }
+
+ uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY_THROW);
+ uno::Reference<text::XTextRange> xTextRange(xTextAppend->createTextCursorByRange(xTextAppend->getEnd()), uno::UNO_QUERY_THROW);
+ xTextAppend->insertTextContent(xTextRange, xTextContent, false);
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
+ // we need to re-set this value to xTextContent, then only values are preserved.
+ xPropertySet->setPropertyValue("FrameInteropGrabBag",uno::Any(aGrabBag));
+ }
+ else if (nAnchorType == text::TextContentAnchorType_AS_CHARACTER)
+ {
+ // Fix spacing for as-character objects. If the paragraph has CT_Spacing_after set,
+ // it needs to be set on the object too, as that's what object placement code uses.
+ PropertyMapPtr paragraphContext = GetTopContextOfType( CONTEXT_PARAGRAPH );
+ std::optional<PropertyMap::Property> aPropMargin = paragraphContext->getProperty(PROP_PARA_BOTTOM_MARGIN);
+ if(aPropMargin)
+ xProps->setPropertyValue( getPropertyName( PROP_BOTTOM_MARGIN ), aPropMargin->second );
+ }
+ else
+ {
+ uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ xShapePropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
+ for (const auto& rProp : std::as_const(aGrabBag))
+ {
+ if (rProp.Name == "VML-Z-ORDER")
+ {
+ GraphicZOrderHelper* pZOrderHelper = m_rDMapper.graphicZOrderHelper();
+ sal_Int32 zOrder(0);
+ rProp.Value >>= zOrder;
+ xShapePropertySet->setPropertyValue( "ZOrder", uno::Any(pZOrderHelper->findZOrder(zOrder)));
+ pZOrderHelper->addItem(xShapePropertySet, zOrder);
+ xShapePropertySet->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::Any( zOrder >= 0 ) );
+ checkZOrderStatus = true;
+ }
+ else if ( rProp.Name == "TxbxHasLink" )
+ {
+ //Chaining of textboxes will happen in ~DomainMapper_Impl
+ //i.e when all the textboxes are read and all its attributes
+ //have been set ( basically the Name/LinkedDisplayName )
+ //which is set in Graphic Import.
+ m_vTextFramesForChaining.push_back(xShape);
+ }
+ }
+
+ if(IsSdtEndBefore())
+ {
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo;
+ if(xShapePropertySet.is())
+ {
+ xPropSetInfo = xShapePropertySet->getPropertySetInfo();
+ if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag"))
+ {
+ uno::Sequence<beans::PropertyValue> aShapeGrabBag( comphelper::InitPropertySequence({
+ { "SdtEndBefore", uno::Any(true) }
+ }));
+ xShapePropertySet->setPropertyValue("InteropGrabBag",uno::Any(aShapeGrabBag));
+ }
+ }
+ }
+ }
+ if (!IsInHeaderFooter() && !checkZOrderStatus)
+ xProps->setPropertyValue(
+ getPropertyName( PROP_OPAQUE ),
+ uno::Any( true ) );
+ }
+ m_bParaChanged = true;
+ getTableManager().setIsInShape(true);
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Exception when adding shape");
+ }
+}
+/*
+ * Updating chart height and width after reading the actual values from wp:extent
+*/
+void DomainMapper_Impl::UpdateEmbeddedShapeProps(const uno::Reference< drawing::XShape > & xShape)
+{
+ if (!xShape.is())
+ return;
+
+ uno::Reference<beans::XPropertySet> xEmbeddedProperties(m_xEmbedded, uno::UNO_QUERY_THROW);
+ awt::Size aSize = xShape->getSize( );
+ xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_WIDTH), uno::Any(sal_Int32(aSize.Width)));
+ xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_HEIGHT), uno::Any(sal_Int32(aSize.Height)));
+}
+
+
+void DomainMapper_Impl::PopShapeContext()
+{
+ if (hasTableManager())
+ {
+ getTableManager().endLevel();
+ popTableManager();
+ }
+ if ( m_aAnchoredStack.empty() )
+ return;
+
+ // For OLE object replacement shape, the text append context was already removed
+ // or the OLE object couldn't be inserted.
+ if ( !m_aAnchoredStack.top().bToRemove )
+ {
+ RemoveLastParagraph();
+ if (!m_aTextAppendStack.empty())
+ m_aTextAppendStack.pop();
+ }
+
+ uno::Reference< text::XTextContent > xObj = m_aAnchoredStack.top( ).xTextContent;
+ try
+ {
+ appendTextContent( xObj, uno::Sequence< beans::PropertyValue >() );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ // this is normal: the shape is already attached
+ }
+
+ const uno::Reference<drawing::XShape> xShape( xObj, uno::UNO_QUERY_THROW );
+ // Remove the shape if required (most likely replacement shape for OLE object)
+ // or anchored to a discarded header or footer
+ if ( m_aAnchoredStack.top().bToRemove || m_bDiscardHeaderFooter )
+ {
+ try
+ {
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(m_xTextDocument, uno::UNO_QUERY_THROW);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ if ( xDrawPage.is() )
+ xDrawPage->remove( xShape );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+
+ // Relative width calculations deferred until section's margins are defined.
+ // Being cautious: only deferring undefined/minimum-width shapes in order to avoid causing potential regressions
+ css::awt::Size aShapeSize;
+ try
+ {
+ aShapeSize = xShape->getSize();
+ }
+ catch (const css::uno::RuntimeException& e)
+ {
+ // May happen e.g. when text frame has no frame format
+ // See sw/qa/extras/ooxmlimport/data/n779627.docx
+ SAL_WARN("writerfilter.dmapper", "getSize failed. " << e.Message);
+ }
+ if( aShapeSize.Width <= 2 )
+ {
+ const uno::Reference<beans::XPropertySet> xShapePropertySet( xShape, uno::UNO_QUERY );
+ SectionPropertyMap* pSectionContext = GetSectionContext();
+ if ( pSectionContext && (!hasTableManager() || !getTableManager().isInTable()) &&
+ xShapePropertySet->getPropertySetInfo()->hasPropertyByName(getPropertyName(PROP_RELATIVE_WIDTH)) )
+ {
+ pSectionContext->addRelativeWidthShape(xShape);
+ }
+ }
+
+ m_aAnchoredStack.pop();
+}
+
+bool DomainMapper_Impl::IsSdtEndBefore()
+{
+ bool bIsSdtEndBefore = false;
+ PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_CHARACTER);
+ if(pContext)
+ {
+ const uno::Sequence< beans::PropertyValue > currentCharProps = pContext->GetPropertyValues();
+ for (const auto& rCurrentCharProp : currentCharProps)
+ {
+ if (rCurrentCharProp.Name == "CharInteropGrabBag")
+ {
+ uno::Sequence<beans::PropertyValue> aCharGrabBag;
+ rCurrentCharProp.Value >>= aCharGrabBag;
+ for (const auto& rProp : std::as_const(aCharGrabBag))
+ {
+ if(rProp.Name == "SdtEndBefore")
+ {
+ rProp.Value >>= bIsSdtEndBefore;
+ }
+ }
+ }
+ }
+ }
+ return bIsSdtEndBefore;
+}
+
+bool DomainMapper_Impl::IsDiscardHeaderFooter() const
+{
+ return m_bDiscardHeaderFooter;
+}
+
+// called from TableManager::closeCell()
+void DomainMapper_Impl::ClearPreviousParagraph()
+{
+ // in table cells, set bottom auto margin of last paragraph to 0, except in paragraphs with numbering
+ if ((m_nTableDepth == (m_nTableCellDepth + 1))
+ && m_xPreviousParagraph.is()
+ && hasTableManager() && getTableManager().isCellLastParaAfterAutospacing())
+ {
+ uno::Reference<container::XNamed> xPreviousNumberingRules(m_xPreviousParagraph->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
+ if ( !xPreviousNumberingRules.is() || xPreviousNumberingRules->getName().isEmpty() )
+ m_xPreviousParagraph->setPropertyValue("ParaBottomMargin", uno::Any(static_cast<sal_Int32>(0)));
+ }
+
+ m_xPreviousParagraph.clear();
+
+ // next table paragraph will be first paragraph in a cell
+ m_bFirstParagraphInCell = true;
+}
+
+void DomainMapper_Impl::HandleAltChunk(const OUString& rStreamName)
+{
+ try
+ {
+ // Create the import filter.
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
+ comphelper::getProcessServiceFactory());
+ uno::Reference<uno::XInterface> xDocxFilter
+ = xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.WriterFilter");
+
+ // Set the target document.
+ uno::Reference<document::XImporter> xImporter(xDocxFilter, uno::UNO_QUERY);
+ xImporter->setTargetDocument(m_xTextDocument);
+
+ // Set the import parameters.
+ uno::Reference<embed::XHierarchicalStorageAccess> xStorageAccess(m_xDocumentStorage,
+ uno::UNO_QUERY);
+ if (!xStorageAccess.is())
+ {
+ return;
+ }
+ // Turn the ZIP stream into a seekable one, as the importer only works with such streams.
+ uno::Reference<io::XStream> xStream = xStorageAccess->openStreamElementByHierarchicalName(
+ rStreamName, embed::ElementModes::READ);
+ std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(xStream, true);
+ SvMemoryStream aMemory;
+ aMemory.WriteStream(*pStream);
+ uno::Reference<io::XStream> xInputStream = new utl::OStreamWrapper(aMemory);
+ // Not handling AltChunk during paste for now.
+ uno::Reference<text::XTextRange> xInsertTextRange = GetCurrentXText()->getEnd();
+ uno::Reference<text::XTextRange> xSectionStartingRange;
+ SectionPropertyMap* pSectionContext = GetSectionContext();
+ if (pSectionContext)
+ {
+ xSectionStartingRange = pSectionContext->GetStartingRange();
+ }
+ uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence({
+ { "InputStream", uno::Any(xInputStream) },
+ { "InsertMode", uno::Any(true) },
+ { "TextInsertModeRange", uno::Any(xInsertTextRange) },
+ { "AltChunkMode", uno::Any(true) },
+ { "AltChunkStartingRange", uno::Any(xSectionStartingRange) },
+ }));
+
+ // Do the actual import.
+ uno::Reference<document::XFilter> xFilter(xDocxFilter, uno::UNO_QUERY);
+ xFilter->filter(aDescriptor);
+ }
+ catch (const uno::Exception& rException)
+ {
+ SAL_WARN("writerfilter", "DomainMapper_Impl::HandleAltChunk: failed to handle alt chunk: "
+ << rException.Message);
+ }
+}
+
+void DomainMapper_Impl::HandlePTab(sal_Int32 nAlignment)
+{
+ // We only handle the case when the line already has content, so the left-aligned ptab is
+ // equivalent to a line break.
+ if (nAlignment != NS_ooxml::LN_Value_ST_PTabAlignment_left)
+ {
+ return;
+ }
+
+ if (m_aTextAppendStack.empty())
+ {
+ return;
+ }
+
+ uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (!xTextAppend.is())
+ {
+ return;
+ }
+
+ uno::Reference<css::text::XTextRange> xInsertPosition
+ = m_aTextAppendStack.top().xInsertPosition;
+ if (!xInsertPosition.is())
+ {
+ xInsertPosition = xTextAppend->getEnd();
+ }
+ uno::Reference<text::XTextCursor> xCursor
+ = xTextAppend->createTextCursorByRange(xInsertPosition);
+
+ // Assume that we just inserted a tab character.
+ xCursor->goLeft(1, true);
+ if (xCursor->getString() != "\t")
+ {
+ return;
+ }
+
+ // Assume that there is some content before the tab character.
+ uno::Reference<text::XParagraphCursor> xParagraphCursor(xCursor, uno::UNO_QUERY);
+ if (!xParagraphCursor.is())
+ {
+ return;
+ }
+
+ xCursor->collapseToStart();
+ xParagraphCursor->gotoStartOfParagraph(true);
+ if (xCursor->isCollapsed())
+ {
+ return;
+ }
+
+ // Then select the tab again and replace with a line break.
+ xCursor->collapseToEnd();
+ xCursor->goRight(1, true);
+ xTextAppend->insertControlCharacter(xCursor, text::ControlCharacter::LINE_BREAK, true);
+}
+
+void DomainMapper_Impl::HandleLineBreakClear(sal_Int32 nClear)
+{
+ switch (nClear)
+ {
+ case NS_ooxml::LN_Value_ST_BrClear_left:
+ // SwLineBreakClear::LEFT
+ m_oLineBreakClear = 1;
+ break;
+ case NS_ooxml::LN_Value_ST_BrClear_right:
+ // SwLineBreakClear::RIGHT
+ m_oLineBreakClear = 2;
+ break;
+ case NS_ooxml::LN_Value_ST_BrClear_all:
+ // SwLineBreakClear::ALL
+ m_oLineBreakClear = 3;
+ break;
+ }
+}
+
+void DomainMapper_Impl::HandleLineBreak(const PropertyMapPtr& pPropertyMap)
+{
+ if (!m_oLineBreakClear.has_value())
+ {
+ appendTextPortion("\n", pPropertyMap);
+ return;
+ }
+
+ if (GetTextFactory().is())
+ {
+ uno::Reference<text::XTextContent> xLineBreak(
+ GetTextFactory()->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xLineBreakProps(xLineBreak, uno::UNO_QUERY);
+ xLineBreakProps->setPropertyValue("Clear", uno::Any(*m_oLineBreakClear));
+ appendTextContent(xLineBreak, pPropertyMap->GetPropertyValues());
+ }
+ m_oLineBreakClear.reset();
+}
+
+static sal_Int16 lcl_ParseNumberingType( std::u16string_view rCommand )
+{
+ sal_Int16 nRet = style::NumberingType::PAGE_DESCRIPTOR;
+
+ // The command looks like: " PAGE \* Arabic "
+ // tdf#132185: but may as well be "PAGE \* Arabic"
+ OUString sNumber;
+ constexpr OUStringLiteral rSeparator(u"\\* ");
+ if (size_t nStartIndex = rCommand.find(rSeparator); nStartIndex != std::u16string_view::npos)
+ {
+ sal_Int32 nStartIndex2 = nStartIndex + rSeparator.getLength();
+ sNumber = o3tl::getToken(rCommand, 0, ' ', nStartIndex2);
+ }
+
+ if( !sNumber.isEmpty() )
+ {
+ //todo: might make sense to hash this list, too
+ struct NumberingPairs
+ {
+ const char* cWordName;
+ sal_Int16 nType;
+ };
+ static const NumberingPairs aNumberingPairs[] =
+ {
+ {"Arabic", style::NumberingType::ARABIC}
+ ,{"ROMAN", style::NumberingType::ROMAN_UPPER}
+ ,{"roman", style::NumberingType::ROMAN_LOWER}
+ ,{"ALPHABETIC", style::NumberingType::CHARS_UPPER_LETTER}
+ ,{"alphabetic", style::NumberingType::CHARS_LOWER_LETTER}
+ ,{"CircleNum", style::NumberingType::CIRCLE_NUMBER}
+ ,{"ThaiArabic", style::NumberingType::CHARS_THAI}
+ ,{"ThaiCardText", style::NumberingType::CHARS_THAI}
+ ,{"ThaiLetter", style::NumberingType::CHARS_THAI}
+// ,{"SBCHAR", style::NumberingType::}
+// ,{"DBCHAR", style::NumberingType::}
+// ,{"DBNUM1", style::NumberingType::}
+// ,{"DBNUM2", style::NumberingType::}
+// ,{"DBNUM3", style::NumberingType::}
+// ,{"DBNUM4", style::NumberingType::}
+ ,{"Aiueo", style::NumberingType::AIU_FULLWIDTH_JA}
+ ,{"Iroha", style::NumberingType::IROHA_FULLWIDTH_JA}
+// ,{"ZODIAC1", style::NumberingType::}
+// ,{"ZODIAC2", style::NumberingType::}
+// ,{"ZODIAC3", style::NumberingType::}
+// ,{"CHINESENUM1", style::NumberingType::}
+// ,{"CHINESENUM2", style::NumberingType::}
+// ,{"CHINESENUM3", style::NumberingType::}
+ ,{"ArabicAlpha", style::NumberingType::CHARS_ARABIC}
+ ,{"ArabicAbjad", style::NumberingType::FULLWIDTH_ARABIC}
+ ,{"Ganada", style::NumberingType::HANGUL_JAMO_KO}
+ ,{"Chosung", style::NumberingType::HANGUL_SYLLABLE_KO}
+ ,{"KoreanCounting", style::NumberingType::NUMBER_HANGUL_KO}
+ ,{"KoreanLegal", style::NumberingType::NUMBER_LEGAL_KO}
+ ,{"KoreanDigital", style::NumberingType::NUMBER_DIGITAL_KO}
+ ,{"KoreanDigital2", style::NumberingType::NUMBER_DIGITAL2_KO}
+/* possible values:
+style::NumberingType::
+
+ CHARS_UPPER_LETTER_N
+ CHARS_LOWER_LETTER_N
+ TRANSLITERATION
+ NATIVE_NUMBERING
+ CIRCLE_NUMBER
+ NUMBER_LOWER_ZH
+ NUMBER_UPPER_ZH
+ NUMBER_UPPER_ZH_TW
+ TIAN_GAN_ZH
+ DI_ZI_ZH
+ NUMBER_TRADITIONAL_JA
+ AIU_HALFWIDTH_JA
+ IROHA_HALFWIDTH_JA
+ NUMBER_UPPER_KO
+ NUMBER_HANGUL_KO
+ HANGUL_JAMO_KO
+ HANGUL_SYLLABLE_KO
+ HANGUL_CIRCLED_JAMO_KO
+ HANGUL_CIRCLED_SYLLABLE_KO
+ CHARS_HEBREW
+ CHARS_NEPALI
+ CHARS_KHMER
+ CHARS_LAO
+ CHARS_TIBETAN
+ CHARS_CYRILLIC_UPPER_LETTER_BG
+ CHARS_CYRILLIC_LOWER_LETTER_BG
+ CHARS_CYRILLIC_UPPER_LETTER_N_BG
+ CHARS_CYRILLIC_LOWER_LETTER_N_BG
+ CHARS_CYRILLIC_UPPER_LETTER_RU
+ CHARS_CYRILLIC_LOWER_LETTER_RU
+ CHARS_CYRILLIC_UPPER_LETTER_N_RU
+ CHARS_CYRILLIC_LOWER_LETTER_N_RU
+ CHARS_CYRILLIC_UPPER_LETTER_SR
+ CHARS_CYRILLIC_LOWER_LETTER_SR
+ CHARS_CYRILLIC_UPPER_LETTER_N_SR
+ CHARS_CYRILLIC_LOWER_LETTER_N_SR*/
+
+ };
+ for(const NumberingPairs& rNumberingPair : aNumberingPairs)
+ {
+ if( /*sCommand*/sNumber.equalsAscii(rNumberingPair.cWordName ))
+ {
+ nRet = rNumberingPair.nType;
+ break;
+ }
+ }
+
+ }
+ return nRet;
+}
+
+
+static OUString lcl_ParseFormat( const OUString& rCommand )
+{
+ // The command looks like: " DATE \@"dd MMMM yyyy" or "09/02/2014"
+ OUString command;
+ sal_Int32 delimPos = rCommand.indexOf("\\@");
+ if (delimPos != -1)
+ {
+ // Remove whitespace permitted by standard between \@ and "
+ sal_Int32 wsChars = rCommand.indexOf('\"') - delimPos - 2;
+ command = rCommand.replaceAt(delimPos+2, wsChars, u"");
+ return OUString(msfilter::util::findQuotedText(command, "\\@\"", '\"'));
+ }
+
+ return OUString();
+}
+/*-------------------------------------------------------------------------
+extract a parameter (with or without quotes) between the command and the following backslash
+ -----------------------------------------------------------------------*/
+static OUString lcl_ExtractToken(OUString const& rCommand,
+ sal_Int32 & rIndex, bool & rHaveToken, bool & rIsSwitch)
+{
+ rHaveToken = false;
+ rIsSwitch = false;
+
+ OUStringBuffer token;
+ bool bQuoted(false);
+ for (; rIndex < rCommand.getLength(); ++rIndex)
+ {
+ sal_Unicode const currentChar(rCommand[rIndex]);
+ switch (currentChar)
+ {
+ case '\\':
+ {
+ if (rIndex == rCommand.getLength() - 1)
+ {
+ SAL_INFO("writerfilter.dmapper", "field: trailing escape");
+ ++rIndex;
+ return OUString();
+ }
+ sal_Unicode const nextChar(rCommand[rIndex+1]);
+ if (bQuoted || '\\' == nextChar)
+ {
+ ++rIndex; // read 2 chars
+ token.append(nextChar);
+ }
+ else // field switch (case insensitive)
+ {
+ rHaveToken = true;
+ if (token.isEmpty())
+ {
+ rIsSwitch = true;
+ rIndex += 2; // read 2 chars
+ return rCommand.copy(rIndex - 2, 2).toAsciiUpperCase();
+ }
+ else
+ { // leave rIndex, read it again next time
+ return token.makeStringAndClear();
+ }
+ }
+ }
+ break;
+ case '\"':
+ if (bQuoted || !token.isEmpty())
+ {
+ rHaveToken = true;
+ if (bQuoted)
+ {
+ ++rIndex;
+ }
+ return token.makeStringAndClear();
+ }
+ else
+ {
+ bQuoted = true;
+ }
+ break;
+ case ' ':
+ if (bQuoted)
+ {
+ token.append(' ');
+ }
+ else
+ {
+ if (!token.isEmpty())
+ {
+ rHaveToken = true;
+ ++rIndex;
+ return token.makeStringAndClear();
+ }
+ }
+ break;
+ case '=':
+ if (token.isEmpty())
+ {
+ rHaveToken = true;
+ ++rIndex;
+ return "FORMULA";
+ }
+ else
+ token.append('=');
+ break;
+ default:
+ token.append(currentChar);
+ break;
+ }
+ }
+ assert(rIndex == rCommand.getLength());
+ if (bQuoted)
+ {
+ // MS Word allows this, so just emit a debug message
+ SAL_INFO("writerfilter.dmapper",
+ "field argument with unterminated quote");
+ }
+ rHaveToken = !token.isEmpty();
+ return token.makeStringAndClear();
+}
+
+std::tuple<OUString, std::vector<OUString>, std::vector<OUString> > splitFieldCommand(const OUString& rCommand)
+{
+ OUString sType;
+ std::vector<OUString> arguments;
+ std::vector<OUString> switches;
+ sal_Int32 nStartIndex(0);
+ // tdf#54584: Field may be prepended by a backslash
+ // This is not an escapement, but already escaped literal "\"
+ // MS Word allows this, so just skip it
+ if ((rCommand.getLength() >= nStartIndex + 2) &&
+ (rCommand[nStartIndex] == L'\\') &&
+ (rCommand[nStartIndex + 1] != L'\\') &&
+ (rCommand[nStartIndex + 1] != L' '))
+ {
+ ++nStartIndex;
+ }
+
+ do
+ {
+ bool bHaveToken;
+ bool bIsSwitch;
+ OUString const token =
+ lcl_ExtractToken(rCommand, nStartIndex, bHaveToken, bIsSwitch);
+ assert(nStartIndex <= rCommand.getLength());
+ if (bHaveToken)
+ {
+ if (sType.isEmpty())
+ {
+ sType = token.toAsciiUpperCase();
+ }
+ else if (bIsSwitch || !switches.empty())
+ {
+ switches.push_back(token);
+ }
+ else
+ {
+ arguments.push_back(token);
+ }
+ }
+ } while (nStartIndex < rCommand.getLength());
+
+ return std::make_tuple(sType, arguments, switches);
+}
+
+static OUString lcl_ExtractVariableAndHint( std::u16string_view rCommand, OUString& rHint )
+{
+ // the first word after "ASK " is the variable
+ // the text after the variable and before a '\' is the hint
+ // if no hint is set the variable is used as hint
+ // the quotes of the hint have to be removed
+ size_t nIndex = rCommand.find( ' ', 2); //find last space after 'ASK'
+ if (nIndex == std::u16string_view::npos)
+ return OUString();
+ while (nIndex < rCommand.size() && rCommand[nIndex] == ' ')
+ ++nIndex;
+ std::u16string_view sShortCommand( rCommand.substr( nIndex ) ); //cut off the " ASK "
+
+ sShortCommand = o3tl::getToken(sShortCommand, 0, '\\');
+ sal_Int32 nIndex2 = 0;
+ std::u16string_view sRet = o3tl::getToken(sShortCommand, 0, ' ', nIndex2);
+ if( nIndex2 > 0)
+ rHint = sShortCommand.substr( nIndex2 );
+ if( rHint.isEmpty() )
+ rHint = sRet;
+ return OUString(sRet);
+}
+
+
+static bool lcl_FindInCommand(
+ const OUString& rCommand,
+ sal_Unicode cSwitch,
+ OUString& rValue )
+{
+ bool bRet = false;
+ OUString sSearch = "\\" + OUStringChar( cSwitch );
+ sal_Int32 nIndex = rCommand.indexOf( sSearch );
+ if( nIndex >= 0 )
+ {
+ bRet = true;
+ //find next '\' or end of string
+ sal_Int32 nEndIndex = rCommand.indexOf( '\\', nIndex + 1);
+ if( nEndIndex < 0 )
+ nEndIndex = rCommand.getLength() ;
+ if( nEndIndex - nIndex > 3 )
+ rValue = rCommand.copy( nIndex + 3, nEndIndex - nIndex - 3);
+ }
+ return bRet;
+}
+
+static OUString lcl_trim(std::u16string_view sValue)
+{
+ // it seems, all kind of quotation marks are allowed around index type identifiers
+ // TODO apply this on bookmarks, too, if needed
+ return OUString(o3tl::trim(sValue)).replaceAll("\"","").replaceAll(u"“", "").replaceAll(u"”", "");
+}
+
+/*-------------------------------------------------------------------------
+ extract the number format from the command and apply the resulting number
+ format to the XPropertySet
+ -----------------------------------------------------------------------*/
+void DomainMapper_Impl::SetNumberFormat( const OUString& rCommand,
+ uno::Reference< beans::XPropertySet > const& xPropertySet,
+ bool const bDetectFormat)
+{
+ OUString sFormatString = lcl_ParseFormat( rCommand );
+ // find \h - hijri/luna calendar todo: what about saka/era calendar?
+ bool bHijri = 0 < rCommand.indexOf("\\h ");
+ lang::Locale aUSLocale;
+ aUSLocale.Language = "en";
+ aUSLocale.Country = "US";
+
+ lang::Locale aCurrentLocale;
+ GetAnyProperty(PROP_CHAR_LOCALE, GetTopContext()) >>= aCurrentLocale;
+
+ if (sFormatString.isEmpty())
+ {
+ // No format specified. MS Word uses different formats depending on w:lang,
+ // "M/d/yyyy h:mm:ss AM/PM" for en-US, and "dd/MM/yyyy hh:mm:ss AM/PM" for en-GB.
+ // ALSO SEE: ww8par5's GetWordDefaultDateStringAsUS.
+ sal_Int32 nPos = rCommand.indexOf(" \\");
+ OUString sCommand = nPos == -1 ? rCommand.trim()
+ : OUString(o3tl::trim(rCommand.subView(0, nPos)));
+ if (sCommand == "CREATEDATE" || sCommand == "PRINTDATE" || sCommand == "SAVEDATE")
+ {
+ try
+ {
+ css::uno::Reference<css::i18n::XNumberFormatCode> const& xNumberFormatCode =
+ i18n::NumberFormatMapper::create(m_xComponentContext);
+ sFormatString = xNumberFormatCode->getFormatCode(
+ css::i18n::NumberFormatIndex::DATE_SYSTEM_SHORT, aCurrentLocale).Code;
+ nPos = sFormatString.indexOf("YYYY");
+ if (nPos == -1)
+ sFormatString = sFormatString.replaceFirst("YY", "YYYY");
+ if (aCurrentLocale == aUSLocale)
+ sFormatString += " h:mm:ss AM/PM";
+ else
+ sFormatString += " hh:mm:ss AM/PM";
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
+ }
+ }
+ }
+ OUString sFormat = ConversionHelper::ConvertMSFormatStringToSO( sFormatString, aCurrentLocale, bHijri);
+ //get the number formatter and convert the string to a format value
+ try
+ {
+ sal_Int32 nKey = 0;
+ uno::Reference< util::XNumberFormatsSupplier > xNumberSupplier( m_xTextDocument, uno::UNO_QUERY_THROW );
+ if( bDetectFormat )
+ {
+ uno::Reference< util::XNumberFormatter> xFormatter(util::NumberFormatter::create(m_xComponentContext), uno::UNO_QUERY_THROW);
+ xFormatter->attachNumberFormatsSupplier( xNumberSupplier );
+ nKey = xFormatter->detectNumberFormat( 0, rCommand );
+ }
+ else
+ {
+ nKey = xNumberSupplier->getNumberFormats()->addNewConverted( sFormat, aUSLocale, aCurrentLocale );
+ }
+ xPropertySet->setPropertyValue(
+ getPropertyName(PROP_NUMBER_FORMAT),
+ uno::Any( nKey ));
+ }
+ catch(const uno::Exception&)
+ {
+ }
+}
+
+static uno::Any lcl_getGrabBagValue( const uno::Sequence<beans::PropertyValue>& grabBag, OUString const & name )
+{
+ auto pProp = std::find_if(grabBag.begin(), grabBag.end(),
+ [&name](const beans::PropertyValue& rProp) { return rProp.Name == name; });
+ if (pProp != grabBag.end())
+ return pProp->Value;
+ return uno::Any();
+}
+
+//Link the text frames.
+void DomainMapper_Impl::ChainTextFrames()
+{
+ //can't link textboxes if there are not even two of them...
+ if( 2 > m_vTextFramesForChaining.size() )
+ return ;
+
+ struct TextFramesForChaining {
+ css::uno::Reference< css::drawing::XShape > xShape;
+ sal_Int32 nId;
+ sal_Int32 nSeq;
+ OUString s_mso_next_textbox;
+ OUString shapeName;
+ TextFramesForChaining() : nId(0), nSeq(0) {}
+ } ;
+ typedef std::map <OUString, TextFramesForChaining> ChainMap;
+
+ try
+ {
+ ChainMap aTextFramesForChainingHelper;
+ ::std::vector<TextFramesForChaining> chainingWPS;
+ OUString sChainNextName("ChainNextName");
+
+ //learn about ALL of the textboxes and their chaining values first - because frames are processed in no specific order.
+ for( const auto& rTextFrame : m_vTextFramesForChaining )
+ {
+ uno::Reference<text::XTextContent> xTextContent(rTextFrame, uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xPropertySetInfo;
+ if( xPropertySet.is() )
+ xPropertySetInfo = xPropertySet->getPropertySetInfo();
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xPropertySet, uno::UNO_QUERY);
+
+ TextFramesForChaining aChainStruct;
+ OUString sShapeName;
+ OUString sLinkChainName;
+
+ //The chaining name and the shape name CAN be different in .docx.
+ //MUST use LinkDisplayName/ChainName as the shape name for establishing links.
+ if ( xServiceInfo->supportsService("com.sun.star.text.TextFrame") )
+ {
+ xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
+ xPropertySet->getPropertyValue("LinkDisplayName") >>= sShapeName;
+ }
+ else
+ {
+ xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
+ xPropertySet->getPropertyValue("ChainName") >>= sShapeName;
+ }
+
+ lcl_getGrabBagValue( aGrabBag, "Txbx-Id") >>= aChainStruct.nId;
+ lcl_getGrabBagValue( aGrabBag, "Txbx-Seq") >>= aChainStruct.nSeq;
+ lcl_getGrabBagValue( aGrabBag, "LinkChainName") >>= sLinkChainName;
+ lcl_getGrabBagValue( aGrabBag, "mso-next-textbox") >>= aChainStruct.s_mso_next_textbox;
+
+ //Sometimes the shape names have not been imported. If not, we may have a fallback name.
+ //Set name later, only if required for linking.
+ aChainStruct.shapeName = sShapeName;
+
+ if (!sLinkChainName.isEmpty())
+ {
+ aChainStruct.xShape = rTextFrame;
+ aTextFramesForChainingHelper[sLinkChainName] = aChainStruct;
+ }
+ if (aChainStruct.s_mso_next_textbox.isEmpty())
+ { // no VML chaining => try to chain DrawingML via IDs
+ aChainStruct.xShape = rTextFrame;
+ if (!sLinkChainName.isEmpty())
+ { // for member of group shapes, TestTdf73499
+ aChainStruct.shapeName = sLinkChainName;
+ }
+ chainingWPS.emplace_back(aChainStruct);
+ }
+ }
+
+ //if mso-next-textbox tags are provided, create those vml-style links first. Afterwards we will make dml-style id/seq links.
+ for (auto& msoItem : aTextFramesForChainingHelper)
+ {
+ //if no mso-next-textbox, we are done.
+ //if it points to itself, we are done.
+ if( !msoItem.second.s_mso_next_textbox.isEmpty()
+ && msoItem.second.s_mso_next_textbox != msoItem.first )
+ {
+ ChainMap::iterator nextFinder=aTextFramesForChainingHelper.find(msoItem.second.s_mso_next_textbox);
+ if( nextFinder != aTextFramesForChainingHelper.end() )
+ {
+ //if the frames have no name yet, then set them. LinkDisplayName / ChainName are read-only.
+ if (msoItem.second.shapeName.isEmpty())
+ {
+ uno::Reference< container::XNamed > xNamed( msoItem.second.xShape, uno::UNO_QUERY );
+ if ( xNamed.is() )
+ {
+ xNamed->setName( msoItem.first );
+ msoItem.second.shapeName = msoItem.first;
+ }
+ }
+ if (nextFinder->second.shapeName.isEmpty())
+ {
+ uno::Reference< container::XNamed > xNamed( nextFinder->second.xShape, uno::UNO_QUERY );
+ if ( xNamed.is() )
+ {
+ xNamed->setName( nextFinder->first );
+ nextFinder->second.shapeName = msoItem.first;
+ }
+ }
+
+ uno::Reference<text::XTextContent> xTextContent(msoItem.second.xShape, uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
+
+ //The reverse chaining happens automatically, so only one direction needs to be set
+ xPropertySet->setPropertyValue(sChainNextName, uno::Any(nextFinder->second.shapeName));
+
+ //the last item in an mso-next-textbox chain is indistinguishable from id/seq items. Now that it is handled, remove it.
+ if( nextFinder->second.s_mso_next_textbox.isEmpty() )
+ aTextFramesForChainingHelper.erase(nextFinder->first);
+ }
+ }
+ }
+
+ //TODO: Perhaps allow reverse sequences when mso-layout-flow-alt = "bottom-to-top"
+ const sal_Int32 nDirection = 1;
+
+ //Finally - go through and attach the chains based on matching ID and incremented sequence number (dml-style).
+ for (const auto& rOuter : chainingWPS)
+ {
+ for (const auto& rInner : chainingWPS)
+ {
+ if (rInner.nId == rOuter.nId)
+ {
+ if (rInner.nSeq == (rOuter.nSeq + nDirection))
+ {
+ uno::Reference<text::XTextContent> const xTextContent(rOuter.xShape, uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
+
+ //The reverse chaining happens automatically, so only one direction needs to be set
+ xPropertySet->setPropertyValue(sChainNextName, uno::Any(rInner.shapeName));
+ break ; //there cannot be more than one next frame
+ }
+ }
+ }
+ }
+ m_vTextFramesForChaining.clear(); //clear the vector
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
+ }
+}
+
+void DomainMapper_Impl::PushTextBoxContent()
+{
+ if (m_bIsInTextBox)
+ return;
+
+ // tdf#154481: check for TOC creation with empty field stack,
+ // and close TOC, unless pages will lost. FIXME.
+ if (IsInTOC() && m_aFieldStack.size() == 0)
+ {
+ m_bStartTOC = false;
+ SAL_WARN("writerfilter.dmapper",
+ "broken TOC creation in textbox, but field stack is empty, so closing TOC!");
+ }
+
+ try
+ {
+ uno::Reference<text::XTextFrame> xTBoxFrame(
+ m_xTextFactory->createInstance("com.sun.star.text.TextFrame"), uno::UNO_QUERY_THROW);
+ uno::Reference<container::XNamed>(xTBoxFrame, uno::UNO_QUERY_THROW)
+ ->setName("textbox" + OUString::number(m_xPendingTextBoxFrames.size() + 1));
+ uno::Reference<text::XTextAppendAndConvert>(m_aTextAppendStack.top().xTextAppend,
+ uno::UNO_QUERY_THROW)
+ ->appendTextContent(xTBoxFrame, beans::PropertyValues());
+ m_xPendingTextBoxFrames.push(xTBoxFrame);
+
+ m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xTBoxFrame, uno::UNO_QUERY_THROW), {}));
+ m_bIsInTextBox = true;
+
+ appendTableManager();
+ appendTableHandler();
+ getTableManager().startLevel();
+ }
+ catch (uno::Exception& e)
+ {
+ SAL_WARN("writerfilter.dmapper", "Exception during creating textbox (" + e.Message + ")!");
+ }
+}
+
+void DomainMapper_Impl::PopTextBoxContent()
+{
+ if (!m_bIsInTextBox || m_xPendingTextBoxFrames.empty())
+ return;
+
+ if (uno::Reference<text::XTextFrame>(m_aTextAppendStack.top().xTextAppend, uno::UNO_QUERY).is())
+ {
+ if (hasTableManager())
+ {
+ getTableManager().endLevel();
+ popTableManager();
+ }
+ RemoveLastParagraph();
+
+ m_aTextAppendStack.pop();
+ m_bIsInTextBox = false;
+ }
+}
+
+void DomainMapper_Impl::AttachTextBoxContentToShape(css::uno::Reference<css::drawing::XShape> xShape)
+{
+ // Without textbox or shape pointless to continue
+ if (m_xPendingTextBoxFrames.empty() || !xShape)
+ return;
+
+ uno::Reference< drawing::XShapes >xGroup(xShape, uno::UNO_QUERY);
+ uno::Reference< beans::XPropertySet >xProps(xShape, uno::UNO_QUERY);
+
+ // If this is a group go inside
+ if (xGroup)
+ for (sal_Int32 i = 0; i < xGroup->getCount(); ++i)
+ AttachTextBoxContentToShape(
+ uno::Reference<drawing::XShape>(xGroup->getByIndex(i), uno::UNO_QUERY));
+
+ // if this shape has to be a textbox, attach the frame
+ if (!xProps->getPropertyValue("TextBox").get<bool>())
+ return;
+
+ // if this is a textbox there must be a waiting frame
+ auto xTextBox = m_xPendingTextBoxFrames.front();
+ if (!xTextBox)
+ return;
+
+ // Pop the pending frames
+ m_xPendingTextBoxFrames.pop();
+
+ // Attach the textbox to the shape
+ try
+ {
+ xProps->setPropertyValue("TextBoxContent", uno::Any(xTextBox));
+ }
+ catch (...)
+ {
+ SAL_WARN("writerfilter.dmapper", "Exception while trying to attach textboxes!");
+ return;
+ }
+
+ // If attaching is successful, then do the linking
+ try
+ {
+ // Get the name of the textbox
+ OUString sTextBoxName;
+ uno::Reference<container::XNamed> xName(xTextBox, uno::UNO_QUERY);
+ if (xName && !xName->getName().isEmpty())
+ sTextBoxName = xName->getName();
+
+ // Try to get the grabbag
+ uno::Sequence<beans::PropertyValue> aOldGrabBagSeq;
+ if (xProps->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
+ xProps->getPropertyValue("InteropGrabBag") >>= aOldGrabBagSeq;
+
+ // If the grabbag successfully get...
+ if (!aOldGrabBagSeq.hasElements())
+ return;
+
+ // Check for the existing linking information
+ bool bSuccess = false;
+ beans::PropertyValues aNewGrabBagSeq;
+ const auto& aHasLink = lcl_getGrabBagValue(aOldGrabBagSeq, "TxbxHasLink");
+
+ // If there must be a link, do it
+ if (aHasLink.hasValue() && aHasLink.get<bool>())
+ {
+ auto aLinkProp = comphelper::makePropertyValue("LinkChainName", sTextBoxName);
+ for (sal_uInt32 i = 0; i < aOldGrabBagSeq.size(); ++i)
+ {
+ aNewGrabBagSeq.realloc(i + 1);
+ // If this is the link name replace it
+ if (!aOldGrabBagSeq[i].Name.isEmpty() && !aLinkProp.Name.isEmpty()
+ && (aOldGrabBagSeq[i].Name == aLinkProp.Name))
+ {
+ aNewGrabBagSeq.getArray()[i] = aLinkProp;
+ bSuccess = true;
+ }
+ // else copy
+ else
+ aNewGrabBagSeq.getArray()[i] = aOldGrabBagSeq[i];
+ }
+
+ // If there was no replacement, append the linking data
+ if (!bSuccess)
+ {
+ aNewGrabBagSeq.realloc(aNewGrabBagSeq.size() + 1);
+ aNewGrabBagSeq.getArray()[aNewGrabBagSeq.size() - 1] = aLinkProp;
+ bSuccess = true;
+ }
+ }
+
+ // If the linking changed the grabbag, apply the modifications
+ if (aNewGrabBagSeq.hasElements() && bSuccess)
+ {
+ xProps->setPropertyValue("InteropGrabBag", uno::Any(aNewGrabBagSeq));
+ m_vTextFramesForChaining.push_back(xShape);
+ }
+ }
+ catch (...)
+ {
+ SAL_WARN("writerfilter.dmapper", "Exception while trying to link textboxes!");
+ }
+}
+
+uno::Reference<beans::XPropertySet> DomainMapper_Impl::FindOrCreateFieldMaster(const char* pFieldMasterService, const OUString& rFieldMasterName)
+{
+ // query master, create if not available
+ uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
+ uno::Reference< beans::XPropertySet > xMaster;
+ OUString sFieldMasterService( OUString::createFromAscii(pFieldMasterService) );
+ OUStringBuffer aFieldMasterName;
+ OUString sDatabaseDataSourceName = GetSettingsTable()->GetCurrentDatabaseDataSource();
+ bool bIsMergeField = sFieldMasterService.endsWith("Database");
+ aFieldMasterName.appendAscii( pFieldMasterService );
+ aFieldMasterName.append('.');
+ if ( bIsMergeField && !sDatabaseDataSourceName.isEmpty() )
+ {
+ aFieldMasterName.append(sDatabaseDataSourceName);
+ aFieldMasterName.append('.');
+ }
+ aFieldMasterName.append(rFieldMasterName);
+ OUString sFieldMasterName = aFieldMasterName.makeStringAndClear();
+ if(xFieldMasterAccess->hasByName(sFieldMasterName))
+ {
+ //get the master
+ xMaster.set(xFieldMasterAccess->getByName(sFieldMasterName), uno::UNO_QUERY_THROW);
+ }
+ else if( m_xTextFactory.is() )
+ {
+ //create the master
+ xMaster.set( m_xTextFactory->createInstance(sFieldMasterService), uno::UNO_QUERY_THROW);
+ if ( !bIsMergeField || sDatabaseDataSourceName.isEmpty() )
+ {
+ //set the master's name
+ xMaster->setPropertyValue(
+ getPropertyName(PROP_NAME),
+ uno::Any(rFieldMasterName));
+ } else {
+ // set database data, based on the "databasename.tablename" of sDatabaseDataSourceName
+ xMaster->setPropertyValue(
+ getPropertyName(PROP_DATABASE_NAME),
+ uno::Any(sDatabaseDataSourceName.copy(0, sDatabaseDataSourceName.indexOf('.'))));
+ xMaster->setPropertyValue(
+ getPropertyName(PROP_COMMAND_TYPE),
+ uno::Any(sal_Int32(0)));
+ xMaster->setPropertyValue(
+ getPropertyName(PROP_DATATABLE_NAME),
+ uno::Any(sDatabaseDataSourceName.copy(sDatabaseDataSourceName.indexOf('.') + 1)));
+ xMaster->setPropertyValue(
+ getPropertyName(PROP_DATACOLUMN_NAME),
+ uno::Any(rFieldMasterName));
+ }
+ }
+ return xMaster;
+}
+
+void DomainMapper_Impl::PushFieldContext()
+{
+ m_bParaHadField = true;
+ if(m_bDiscardHeaderFooter)
+ return;
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("pushFieldContext");
+#endif
+
+ uno::Reference<text::XTextCursor> xCrsr;
+ if (!m_aTextAppendStack.empty())
+ {
+ uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (xTextAppend.is())
+ xCrsr = xTextAppend->createTextCursorByRange(
+ m_aTextAppendStack.top().xInsertPosition.is()
+ ? m_aTextAppendStack.top().xInsertPosition
+ : xTextAppend->getEnd());
+ }
+
+ uno::Reference< text::XTextRange > xStart;
+ if (xCrsr.is())
+ xStart = xCrsr->getStart();
+ m_aFieldStack.push_back(new FieldContext(xStart));
+}
+/*-------------------------------------------------------------------------
+//the current field context waits for the completion of the command
+ -----------------------------------------------------------------------*/
+bool DomainMapper_Impl::IsOpenFieldCommand() const
+{
+ return !m_aFieldStack.empty() && !m_aFieldStack.back()->IsCommandCompleted();
+}
+/*-------------------------------------------------------------------------
+//the current field context waits for the completion of the command
+ -----------------------------------------------------------------------*/
+bool DomainMapper_Impl::IsOpenField() const
+{
+ return !m_aFieldStack.empty();
+}
+
+// Mark top field context as containing a fixed field
+void DomainMapper_Impl::SetFieldLocked()
+{
+ if (IsOpenField())
+ m_aFieldStack.back()->SetFieldLocked();
+}
+
+HeaderFooterContext::HeaderFooterContext(bool bTextInserted, sal_Int32 nTableDepth)
+ : m_bTextInserted(bTextInserted)
+ , m_nTableDepth(nTableDepth)
+{
+}
+
+bool HeaderFooterContext::getTextInserted() const
+{
+ return m_bTextInserted;
+}
+
+sal_Int32 HeaderFooterContext::getTableDepth() const { return m_nTableDepth; }
+
+FieldContext::FieldContext(uno::Reference< text::XTextRange > const& xStart)
+ : m_bFieldCommandCompleted(false)
+ , m_xStartRange( xStart )
+ , m_bFieldLocked( false )
+{
+ m_pProperties = new PropertyMap();
+}
+
+
+FieldContext::~FieldContext()
+{
+}
+
+void FieldContext::SetTextField(uno::Reference<text::XTextField> const& xTextField)
+{
+#ifndef NDEBUG
+ if (xTextField.is())
+ {
+ uno::Reference<lang::XServiceInfo> const xServiceInfo(xTextField, uno::UNO_QUERY);
+ assert(xServiceInfo.is());
+ // those must be set by SetFormField()
+ assert(!xServiceInfo->supportsService("com.sun.star.text.Fieldmark")
+ && !xServiceInfo->supportsService("com.sun.star.text.FormFieldmark"));
+ }
+#endif
+ m_xTextField = xTextField;
+}
+
+void FieldContext::CacheVariableValue(const uno::Any& rAny)
+{
+ rAny >>= m_sVariableValue;
+}
+
+void FieldContext::AppendCommand(std::u16string_view rPart)
+{
+ m_sCommand += rPart;
+}
+
+::std::vector<OUString> FieldContext::GetCommandParts() const
+{
+ ::std::vector<OUString> aResult;
+ sal_Int32 nIndex = 0;
+ bool bInString = false;
+ OUString sPart;
+ while (nIndex != -1)
+ {
+ OUString sToken = GetCommand().getToken(0, ' ', nIndex);
+ bool bInStringNext = bInString;
+
+ if (sToken.isEmpty())
+ continue;
+
+ if (sToken[0] == '"')
+ {
+ bInStringNext = true;
+ sToken = sToken.copy(1);
+ }
+ if (sToken.endsWith("\""))
+ {
+ bInStringNext = false;
+ sToken = sToken.copy(0, sToken.getLength() - 1);
+ }
+
+ if (bInString)
+ {
+ sPart += " " + sToken;
+ if (!bInStringNext)
+ {
+ aResult.push_back(sPart);
+ }
+ }
+ else
+ {
+ if (bInStringNext)
+ {
+ sPart = sToken;
+ }
+ else
+ {
+ aResult.push_back(sToken);
+ }
+ }
+
+ bInString = bInStringNext;
+ }
+
+ return aResult;
+}
+
+/*-------------------------------------------------------------------------
+//collect the pieces of the command
+ -----------------------------------------------------------------------*/
+void DomainMapper_Impl::AppendFieldCommand(OUString const & rPartOfCommand)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("appendFieldCommand");
+ TagLogger::getInstance().chars(rPartOfCommand);
+ TagLogger::getInstance().endElement();
+#endif
+
+ FieldContextPtr pContext = m_aFieldStack.back();
+ OSL_ENSURE( pContext, "no field context available");
+ if( pContext )
+ {
+ pContext->AppendCommand( rPartOfCommand );
+ }
+}
+
+
+typedef std::multimap < sal_Int32, OUString > TOCStyleMap;
+
+
+static ww::eField GetWW8FieldId(OUString const& rType)
+{
+ std::unordered_map<OUString, ww::eField> mapID
+ {
+ {"ADDRESSBLOCK", ww::eADDRESSBLOCK},
+ {"ADVANCE", ww::eADVANCE},
+ {"ASK", ww::eASK},
+ {"AUTONUM", ww::eAUTONUM},
+ {"AUTONUMLGL", ww::eAUTONUMLGL},
+ {"AUTONUMOUT", ww::eAUTONUMOUT},
+ {"AUTOTEXT", ww::eAUTOTEXT},
+ {"AUTOTEXTLIST", ww::eAUTOTEXTLIST},
+ {"AUTHOR", ww::eAUTHOR},
+ {"BARCODE", ww::eBARCODE},
+ {"BIDIOUTLINE", ww::eBIDIOUTLINE},
+ {"DATE", ww::eDATE},
+ {"COMMENTS", ww::eCOMMENTS},
+ {"COMPARE", ww::eCOMPARE},
+ {"CONTROL", ww::eCONTROL},
+ {"CREATEDATE", ww::eCREATEDATE},
+ {"DATABASE", ww::eDATABASE},
+ {"DDEAUTOREF", ww::eDDEAUTOREF},
+ {"DDEREF", ww::eDDEREF},
+ {"DOCPROPERTY", ww::eDOCPROPERTY},
+ {"DOCVARIABLE", ww::eDOCVARIABLE},
+ {"EDITTIME", ww::eEDITTIME},
+ {"EMBED", ww::eEMBED},
+ {"EQ", ww::eEQ},
+ {"FILLIN", ww::eFILLIN},
+ {"FILENAME", ww::eFILENAME},
+ {"FILESIZE", ww::eFILESIZE},
+ {"FOOTREF", ww::eFOOTREF},
+// {"FORMULA", ww::},
+ {"FORMCHECKBOX", ww::eFORMCHECKBOX},
+ {"FORMDROPDOWN", ww::eFORMDROPDOWN},
+ {"FORMTEXT", ww::eFORMTEXT},
+ {"GLOSSREF", ww::eGLOSSREF},
+ {"GOTOBUTTON", ww::eGOTOBUTTON},
+ {"GREETINGLINE", ww::eGREETINGLINE},
+ {"HTMLCONTROL", ww::eHTMLCONTROL},
+ {"HYPERLINK", ww::eHYPERLINK},
+ {"IF", ww::eIF},
+ {"INFO", ww::eINFO},
+ {"INCLUDEPICTURE", ww::eINCLUDEPICTURE},
+ {"INCLUDETEXT", ww::eINCLUDETEXT},
+ {"INCLUDETIFF", ww::eINCLUDETIFF},
+ {"KEYWORDS", ww::eKEYWORDS},
+ {"LASTSAVEDBY", ww::eLASTSAVEDBY},
+ {"LINK", ww::eLINK},
+ {"LISTNUM", ww::eLISTNUM},
+ {"MACRO", ww::eMACRO},
+ {"MACROBUTTON", ww::eMACROBUTTON},
+ {"MERGEDATA", ww::eMERGEDATA},
+ {"MERGEFIELD", ww::eMERGEFIELD},
+ {"MERGEINC", ww::eMERGEINC},
+ {"MERGEREC", ww::eMERGEREC},
+ {"MERGESEQ", ww::eMERGESEQ},
+ {"NEXT", ww::eNEXT},
+ {"NEXTIF", ww::eNEXTIF},
+ {"NOTEREF", ww::eNOTEREF},
+ {"PAGE", ww::ePAGE},
+ {"PAGEREF", ww::ePAGEREF},
+ {"PLUGIN", ww::ePLUGIN},
+ {"PRINT", ww::ePRINT},
+ {"PRINTDATE", ww::ePRINTDATE},
+ {"PRIVATE", ww::ePRIVATE},
+ {"QUOTE", ww::eQUOTE},
+ {"RD", ww::eRD},
+ {"REF", ww::eREF},
+ {"REVNUM", ww::eREVNUM},
+ {"SAVEDATE", ww::eSAVEDATE},
+ {"SECTION", ww::eSECTION},
+ {"SECTIONPAGES", ww::eSECTIONPAGES},
+ {"SEQ", ww::eSEQ},
+ {"SET", ww::eSET},
+ {"SKIPIF", ww::eSKIPIF},
+ {"STYLEREF", ww::eSTYLEREF},
+ {"SUBSCRIBER", ww::eSUBSCRIBER},
+ {"SUBJECT", ww::eSUBJECT},
+ {"SYMBOL", ww::eSYMBOL},
+ {"TA", ww::eTA},
+ {"TEMPLATE", ww::eTEMPLATE},
+ {"TIME", ww::eTIME},
+ {"TITLE", ww::eTITLE},
+ {"TOA", ww::eTOA},
+ {"USERINITIALS", ww::eUSERINITIALS},
+ {"USERADDRESS", ww::eUSERADDRESS},
+ {"USERNAME", ww::eUSERNAME},
+
+ {"TOC", ww::eTOC},
+ {"TC", ww::eTC},
+ {"NUMCHARS", ww::eNUMCHARS},
+ {"NUMWORDS", ww::eNUMWORDS},
+ {"NUMPAGES", ww::eNUMPAGES},
+ {"INDEX", ww::eINDEX},
+ {"XE", ww::eXE},
+ {"BIBLIOGRAPHY", ww::eBIBLIOGRAPHY},
+ {"CITATION", ww::eCITATION},
+ };
+ auto const it = mapID.find(rType);
+ return (it == mapID.end()) ? ww::eNONE : it->second;
+}
+
+static const FieldConversionMap_t & lcl_GetFieldConversion()
+{
+ static const FieldConversionMap_t aFieldConversionMap
+ {
+// {"ADDRESSBLOCK", {"", FIELD_ADDRESSBLOCK }},
+// {"ADVANCE", {"", FIELD_ADVANCE }},
+ {"ASK", {"SetExpression", FIELD_ASK }},
+ {"AUTONUM", {"SetExpression", FIELD_AUTONUM }},
+ {"AUTONUMLGL", {"SetExpression", FIELD_AUTONUMLGL }},
+ {"AUTONUMOUT", {"SetExpression", FIELD_AUTONUMOUT }},
+ {"AUTHOR", {"DocInfo.CreateAuthor", FIELD_AUTHOR }},
+ {"DATE", {"DateTime", FIELD_DATE }},
+ {"COMMENTS", {"DocInfo.Description", FIELD_COMMENTS }},
+ {"CREATEDATE", {"DocInfo.CreateDateTime", FIELD_CREATEDATE }},
+ {"DOCPROPERTY", {"", FIELD_DOCPROPERTY }},
+ {"DOCVARIABLE", {"User", FIELD_DOCVARIABLE }},
+ {"EDITTIME", {"DocInfo.EditTime", FIELD_EDITTIME }},
+ {"EQ", {"", FIELD_EQ }},
+ {"FILLIN", {"Input", FIELD_FILLIN }},
+ {"FILENAME", {"FileName", FIELD_FILENAME }},
+// {"FILESIZE", {"", FIELD_FILESIZE }},
+ {"FORMULA", {"TableFormula", FIELD_FORMULA }},
+ {"FORMCHECKBOX", {"", FIELD_FORMCHECKBOX }},
+ {"FORMDROPDOWN", {"DropDown", FIELD_FORMDROPDOWN }},
+ {"FORMTEXT", {"Input", FIELD_FORMTEXT }},
+ {"GOTOBUTTON", {"", FIELD_GOTOBUTTON }},
+ {"HYPERLINK", {"", FIELD_HYPERLINK }},
+ {"IF", {"ConditionalText", FIELD_IF }},
+// {"INFO", {"", FIELD_INFO }},
+ {"INCLUDEPICTURE", {"", FIELD_INCLUDEPICTURE}},
+ {"KEYWORDS", {"DocInfo.KeyWords", FIELD_KEYWORDS }},
+ {"LASTSAVEDBY", {"DocInfo.ChangeAuthor", FIELD_LASTSAVEDBY }},
+ {"MACROBUTTON", {"Macro", FIELD_MACROBUTTON }},
+ {"MERGEFIELD", {"Database", FIELD_MERGEFIELD }},
+ {"MERGEREC", {"DatabaseNumberOfSet", FIELD_MERGEREC }},
+// {"MERGESEQ", {"", FIELD_MERGESEQ }},
+ {"NEXT", {"DatabaseNextSet", FIELD_NEXT }},
+ {"NEXTIF", {"DatabaseNextSet", FIELD_NEXTIF }},
+ {"PAGE", {"PageNumber", FIELD_PAGE }},
+ {"PAGEREF", {"GetReference", FIELD_PAGEREF }},
+ {"PRINTDATE", {"DocInfo.PrintDateTime", FIELD_PRINTDATE }},
+ {"REF", {"GetReference", FIELD_REF }},
+ {"REVNUM", {"DocInfo.Revision", FIELD_REVNUM }},
+ {"SAVEDATE", {"DocInfo.ChangeDateTime", FIELD_SAVEDATE }},
+// {"SECTION", {"", FIELD_SECTION }},
+// {"SECTIONPAGES", {"", FIELD_SECTIONPAGES }},
+ {"SEQ", {"SetExpression", FIELD_SEQ }},
+ {"SET", {"SetExpression", FIELD_SET }},
+// {"SKIPIF", {"", FIELD_SKIPIF }},
+// {"STYLEREF", {"", FIELD_STYLEREF }},
+ {"SUBJECT", {"DocInfo.Subject", FIELD_SUBJECT }},
+ {"SYMBOL", {"", FIELD_SYMBOL }},
+ {"TEMPLATE", {"TemplateName", FIELD_TEMPLATE }},
+ {"TIME", {"DateTime", FIELD_TIME }},
+ {"TITLE", {"DocInfo.Title", FIELD_TITLE }},
+ {"USERINITIALS", {"Author", FIELD_USERINITIALS }},
+// {"USERADDRESS", {"", FIELD_USERADDRESS }},
+ {"USERNAME", {"Author", FIELD_USERNAME }},
+
+
+ {"TOC", {"com.sun.star.text.ContentIndex", FIELD_TOC }},
+ {"TC", {"com.sun.star.text.ContentIndexMark", FIELD_TC }},
+ {"NUMCHARS", {"CharacterCount", FIELD_NUMCHARS }},
+ {"NUMWORDS", {"WordCount", FIELD_NUMWORDS }},
+ {"NUMPAGES", {"PageCount", FIELD_NUMPAGES }},
+ {"INDEX", {"com.sun.star.text.DocumentIndex", FIELD_INDEX }},
+ {"XE", {"com.sun.star.text.DocumentIndexMark", FIELD_XE }},
+ {"BIBLIOGRAPHY",{"com.sun.star.text.Bibliography", FIELD_BIBLIOGRAPHY }},
+ {"CITATION", {"com.sun.star.text.TextField.Bibliography",FIELD_CITATION }},
+ };
+
+ return aFieldConversionMap;
+}
+
+static const FieldConversionMap_t & lcl_GetEnhancedFieldConversion()
+{
+ static const FieldConversionMap_t aEnhancedFieldConversionMap =
+ {
+ {"FORMCHECKBOX", {"FormFieldmark", FIELD_FORMCHECKBOX}},
+ {"FORMDROPDOWN", {"FormFieldmark", FIELD_FORMDROPDOWN}},
+ {"FORMTEXT", {"Fieldmark", FIELD_FORMTEXT}},
+ };
+
+ return aEnhancedFieldConversionMap;
+}
+
+void DomainMapper_Impl::handleFieldSet
+ (const FieldContextPtr& pContext,
+ uno::Reference< uno::XInterface > const & xFieldInterface,
+ uno::Reference< beans::XPropertySet > const& xFieldProperties)
+{
+ OUString sVariable, sHint;
+
+ sVariable = lcl_ExtractVariableAndHint(pContext->GetCommand(), sHint);
+
+ // remove surrounding "" if exists
+ if(sHint.getLength() >= 2)
+ {
+ std::u16string_view sTmp = o3tl::trim(sHint);
+ if (o3tl::starts_with(sTmp, u"\"") && o3tl::ends_with(sTmp, u"\""))
+ {
+ sHint = sTmp.substr(1, sTmp.size() - 2);
+ }
+ }
+
+ // determine field master name
+ uno::Reference< beans::XPropertySet > xMaster =
+ FindOrCreateFieldMaster
+ ("com.sun.star.text.FieldMaster.SetExpression", sVariable);
+
+ // a set field is a string
+ xMaster->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
+
+ // attach the master to the field
+ uno::Reference< text::XDependentTextField > xDependentField
+ ( xFieldInterface, uno::UNO_QUERY_THROW );
+ xDependentField->attachTextFieldMaster( xMaster );
+
+ uno::Any aAnyHint(sHint);
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_HINT), aAnyHint);
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), aAnyHint);
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
+
+ // Mimic MS Word behavior (hide the SET)
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_IS_VISIBLE), uno::Any(false));
+}
+
+void DomainMapper_Impl::handleFieldAsk
+ (const FieldContextPtr& pContext,
+ uno::Reference< uno::XInterface > & xFieldInterface,
+ uno::Reference< beans::XPropertySet > const& xFieldProperties)
+{
+ //doesn the command contain a variable name?
+ OUString sVariable, sHint;
+
+ sVariable = lcl_ExtractVariableAndHint( pContext->GetCommand(),
+ sHint );
+ if(!sVariable.isEmpty())
+ {
+ // determine field master name
+ uno::Reference< beans::XPropertySet > xMaster =
+ FindOrCreateFieldMaster
+ ("com.sun.star.text.FieldMaster.SetExpression", sVariable );
+ // An ASK field is always a string of characters
+ xMaster->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
+
+ // attach the master to the field
+ uno::Reference< text::XDependentTextField > xDependentField
+ ( xFieldInterface, uno::UNO_QUERY_THROW );
+ xDependentField->attachTextFieldMaster( xMaster );
+
+ // set input flag at the field
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_IS_INPUT), uno::Any( true ));
+ // set the prompt
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_HINT),
+ uno::Any( sHint ));
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
+ // The ASK has no field value to display
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_IS_VISIBLE), uno::Any(false));
+ }
+ else
+ {
+ //don't insert the field
+ //todo: maybe import a 'normal' input field here?
+ xFieldInterface = nullptr;
+ }
+}
+
+/**
+ * Converts a Microsoft Word field formula into LibreOffice syntax
+ * @param input The Microsoft Word field formula, with no leading '=' sign
+ * @return An equivalent LibreOffice field formula
+ */
+OUString DomainMapper_Impl::convertFieldFormula(const OUString& input) {
+
+ if (!m_pSettingsTable)
+ {
+ return input;
+ }
+
+ OUString listSeparator = m_pSettingsTable->GetListSeparator();
+
+ /* Replace logical condition functions with LO equivalent operators */
+ OUString changed = input.replaceAll(" <> ", " NEQ ");
+ changed = changed.replaceAll(" <= ", " LEQ ");
+ changed = changed.replaceAll(" >= ", " GEQ ");
+ changed = changed.replaceAll(" = " , " EQ ");
+ changed = changed.replaceAll(" < " , " L ");
+ changed = changed.replaceAll(" > " , " G ");
+
+ changed = changed.replaceAll("<>", " NEQ ");
+ changed = changed.replaceAll("<=", " LEQ ");
+ changed = changed.replaceAll(">=", " GEQ ");
+ changed = changed.replaceAll("=" , " EQ ");
+ changed = changed.replaceAll("<" , " L ");
+ changed = changed.replaceAll(">" , " G ");
+
+ /* Replace function calls with infix keywords for AND(), OR(), and ROUND(). Nothing needs to be
+ * done for NOT(). This simple regex will work properly with most common cases. However, it may
+ * not work correctly when the arguments are nested subcalls to other functions, like
+ * ROUND(MIN(1,2),MAX(3,4)). See TDF#134765. */
+ icu::ErrorCode status;
+ icu::UnicodeString usInput(changed.getStr());
+ const uint32_t rMatcherFlags = UREGEX_CASE_INSENSITIVE;
+ OUString regex = "\\b(AND|OR|ROUND)\\s*\\(\\s*([^" + listSeparator + "]+)\\s*" + listSeparator + "\\s*([^)]+)\\s*\\)";
+ icu::UnicodeString usRegex(regex.getStr());
+ icu::RegexMatcher rmatch1(usRegex, usInput, rMatcherFlags, status);
+ usInput = rmatch1.replaceAll(icu::UnicodeString("(($2) $1 ($3))"), status);
+
+ /* Assumes any remaining list separators separate arguments to functions that accept lists
+ * (SUM, MIN, MAX, MEAN, etc.) */
+ usInput.findAndReplace(icu::UnicodeString(listSeparator.getStr()), "|");
+
+ /* Surround single cell references with angle brackets.
+ * If there is ever added a function name that ends with a digit, this regex will need to be revisited. */
+ icu::RegexMatcher rmatch2("\\b([A-Z]{1,3}[0-9]+)\\b(?![(])", usInput, rMatcherFlags, status);
+ usInput = rmatch2.replaceAll(icu::UnicodeString("<$1>"), status);
+
+ /* Cell references must be upper case
+ * TODO: convert reference to other tables, e.g. SUM(Table1 A1:B2), where "Table1" is a bookmark of the table,
+ * TODO: also column range A:A */
+ icu::RegexMatcher rmatch3("(<[a-z]{1,3}[0-9]+>|\\b(above|below|left|right)\\b)", usInput, rMatcherFlags, status);
+ icu::UnicodeString replacedCellRefs;
+ while (rmatch3.find(status) && status.isSuccess()) {
+ rmatch3.appendReplacement(replacedCellRefs, rmatch3.group(status).toUpper(), status);
+ }
+ rmatch3.appendTail(replacedCellRefs);
+
+ /* Fix up cell ranges */
+ icu::RegexMatcher rmatch4("<([A-Z]{1,3}[0-9]+)>:<([A-Z]{1,3}[0-9]+)>", replacedCellRefs, rMatcherFlags, status);
+ usInput = rmatch4.replaceAll(icu::UnicodeString("<$1:$2>"), status);
+
+ /* Fix up user defined names */
+ icu::RegexMatcher rmatch5("\\bDEFINED\\s*\\(<([A-Z]+[0-9]+)>\\)", usInput, rMatcherFlags, status);
+ usInput = rmatch5.replaceAll(icu::UnicodeString("DEFINED($1)"), status);
+
+ /* Prepare replace of ABOVE/BELOW/LEFT/RIGHT by adding spaces around them */
+ icu::RegexMatcher rmatch6("\\b(ABOVE|BELOW|LEFT|RIGHT)\\b", usInput, rMatcherFlags, status);
+ usInput = rmatch6.replaceAll(icu::UnicodeString(" $1 "), status);
+
+ /* DOCX allows to set decimal symbol independently from the locale of the document, so if
+ * needed, convert decimal comma to get working formula in a document language (locale),
+ * which doesn't use decimal comma */
+ if ( m_pSettingsTable->GetDecimalSymbol() == "," && !m_bIsDecimalComma )
+ {
+ icu::RegexMatcher rmatch7("\\b([0-9]+),([0-9]+([eE][-]?[0-9]+)?)\\b", usInput, rMatcherFlags, status);
+ usInput = rmatch7.replaceAll(icu::UnicodeString("$1.$2"), status);
+ }
+
+ return OUString(usInput.getTerminatedBuffer());
+}
+
+void DomainMapper_Impl::handleFieldFormula
+ (const FieldContextPtr& pContext,
+ uno::Reference< beans::XPropertySet > const& xFieldProperties)
+{
+ OUString command = pContext->GetCommand().trim();
+
+ // Remove number formatting from \# to end of command
+ // TODO: handle custom number formatting
+ sal_Int32 delimPos = command.indexOf("\\#");
+ if (delimPos != -1)
+ {
+ command = command.replaceAt(delimPos, command.getLength() - delimPos, u"").trim();
+ }
+
+ // command must contains = and at least another char
+ if (command.getLength() < 2)
+ return;
+
+ // we don't copy the = symbol from the command
+ OUString formula = convertFieldFormula(command.copy(1));
+
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::Any(formula));
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_NUMBER_FORMAT), uno::Any(sal_Int32(0)));
+ xFieldProperties->setPropertyValue("IsShowFormula", uno::Any(false));
+
+ // grab-bag the original and converted formula
+ if (hasTableManager())
+ {
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(PROP_CELL_FORMULA, uno::Any(command.copy(1)), true, CELL_GRAB_BAG);
+ pPropMap->Insert(PROP_CELL_FORMULA_CONVERTED, uno::Any(formula), true, CELL_GRAB_BAG);
+ getTableManager().cellProps(pPropMap);
+ }
+}
+
+void DomainMapper_Impl::handleRubyEQField( const FieldContextPtr& pContext)
+{
+ const OUString & rCommand(pContext->GetCommand());
+ sal_Int32 nIndex = 0, nEnd = 0;
+ RubyInfo aInfo ;
+ nIndex = rCommand.indexOf("\\* jc" );
+ if (nIndex != -1)
+ {
+ nIndex += 5;
+ sal_uInt32 nJc = o3tl::toInt32(o3tl::getToken(rCommand, 0, ' ',nIndex));
+ const sal_Int32 aRubyAlignValues[] =
+ {
+ NS_ooxml::LN_Value_ST_RubyAlign_center,
+ NS_ooxml::LN_Value_ST_RubyAlign_distributeLetter,
+ NS_ooxml::LN_Value_ST_RubyAlign_distributeSpace,
+ NS_ooxml::LN_Value_ST_RubyAlign_left,
+ NS_ooxml::LN_Value_ST_RubyAlign_right,
+ NS_ooxml::LN_Value_ST_RubyAlign_rightVertical,
+ };
+ aInfo.nRubyAlign = aRubyAlignValues[(nJc<SAL_N_ELEMENTS(aRubyAlignValues))?nJc:0];
+ }
+
+ // we don't parse or use the font field in rCommand
+
+ nIndex = rCommand.indexOf("\\* hps" );
+ if (nIndex != -1)
+ {
+ nIndex += 6;
+ aInfo.nHps = o3tl::toInt32(o3tl::getToken(rCommand, 0, ' ',nIndex));
+ }
+
+ nIndex = rCommand.indexOf("\\o");
+ if (nIndex == -1)
+ return;
+ nIndex = rCommand.indexOf('(', nIndex);
+ if (nIndex == -1)
+ return;
+ nEnd = rCommand.lastIndexOf(')');
+ if (nEnd == -1)
+ return;
+ if (nEnd <= nIndex)
+ return;
+
+ std::u16string_view sRubyParts = rCommand.subView(nIndex+1,nEnd-nIndex-1);
+ nIndex = 0;
+ std::u16string_view sPart1 = o3tl::getToken(sRubyParts, 0, ',', nIndex);
+ std::u16string_view sPart2 = o3tl::getToken(sRubyParts, 0, ',', nIndex);
+ size_t nIndex2 = 0;
+ size_t nEnd2 = 0;
+ if ((nIndex2 = sPart1.find('(')) != std::u16string_view::npos && (nEnd2 = sPart1.rfind(')')) != std::u16string_view::npos && nEnd2 > nIndex2)
+ {
+ aInfo.sRubyText = sPart1.substr(nIndex2+1,nEnd2-nIndex2-1);
+ }
+
+ PropertyMapPtr pRubyContext(new PropertyMap());
+ pRubyContext->InsertProps(GetTopContext());
+ if (aInfo.nHps > 0)
+ {
+ double fVal = double(aInfo.nHps) / 2.;
+ uno::Any aVal( fVal );
+
+ pRubyContext->Insert(PROP_CHAR_HEIGHT, aVal);
+ pRubyContext->Insert(PROP_CHAR_HEIGHT_ASIAN, aVal);
+ }
+ PropertyValueVector_t aProps = comphelper::sequenceToContainer< PropertyValueVector_t >(pRubyContext->GetPropertyValues());
+ aInfo.sRubyStyle = m_rDMapper.getOrCreateCharStyle(aProps, /*bAlwaysCreate=*/false);
+ PropertyMapPtr pCharContext(new PropertyMap());
+ if (m_pLastCharacterContext)
+ pCharContext->InsertProps(m_pLastCharacterContext);
+ pCharContext->InsertProps(pContext->getProperties());
+ pCharContext->Insert(PROP_RUBY_TEXT, uno::Any( aInfo.sRubyText ) );
+ pCharContext->Insert(PROP_RUBY_ADJUST, uno::Any(static_cast<sal_Int16>(ConversionHelper::convertRubyAlign(aInfo.nRubyAlign))));
+ if ( aInfo.nRubyAlign == NS_ooxml::LN_Value_ST_RubyAlign_rightVertical )
+ pCharContext->Insert(PROP_RUBY_POSITION, uno::Any(css::text::RubyPosition::INTER_CHARACTER));
+ pCharContext->Insert(PROP_RUBY_STYLE, uno::Any(aInfo.sRubyStyle));
+ appendTextPortion(OUString(sPart2), pCharContext);
+
+}
+
+void DomainMapper_Impl::handleAutoNum
+ (const FieldContextPtr& pContext,
+ uno::Reference< uno::XInterface > const & xFieldInterface,
+ uno::Reference< beans::XPropertySet > const& xFieldProperties)
+{
+ //create a sequence field master "AutoNr"
+ uno::Reference< beans::XPropertySet > xMaster =
+ FindOrCreateFieldMaster
+ ("com.sun.star.text.FieldMaster.SetExpression",
+ "AutoNr");
+
+ xMaster->setPropertyValue( getPropertyName(PROP_SUB_TYPE),
+ uno::Any(text::SetVariableType::SEQUENCE));
+
+ //apply the numbering type
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_NUMBERING_TYPE),
+ uno::Any( lcl_ParseNumberingType(pContext->GetCommand()) ));
+ // attach the master to the field
+ uno::Reference< text::XDependentTextField > xDependentField
+ ( xFieldInterface, uno::UNO_QUERY_THROW );
+ xDependentField->attachTextFieldMaster( xMaster );
+}
+
+void DomainMapper_Impl::handleAuthor
+ (std::u16string_view,
+ uno::Reference< beans::XPropertySet > const& xFieldProperties,
+ FieldId eFieldId )
+{
+ if (eFieldId == FIELD_USERNAME)
+ xFieldProperties->setPropertyValue
+ ( getPropertyName(PROP_FULL_NAME), uno::Any( true ));
+
+ // Always set as FIXED b/c MS Word only updates these fields via user intervention (F9)
+ // AUTHOR of course never changes and USERNAME is easily mis-used as an original author field.
+ // Additionally, this was forced as fixed if any special case-formatting was provided.
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName( PROP_IS_FIXED ),
+ uno::Any( true ));
+ //PROP_CURRENT_PRESENTATION is set later anyway
+ }
+}
+
+ void DomainMapper_Impl::handleDocProperty
+ (const FieldContextPtr& pContext,
+ OUString const& rFirstParam,
+ uno::Reference< uno::XInterface > & xFieldInterface)
+{
+ //some docproperties should be imported as document statistic fields, some as DocInfo fields
+ //others should be user fields
+ if (rFirstParam.isEmpty())
+ return;
+
+ constexpr sal_uInt8 SET_ARABIC = 0x01;
+ constexpr sal_uInt8 SET_DATE = 0x04;
+ struct DocPropertyMap
+ {
+ const char* pDocPropertyName;
+ const char* pServiceName;
+ sal_uInt8 nFlags;
+ };
+ static const DocPropertyMap aDocProperties[] =
+ {
+ {"CreateTime", "DocInfo.CreateDateTime", SET_DATE},
+ {"Characters", "CharacterCount", SET_ARABIC},
+ {"Comments", "DocInfo.Description", 0},
+ {"Keywords", "DocInfo.KeyWords", 0},
+ {"LastPrinted", "DocInfo.PrintDateTime", 0},
+ {"LastSavedBy", "DocInfo.ChangeAuthor", 0},
+ {"LastSavedTime", "DocInfo.ChangeDateTime", SET_DATE},
+ {"Paragraphs", "ParagraphCount", SET_ARABIC},
+ {"RevisionNumber", "DocInfo.Revision", 0},
+ {"Subject", "DocInfo.Subject", 0},
+ {"Template", "TemplateName", 0},
+ {"Title", "DocInfo.Title", 0},
+ {"TotalEditingTime", "DocInfo.EditTime", 0},
+ {"Words", "WordCount", SET_ARABIC}
+
+ //other available DocProperties:
+ //Bytes, Category, CharactersWithSpaces, Company
+ //HyperlinkBase,
+ //Lines, Manager, NameofApplication, ODMADocId, Pages,
+ //Security,
+ };
+ uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(m_xTextDocument, uno::UNO_QUERY);
+ uno::Reference<document::XDocumentProperties> xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties();
+ uno::Reference<beans::XPropertySet> xUserDefinedProps(xDocumentProperties->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xUserDefinedProps->getPropertySetInfo();
+ //search for a field mapping
+ OUString sFieldServiceName;
+ size_t nMap = 0;
+ if (!xPropertySetInfo->hasPropertyByName(rFirstParam))
+ {
+ for( ; nMap < SAL_N_ELEMENTS(aDocProperties); ++nMap )
+ {
+ if (rFirstParam.equalsAscii(aDocProperties[nMap].pDocPropertyName))
+ {
+ sFieldServiceName = OUString::createFromAscii(aDocProperties[nMap].pServiceName);
+ break;
+ }
+ }
+ }
+ else
+ pContext->CacheVariableValue(xUserDefinedProps->getPropertyValue(rFirstParam));
+
+ OUString sServiceName("com.sun.star.text.TextField.");
+ bool bIsCustomField = false;
+ if(sFieldServiceName.isEmpty())
+ {
+ //create a custom property field
+ sServiceName += "DocInfo.Custom";
+ bIsCustomField = true;
+ }
+ else
+ {
+ sServiceName += sFieldServiceName;
+ }
+ if (m_xTextFactory.is())
+ xFieldInterface = m_xTextFactory->createInstance(sServiceName);
+ uno::Reference<beans::XPropertySet> xFieldProperties( xFieldInterface, uno::UNO_QUERY_THROW);
+ if( bIsCustomField )
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_NAME), uno::Any(rFirstParam));
+ pContext->SetCustomField( xFieldProperties );
+ }
+ else
+ {
+ if(0 != (aDocProperties[nMap].nFlags & SET_ARABIC))
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_NUMBERING_TYPE),
+ uno::Any( style::NumberingType::ARABIC ));
+ else if(0 != (aDocProperties[nMap].nFlags & SET_DATE))
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_IS_DATE),
+ uno::Any( true ));
+ SetNumberFormat( pContext->GetCommand(), xFieldProperties );
+ }
+ }
+}
+
+static uno::Sequence< beans::PropertyValues > lcl_createTOXLevelHyperlinks( bool bHyperlinks, const OUString& sChapterNoSeparator,
+ const uno::Sequence< beans::PropertyValues >& aLevel )
+{
+ //create a copy of the level and add two new entries - hyperlink start and end
+ bool bChapterNoSeparator = !sChapterNoSeparator.isEmpty();
+ sal_Int32 nAdd = (bHyperlinks && bChapterNoSeparator) ? 4 : 2;
+ uno::Sequence< beans::PropertyValues > aNewLevel( aLevel.getLength() + nAdd);
+ beans::PropertyValues* pNewLevel = aNewLevel.getArray();
+ if( bHyperlinks )
+ {
+ beans::PropertyValues aHyperlink{ comphelper::makePropertyValue(
+ getPropertyName( PROP_TOKEN_TYPE ), getPropertyName( PROP_TOKEN_HYPERLINK_START )) };
+ pNewLevel[0] = aHyperlink;
+ aHyperlink = { comphelper::makePropertyValue(
+ getPropertyName(PROP_TOKEN_TYPE), getPropertyName( PROP_TOKEN_HYPERLINK_END )) };
+ pNewLevel[aNewLevel.getLength() -1] = aHyperlink;
+ }
+ if( bChapterNoSeparator )
+ {
+ beans::PropertyValues aChapterNo{
+ comphelper::makePropertyValue(getPropertyName( PROP_TOKEN_TYPE ),
+ getPropertyName( PROP_TOKEN_CHAPTER_INFO )),
+ comphelper::makePropertyValue(getPropertyName( PROP_CHAPTER_FORMAT ),
+ //todo: is ChapterFormat::Number correct?
+ sal_Int16(text::ChapterFormat::NUMBER))
+ };
+ pNewLevel[aNewLevel.getLength() - (bHyperlinks ? 4 : 2) ] = aChapterNo;
+
+ beans::PropertyValues aChapterSeparator{
+ comphelper::makePropertyValue(getPropertyName( PROP_TOKEN_TYPE ),
+ getPropertyName( PROP_TOKEN_TEXT )),
+ comphelper::makePropertyValue(getPropertyName( PROP_TEXT ), sChapterNoSeparator)
+ };
+ pNewLevel[aNewLevel.getLength() - (bHyperlinks ? 3 : 1)] = aChapterSeparator;
+ }
+ //copy the 'old' entries except the last (page no)
+ std::copy(aLevel.begin(), std::prev(aLevel.end()), std::next(pNewLevel));
+ //copy page no entry (last or last but one depending on bHyperlinks
+ sal_Int32 nPageNo = aNewLevel.getLength() - (bHyperlinks ? 2 : 3);
+ pNewLevel[nPageNo] = aLevel[aLevel.getLength() - 1];
+
+ return aNewLevel;
+}
+
+/// Returns title of the TOC placed in paragraph(s) before TOC field inside STD-frame
+OUString DomainMapper_Impl::extractTocTitle()
+{
+ if (!m_xSdtEntryStart.is())
+ return OUString();
+
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if(!xTextAppend.is())
+ return OUString();
+
+ // try-catch was added in the same way as inside appendTextSectionAfter()
+ try
+ {
+ uno::Reference<text::XParagraphCursor> xCursor(xTextAppend->createTextCursorByRange(m_xSdtEntryStart), uno::UNO_QUERY_THROW);
+ if (!xCursor.is())
+ return OUString();
+
+ //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
+ xCursor->gotoStartOfParagraph( false );
+ if (m_aTextAppendStack.top().xInsertPosition.is())
+ xCursor->gotoRange( m_aTextAppendStack.top().xInsertPosition, true );
+ else
+ xCursor->gotoEnd( true );
+
+ // the paragraph after this new section might have been already inserted
+ OUString sResult = xCursor->getString();
+ if (sResult.endsWith(SAL_NEWLINE_STRING))
+ sResult = sResult.copy(0, sResult.getLength() - SAL_N_ELEMENTS(SAL_NEWLINE_STRING) + 1);
+
+ return sResult;
+ }
+ catch(const uno::Exception&)
+ {
+ }
+
+ return OUString();
+}
+
+css::uno::Reference<css::beans::XPropertySet>
+DomainMapper_Impl::StartIndexSectionChecked(const OUString& sServiceName)
+{
+ if (m_bParaChanged)
+ {
+ finishParagraph(GetTopContextOfType(CONTEXT_PARAGRAPH), false); // resets m_bParaChanged
+ PopProperties(CONTEXT_PARAGRAPH);
+ PushProperties(CONTEXT_PARAGRAPH);
+ SetIsFirstRun(true);
+ // The first paragraph of the index that is continuation of just finished one needs to be
+ // removed when finished (unless more content will arrive, which will set m_bParaChanged)
+ m_bRemoveThisParagraph = true;
+ }
+ const auto& xTextAppend = GetTopTextAppend();
+ const auto xTextRange = xTextAppend->getEnd();
+ const auto xRet = createSectionForRange(xTextRange, xTextRange, sServiceName, false);
+ if (!m_aTextAppendStack.top().xInsertPosition)
+ {
+ try
+ {
+ m_bStartedTOC = true;
+ uno::Reference<text::XTextCursor> xTOCTextCursor
+ = xTextRange->getText()->createTextCursor();
+ assert(xTOCTextCursor.is());
+ xTOCTextCursor->gotoEnd(false);
+ m_aTextAppendStack.push(TextAppendContext(xTextAppend, xTOCTextCursor));
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter.dmapper",
+ "DomainMapper_Impl::StartIndexSectionChecked:");
+ }
+ }
+ return xRet;
+}
+
+void DomainMapper_Impl::handleToc
+ (const FieldContextPtr& pContext,
+ const OUString & sTOCServiceName)
+{
+ OUString sValue;
+ if (IsInHeaderFooter())
+ m_bStartTOCHeaderFooter = true;
+ bool bTableOfFigures = false;
+ bool bHyperlinks = false;
+ bool bFromOutline = false;
+ bool bFromEntries = false;
+ bool bHideTabLeaderPageNumbers = false ;
+ bool bIsTabEntry = false ;
+ bool bNewLine = false ;
+ bool bParagraphOutlineLevel = false;
+
+ sal_Int16 nMaxLevel = 10;
+ OUString sTemplate;
+ OUString sChapterNoSeparator;
+ OUString sFigureSequence;
+ uno::Reference< beans::XPropertySet > xTOC;
+ OUString aBookmarkName;
+
+// \a Builds a table of figures but does not include the captions's label and number
+ if( lcl_FindInCommand( pContext->GetCommand(), 'a', sValue ))
+ { //make it a table of figures
+ bTableOfFigures = true;
+ }
+// \b Uses a bookmark to specify area of document from which to build table of contents
+ if( lcl_FindInCommand( pContext->GetCommand(), 'b', sValue ))
+ {
+ aBookmarkName = sValue.trim().replaceAll("\"","");
+ }
+ if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
+// \c Builds a table of figures of the given label
+ {
+ //todo: sValue contains the label's name
+ bTableOfFigures = true;
+ sFigureSequence = sValue.trim();
+ sFigureSequence = sFigureSequence.replaceAll("\"", "").replaceAll("'","");
+ }
+// \d Defines the separator between sequence and page numbers
+ if( lcl_FindInCommand( pContext->GetCommand(), 'd', sValue ))
+ {
+ //todo: insert the chapter number into each level and insert the separator additionally
+ sChapterNoSeparator = sValue;
+ }
+// \f Builds a table of contents using TC entries instead of outline levels
+ if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
+ {
+ //todo: sValue can contain a TOC entry identifier - use unclear
+ bFromEntries = true;
+ }
+// \h Hyperlinks the entries and page numbers within the table of contents
+ if( lcl_FindInCommand( pContext->GetCommand(), 'h', sValue ))
+ {
+ //todo: make all entries to hyperlinks
+ bHyperlinks = true;
+ }
+// \l Defines the TC entries field level used to build a table of contents
+// if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
+// {
+ //todo: entries can only be included completely
+// }
+// \n Builds a table of contents or a range of entries, such as 1-9 in a table of contents without page numbers
+// if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
+// {
+ //todo: what does the description mean?
+// }
+// \o Builds a table of contents by using outline levels instead of TC entries
+ if( lcl_FindInCommand( pContext->GetCommand(), 'o', sValue ))
+ {
+ bFromOutline = true;
+ if (sValue.isEmpty())
+ nMaxLevel = WW_OUTLINE_MAX;
+ else
+ {
+ sal_Int32 nIndex = 0;
+ o3tl::getToken(sValue, 0, '-', nIndex );
+ nMaxLevel = static_cast<sal_Int16>(nIndex != -1 ? o3tl::toInt32(sValue.subView(nIndex)) : 0);
+ }
+ }
+// \p Defines the separator between the table entry and its page number
+// \s Builds a table of contents by using a sequence type
+// \t Builds a table of contents by using style names other than the standard outline styles
+ if( lcl_FindInCommand( pContext->GetCommand(), 't', sValue ))
+ {
+ OUString sToken = sValue.getToken(1, '"');
+ sTemplate = sToken.isEmpty() ? sValue : sToken;
+ }
+// \u Builds a table of contents by using the applied paragraph outline level
+ if( lcl_FindInCommand( pContext->GetCommand(), 'u', sValue ))
+ {
+ bFromOutline = true;
+ bParagraphOutlineLevel = true;
+ //todo: what doesn 'the applied paragraph outline level' refer to?
+ }
+// \w Preserve tab characters within table entries
+ if( lcl_FindInCommand( pContext->GetCommand(), 'w', sValue ))
+ {
+ bIsTabEntry = true ;
+ }
+// \x Preserve newline characters within table entries
+ if( lcl_FindInCommand( pContext->GetCommand(), 'x', sValue ))
+ {
+ bNewLine = true ;
+ }
+// \z Hides page numbers within the table of contents when shown in Web Layout View
+ if( lcl_FindInCommand( pContext->GetCommand(), 'z', sValue ))
+ {
+ bHideTabLeaderPageNumbers = true ;
+ }
+
+ //if there's no option then it should be created from outline
+ if( !bFromOutline && !bFromEntries && sTemplate.isEmpty() )
+ bFromOutline = true;
+
+ const OUString aTocTitle = extractTocTitle();
+
+ if (m_xTextFactory.is() && ! m_aTextAppendStack.empty())
+ {
+ const auto& xTextAppend = GetTopTextAppend();
+ if (aTocTitle.isEmpty() || bTableOfFigures)
+ {
+ // reset marker of the TOC title
+ m_xSdtEntryStart.clear();
+
+ // Create section before setting m_bStartTOC: finishing paragraph
+ // inside StartIndexSectionChecked could do the wrong thing otherwise
+ xTOC = StartIndexSectionChecked(bTableOfFigures ? "com.sun.star.text.IllustrationsIndex"
+ : sTOCServiceName);
+
+ const auto xTextCursor = xTextAppend->getText()->createTextCursor();
+ if (xTextCursor)
+ xTextCursor->gotoEnd(false);
+ xTOCMarkerCursor = xTextCursor;
+ }
+ else
+ {
+ // create TOC section
+ css::uno::Reference<css::text::XTextRange> xTextRangeEndOfTocHeader = GetTopTextAppend()->getEnd();
+ xTOC = createSectionForRange(m_xSdtEntryStart, xTextRangeEndOfTocHeader, sTOCServiceName, false);
+
+ // init [xTOCMarkerCursor]
+ uno::Reference< text::XText > xText = xTextAppend->getText();
+ uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
+ xTOCMarkerCursor = xCrsr;
+
+ // create header of the TOC with the TOC title inside
+ createSectionForRange(m_xSdtEntryStart, xTextRangeEndOfTocHeader, "com.sun.star.text.IndexHeaderSection", true);
+ }
+ }
+
+ m_bStartTOC = true;
+
+ if (xTOC.is())
+ xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::Any(aTocTitle));
+
+ if (!aBookmarkName.isEmpty())
+ xTOC->setPropertyValue(getPropertyName(PROP_TOC_BOOKMARK), uno::Any(aBookmarkName));
+ if( !bTableOfFigures && xTOC.is() )
+ {
+ xTOC->setPropertyValue( getPropertyName( PROP_LEVEL ), uno::Any( nMaxLevel ) );
+ xTOC->setPropertyValue( getPropertyName( PROP_CREATE_FROM_OUTLINE ), uno::Any( bFromOutline ));
+ xTOC->setPropertyValue( getPropertyName( PROP_CREATE_FROM_MARKS ), uno::Any( bFromEntries ));
+ xTOC->setPropertyValue( getPropertyName( PROP_HIDE_TAB_LEADER_AND_PAGE_NUMBERS ), uno::Any( bHideTabLeaderPageNumbers ));
+ xTOC->setPropertyValue( getPropertyName( PROP_TAB_IN_TOC ), uno::Any( bIsTabEntry ));
+ xTOC->setPropertyValue( getPropertyName( PROP_TOC_NEW_LINE ), uno::Any( bNewLine ));
+ xTOC->setPropertyValue( getPropertyName( PROP_TOC_PARAGRAPH_OUTLINE_LEVEL ), uno::Any( bParagraphOutlineLevel ));
+ if( !sTemplate.isEmpty() )
+ {
+ //the string contains comma separated the names and related levels
+ //like: "Heading 1,1,Heading 2,2"
+ TOCStyleMap aMap;
+ sal_Int32 nLevel;
+ sal_Int32 nPosition = 0;
+ while( nPosition >= 0)
+ {
+ OUString sStyleName = sTemplate.getToken( 0, ',', nPosition );
+ //empty tokens should be skipped
+ while( sStyleName.isEmpty() && nPosition > 0 )
+ sStyleName = sTemplate.getToken( 0, ',', nPosition );
+ nLevel = o3tl::toInt32(o3tl::getToken(sTemplate, 0, ',', nPosition ));
+ if( !nLevel )
+ nLevel = 1;
+ if( !sStyleName.isEmpty() )
+ aMap.emplace(nLevel, sStyleName);
+ }
+ uno::Reference< container::XIndexReplace> xParaStyles;
+ xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_PARAGRAPH_STYLES)) >>= xParaStyles;
+ for( nLevel = 1; nLevel < 10; ++nLevel)
+ {
+ sal_Int32 nLevelCount = aMap.count( nLevel );
+ if( nLevelCount )
+ {
+ TOCStyleMap::iterator aTOCStyleIter = aMap.find( nLevel );
+
+ uno::Sequence< OUString> aStyles( nLevelCount );
+ for ( auto& rStyle : asNonConstRange(aStyles) )
+ {
+ rStyle = (aTOCStyleIter++)->second;
+ }
+ xParaStyles->replaceByIndex(nLevel - 1, uno::Any(aStyles));
+ }
+ }
+ xTOC->setPropertyValue(getPropertyName(PROP_CREATE_FROM_LEVEL_PARAGRAPH_STYLES), uno::Any( true ));
+
+ }
+ if(bHyperlinks || !sChapterNoSeparator.isEmpty())
+ {
+ uno::Reference< container::XIndexReplace> xLevelFormats;
+ xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT)) >>= xLevelFormats;
+ sal_Int32 nLevelCount = xLevelFormats->getCount();
+ //start with level 1, 0 is the header level
+ for( sal_Int32 nLevel = 1; nLevel < nLevelCount; ++nLevel)
+ {
+ uno::Sequence< beans::PropertyValues > aLevel;
+ xLevelFormats->getByIndex( nLevel ) >>= aLevel;
+
+ uno::Sequence< beans::PropertyValues > aNewLevel = lcl_createTOXLevelHyperlinks(
+ bHyperlinks, sChapterNoSeparator,
+ aLevel );
+ xLevelFormats->replaceByIndex( nLevel, uno::Any( aNewLevel ) );
+ }
+ }
+ }
+ else if (bTableOfFigures && xTOC.is())
+ {
+ if (!sFigureSequence.isEmpty())
+ xTOC->setPropertyValue(getPropertyName(PROP_LABEL_CATEGORY),
+ uno::Any(sFigureSequence));
+
+ if ( bHyperlinks )
+ {
+ uno::Reference< container::XIndexReplace> xLevelFormats;
+ xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT)) >>= xLevelFormats;
+ uno::Sequence< beans::PropertyValues > aLevel;
+ xLevelFormats->getByIndex( 1 ) >>= aLevel;
+
+ uno::Sequence< beans::PropertyValues > aNewLevel = lcl_createTOXLevelHyperlinks(
+ bHyperlinks, sChapterNoSeparator,
+ aLevel );
+ xLevelFormats->replaceByIndex( 1, uno::Any( aNewLevel ) );
+ }
+ }
+ pContext->SetTOC( xTOC );
+ m_bParaHadField = false;
+}
+
+uno::Reference<beans::XPropertySet> DomainMapper_Impl::createSectionForRange(
+ uno::Reference< css::text::XTextRange > xStart,
+ uno::Reference< css::text::XTextRange > xEnd,
+ const OUString & sObjectType,
+ bool stepLeft)
+{
+ if (!xStart.is())
+ return uno::Reference<beans::XPropertySet>();
+ if (!xEnd.is())
+ return uno::Reference<beans::XPropertySet>();
+
+ uno::Reference< beans::XPropertySet > xRet;
+ if (m_aTextAppendStack.empty())
+ return xRet;
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if(xTextAppend.is())
+ {
+ try
+ {
+ uno::Reference< text::XParagraphCursor > xCursor(
+ xTextAppend->createTextCursorByRange( xStart ), uno::UNO_QUERY_THROW);
+ //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
+ xCursor->gotoStartOfParagraph( false );
+ xCursor->gotoRange( xEnd, true );
+ //the paragraph after this new section is already inserted
+ if (stepLeft)
+ xCursor->goLeft(1, true);
+ uno::Reference< text::XTextContent > xSection( m_xTextFactory->createInstance(sObjectType), uno::UNO_QUERY_THROW );
+ xSection->attach( uno::Reference< text::XTextRange >( xCursor, uno::UNO_QUERY_THROW) );
+ xRet.set(xSection, uno::UNO_QUERY );
+ }
+ catch(const uno::Exception&)
+ {
+ }
+ }
+
+ return xRet;
+}
+
+void DomainMapper_Impl::handleBibliography
+ (const FieldContextPtr& pContext,
+ const OUString & sTOCServiceName)
+{
+ if (m_aTextAppendStack.empty())
+ {
+ // tdf#130214: a workaround to avoid crash on import errors
+ SAL_WARN("writerfilter.dmapper", "no text append stack");
+ return;
+ }
+ // Create section before setting m_bStartTOC and m_bStartBibliography: finishing paragraph
+ // inside StartIndexSectionChecked could do the wrong thing otherwise
+ const auto xTOC = StartIndexSectionChecked(sTOCServiceName);
+ m_bStartTOC = true;
+ m_bStartBibliography = true;
+
+ if (xTOC.is())
+ xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::Any(OUString()));
+
+ pContext->SetTOC( xTOC );
+ m_bParaHadField = false;
+
+ uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
+ appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() );
+}
+
+void DomainMapper_Impl::handleIndex
+ (const FieldContextPtr& pContext,
+ const OUString & sTOCServiceName)
+{
+ // only UserIndex can handle user index defined by \f
+ // e.g. INDEX \f "user-index-id"
+ OUString sUserIndex;
+ if ( lcl_FindInCommand( pContext->GetCommand(), 'f', sUserIndex ) )
+ sUserIndex = lcl_trim(sUserIndex);
+
+ // Create section before setting m_bStartTOC and m_bStartIndex: finishing paragraph
+ // inside StartIndexSectionChecked could do the wrong thing otherwise
+ const auto xTOC = StartIndexSectionChecked( sUserIndex.isEmpty()
+ ? sTOCServiceName
+ : "com.sun.star.text.UserIndex");
+
+ m_bStartTOC = true;
+ m_bStartIndex = true;
+ OUString sValue;
+ if (xTOC.is())
+ {
+ xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::Any(OUString()));
+
+ if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
+ {
+ xTOC->setPropertyValue("IsCommaSeparated", uno::Any(true));
+ }
+ if( lcl_FindInCommand( pContext->GetCommand(), 'h', sValue ))
+ {
+ xTOC->setPropertyValue("UseAlphabeticalSeparators", uno::Any(true));
+ }
+ if( !sUserIndex.isEmpty() )
+ {
+ xTOC->setPropertyValue("UserIndexName", uno::Any(sUserIndex));
+ }
+ }
+ pContext->SetTOC( xTOC );
+ m_bParaHadField = false;
+
+ uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
+ appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() );
+
+ if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
+ {
+ sValue = sValue.replaceAll("\"", "");
+ uno::Reference<text::XTextColumns> xTextColumns;
+ xTOC->getPropertyValue(getPropertyName( PROP_TEXT_COLUMNS )) >>= xTextColumns;
+ if (xTextColumns.is())
+ {
+ xTextColumns->setColumnCount( sValue.toInt32() );
+ xTOC->setPropertyValue( getPropertyName( PROP_TEXT_COLUMNS ), uno::Any( xTextColumns ) );
+ }
+ }
+}
+
+static auto InsertFieldmark(std::stack<TextAppendContext> & rTextAppendStack,
+ uno::Reference<text::XFormField> const& xFormField,
+ uno::Reference<text::XTextRange> const& xStartRange,
+ std::optional<FieldId> const oFieldId) -> void
+{
+ uno::Reference<text::XTextContent> const xTextContent(xFormField, uno::UNO_QUERY_THROW);
+ uno::Reference<text::XTextAppend> const& xTextAppend(rTextAppendStack.top().xTextAppend);
+ uno::Reference<text::XTextCursor> const xCursor =
+ xTextAppend->createTextCursorByRange(xStartRange);
+ if (rTextAppendStack.top().xInsertPosition.is())
+ {
+ uno::Reference<text::XTextRangeCompare> const xCompare(
+ rTextAppendStack.top().xTextAppend,
+ uno::UNO_QUERY);
+ if (xCompare->compareRegionStarts(xStartRange, rTextAppendStack.top().xInsertPosition) < 0)
+ {
+ SAL_WARN("writerfilter.dmapper", "invalid field mark positions");
+ assert(false);
+ }
+ xCursor->gotoRange(rTextAppendStack.top().xInsertPosition, true);
+ }
+ else
+ {
+ xCursor->gotoEnd(true);
+ }
+ xTextAppend->insertTextContent(xCursor, xTextContent, true);
+ if (oFieldId
+ && (oFieldId == FIELD_FORMCHECKBOX || oFieldId == FIELD_FORMDROPDOWN))
+ {
+ return; // only a single CH_TXT_ATR_FORMELEMENT!
+ }
+ // problem: the fieldmark must be inserted in CloseFieldCommand(), because
+ // attach() takes 2 positions, not 3!
+ // FAIL: AppendTextNode() ignores the content index!
+ // plan B: insert a spurious paragraph break now and join
+ // it in PopFieldContext()!
+ xCursor->gotoRange(xTextContent->getAnchor()->getEnd(), false);
+ xCursor->goLeft(1, false); // skip CH_TXT_ATR_FIELDEND
+ xTextAppend->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false);
+ xCursor->goLeft(1, false); // back to previous paragraph
+ rTextAppendStack.push(TextAppendContext(xTextAppend, xCursor));
+}
+
+static auto PopFieldmark(std::stack<TextAppendContext> & rTextAppendStack,
+ uno::Reference<text::XTextCursor> const& xCursor,
+ std::optional<FieldId> const oFieldId) -> void
+{
+ if (oFieldId
+ && (oFieldId == FIELD_FORMCHECKBOX || oFieldId == FIELD_FORMDROPDOWN))
+ {
+ return; // only a single CH_TXT_ATR_FORMELEMENT!
+ }
+ xCursor->gotoRange(rTextAppendStack.top().xInsertPosition, false);
+ xCursor->goRight(1, true);
+ xCursor->setString(OUString()); // undo SplitNode from CloseFieldCommand()
+ // note: paragraph properties will be overwritten
+ // by finishParagraph() anyway so ignore here
+ rTextAppendStack.pop();
+}
+
+void DomainMapper_Impl::CloseFieldCommand()
+{
+ if(m_bDiscardHeaderFooter)
+ return;
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("closeFieldCommand");
+#endif
+
+ FieldContextPtr pContext;
+ if(!m_aFieldStack.empty())
+ pContext = m_aFieldStack.back();
+ OSL_ENSURE( pContext, "no field context available");
+ if( !pContext )
+ return;
+
+ m_bSetUserFieldContent = false;
+ m_bSetCitation = false;
+ m_bSetDateValue = false;
+ const FieldConversionMap_t& aFieldConversionMap = lcl_GetFieldConversion();
+
+ try
+ {
+ uno::Reference< uno::XInterface > xFieldInterface;
+
+ const auto& [sType, vArguments, vSwitches]{ splitFieldCommand(pContext->GetCommand()) };
+ (void)vSwitches;
+ OUString const sFirstParam(vArguments.empty() ? OUString() : vArguments.front());
+
+ // apply font size to the form control
+ if (!m_aTextAppendStack.empty() && m_pLastCharacterContext && ( m_pLastCharacterContext->isSet(PROP_CHAR_HEIGHT) || m_pLastCharacterContext->isSet(PROP_CHAR_FONT_NAME )))
+ {
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (xTextAppend.is())
+ {
+ uno::Reference< text::XTextCursor > xCrsr = xTextAppend->getText()->createTextCursor();
+ if (xCrsr.is())
+ {
+ xCrsr->gotoEnd(false);
+ uno::Reference< beans::XPropertySet > xProp( xCrsr, uno::UNO_QUERY );
+ if (m_pLastCharacterContext->isSet(PROP_CHAR_HEIGHT))
+ {
+ xProp->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT), m_pLastCharacterContext->getProperty(PROP_CHAR_HEIGHT)->second);
+ if (m_pLastCharacterContext->isSet(PROP_CHAR_HEIGHT_COMPLEX))
+ xProp->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT_COMPLEX), m_pLastCharacterContext->getProperty(PROP_CHAR_HEIGHT_COMPLEX)->second);
+ }
+ if (m_pLastCharacterContext->isSet(PROP_CHAR_FONT_NAME))
+ xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME), m_pLastCharacterContext->getProperty(PROP_CHAR_FONT_NAME)->second);
+ }
+ }
+ }
+
+ FieldConversionMap_t::const_iterator const aIt = aFieldConversionMap.find(sType);
+ if (aIt != aFieldConversionMap.end()
+ && (!m_bForceGenericFields
+ // these need to convert ffData to properties...
+ || (aIt->second.eFieldId == FIELD_FORMCHECKBOX)
+ || (aIt->second.eFieldId == FIELD_FORMDROPDOWN)
+ || (aIt->second.eFieldId == FIELD_FORMTEXT)))
+ {
+ pContext->SetFieldId(aIt->second.eFieldId);
+ bool bCreateEnhancedField = false;
+ uno::Reference< beans::XPropertySet > xFieldProperties;
+ bool bCreateField = true;
+ switch (aIt->second.eFieldId)
+ {
+ case FIELD_HYPERLINK:
+ case FIELD_DOCPROPERTY:
+ case FIELD_TOC:
+ case FIELD_INDEX:
+ case FIELD_XE:
+ case FIELD_BIBLIOGRAPHY:
+ case FIELD_CITATION:
+ case FIELD_TC:
+ case FIELD_EQ:
+ case FIELD_INCLUDEPICTURE:
+ case FIELD_SYMBOL:
+ case FIELD_GOTOBUTTON:
+ bCreateField = false;
+ break;
+ case FIELD_FORMCHECKBOX :
+ case FIELD_FORMTEXT :
+ case FIELD_FORMDROPDOWN :
+ {
+ // If we use 'enhanced' fields then FIELD_FORMCHECKBOX,
+ // FIELD_FORMTEXT & FIELD_FORMDROPDOWN are treated specially
+ if ( m_bUsingEnhancedFields )
+ {
+ bCreateField = false;
+ bCreateEnhancedField = true;
+ }
+ // for non enhanced fields checkboxes are displayed
+ // as an awt control not a field
+ else if ( aIt->second.eFieldId == FIELD_FORMCHECKBOX )
+ bCreateField = false;
+ break;
+ }
+ default:
+ {
+ FieldContextPtr pOuter = GetParentFieldContext(m_aFieldStack);
+ if (pOuter)
+ {
+ if (!IsFieldNestingAllowed(pOuter, m_aFieldStack.back()))
+ {
+ // Parent field can't host this child field: don't create a child field
+ // in this case.
+ bCreateField = false;
+ }
+ }
+ break;
+ }
+ }
+ if (m_bStartTOC && (aIt->second.eFieldId == FIELD_PAGEREF) )
+ {
+ bCreateField = false;
+ }
+
+ if( bCreateField || bCreateEnhancedField )
+ {
+ //add the service prefix
+ OUString sServiceName("com.sun.star.text.");
+ if ( bCreateEnhancedField )
+ {
+ const FieldConversionMap_t& aEnhancedFieldConversionMap = lcl_GetEnhancedFieldConversion();
+ FieldConversionMap_t::const_iterator aEnhancedIt =
+ aEnhancedFieldConversionMap.find(sType);
+ if ( aEnhancedIt != aEnhancedFieldConversionMap.end())
+ sServiceName += OUString::createFromAscii(aEnhancedIt->second.cFieldServiceName );
+ }
+ else
+ {
+ sServiceName += "TextField." + OUString::createFromAscii(aIt->second.cFieldServiceName );
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("fieldService");
+ TagLogger::getInstance().chars(sServiceName);
+ TagLogger::getInstance().endElement();
+#endif
+
+ if (m_xTextFactory.is())
+ {
+ xFieldInterface = m_xTextFactory->createInstance(sServiceName);
+ xFieldProperties.set( xFieldInterface, uno::UNO_QUERY_THROW);
+ }
+ }
+ switch( aIt->second.eFieldId )
+ {
+ case FIELD_ADDRESSBLOCK: break;
+ case FIELD_ADVANCE : break;
+ case FIELD_ASK :
+ handleFieldAsk(pContext, xFieldInterface, xFieldProperties);
+ break;
+ case FIELD_AUTONUM :
+ case FIELD_AUTONUMLGL :
+ case FIELD_AUTONUMOUT :
+ handleAutoNum(pContext, xFieldInterface, xFieldProperties);
+ break;
+ case FIELD_AUTHOR :
+ case FIELD_USERNAME :
+ case FIELD_USERINITIALS :
+ handleAuthor(sFirstParam,
+ xFieldProperties,
+ aIt->second.eFieldId);
+ break;
+ case FIELD_DATE:
+ if (xFieldProperties.is())
+ {
+ // Get field fixed property from the context handler
+ if (pContext->IsFieldLocked())
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_IS_FIXED),
+ uno::Any( true ));
+ m_bSetDateValue = true;
+ }
+ else
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_IS_FIXED),
+ uno::Any( false ));
+
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_IS_DATE),
+ uno::Any( true ));
+ SetNumberFormat( pContext->GetCommand(), xFieldProperties );
+ }
+ break;
+ case FIELD_COMMENTS :
+ {
+ // OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" COMMENTS") );
+ // A parameter with COMMENTS shouldn't set fixed
+ // ( or at least the binary filter doesn't )
+ // If we set fixed then we won't export a field cmd.
+ // Additionally the para in COMMENTS is more like an
+ // instruction to set the document property comments
+ // with the param ( e.g. each COMMENT with a param will
+ // overwrite the Comments document property
+ // #TODO implement the above too
+ xFieldProperties->setPropertyValue(
+ getPropertyName( PROP_IS_FIXED ), uno::Any( false ));
+ //PROP_CURRENT_PRESENTATION is set later anyway
+ }
+ break;
+ case FIELD_CREATEDATE :
+ case FIELD_PRINTDATE:
+ case FIELD_SAVEDATE:
+ {
+ if (pContext->IsFieldLocked())
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_IS_FIXED), uno::Any( true ));
+ }
+ xFieldProperties->setPropertyValue(
+ getPropertyName( PROP_IS_DATE ), uno::Any( true ));
+ SetNumberFormat( pContext->GetCommand(), xFieldProperties );
+ }
+ break;
+ case FIELD_DOCPROPERTY :
+ handleDocProperty(pContext, sFirstParam,
+ xFieldInterface);
+ break;
+ case FIELD_DOCVARIABLE :
+ {
+ if (bCreateField)
+ {
+ //create a user field and type
+ uno::Reference<beans::XPropertySet> xMaster = FindOrCreateFieldMaster(
+ "com.sun.star.text.FieldMaster.User", sFirstParam);
+ uno::Reference<text::XDependentTextField> xDependentField(
+ xFieldInterface, uno::UNO_QUERY_THROW);
+ xDependentField->attachTextFieldMaster(xMaster);
+ m_bSetUserFieldContent = true;
+ }
+ }
+ break;
+ case FIELD_EDITTIME :
+ //it's a numbering type, no number format! SetNumberFormat( pContext->GetCommand(), xFieldProperties );
+ break;
+ case FIELD_EQ:
+ {
+ OUString aCommand = pContext->GetCommand().trim();
+
+ msfilter::util::EquationResult aResult(msfilter::util::ParseCombinedChars(aCommand));
+ if (!aResult.sType.isEmpty() && m_xTextFactory.is())
+ {
+ xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.TextField." + aResult.sType);
+ xFieldProperties =
+ uno::Reference< beans::XPropertySet >( xFieldInterface,
+ uno::UNO_QUERY_THROW);
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::Any(aResult.sResult));
+ }
+ else
+ {
+ //merge Read_SubF_Ruby into filter/.../util.cxx and reuse that ?
+ sal_Int32 nSpaceIndex = aCommand.indexOf(' ');
+ if(nSpaceIndex > 0)
+ aCommand = o3tl::trim(aCommand.subView(nSpaceIndex));
+ if (aCommand.startsWith("\\s"))
+ {
+ aCommand = aCommand.copy(2);
+ if (aCommand.startsWith("\\do"))
+ {
+ aCommand = aCommand.copy(3);
+ sal_Int32 nStartIndex = aCommand.indexOf('(');
+ sal_Int32 nEndIndex = aCommand.indexOf(')');
+ if (nStartIndex > 0 && nEndIndex > 0)
+ {
+ // nDown is the requested "lower by" value in points.
+ sal_Int32 nDown = o3tl::toInt32(aCommand.subView(0, nStartIndex));
+ OUString aContent = aCommand.copy(nStartIndex + 1, nEndIndex - nStartIndex - 1);
+ PropertyMapPtr pCharContext = GetTopContext();
+ // dHeight is the font size of the current style.
+ double dHeight = 0;
+ if ((GetPropertyFromParaStyleSheet(PROP_CHAR_HEIGHT) >>= dHeight) && dHeight != 0)
+ // Character escapement should be given in negative percents for subscripts.
+ pCharContext->Insert(PROP_CHAR_ESCAPEMENT, uno::Any( sal_Int16(- 100 * nDown / dHeight) ) );
+ appendTextPortion(aContent, pCharContext);
+ }
+ }
+ }
+ else if (aCommand.startsWith("\\* jc"))
+ {
+ handleRubyEQField(pContext);
+ }
+ }
+ }
+ break;
+ case FIELD_FILLIN :
+ if (xFieldProperties.is())
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_HINT), uno::Any( pContext->GetCommand().getToken(1, '\"')));
+ break;
+ case FIELD_FILENAME:
+ {
+ sal_Int32 nNumberingTypeIndex = pContext->GetCommand().indexOf("\\p");
+ if (xFieldProperties.is())
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_FILE_FORMAT),
+ uno::Any( nNumberingTypeIndex > 0 ? text::FilenameDisplayFormat::FULL : text::FilenameDisplayFormat::NAME_AND_EXT ));
+ }
+ break;
+ case FIELD_FILESIZE : break;
+ case FIELD_FORMULA :
+ if (bCreateField)
+ {
+ handleFieldFormula(pContext, xFieldProperties);
+ }
+ break;
+ case FIELD_FORMCHECKBOX :
+ case FIELD_FORMDROPDOWN :
+ case FIELD_FORMTEXT :
+ {
+ if (bCreateEnhancedField)
+ {
+ FFDataHandler::Pointer_t
+ pFFDataHandler(pContext->getFFDataHandler());
+ FormControlHelper::Pointer_t
+ pFormControlHelper(new FormControlHelper
+ (m_bUsingEnhancedFields ? aIt->second.eFieldId : FIELD_FORMCHECKBOX,
+
+ m_xTextDocument, pFFDataHandler));
+ pContext->setFormControlHelper(pFormControlHelper);
+ uno::Reference< text::XFormField > xFormField( xFieldInterface, uno::UNO_QUERY );
+ uno::Reference< container::XNamed > xNamed( xFormField, uno::UNO_QUERY );
+ if ( xNamed.is() )
+ {
+ if ( pFFDataHandler && !pFFDataHandler->getName().isEmpty() )
+ xNamed->setName( pFFDataHandler->getName() );
+ pContext->SetFormField( xFormField );
+ }
+ InsertFieldmark(m_aTextAppendStack,
+ xFormField, pContext->GetStartRange(),
+ pContext->GetFieldId());
+ }
+ else
+ {
+ if ( aIt->second.eFieldId == FIELD_FORMDROPDOWN )
+ lcl_handleDropdownField( xFieldProperties, pContext->getFFDataHandler() );
+ else
+ lcl_handleTextField( xFieldProperties, pContext->getFFDataHandler() );
+ }
+ }
+ break;
+ case FIELD_GOTOBUTTON : break;
+ case FIELD_HYPERLINK:
+ {
+ ::std::vector<OUString> aParts = pContext->GetCommandParts();
+
+ // Syntax is either:
+ // HYPERLINK "" \l "link"
+ // or
+ // HYPERLINK \l "link"
+ // Make sure "HYPERLINK" doesn't end up as part of link in the second case.
+ if (!aParts.empty() && aParts[0] == "HYPERLINK")
+ aParts.erase(aParts.begin());
+
+ ::std::vector<OUString>::const_iterator aItEnd = aParts.end();
+ ::std::vector<OUString>::const_iterator aPartIt = aParts.begin();
+
+ OUString sURL;
+ OUString sTarget;
+
+ while (aPartIt != aItEnd)
+ {
+ if ( *aPartIt == "\\l" )
+ {
+ ++aPartIt;
+
+ if (aPartIt == aItEnd)
+ break;
+
+ sURL += "#" + *aPartIt;
+ }
+ else if (*aPartIt == "\\m" || *aPartIt == "\\n" || *aPartIt == "\\h")
+ {
+ }
+ else if ( *aPartIt == "\\o" || *aPartIt == "\\t" )
+ {
+ ++aPartIt;
+
+ if (aPartIt == aItEnd)
+ break;
+
+ sTarget = *aPartIt;
+ }
+ else
+ {
+ sURL = *aPartIt;
+ }
+
+ ++aPartIt;
+ }
+
+ if (!sURL.isEmpty())
+ {
+ if (sURL.startsWith("file:///"))
+ {
+ // file:///absolute\\path\\to\\file => invalid file URI (Writer cannot open)
+ // convert all double backslashes to slashes:
+ sURL = sURL.replaceAll("\\\\", "/");
+
+ // file:///absolute\path\to\file => invalid file URI (Writer cannot open)
+ // convert all backslashes to slashes:
+ sURL = sURL.replace('\\', '/');
+ }
+ // Try to make absolute any relative URLs, except
+ // for relative same-document URLs that only contain
+ // a fragment part:
+ else if (!sURL.startsWith("#")) {
+ try {
+ sURL = rtl::Uri::convertRelToAbs(
+ m_aBaseUrl, sURL);
+ } catch (rtl::MalformedUriException & e) {
+ SAL_WARN(
+ "writerfilter.dmapper",
+ "MalformedUriException "
+ << e.getMessage());
+ }
+ }
+ pContext->SetHyperlinkURL(sURL);
+ }
+
+ if (!sTarget.isEmpty())
+ pContext->SetHyperlinkTarget(sTarget);
+ }
+ break;
+ case FIELD_IF : break;
+ case FIELD_INFO : break;
+ case FIELD_INCLUDEPICTURE: break;
+ case FIELD_KEYWORDS :
+ {
+ if (!sFirstParam.isEmpty())
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName( PROP_IS_FIXED ), uno::Any( true ));
+ //PROP_CURRENT_PRESENTATION is set later anyway
+ }
+ }
+ break;
+ case FIELD_LASTSAVEDBY :
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_IS_FIXED), uno::Any(true));
+ break;
+ case FIELD_MACROBUTTON:
+ {
+ if (xFieldProperties.is())
+ {
+ sal_Int32 nIndex = sizeof(" MACROBUTTON ");
+ OUString sCommand = pContext->GetCommand();
+
+ //extract macro name
+ if (sCommand.getLength() >= nIndex)
+ {
+ OUString sMacro = sCommand.getToken(0, ' ', nIndex);
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_MACRO_NAME), uno::Any( sMacro ));
+ }
+
+ //extract quick help text
+ if (sCommand.getLength() > nIndex + 1)
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_HINT),
+ uno::Any( sCommand.copy( nIndex )));
+ }
+ }
+ }
+ break;
+ case FIELD_MERGEFIELD :
+ {
+ //todo: create a database field and fieldmaster pointing to a column, only
+ //create a user field and type
+ uno::Reference< beans::XPropertySet > xMaster =
+ FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.Database", sFirstParam);
+
+// xFieldProperties->setPropertyValue(
+// "FieldCode",
+// uno::makeAny( pContext->GetCommand().copy( nIndex + 1 )));
+ uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
+ xDependentField->attachTextFieldMaster( xMaster );
+ }
+ break;
+ case FIELD_MERGEREC : break;
+ case FIELD_MERGESEQ : break;
+ case FIELD_NEXT : break;
+ case FIELD_NEXTIF : break;
+ case FIELD_PAGE :
+ if (xFieldProperties.is())
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_NUMBERING_TYPE),
+ uno::Any( lcl_ParseNumberingType(pContext->GetCommand()) ));
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_SUB_TYPE),
+ uno::Any( text::PageNumberType_CURRENT ));
+ }
+
+ break;
+ case FIELD_PAGEREF:
+ case FIELD_REF:
+ if (xFieldProperties.is() && !m_bStartTOC)
+ {
+ bool bPageRef = aIt->second.eFieldId == FIELD_PAGEREF;
+
+ // Do we need a GetReference (default) or a GetExpression field?
+ uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY );
+ uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
+
+ if (!xFieldMasterAccess->hasByName(
+ "com.sun.star.text.FieldMaster.SetExpression."
+ + sFirstParam))
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_REFERENCE_FIELD_SOURCE),
+ uno::Any( sal_Int16(text::ReferenceFieldSource::BOOKMARK)) );
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_SOURCE_NAME),
+ uno::Any(sFirstParam) );
+ sal_Int16 nFieldPart = (bPageRef ? text::ReferenceFieldPart::PAGE : text::ReferenceFieldPart::TEXT);
+ OUString sValue;
+ if( lcl_FindInCommand( pContext->GetCommand(), 'p', sValue ))
+ {
+ //above-below
+ nFieldPart = text::ReferenceFieldPart::UP_DOWN;
+ }
+ else if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
+ {
+ //number
+ nFieldPart = text::ReferenceFieldPart::NUMBER;
+ }
+ else if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
+ {
+ //number-no-context
+ nFieldPart = text::ReferenceFieldPart::NUMBER_NO_CONTEXT;
+ }
+ else if( lcl_FindInCommand( pContext->GetCommand(), 'w', sValue ))
+ {
+ //number-full-context
+ nFieldPart = text::ReferenceFieldPart::NUMBER_FULL_CONTEXT;
+ }
+ xFieldProperties->setPropertyValue(
+ getPropertyName( PROP_REFERENCE_FIELD_PART ), uno::Any( nFieldPart ));
+ }
+ else if( m_xTextFactory.is() )
+ {
+ xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.TextField.GetExpression");
+ xFieldProperties.set(xFieldInterface, uno::UNO_QUERY);
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_CONTENT),
+ uno::Any(sFirstParam));
+ xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::Any(text::SetVariableType::STRING));
+ }
+ }
+ break;
+ case FIELD_REVNUM : break;
+ case FIELD_SECTION : break;
+ case FIELD_SECTIONPAGES : break;
+ case FIELD_SEQ :
+ {
+ // command looks like: " SEQ Table \* ARABIC "
+ OUString sCmd(pContext->GetCommand());
+ // find the sequence name, e.g. "SEQ"
+ std::u16string_view sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\');
+ sSeqName = o3tl::trim(sSeqName);
+
+ // create a sequence field master using the sequence name
+ uno::Reference< beans::XPropertySet > xMaster = FindOrCreateFieldMaster(
+ "com.sun.star.text.FieldMaster.SetExpression",
+ OUString(sSeqName));
+
+ xMaster->setPropertyValue(
+ getPropertyName(PROP_SUB_TYPE),
+ uno::Any(text::SetVariableType::SEQUENCE));
+
+ // apply the numbering type
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_NUMBERING_TYPE),
+ uno::Any( lcl_ParseNumberingType(pContext->GetCommand()) ));
+
+ // attach the master to the field
+ uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
+ xDependentField->attachTextFieldMaster( xMaster );
+
+ OUString sFormula = OUString::Concat(sSeqName) + "+1";
+ OUString sValue;
+ if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
+ {
+ sFormula = sSeqName;
+ }
+ else if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
+ {
+ sFormula = sValue;
+ }
+ // TODO \s isn't handled, but the spec isn't easy to understand without
+ // an example for this one.
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_CONTENT),
+ uno::Any(sFormula));
+
+ // Take care of the numeric formatting definition, default is Arabic
+ sal_Int16 nNumberingType = lcl_ParseNumberingType(pContext->GetCommand());
+ if (nNumberingType == style::NumberingType::PAGE_DESCRIPTOR)
+ nNumberingType = style::NumberingType::ARABIC;
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_NUMBERING_TYPE),
+ uno::Any(nNumberingType));
+ }
+ break;
+ case FIELD_SET :
+ handleFieldSet(pContext, xFieldInterface, xFieldProperties);
+ break;
+ case FIELD_SKIPIF : break;
+ case FIELD_STYLEREF : break;
+ case FIELD_SUBJECT :
+ {
+ if (!sFirstParam.isEmpty())
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName( PROP_IS_FIXED ), uno::Any( true ));
+ //PROP_CURRENT_PRESENTATION is set later anyway
+ }
+ }
+ break;
+ case FIELD_SYMBOL:
+ {
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ OUString sSymbol( sal_Unicode( sFirstParam.startsWithIgnoreAsciiCase("0x") ? o3tl::toUInt32(sFirstParam.subView(2),16) : sFirstParam.toUInt32() ) );
+ OUString sFont;
+ bool bHasFont = lcl_FindInCommand( pContext->GetCommand(), 'f', sFont);
+ if ( bHasFont )
+ {
+ sFont = sFont.trim();
+ if (sFont.startsWith("\""))
+ sFont = sFont.copy(1);
+ if (sFont.endsWith("\""))
+ sFont = sFont.copy(0,sFont.getLength()-1);
+ }
+
+
+
+ if (xTextAppend.is())
+ {
+ uno::Reference< text::XText > xText = xTextAppend->getText();
+ uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
+ if (xCrsr.is())
+ {
+ xCrsr->gotoEnd(false);
+ xText->insertString(xCrsr, sSymbol, true);
+ uno::Reference< beans::XPropertySet > xProp( xCrsr, uno::UNO_QUERY );
+ xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_CHAR_SET), uno::Any(awt::CharSet::SYMBOL));
+ if(bHasFont)
+ {
+ uno::Any aVal( sFont );
+ xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME), aVal);
+ xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_ASIAN), aVal);
+ xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_COMPLEX), aVal);
+
+ }
+ }
+ }
+ }
+ break;
+ case FIELD_TEMPLATE: break;
+ case FIELD_TIME :
+ {
+ if (pContext->IsFieldLocked())
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_IS_FIXED),
+ uno::Any( true ));
+ m_bSetDateValue = true;
+ }
+ SetNumberFormat( pContext->GetCommand(), xFieldProperties );
+ }
+ break;
+ case FIELD_TITLE :
+ {
+ if (!sFirstParam.isEmpty())
+ {
+ xFieldProperties->setPropertyValue(
+ getPropertyName( PROP_IS_FIXED ), uno::Any( true ));
+ //PROP_CURRENT_PRESENTATION is set later anyway
+ }
+ }
+ break;
+ case FIELD_USERADDRESS : //todo: user address collects street, city ...
+ break;
+ case FIELD_INDEX:
+ handleIndex(pContext,
+ OUString::createFromAscii(aIt->second.cFieldServiceName));
+ break;
+ case FIELD_BIBLIOGRAPHY:
+ handleBibliography(pContext,
+ OUString::createFromAscii(aIt->second.cFieldServiceName));
+ break;
+ case FIELD_TOC:
+ handleToc(pContext,
+ OUString::createFromAscii(aIt->second.cFieldServiceName));
+ break;
+ case FIELD_XE:
+ {
+ if( !m_xTextFactory.is() )
+ break;
+
+ // only UserIndexMark can handle user index types defined by \f
+ // e.g. XE "text" \f "user-index-id"
+ OUString sUserIndex;
+ OUString sFieldServiceName =
+ lcl_FindInCommand( pContext->GetCommand(), 'f', sUserIndex )
+ ? "com.sun.star.text.UserIndexMark"
+ : OUString::createFromAscii(aIt->second.cFieldServiceName);
+ uno::Reference< beans::XPropertySet > xTC(
+ m_xTextFactory->createInstance(sFieldServiceName),
+ uno::UNO_QUERY_THROW);
+
+ if (!sFirstParam.isEmpty())
+ {
+ xTC->setPropertyValue(sUserIndex.isEmpty()
+ ? OUString("PrimaryKey")
+ : OUString("AlternativeText"),
+ uno::Any(sFirstParam));
+ }
+
+ sUserIndex = lcl_trim(sUserIndex);
+ if (!sUserIndex.isEmpty())
+ {
+ xTC->setPropertyValue("UserIndexName",
+ uno::Any(sUserIndex));
+ }
+ uno::Reference< text::XTextContent > xToInsert( xTC, uno::UNO_QUERY );
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (xTextAppend.is())
+ {
+ uno::Reference< text::XText > xText = xTextAppend->getText();
+ uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
+ if (xCrsr.is())
+ {
+ xCrsr->gotoEnd(false);
+ xText->insertTextContent(uno::Reference< text::XTextRange >( xCrsr, uno::UNO_QUERY_THROW ), xToInsert, false);
+ }
+ }
+ }
+ break;
+ case FIELD_CITATION:
+ {
+ if( !m_xTextFactory.is() )
+ break;
+
+ xFieldInterface = m_xTextFactory->createInstance(
+ OUString::createFromAscii(aIt->second.cFieldServiceName));
+ uno::Reference< beans::XPropertySet > xTC(xFieldInterface,
+ uno::UNO_QUERY_THROW);
+ OUString sCmd(pContext->GetCommand());//sCmd is the entire instrText including the index e.g. CITATION Kra06 \l 1033
+ if( !sCmd.isEmpty()){
+ uno::Sequence<beans::PropertyValue> aValues( comphelper::InitPropertySequence({
+ { "Identifier", uno::Any(sCmd) }
+ }));
+ xTC->setPropertyValue("Fields", uno::Any(aValues));
+ }
+ uno::Reference< text::XTextContent > xToInsert( xTC, uno::UNO_QUERY );
+
+ uno::Sequence<beans::PropertyValue> aValues
+ = m_aFieldStack.back()->getProperties()->GetPropertyValues();
+ appendTextContent(xToInsert, aValues);
+ m_bSetCitation = true;
+ }
+ break;
+
+ case FIELD_TC :
+ {
+ if( !m_xTextFactory.is() )
+ break;
+
+ uno::Reference< beans::XPropertySet > xTC(
+ m_xTextFactory->createInstance(
+ OUString::createFromAscii(aIt->second.cFieldServiceName)),
+ uno::UNO_QUERY_THROW);
+ if (!sFirstParam.isEmpty())
+ {
+ xTC->setPropertyValue(getPropertyName(PROP_ALTERNATIVE_TEXT),
+ uno::Any(sFirstParam));
+ }
+ OUString sValue;
+ // \f TC entry in doc with multiple tables
+// if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
+// {
+ // todo: unsupported
+// }
+ if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
+ // \l Outline Level
+ {
+ sal_Int32 nLevel = sValue.toInt32();
+ if( !sValue.isEmpty() && nLevel >= 0 && nLevel <= 10 )
+ xTC->setPropertyValue(getPropertyName(PROP_LEVEL), uno::Any( static_cast<sal_Int16>(nLevel) ));
+ }
+// if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
+// \n Suppress page numbers
+// {
+ //todo: unsupported feature
+// }
+ pContext->SetTC( xTC );
+ }
+ break;
+ case FIELD_NUMCHARS:
+ case FIELD_NUMWORDS:
+ case FIELD_NUMPAGES:
+ if (xFieldProperties.is())
+ xFieldProperties->setPropertyValue(
+ getPropertyName(PROP_NUMBERING_TYPE),
+ uno::Any( lcl_ParseNumberingType(pContext->GetCommand()) ));
+ break;
+ }
+
+ if (!bCreateEnhancedField)
+ {
+ pContext->SetTextField( uno::Reference<text::XTextField>(xFieldInterface, uno::UNO_QUERY) );
+ }
+ }
+ else
+ {
+ /* Unsupported fields will be handled here for docx file.
+ * To handle unsupported fields used fieldmark API.
+ */
+ OUString aCode( pContext->GetCommand().trim() );
+ // Don't waste resources on wrapping shapes inside a fieldmark.
+ if (sType != "SHAPE" && m_xTextFactory.is() && !m_aTextAppendStack.empty())
+ {
+ xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.Fieldmark");
+
+ uno::Reference<text::XFormField> const xFormField(xFieldInterface, uno::UNO_QUERY);
+ InsertFieldmark(m_aTextAppendStack, xFormField, pContext->GetStartRange(),
+ pContext->GetFieldId());
+ xFormField->setFieldType(ODF_UNHANDLED);
+ ++m_nStartGenericField;
+ pContext->SetFormField( xFormField );
+ uno::Reference<container::XNameContainer> const xNameCont(xFormField->getParameters());
+ // note: setting the code to empty string is *required* in
+ // m_bForceGenericFields mode, or the export will write
+ // the ODF_UNHANDLED string!
+ assert(!m_bForceGenericFields || aCode.isEmpty());
+ xNameCont->insertByName(ODF_CODE_PARAM, uno::Any(aCode));
+ ww::eField const id(GetWW8FieldId(sType));
+ if (id != ww::eNONE)
+ { // tdf#129247 tdf#134264 set WW8 id for WW8 export
+ xNameCont->insertByName(ODF_ID_PARAM, uno::Any(OUString::number(id)));
+ }
+ }
+ else
+ m_bParaHadField = false;
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "Exception in CloseFieldCommand()" );
+ }
+ pContext->SetCommandCompleted();
+}
+/*-------------------------------------------------------------------------
+//the _current_ fields require a string type result while TOCs accept richt results
+ -----------------------------------------------------------------------*/
+bool DomainMapper_Impl::IsFieldResultAsString()
+{
+ bool bRet = false;
+ OSL_ENSURE( !m_aFieldStack.empty(), "field stack empty?");
+ FieldContextPtr pContext = m_aFieldStack.back();
+ OSL_ENSURE( pContext, "no field context available");
+ if( pContext )
+ {
+ bRet = pContext->GetTextField().is()
+ || pContext->GetFieldId() == FIELD_FORMDROPDOWN
+ || pContext->GetFieldId() == FIELD_FILLIN;
+ }
+
+ if (!bRet)
+ {
+ FieldContextPtr pOuter = GetParentFieldContext(m_aFieldStack);
+ if (pOuter)
+ {
+ if (!IsFieldNestingAllowed(pOuter, m_aFieldStack.back()))
+ {
+ // If nesting is not allowed, then the result can only be a string.
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+void DomainMapper_Impl::AppendFieldResult(std::u16string_view rString)
+{
+ assert(!m_aFieldStack.empty());
+ FieldContextPtr pContext = m_aFieldStack.back();
+ SAL_WARN_IF(!pContext, "writerfilter.dmapper", "no field context");
+ if (!pContext)
+ return;
+
+ FieldContextPtr pOuter = GetParentFieldContext(m_aFieldStack);
+ if (pOuter)
+ {
+ if (!IsFieldNestingAllowed(pOuter, pContext))
+ {
+ if (pOuter->IsCommandCompleted())
+ {
+ // Child can't host the field result, forward to parent's result.
+ pOuter->AppendResult(rString);
+ }
+ return;
+ }
+ }
+
+ pContext->AppendResult(rString);
+}
+
+// Calculates css::DateTime based on ddddd.sssss since 1899-12-30
+static util::DateTime lcl_dateTimeFromSerial(const double& dSerial)
+{
+ DateTime d(Date(30, 12, 1899));
+ d.AddTime(dSerial);
+ return d.GetUNODateTime();
+}
+
+void DomainMapper_Impl::SetFieldResult(OUString const& rResult)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("setFieldResult");
+ TagLogger::getInstance().chars(rResult);
+#endif
+
+ FieldContextPtr pContext = m_aFieldStack.back();
+ OSL_ENSURE( pContext, "no field context available");
+
+ if (m_aFieldStack.size() > 1)
+ {
+ // This is a nested field. See if the parent supports nesting on the Writer side.
+ FieldContextPtr pParentContext = m_aFieldStack[m_aFieldStack.size() - 2];
+ if (pParentContext)
+ {
+ std::vector<OUString> aParentParts = pParentContext->GetCommandParts();
+ // Conditional text fields don't support nesting in Writer.
+ if (!aParentParts.empty() && aParentParts[0] == "IF")
+ {
+ return;
+ }
+ }
+ }
+
+ if( !pContext )
+ return;
+
+ uno::Reference<text::XTextField> xTextField = pContext->GetTextField();
+ try
+ {
+ OSL_ENSURE( xTextField.is()
+ //||m_xTOC.is() ||m_xTC.is()
+ //||m_sHyperlinkURL.getLength()
+ , "DomainMapper_Impl::SetFieldResult: field not created" );
+ if(xTextField.is())
+ {
+ try
+ {
+ if( m_bSetUserFieldContent )
+ {
+ // user field content has to be set at the field master
+ uno::Reference< text::XDependentTextField > xDependentField( xTextField, uno::UNO_QUERY_THROW );
+ xDependentField->getTextFieldMaster()->setPropertyValue(
+ getPropertyName(PROP_CONTENT),
+ uno::Any( rResult ));
+ }
+ else if ( m_bSetCitation )
+ {
+
+ uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
+ // In case of SetExpression, the field result contains the content of the variable.
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xTextField, uno::UNO_QUERY);
+
+ bool bIsSetbiblio = xServiceInfo->supportsService("com.sun.star.text.TextField.Bibliography");
+ if( bIsSetbiblio )
+ {
+ uno::Any aProperty = xFieldProperties->getPropertyValue("Fields");
+ uno::Sequence<beans::PropertyValue> aValues ;
+ aProperty >>= aValues;
+ beans::PropertyValue propertyVal;
+ sal_Int32 nTitleFoundIndex = -1;
+ for (sal_Int32 i = 0; i < aValues.getLength(); ++i)
+ {
+ propertyVal = aValues[i];
+ if (propertyVal.Name == "Title")
+ {
+ nTitleFoundIndex = i;
+ break;
+ }
+ }
+ if (nTitleFoundIndex != -1)
+ {
+ OUString titleStr;
+ uno::Any aValue(propertyVal.Value);
+ aValue >>= titleStr;
+ titleStr += rResult;
+ propertyVal.Value <<= titleStr;
+ aValues.getArray()[nTitleFoundIndex] = propertyVal;
+ }
+ else
+ {
+ aValues.realloc(aValues.getLength() + 1);
+ propertyVal.Name = "Title";
+ propertyVal.Value <<= rResult;
+ aValues.getArray()[aValues.getLength() - 1] = propertyVal;
+ }
+ xFieldProperties->setPropertyValue("Fields",
+ uno::Any(aValues));
+ }
+ }
+ else if ( m_bSetDateValue )
+ {
+ uno::Reference< util::XNumberFormatsSupplier > xNumberSupplier( m_xTextDocument, uno::UNO_QUERY_THROW );
+
+ uno::Reference<util::XNumberFormatter> xFormatter(util::NumberFormatter::create(m_xComponentContext), uno::UNO_QUERY_THROW);
+ xFormatter->attachNumberFormatsSupplier( xNumberSupplier );
+ sal_Int32 nKey = 0;
+
+ uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
+
+ xFieldProperties->getPropertyValue( "NumberFormat" ) >>= nKey;
+ xFieldProperties->setPropertyValue(
+ "DateTimeValue",
+ uno::Any( lcl_dateTimeFromSerial( xFormatter->convertStringToNumber( nKey, rResult ) ) ) );
+ }
+ else
+ {
+ uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
+ // In case of SetExpression, and Input fields the field result contains the content of the variable.
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xTextField, uno::UNO_QUERY);
+ // there are fields with a content property, which aren't working correctly with
+ // a generalized try catch of the content, property, so just restrict content
+ // handling to these explicit services.
+ const bool bHasContent = xServiceInfo->supportsService("com.sun.star.text.TextField.SetExpression") ||
+ xServiceInfo->supportsService("com.sun.star.text.TextField.Input");
+ // If we already have content set, then use the current presentation
+ OUString sValue;
+ if (bHasContent)
+ {
+ // this will throw for field types without Content
+ uno::Any aValue(xFieldProperties->getPropertyValue(
+ getPropertyName(PROP_CONTENT)));
+ aValue >>= sValue;
+ }
+ xFieldProperties->setPropertyValue(
+ getPropertyName(bHasContent && sValue.isEmpty()? PROP_CONTENT : PROP_CURRENT_PRESENTATION),
+ uno::Any( rResult ));
+
+ // LO always automatically updates a DocInfo field from the File-Properties-Custom Prop
+ // while MS Word requires the user to manually refresh the field (with F9).
+ // In other words, Word lets the field to be out of sync with the controlling variable.
+ // Marking as FIXEDFLD solves the automatic replacement problem, but of course prevents
+ // Writer from making any changes, even on an F9 refresh.
+ OUString sVariable = pContext->GetVariableValue();
+ if (rResult.getLength() != sVariable.getLength())
+ {
+ sal_Int32 nLen = sVariable.indexOf('\x0');
+ if (nLen >= 0)
+ sVariable = sVariable.copy(0, nLen);
+ }
+ bool bCustomFixedField = rResult != sVariable &&
+ xServiceInfo->supportsService("com.sun.star.text.TextField.DocInfo.Custom");
+
+ if (bCustomFixedField || xServiceInfo->supportsService(
+ "com.sun.star.text.TextField.DocInfo.CreateDateTime"))
+ {
+ // Creation time is const, don't try to update it.
+ xFieldProperties->setPropertyValue("IsFixed", uno::Any(true));
+ }
+ }
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ //some fields don't have a CurrentPresentation (DateTime)
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "DomainMapper_Impl::SetFieldResult");
+ }
+}
+
+void DomainMapper_Impl::SetFieldFFData(const FFDataHandler::Pointer_t& pFFDataHandler)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("setFieldFFData");
+#endif
+
+ if (!m_aFieldStack.empty())
+ {
+ FieldContextPtr pContext = m_aFieldStack.back();
+ if (pContext)
+ {
+ pContext->setFFDataHandler(pFFDataHandler);
+ }
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void DomainMapper_Impl::PopFieldContext()
+{
+ if(m_bDiscardHeaderFooter)
+ return;
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("popFieldContext");
+#endif
+
+ if (m_aFieldStack.empty())
+ return;
+
+ FieldContextPtr pContext = m_aFieldStack.back();
+ OSL_ENSURE( pContext, "no field context available");
+ if( pContext )
+ {
+ if( !pContext->IsCommandCompleted() )
+ CloseFieldCommand();
+
+ if (!pContext->GetResult().isEmpty())
+ {
+ uno::Reference< beans::XPropertySet > xFieldProperties = pContext->GetCustomField();
+ if(xFieldProperties.is())
+ SetNumberFormat( pContext->GetResult(), xFieldProperties, true );
+ SetFieldResult( pContext->GetResult() );
+ }
+
+ //insert the field, TC or TOC
+ uno::Reference< text::XTextAppend > xTextAppend;
+ if (!m_aTextAppendStack.empty())
+ xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if(xTextAppend.is())
+ {
+ try
+ {
+ uno::Reference< text::XTextContent > xToInsert( pContext->GetTOC(), uno::UNO_QUERY );
+ if( xToInsert.is() )
+ {
+ if (m_bStartedTOC || m_bStartIndex || m_bStartBibliography)
+ {
+ // inside SDT, last empty paragraph is also part of index
+ if (!m_bParaChanged && !m_xSdtEntryStart)
+ {
+ // End of index is the first item on a new paragraph - this paragraph
+ // should not be part of index
+ auto xCursor
+ = xTextAppend->createTextCursorByRange(
+ m_aTextAppendStack.top().xInsertPosition.is()
+ ? m_aTextAppendStack.top().xInsertPosition
+ : xTextAppend->getEnd());
+ xCursor->goLeft(1, true);
+ // delete
+ xCursor->setString(OUString());
+ // But a new paragraph should be started after the index instead
+ if (m_bIsNewDoc) // this check - see testTdf129402
+ { // where finishParagraph inserts between 2 EndNode
+ xTextAppend->finishParagraph(css::beans::PropertyValues());
+ }
+ else
+ {
+ xTextAppend->finishParagraphInsert(css::beans::PropertyValues(),
+ m_aTextAppendStack.top().xInsertPosition);
+ }
+ }
+ m_bStartedTOC = false;
+ m_aTextAppendStack.pop();
+ m_bTextInserted = false;
+ m_bParaChanged = true; // the paragraph must stay anyway
+ }
+ m_bStartTOC = false;
+ m_bStartIndex = false;
+ m_bStartBibliography = false;
+ if (IsInHeaderFooter() && m_bStartTOCHeaderFooter)
+ m_bStartTOCHeaderFooter = false;
+ }
+ else
+ {
+ xToInsert.set(pContext->GetTC(), uno::UNO_QUERY);
+ if( !xToInsert.is() && !m_bStartTOC && !m_bStartIndex && !m_bStartBibliography )
+ xToInsert = pContext->GetTextField();
+ if( xToInsert.is() && !m_bStartTOC && !m_bStartIndex && !m_bStartBibliography)
+ {
+ PropertyMap aMap;
+ // Character properties of the field show up here the
+ // last (always empty) run. Inherit character
+ // properties from there.
+ // Also merge in the properties from the field context,
+ // e.g. SdtEndBefore.
+ if (m_pLastCharacterContext)
+ aMap.InsertProps(m_pLastCharacterContext);
+ aMap.InsertProps(m_aFieldStack.back()->getProperties());
+ appendTextContent(xToInsert, aMap.GetPropertyValues());
+ CheckRedline( xToInsert->getAnchor( ) );
+ }
+ else
+ {
+ uno::Reference< text::XTextCursor > xCrsr = xTextAppend->createTextCursorByRange(pContext->GetStartRange());
+ FormControlHelper::Pointer_t pFormControlHelper(pContext->getFormControlHelper());
+ if (pFormControlHelper)
+ {
+ // xCrsr may be empty e.g. when pContext->GetStartRange() is outside of
+ // xTextAppend, like when a field started in a parent paragraph is being
+ // closed inside an anchored text box. It could be possible to throw an
+ // exception here, and abort import, but Word tolerates such invalid
+ // input, so it makes sense to do the same (tdf#152200)
+ if (xCrsr.is())
+ {
+ uno::Reference< text::XFormField > xFormField(pContext->GetFormField());
+ if (pFormControlHelper->hasFFDataHandler())
+ {
+ xToInsert.set(xFormField, uno::UNO_QUERY);
+ if (xFormField.is() && xToInsert.is())
+ {
+ PopFieldmark(m_aTextAppendStack, xCrsr,
+ pContext->GetFieldId());
+ pFormControlHelper->processField(xFormField);
+ }
+ else
+ {
+ pFormControlHelper->insertControl(xCrsr);
+ }
+ }
+ else
+ {
+ PopFieldmark(m_aTextAppendStack, xCrsr,
+ pContext->GetFieldId());
+ uno::Reference<lang::XComponent>(xFormField, uno::UNO_QUERY_THROW)->dispose(); // presumably invalid?
+ }
+ }
+ }
+ else if (!pContext->GetHyperlinkURL().isEmpty() && xCrsr.is())
+ {
+ if (m_aTextAppendStack.top().xInsertPosition.is())
+ {
+ xCrsr->gotoRange(m_aTextAppendStack.top().xInsertPosition, true);
+ }
+ else
+ {
+ xCrsr->gotoEnd(true);
+ }
+
+ // Draw components (like comments) need hyperlinks set differently
+ SvxUnoTextRangeBase* pDrawText = dynamic_cast<SvxUnoTextRangeBase*>(xCrsr.get());
+ if ( pDrawText )
+ pDrawText->attachField( std::make_unique<SvxURLField>(pContext->GetHyperlinkURL(), xCrsr->getString(), SvxURLFormat::AppDefault) );
+ else
+ {
+ uno::Reference< beans::XPropertySet > xCrsrProperties( xCrsr, uno::UNO_QUERY_THROW );
+ xCrsrProperties->setPropertyValue(getPropertyName(PROP_HYPER_LINK_U_R_L), uno::
+ Any(pContext->GetHyperlinkURL()));
+
+ if (!pContext->GetHyperlinkTarget().isEmpty())
+ xCrsrProperties->setPropertyValue("HyperLinkTarget", uno::Any(pContext->GetHyperlinkTarget()));
+
+ if (m_bStartTOC) {
+ OUString sDisplayName("Index Link");
+ xCrsrProperties->setPropertyValue("VisitedCharStyleName",uno::Any(sDisplayName));
+ xCrsrProperties->setPropertyValue("UnvisitedCharStyleName",uno::Any(sDisplayName));
+ }
+ else
+ {
+ uno::Any aAny = xCrsrProperties->getPropertyValue("CharStyleName");
+ OUString charStyle;
+ if (css::uno::fromAny(aAny, &charStyle))
+ {
+ if (charStyle.isEmpty())
+ {
+ xCrsrProperties->setPropertyValue("VisitedCharStyleName", uno::Any(OUString("Default Style")));
+ xCrsrProperties->setPropertyValue("UnvisitedCharStyleName", uno::Any(OUString("Default Style")));
+ }
+ else if (charStyle.equalsIgnoreAsciiCase("Internet Link"))
+ {
+ xCrsrProperties->setPropertyValue("CharStyleName", uno::Any(OUString("Default Style")));
+ }
+ else
+ {
+ xCrsrProperties->setPropertyValue("VisitedCharStyleName", aAny);
+ xCrsrProperties->setPropertyValue("UnvisitedCharStyleName", aAny);
+ }
+ }
+ }
+ }
+ }
+ else if (m_nStartGenericField != 0)
+ {
+ --m_nStartGenericField;
+ PopFieldmark(m_aTextAppendStack, xCrsr, pContext->GetFieldId());
+ if(m_bTextInserted)
+ {
+ m_bTextInserted = false;
+ }
+ }
+ }
+ }
+ }
+ catch(const lang::IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "PopFieldContext()" );
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "PopFieldContext()" );
+ }
+ }
+
+ //TOCs have to include all the imported content
+ }
+
+ std::vector<FieldParagraph> aParagraphsToFinish;
+ if (pContext)
+ {
+ aParagraphsToFinish = pContext->GetParagraphsToFinish();
+ }
+
+ //remove the field context
+ m_aFieldStack.pop_back();
+
+ // Finish the paragraph(s) now that the field is closed.
+ for (const auto& rFinish : aParagraphsToFinish)
+ {
+ finishParagraph(rFinish.m_pPropertyMap, rFinish.m_bRemove);
+ }
+}
+
+
+void DomainMapper_Impl::SetBookmarkName( const OUString& rBookmarkName )
+{
+ BookmarkMap_t::iterator aBookmarkIter = m_aBookmarkMap.find( m_sCurrentBkmkId );
+ if( aBookmarkIter != m_aBookmarkMap.end() )
+ {
+ // fields are internal bookmarks: consume redundant "normal" bookmark
+ if ( IsOpenField() )
+ {
+ FFDataHandler::Pointer_t pFFDataHandler(GetTopFieldContext()->getFFDataHandler());
+ if (pFFDataHandler && pFFDataHandler->getName() == rBookmarkName)
+ {
+ // HACK: At the END marker, StartOrEndBookmark will START
+ // a bookmark which will eventually be abandoned, not created.
+ m_aBookmarkMap.erase(aBookmarkIter);
+ return;
+ }
+ }
+
+ aBookmarkIter->second.m_sBookmarkName = m_sCurrentBkmkPrefix + rBookmarkName;
+ m_sCurrentBkmkPrefix.clear();
+ }
+ else
+ {
+ m_sCurrentBkmkName = rBookmarkName;
+ m_sCurrentBkmkPrefix.clear();
+ }
+}
+
+// This method was used as-is for DomainMapper_Impl::startOrEndPermissionRange() implementation.
+void DomainMapper_Impl::StartOrEndBookmark( const OUString& rId )
+{
+ /*
+ * Add the dummy paragraph to handle section properties
+ * iff the first element in the section is a table. If the dummy para is not added yet, then add it;
+ * So bookmark is not attached to the wrong paragraph.
+ */
+ if(hasTableManager() && getTableManager().isInCell() && m_nTableDepth == 0 && GetIsFirstParagraphInSection()
+ && !GetIsDummyParaAddedForTableInSection() &&!GetIsTextFrameInserted())
+ {
+ AddDummyParaForTableInSection();
+ }
+
+ bool bIsAfterDummyPara = GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
+ if (m_aTextAppendStack.empty())
+ return;
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ BookmarkMap_t::iterator aBookmarkIter = m_aBookmarkMap.find( rId );
+ //is the bookmark name already registered?
+ try
+ {
+ if( aBookmarkIter != m_aBookmarkMap.end() )
+ {
+ if (m_xTextFactory.is())
+ {
+ uno::Reference< text::XTextContent > xBookmark( m_xTextFactory->createInstance( "com.sun.star.text.Bookmark" ), uno::UNO_QUERY_THROW );
+ uno::Reference< text::XTextCursor > xCursor;
+ uno::Reference< text::XText > xText = aBookmarkIter->second.m_xTextRange->getText();
+ if( aBookmarkIter->second.m_bIsStartOfText && !bIsAfterDummyPara)
+ {
+ xCursor = xText->createTextCursorByRange( xText->getStart() );
+ }
+ else
+ {
+ xCursor = xText->createTextCursorByRange( aBookmarkIter->second.m_xTextRange );
+ xCursor->goRight( 1, false );
+ }
+
+ xCursor->gotoRange( xTextAppend->getEnd(), true );
+ // A Paragraph was recently finished, and a new Paragraph has not been started as yet
+ // then move the bookmark-End to the earlier paragraph
+ if (IsOutsideAParagraph())
+ {
+ // keep bookmark range
+ uno::Reference< text::XTextRange > xStart = xCursor->getStart();
+ xCursor->goLeft( 1, false );
+ xCursor->gotoRange(xStart, true );
+ }
+ uno::Reference< container::XNamed > xBkmNamed( xBookmark, uno::UNO_QUERY_THROW );
+ SAL_WARN_IF(aBookmarkIter->second.m_sBookmarkName.isEmpty(), "writerfilter.dmapper", "anonymous bookmark");
+ //todo: make sure the name is not used already!
+ xBkmNamed->setName( aBookmarkIter->second.m_sBookmarkName );
+ xTextAppend->insertTextContent( uno::Reference< text::XTextRange >( xCursor, uno::UNO_QUERY_THROW), xBookmark, !xCursor->isCollapsed() );
+ }
+ m_aBookmarkMap.erase( aBookmarkIter );
+ m_sCurrentBkmkId.clear();
+ }
+ else
+ {
+ //otherwise insert a text range as marker
+ bool bIsStart = true;
+ uno::Reference< text::XTextRange > xCurrent;
+ if (xTextAppend.is())
+ {
+ uno::Reference<text::XTextCursor> const xCursor =
+ xTextAppend->createTextCursorByRange(
+ m_aTextAppendStack.top().xInsertPosition.is()
+ ? m_aTextAppendStack.top().xInsertPosition
+ : xTextAppend->getEnd() );
+
+ if (!xCursor)
+ return;
+
+ if (!bIsAfterDummyPara)
+ bIsStart = !xCursor->goLeft(1, false);
+ xCurrent = xCursor->getStart();
+ }
+ m_sCurrentBkmkId = rId;
+ m_aBookmarkMap.emplace( rId, BookmarkInsertPosition( bIsStart, m_sCurrentBkmkName, xCurrent ) );
+ m_sCurrentBkmkName.clear();
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ //TODO: What happens to bookmarks where start and end are at different XText objects?
+ }
+}
+
+void DomainMapper_Impl::SetMoveBookmark( bool bIsFrom )
+{
+ static constexpr OUStringLiteral MoveFrom_Bookmark_NamePrefix = u"__RefMoveFrom__";
+ static constexpr OUStringLiteral MoveTo_Bookmark_NamePrefix = u"__RefMoveTo__";
+ if ( bIsFrom )
+ m_sCurrentBkmkPrefix = MoveFrom_Bookmark_NamePrefix;
+ else
+ m_sCurrentBkmkPrefix = MoveTo_Bookmark_NamePrefix;
+}
+
+void DomainMapper_Impl::setPermissionRangeEd(const OUString& user)
+{
+ PermMap_t::iterator aPremIter = m_aPermMap.find(m_sCurrentPermId);
+ if (aPremIter != m_aPermMap.end())
+ aPremIter->second.m_Ed = user;
+ else
+ m_sCurrentPermEd = user;
+}
+
+void DomainMapper_Impl::setPermissionRangeEdGrp(const OUString& group)
+{
+ PermMap_t::iterator aPremIter = m_aPermMap.find(m_sCurrentPermId);
+ if (aPremIter != m_aPermMap.end())
+ aPremIter->second.m_EdGrp = group;
+ else
+ m_sCurrentPermEdGrp = group;
+}
+
+// This method is based on implementation from DomainMapper_Impl::StartOrEndBookmark()
+void DomainMapper_Impl::startOrEndPermissionRange(sal_Int32 permissinId)
+{
+ /*
+ * Add the dummy paragraph to handle section properties
+ * if the first element in the section is a table. If the dummy para is not added yet, then add it;
+ * So permission is not attached to the wrong paragraph.
+ */
+ if (getTableManager().isInCell() && m_nTableDepth == 0 && GetIsFirstParagraphInSection()
+ && !GetIsDummyParaAddedForTableInSection() && !GetIsTextFrameInserted())
+ {
+ AddDummyParaForTableInSection();
+ }
+
+ if (m_aTextAppendStack.empty())
+ return;
+
+ const bool bIsAfterDummyPara = GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
+
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ PermMap_t::iterator aPermIter = m_aPermMap.find(permissinId);
+
+ //is the bookmark name already registered?
+ try
+ {
+ if (aPermIter == m_aPermMap.end())
+ {
+ //otherwise insert a text range as marker
+ bool bIsStart = true;
+ uno::Reference< text::XTextRange > xCurrent;
+ if (xTextAppend.is())
+ {
+ uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());
+
+ if (!bIsAfterDummyPara)
+ bIsStart = !xCursor->goLeft(1, false);
+ xCurrent = xCursor->getStart();
+ }
+
+ // register the start of the new permission
+ m_sCurrentPermId = permissinId;
+ m_aPermMap.emplace(permissinId, PermInsertPosition(bIsStart, permissinId, m_sCurrentPermEd, m_sCurrentPermEdGrp, xCurrent));
+
+ // clean up
+ m_sCurrentPermEd.clear();
+ m_sCurrentPermEdGrp.clear();
+ }
+ else
+ {
+ if (m_xTextFactory.is())
+ {
+ uno::Reference< text::XTextCursor > xCursor;
+ uno::Reference< text::XText > xText = aPermIter->second.m_xTextRange->getText();
+ if (aPermIter->second.m_bIsStartOfText && !bIsAfterDummyPara)
+ {
+ xCursor = xText->createTextCursorByRange(xText->getStart());
+ }
+ else
+ {
+ xCursor = xText->createTextCursorByRange(aPermIter->second.m_xTextRange);
+ xCursor->goRight(1, false);
+ }
+
+ xCursor->gotoRange(xTextAppend->getEnd(), true);
+ // A Paragraph was recently finished, and a new Paragraph has not been started as yet
+ // then move the bookmark-End to the earlier paragraph
+ if (IsOutsideAParagraph())
+ {
+ xCursor->goLeft(1, false);
+ }
+
+ // create a new bookmark using specific bookmark name pattern for permissions
+ uno::Reference< text::XTextContent > xPerm(m_xTextFactory->createInstance("com.sun.star.text.Bookmark"), uno::UNO_QUERY_THROW);
+ uno::Reference< container::XNamed > xPermNamed(xPerm, uno::UNO_QUERY_THROW);
+ xPermNamed->setName(aPermIter->second.createBookmarkName());
+
+ // add new bookmark
+ const bool bAbsorb = !xCursor->isCollapsed();
+ uno::Reference< text::XTextRange > xCurrent(xCursor, uno::UNO_QUERY_THROW);
+ xTextAppend->insertTextContent(xCurrent, xPerm, bAbsorb);
+ }
+
+ // remove processed permission
+ m_aPermMap.erase(aPermIter);
+
+ // clean up
+ m_sCurrentPermId = 0;
+ m_sCurrentPermEd.clear();
+ m_sCurrentPermEdGrp.clear();
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ //TODO: What happens to bookmarks where start and end are at different XText objects?
+ }
+}
+
+void DomainMapper_Impl::AddAnnotationPosition(
+ const bool bStart,
+ const sal_Int32 nAnnotationId)
+{
+ if (m_aTextAppendStack.empty())
+ return;
+
+ // Create a cursor, pointing to the current position.
+ uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ uno::Reference<text::XTextRange> xCurrent;
+ if (xTextAppend.is())
+ {
+ uno::Reference<text::XTextCursor> xCursor;
+ if (m_bIsNewDoc)
+ xCursor = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());
+ else
+ xCursor = m_aTextAppendStack.top().xCursor;
+ if (xCursor.is())
+ xCurrent = xCursor->getStart();
+ }
+
+ // And save it, to be used by PopAnnotation() later.
+ AnnotationPosition& aAnnotationPosition = m_aAnnotationPositions[ nAnnotationId ];
+ if (bStart)
+ {
+ aAnnotationPosition.m_xStart = xCurrent;
+ }
+ else
+ {
+ aAnnotationPosition.m_xEnd = xCurrent;
+ }
+ m_aAnnotationPositions[ nAnnotationId ] = aAnnotationPosition;
+}
+
+GraphicImportPtr const & DomainMapper_Impl::GetGraphicImport(GraphicImportType eGraphicImportType)
+{
+ if(!m_pGraphicImport)
+ m_pGraphicImport = new GraphicImport( m_xComponentContext, m_xTextFactory, m_rDMapper, eGraphicImportType, m_aPositionOffsets, m_aAligns, m_aPositivePercentages );
+ return m_pGraphicImport;
+}
+/*-------------------------------------------------------------------------
+ reset graphic import if the last import resulted in a shape, not a graphic
+ -----------------------------------------------------------------------*/
+void DomainMapper_Impl::ResetGraphicImport()
+{
+ m_pGraphicImport.clear();
+}
+
+
+void DomainMapper_Impl::ImportGraphic(const writerfilter::Reference< Properties >::Pointer_t& ref, GraphicImportType eGraphicImportType)
+{
+ GetGraphicImport(eGraphicImportType);
+ if( eGraphicImportType != IMPORT_AS_DETECTED_INLINE && eGraphicImportType != IMPORT_AS_DETECTED_ANCHOR )
+ {
+ //create the graphic
+ ref->resolve( *m_pGraphicImport );
+ }
+
+ //insert it into the document at the current cursor position
+
+ uno::Reference<text::XTextContent> xTextContent
+ (m_pGraphicImport->GetGraphicObject());
+
+ // In case the SDT starts with the text portion of the graphic, then set the SDT properties here.
+ bool bHasGrabBag = false;
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
+ if (xPropertySet.is())
+ {
+ uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
+ bHasGrabBag = xPropertySetInfo->hasPropertyByName("FrameInteropGrabBag");
+ // In case we're outside a paragraph, then the SDT properties are stored in the paragraph grab-bag, not the frame one.
+ if (!m_pSdtHelper->isInteropGrabBagEmpty() && bHasGrabBag && !m_pSdtHelper->isOutsideAParagraph())
+ {
+ comphelper::SequenceAsHashMap aFrameGrabBag(xPropertySet->getPropertyValue("FrameInteropGrabBag"));
+ aFrameGrabBag["SdtPr"] <<= m_pSdtHelper->getInteropGrabBagAndClear();
+ xPropertySet->setPropertyValue("FrameInteropGrabBag", uno::Any(aFrameGrabBag.getAsConstPropertyValueList()));
+ }
+ }
+
+ /* Set "SdtEndBefore" property on Drawing.
+ * It is required in a case when Drawing appears immediately after first run i.e.
+ * there is no text/space/tab in between two runs.
+ * In this case "SdtEndBefore" property needs to be set on Drawing.
+ */
+ if(IsSdtEndBefore())
+ {
+ if(xPropertySet.is() && bHasGrabBag)
+ {
+ uno::Sequence<beans::PropertyValue> aFrameGrabBag( comphelper::InitPropertySequence({
+ { "SdtEndBefore", uno::Any(true) }
+ }));
+ xPropertySet->setPropertyValue("FrameInteropGrabBag",uno::Any(aFrameGrabBag));
+ }
+ }
+
+
+ // Update the shape properties if it is embedded object.
+ if(m_xEmbedded.is()){
+ if (m_pGraphicImport->GetXShapeObject())
+ m_pGraphicImport->GetXShapeObject()->setPosition(
+ m_pGraphicImport->GetGraphicObjectPosition());
+
+ uno::Reference<drawing::XShape> xShape = m_pGraphicImport->GetXShapeObject();
+ UpdateEmbeddedShapeProps(xShape);
+ if (eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
+ {
+ uno::Reference<beans::XPropertySet> xEmbeddedProps(m_xEmbedded, uno::UNO_QUERY);
+ xEmbeddedProps->setPropertyValue("AnchorType", uno::Any(text::TextContentAnchorType_AT_CHARACTER));
+ xEmbeddedProps->setPropertyValue("IsFollowingTextFlow", uno::Any(m_pGraphicImport->GetLayoutInCell()));
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ xEmbeddedProps->setPropertyValue("HoriOrient", xShapeProps->getPropertyValue("HoriOrient"));
+ xEmbeddedProps->setPropertyValue("HoriOrientPosition", xShapeProps->getPropertyValue("HoriOrientPosition"));
+ xEmbeddedProps->setPropertyValue("HoriOrientRelation", xShapeProps->getPropertyValue("HoriOrientRelation"));
+ xEmbeddedProps->setPropertyValue("VertOrient", xShapeProps->getPropertyValue("VertOrient"));
+ xEmbeddedProps->setPropertyValue("VertOrientPosition", xShapeProps->getPropertyValue("VertOrientPosition"));
+ xEmbeddedProps->setPropertyValue("VertOrientRelation", xShapeProps->getPropertyValue("VertOrientRelation"));
+ //tdf123873 fix missing textwrap import
+ xEmbeddedProps->setPropertyValue("TextWrap", xShapeProps->getPropertyValue("TextWrap"));
+
+ // GraphicZOrderHelper::findZOrder() was called already, so can just copy it over.
+ xEmbeddedProps->setPropertyValue("ZOrder", xShapeProps->getPropertyValue("ZOrder"));
+ }
+ }
+ //insert it into the document at the current cursor position
+ OSL_ENSURE( xTextContent.is(), "DomainMapper_Impl::ImportGraphic");
+ if( xTextContent.is())
+ {
+ bool bAppend = true;
+ // workaround for images anchored to characters: add ZWSPs around the anchoring point
+ if ( eGraphicImportType != IMPORT_AS_DETECTED_INLINE && !m_aRedlines.top().empty() )
+ {
+ uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if(xTextAppend.is())
+ {
+ try
+ {
+ uno::Reference< text::XText > xText = xTextAppend->getText();
+ uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
+ xCrsr->gotoEnd(false);
+ PropertyMapPtr pEmpty(new PropertyMap());
+ appendTextPortion(u"​", pEmpty);
+ appendTextContent( xTextContent, uno::Sequence< beans::PropertyValue >() );
+ bAppend = false;
+ xCrsr->gotoEnd(false);
+ appendTextPortion(u"​", pEmpty);
+
+ m_bRedlineImageInPreviousRun = true;
+ m_previousRedline = m_currentRedline;
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ }
+
+ if ( bAppend )
+ appendTextContent( xTextContent, uno::Sequence< beans::PropertyValue >() );
+
+ if (eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR && !m_aTextAppendStack.empty())
+ {
+ // Remember this object is anchored to the current paragraph.
+ AnchoredObjectInfo aInfo;
+ aInfo.m_xAnchoredObject = xTextContent;
+ if (m_pGraphicImport)
+ {
+ // We still have the graphic import around, remember the original margin, so later
+ // SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing() can use it.
+ aInfo.m_nLeftMargin = m_pGraphicImport->GetLeftMarginOrig();
+ }
+ m_aTextAppendStack.top().m_aAnchoredObjects.push_back(aInfo);
+ }
+ else if (eGraphicImportType == IMPORT_AS_DETECTED_INLINE)
+ {
+ m_bParaWithInlineObject = true;
+
+ // store inline images with track changes, because the anchor point
+ // to set redlining is not available yet
+ if (!m_aTextAppendStack.empty() && !m_aRedlines.top().empty() )
+ {
+ // Remember this object is anchored to the current paragraph.
+ AnchoredObjectInfo aInfo;
+ aInfo.m_xAnchoredObject = xTextContent;
+ aInfo.m_xRedlineForInline = m_aRedlines.top().back();
+ m_aTextAppendStack.top().m_aAnchoredObjects.push_back(aInfo);
+ }
+
+ }
+ }
+
+ // Clear the reference, so in case the embedded object is inside a
+ // TextFrame, we won't try to resize it (to match the size of the
+ // TextFrame) here.
+ m_xEmbedded.clear();
+ m_pGraphicImport.clear();
+}
+
+
+void DomainMapper_Impl::SetLineNumbering( sal_Int32 nLnnMod, sal_uInt32 nLnc, sal_Int32 ndxaLnn )
+{
+ if( !m_bLineNumberingSet )
+ {
+ try
+ {
+ uno::Reference< text::XLineNumberingProperties > xLineProperties( m_xTextDocument, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProperties = xLineProperties->getLineNumberingProperties();
+ uno::Any aTrue( uno::Any( true ));
+ xProperties->setPropertyValue( getPropertyName( PROP_IS_ON ), aTrue);
+ xProperties->setPropertyValue( getPropertyName( PROP_COUNT_EMPTY_LINES ), aTrue );
+ xProperties->setPropertyValue( getPropertyName( PROP_COUNT_LINES_IN_FRAMES ), uno::Any( false ) );
+ xProperties->setPropertyValue( getPropertyName( PROP_INTERVAL ), uno::Any( static_cast< sal_Int16 >( nLnnMod )));
+ xProperties->setPropertyValue( getPropertyName( PROP_DISTANCE ), uno::Any( ConversionHelper::convertTwipToMM100(ndxaLnn) ));
+ xProperties->setPropertyValue( getPropertyName( PROP_NUMBER_POSITION ), uno::Any( style::LineNumberPosition::LEFT));
+ xProperties->setPropertyValue( getPropertyName( PROP_NUMBERING_TYPE ), uno::Any( style::NumberingType::ARABIC));
+ xProperties->setPropertyValue( getPropertyName( PROP_RESTART_AT_EACH_PAGE ), uno::Any( nLnc == NS_ooxml::LN_Value_ST_LineNumberRestart_newPage ));
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+ m_bLineNumberingSet = true;
+ uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier( GetTextDocument(), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameContainer> xStyles;
+ xStyleFamilies->getByName(getPropertyName( PROP_PARAGRAPH_STYLES )) >>= xStyles;
+ lcl_linenumberingHeaderFooter( xStyles, "Header", this );
+ lcl_linenumberingHeaderFooter( xStyles, "Footer", this );
+}
+
+
+void DomainMapper_Impl::SetPageMarginTwip( PageMarElement eElement, sal_Int32 nValue )
+{
+ nValue = ConversionHelper::convertTwipToMM100(nValue);
+ switch(eElement)
+ {
+ case PAGE_MAR_TOP : m_aPageMargins.top = nValue; break;
+ case PAGE_MAR_RIGHT : m_aPageMargins.right = nValue; break;
+ case PAGE_MAR_BOTTOM : m_aPageMargins.bottom = nValue; break;
+ case PAGE_MAR_LEFT : m_aPageMargins.left = nValue; break;
+ case PAGE_MAR_HEADER : m_aPageMargins.header = nValue; break;
+ case PAGE_MAR_FOOTER : m_aPageMargins.footer = nValue; break;
+ case PAGE_MAR_GUTTER:
+ m_aPageMargins.gutter = nValue;
+ break;
+ }
+}
+
+
+PageMar::PageMar()
+ : top(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
+ // This is strange, the RTF spec says it's 1800, but it's clearly 1440 in Word
+ // OOXML seems not to specify a default value
+ , right(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
+ , bottom(top)
+ , left(right)
+ , header(ConversionHelper::convertTwipToMM100(sal_Int32(720)))
+ , footer(header)
+ , gutter(0)
+{
+}
+
+
+void DomainMapper_Impl::RegisterFrameConversion(
+ uno::Reference< text::XTextRange > const& xFrameStartRange,
+ uno::Reference< text::XTextRange > const& xFrameEndRange,
+ std::vector<beans::PropertyValue>&& rFrameProperties
+ )
+{
+ OSL_ENSURE(
+ m_aFrameProperties.empty() && !m_xFrameStartRange.is() && !m_xFrameEndRange.is(),
+ "frame properties not removed");
+ m_aFrameProperties = std::move(rFrameProperties);
+ m_xFrameStartRange = xFrameStartRange;
+ m_xFrameEndRange = xFrameEndRange;
+}
+
+
+void DomainMapper_Impl::ExecuteFrameConversion()
+{
+ if( m_xFrameStartRange.is() && m_xFrameEndRange.is() && !m_bDiscardHeaderFooter )
+ {
+ std::vector<sal_Int32> redPos, redLen;
+ try
+ {
+ uno::Reference< text::XTextAppendAndConvert > xTextAppendAndConvert( GetTopTextAppend(), uno::UNO_QUERY_THROW );
+ // convert redline ranges to cursor movement and character length
+ sal_Int32 redIdx;
+ lcl_CopyRedlines(GetTopTextAppend(), m_aStoredRedlines[StoredRedlines::FRAME], redPos, redLen, redIdx);
+
+ const uno::Reference< text::XTextContent >& xTextContent = xTextAppendAndConvert->convertToTextFrame(
+ m_xFrameStartRange,
+ m_xFrameEndRange,
+ comphelper::containerToSequence(m_aFrameProperties) );
+
+ uno::Reference< text::XText > xDest( xTextContent, uno::UNO_QUERY_THROW );
+ lcl_PasteRedlines(xDest, m_aStoredRedlines[StoredRedlines::FRAME], redPos, redLen, redIdx);
+ }
+ catch( const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION( "writerfilter.dmapper", "Exception caught when converting to frame");
+ }
+
+ m_bIsActualParagraphFramed = false;
+
+ if (redPos.size() == m_aStoredRedlines[StoredRedlines::FRAME].size()/3)
+ {
+ for( sal_Int32 i = m_aStoredRedlines[StoredRedlines::FRAME].size() - 1; i >= 0; --i)
+ {
+ // keep redlines of floating tables to process them in CloseSectionGroup()
+ if ( redPos[i/3] != -1 )
+ {
+ m_aStoredRedlines[StoredRedlines::FRAME].erase(m_aStoredRedlines[StoredRedlines::FRAME].begin() + i);
+ }
+ }
+ }
+ else
+ m_aStoredRedlines[StoredRedlines::FRAME].clear();
+ }
+ m_xFrameStartRange = nullptr;
+ m_xFrameEndRange = nullptr;
+ m_aFrameProperties.clear();
+}
+
+void DomainMapper_Impl::AddNewRedline( sal_uInt32 sprmId )
+{
+ RedlineParamsPtr pNew( new RedlineParams );
+ pNew->m_nToken = XML_mod;
+ if ( !m_bIsParaMarkerChange )
+ {
+ // <w:rPrChange> applies to the whole <w:r>, <w:pPrChange> applies to the whole <w:p>,
+ // so keep those two in CONTEXT_CHARACTERS and CONTEXT_PARAGRAPH, which will take
+ // care of their scope (i.e. when they should be used and discarded).
+ // Let's keep the rest the same way they used to be handled (explicitly dropped
+ // from a global stack by endtrackchange), but quite possibly they should not be handled
+ // that way either (I don't know).
+ if( sprmId == NS_ooxml::LN_EG_RPrContent_rPrChange )
+ GetTopContextOfType( CONTEXT_CHARACTER )->Redlines().push_back( pNew );
+ else if( sprmId == NS_ooxml::LN_CT_PPr_pPrChange )
+ GetTopContextOfType( CONTEXT_PARAGRAPH )->Redlines().push_back( pNew );
+ else if( sprmId != NS_ooxml::LN_CT_ParaRPr_rPrChange )
+ m_aRedlines.top().push_back( pNew );
+ }
+ else
+ {
+ m_pParaMarkerRedline = pNew;
+ }
+ // Newly read data will go into this redline.
+ m_currentRedline = pNew;
+}
+
+void DomainMapper_Impl::SetCurrentRedlineIsRead()
+{
+ m_currentRedline.clear();
+}
+
+sal_Int32 DomainMapper_Impl::GetCurrentRedlineToken( ) const
+{
+ assert(m_currentRedline);
+ return m_currentRedline->m_nToken;
+}
+
+void DomainMapper_Impl::SetCurrentRedlineAuthor( const OUString& sAuthor )
+{
+ if (!m_xAnnotationField.is())
+ {
+ if (m_currentRedline)
+ m_currentRedline->m_sAuthor = sAuthor;
+ else
+ SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
+ }
+ else
+ m_xAnnotationField->setPropertyValue("Author", uno::Any(sAuthor));
+}
+
+void DomainMapper_Impl::SetCurrentRedlineInitials( const OUString& sInitials )
+{
+ if (m_xAnnotationField.is())
+ m_xAnnotationField->setPropertyValue("Initials", uno::Any(sInitials));
+}
+
+void DomainMapper_Impl::SetCurrentRedlineDate( const OUString& sDate )
+{
+ if (!m_xAnnotationField.is())
+ {
+ if (m_currentRedline)
+ m_currentRedline->m_sDate = sDate;
+ else
+ SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
+ }
+ else
+ m_xAnnotationField->setPropertyValue("DateTimeValue", uno::Any(ConversionHelper::ConvertDateStringToDateTime(sDate)));
+}
+
+void DomainMapper_Impl::SetCurrentRedlineId( sal_Int32 sId )
+{
+ if (m_xAnnotationField.is())
+ {
+ m_nAnnotationId = sId;
+ }
+ else
+ {
+ // This should be an assert, but somebody had the smart idea to reuse this function also for comments and whatnot,
+ // and in some cases the id is actually not handled, which may be in fact a bug.
+ if( !m_currentRedline)
+ SAL_INFO("writerfilter.dmapper", "no current redline");
+ }
+}
+
+void DomainMapper_Impl::SetCurrentRedlineToken( sal_Int32 nToken )
+{
+ assert(m_currentRedline);
+ m_currentRedline->m_nToken = nToken;
+}
+
+void DomainMapper_Impl::SetCurrentRedlineRevertProperties( const uno::Sequence<beans::PropertyValue>& aProperties )
+{
+ assert(m_currentRedline);
+ m_currentRedline->m_aRevertProperties = aProperties;
+}
+
+
+// This removes only the last redline stored here, those stored in contexts are automatically removed when
+// the context is destroyed.
+void DomainMapper_Impl::RemoveTopRedline( )
+{
+ if (m_aRedlines.top().empty())
+ {
+ if (GetFootnoteCount() > -1 || GetEndnoteCount() > -1)
+ return;
+ SAL_WARN("writerfilter.dmapper", "RemoveTopRedline called with empty stack");
+ throw uno::Exception("RemoveTopRedline failed", nullptr);
+ }
+ m_aRedlines.top().pop_back( );
+ m_currentRedline.clear();
+}
+
+void DomainMapper_Impl::ApplySettingsTable()
+{
+ if (!(m_pSettingsTable && m_xTextFactory.is()))
+ return;
+
+ try
+ {
+ uno::Reference< beans::XPropertySet > xTextDefaults(m_xTextFactory->createInstance("com.sun.star.text.Defaults"), uno::UNO_QUERY_THROW );
+ sal_Int32 nDefTab = m_pSettingsTable->GetDefaultTabStop();
+ xTextDefaults->setPropertyValue( getPropertyName( PROP_TAB_STOP_DISTANCE ), uno::Any(nDefTab) );
+ if (m_pSettingsTable->GetLinkStyles())
+ {
+ // If linked styles are enabled, set paragraph defaults from Word's default template
+ xTextDefaults->setPropertyValue(getPropertyName(PROP_PARA_BOTTOM_MARGIN), uno::Any(ConversionHelper::convertTwipToMM100(200)));
+ style::LineSpacing aSpacing;
+ aSpacing.Mode = style::LineSpacingMode::PROP;
+ aSpacing.Height = sal_Int16(115);
+ xTextDefaults->setPropertyValue(getPropertyName(PROP_PARA_LINE_SPACING), uno::Any(aSpacing));
+ }
+
+ if (m_pSettingsTable->GetZoomFactor() || m_pSettingsTable->GetView())
+ {
+ std::vector<beans::PropertyValue> aViewProps;
+ if (m_pSettingsTable->GetZoomFactor())
+ {
+ aViewProps.emplace_back("ZoomFactor", -1, uno::Any(m_pSettingsTable->GetZoomFactor()), beans::PropertyState_DIRECT_VALUE);
+ aViewProps.emplace_back("VisibleBottom", -1, uno::Any(sal_Int32(0)), beans::PropertyState_DIRECT_VALUE);
+ aViewProps.emplace_back("ZoomType", -1,
+ uno::Any(m_pSettingsTable->GetZoomType()),
+ beans::PropertyState_DIRECT_VALUE);
+ }
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xBox = new comphelper::IndexedPropertyValuesContainer();
+ xBox->insertByIndex(sal_Int32(0), uno::Any(comphelper::containerToSequence(aViewProps)));
+ uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_xTextDocument, uno::UNO_QUERY);
+ xViewDataSupplier->setViewData(xBox);
+ }
+
+ uno::Reference< beans::XPropertySet > xSettings(m_xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
+
+ if (m_pSettingsTable->GetDoNotExpandShiftReturn())
+ xSettings->setPropertyValue( "DoNotJustifyLinesWithManualBreak", uno::Any(true) );
+ if (m_pSettingsTable->GetUsePrinterMetrics())
+ xSettings->setPropertyValue("PrinterIndependentLayout", uno::Any(document::PrinterIndependentLayout::DISABLED));
+ if( m_pSettingsTable->GetEmbedTrueTypeFonts())
+ xSettings->setPropertyValue( getPropertyName( PROP_EMBED_FONTS ), uno::Any(true) );
+ if( m_pSettingsTable->GetEmbedSystemFonts())
+ xSettings->setPropertyValue( getPropertyName( PROP_EMBED_SYSTEM_FONTS ), uno::Any(true) );
+ xSettings->setPropertyValue("AddParaTableSpacing", uno::Any(m_pSettingsTable->GetDoNotUseHTMLParagraphAutoSpacing()));
+ if (m_pSettingsTable->GetNoLeading())
+ {
+ xSettings->setPropertyValue("AddExternalLeading", uno::Any(!m_pSettingsTable->GetNoLeading()));
+ }
+ if( m_pSettingsTable->GetProtectForm() )
+ xSettings->setPropertyValue("ProtectForm", uno::Any( true ));
+ if( m_pSettingsTable->GetReadOnly() )
+ xSettings->setPropertyValue("LoadReadonly", uno::Any( true ));
+ if (m_pSettingsTable->GetGutterAtTop())
+ {
+ xSettings->setPropertyValue("GutterAtTop", uno::Any(true));
+ }
+ uno::Sequence<beans::PropertyValue> aWriteProtection
+ = m_pSettingsTable->GetWriteProtectionSettings();
+ if (aWriteProtection.hasElements())
+ xSettings->setPropertyValue("ModifyPasswordInfo", uno::Any(aWriteProtection));
+ }
+ catch(const uno::Exception&)
+ {
+ }
+}
+
+SectionPropertyMap * DomainMapper_Impl::GetSectionContext()
+{
+ SectionPropertyMap* pSectionContext = nullptr;
+ //the section context is not available before the first call of startSectionGroup()
+ if( !IsAnyTableImport() )
+ {
+ PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_SECTION);
+ pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
+ }
+
+ return pSectionContext;
+}
+
+void DomainMapper_Impl::deferCharacterProperty(sal_Int32 id, const css::uno::Any& value)
+{
+ deferredCharacterProperties[ id ] = value;
+}
+
+void DomainMapper_Impl::processDeferredCharacterProperties()
+{
+ // Actually process in DomainMapper, so that it's the same source file like normal processing.
+ if( !deferredCharacterProperties.empty())
+ {
+ m_rDMapper.processDeferredCharacterProperties( deferredCharacterProperties );
+ deferredCharacterProperties.clear();
+ }
+}
+
+sal_Int32 DomainMapper_Impl::getNumberingProperty(const sal_Int32 nListId, sal_Int32 nNumberingLevel, const OUString& aProp)
+{
+ sal_Int32 nRet = 0;
+ if ( nListId < 0 )
+ return nRet;
+
+ try
+ {
+ if (nNumberingLevel < 0) // It seems it's valid to omit numbering level, and in that case it means zero.
+ nNumberingLevel = 0;
+
+ auto const pList(GetListTable()->GetList(nListId));
+ assert(pList);
+ const OUString aListName = pList->GetStyleName();
+ const uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(GetTextDocument(), uno::UNO_QUERY_THROW);
+ const uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xNumberingStyles;
+ xStyleFamilies->getByName("NumberingStyles") >>= xNumberingStyles;
+ const uno::Reference<beans::XPropertySet> xStyle(xNumberingStyles->getByName(aListName), uno::UNO_QUERY);
+ const uno::Reference<container::XIndexAccess> xNumberingRules(xStyle->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
+ if (xNumberingRules.is())
+ {
+ uno::Sequence<beans::PropertyValue> aProps;
+ xNumberingRules->getByIndex(nNumberingLevel) >>= aProps;
+ auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
+ [&aProp](const beans::PropertyValue& rProp) { return rProp.Name == aProp; });
+ if (pProp != std::cend(aProps))
+ pProp->Value >>= nRet;
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ // This can happen when the doc contains some hand-crafted invalid list level.
+ }
+
+ return nRet;
+}
+
+sal_Int32 DomainMapper_Impl::getCurrentNumberingProperty(const OUString& aProp)
+{
+ sal_Int32 nRet = 0;
+
+ std::optional<PropertyMap::Property> pProp = m_pTopContext->getProperty(PROP_NUMBERING_RULES);
+ uno::Reference<container::XIndexAccess> xNumberingRules;
+ if (pProp)
+ xNumberingRules.set(pProp->second, uno::UNO_QUERY);
+ pProp = m_pTopContext->getProperty(PROP_NUMBERING_LEVEL);
+ // Default numbering level is the first one.
+ sal_Int32 nNumberingLevel = 0;
+ if (pProp)
+ pProp->second >>= nNumberingLevel;
+ if (xNumberingRules.is())
+ {
+ uno::Sequence<beans::PropertyValue> aProps;
+ xNumberingRules->getByIndex(nNumberingLevel) >>= aProps;
+ auto pPropVal = std::find_if(std::cbegin(aProps), std::cend(aProps),
+ [&aProp](const beans::PropertyValue& rProp) { return rProp.Name == aProp; });
+ if (pPropVal != std::cend(aProps))
+ pPropVal->Value >>= nRet;
+ }
+
+ return nRet;
+}
+
+
+void DomainMapper_Impl::enableInteropGrabBag(const OUString& aName)
+{
+ m_aInteropGrabBagName = aName;
+}
+
+void DomainMapper_Impl::disableInteropGrabBag()
+{
+ m_aInteropGrabBagName.clear();
+ m_aInteropGrabBag.clear();
+ m_aSubInteropGrabBag.clear();
+}
+
+bool DomainMapper_Impl::isInteropGrabBagEnabled() const
+{
+ return !(m_aInteropGrabBagName.isEmpty());
+}
+
+void DomainMapper_Impl::appendGrabBag(std::vector<beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, const OUString& aValue)
+{
+ if (m_aInteropGrabBagName.isEmpty())
+ return;
+ beans::PropertyValue aProperty;
+ aProperty.Name = aKey;
+ aProperty.Value <<= aValue;
+ rInteropGrabBag.push_back(aProperty);
+}
+
+void DomainMapper_Impl::appendGrabBag(std::vector<beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, std::vector<beans::PropertyValue>& rValue)
+{
+ if (m_aInteropGrabBagName.isEmpty())
+ return;
+ beans::PropertyValue aProperty;
+ aProperty.Name = aKey;
+ aProperty.Value <<= comphelper::containerToSequence(rValue);
+ rValue.clear();
+ rInteropGrabBag.push_back(aProperty);
+}
+
+void DomainMapper_Impl::substream(Id rName,
+ ::writerfilter::Reference<Stream>::Pointer_t const& ref)
+{
+#ifndef NDEBUG
+ size_t contextSize(m_aContextStack.size());
+ size_t propSize[NUMBER_OF_CONTEXTS];
+ for (int i = 0; i < NUMBER_OF_CONTEXTS; ++i) {
+ propSize[i] = m_aPropertyStacks[i].size();
+ }
+#endif
+
+ // Save "has footnote" state, which is specific to a section in the body
+ // text, so state from substreams is not relevant.
+ bool bHasFtn = m_bHasFtn;
+
+ //finalize any waiting frames before starting alternate streams
+ CheckUnregisteredFrameConversion();
+ ExecuteFrameConversion();
+
+ appendTableManager();
+ // Appending a TableManager resets its TableHandler, so we need to append
+ // that as well, or tables won't be imported properly in headers/footers.
+ appendTableHandler();
+ getTableManager().startLevel();
+
+ //import of page header/footer
+ //Ensure that only one header/footer per section is pushed
+
+ switch( rName )
+ {
+ case NS_ooxml::LN_headerl:
+ PushPageHeader(SectionPropertyMap::PAGE_LEFT);
+ break;
+ case NS_ooxml::LN_headerr:
+ PushPageHeader(SectionPropertyMap::PAGE_RIGHT);
+ break;
+ case NS_ooxml::LN_headerf:
+ PushPageHeader(SectionPropertyMap::PAGE_FIRST);
+ break;
+ case NS_ooxml::LN_footerl:
+ PushPageFooter(SectionPropertyMap::PAGE_LEFT);
+ break;
+ case NS_ooxml::LN_footerr:
+ PushPageFooter(SectionPropertyMap::PAGE_RIGHT);
+ break;
+ case NS_ooxml::LN_footerf:
+ PushPageFooter(SectionPropertyMap::PAGE_FIRST);
+ break;
+ case NS_ooxml::LN_footnote:
+ case NS_ooxml::LN_endnote:
+ PushFootOrEndnote( NS_ooxml::LN_footnote == rName );
+ break;
+ case NS_ooxml::LN_annotation :
+ PushAnnotation();
+ break;
+ }
+
+ try
+ {
+ ref->resolve(m_rDMapper);
+ }
+ catch (xml::sax::SAXException const&)
+ {
+ m_bSaxError = true;
+ throw;
+ }
+
+ switch( rName )
+ {
+ case NS_ooxml::LN_headerl:
+ case NS_ooxml::LN_headerr:
+ case NS_ooxml::LN_headerf:
+ case NS_ooxml::LN_footerl:
+ case NS_ooxml::LN_footerr:
+ case NS_ooxml::LN_footerf:
+ PopPageHeaderFooter();
+ break;
+ case NS_ooxml::LN_footnote:
+ case NS_ooxml::LN_endnote:
+ PopFootOrEndnote();
+ break;
+ case NS_ooxml::LN_annotation :
+ PopAnnotation();
+ break;
+ }
+
+ getTableManager().endLevel();
+ popTableManager();
+ m_bHasFtn = bHasFtn;
+
+ switch(rName)
+ {
+ case NS_ooxml::LN_footnote:
+ case NS_ooxml::LN_endnote:
+ m_pTableHandler->setHadFootOrEndnote(true);
+ m_bHasFtn = true;
+ break;
+ }
+
+ // check that stacks are the same as before substream
+ assert(m_aContextStack.size() == contextSize);
+ for (int i = 0; i < NUMBER_OF_CONTEXTS; ++i) {
+ assert(m_aPropertyStacks[i].size() == propSize[i]);
+ }
+}
+
+void DomainMapper_Impl::commentProps(const OUString& sId, const CommentProperties& rProps)
+{
+ m_aCommentProps[sId] = rProps;
+}
+
+
+bool DomainMapper_Impl::handlePreviousParagraphBorderInBetween() const
+{
+ if (!m_xPreviousParagraph.is())
+ return false;
+
+ // Connected borders ("ParaIsConnectBorder") are always on by default
+ // and never changed by DomainMapper. Except one case when border in
+ // between is used. So this is not the best, but easiest way to check
+ // is previous paragraph has border in between.
+ bool bConnectBorders = true;
+ m_xPreviousParagraph->getPropertyValue(getPropertyName(PROP_PARA_CONNECT_BORDERS)) >>= bConnectBorders;
+
+ if (bConnectBorders)
+ return false;
+
+ // Previous paragraph has border in between. Current one also has (since this
+ // method is called). So current paragraph will get border above, but
+ // also need to ensure, that no unexpected bottom border are remaining in previous
+ // paragraph: since ParaIsConnectBorder=false it will be displayed in unexpected way.
+ m_xPreviousParagraph->setPropertyValue(getPropertyName(PROP_BOTTOM_BORDER), uno::Any(table::BorderLine2()));
+
+ return true;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
new file mode 100644
index 000000000..4a8db689c
--- /dev/null
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -0,0 +1,1235 @@
+/* -*- 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 <com/sun/star/text/XParagraphCursor.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextCursor.hpp>
+#include <com/sun/star/text/XTextAppend.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
+#include <com/sun/star/style/TabStop.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <queue>
+#include <stack>
+#include <string_view>
+#include <o3tl/sorted_vector.hxx>
+#include <unordered_map>
+#include <vector>
+#include <optional>
+
+#include <dmapper/CommentProperties.hxx>
+
+#include "DomainMapper.hxx"
+#include "DomainMapperTableManager.hxx"
+#include "DomainMapperTableHandler.hxx"
+#include "PropertyMap.hxx"
+#include "FontTable.hxx"
+#include "NumberingManager.hxx"
+#include "StyleSheetTable.hxx"
+#include "SettingsTable.hxx"
+#include "ThemeTable.hxx"
+#include "GraphicImport.hxx"
+#include "OLEHandler.hxx"
+#include "FFDataHandler.hxx"
+#include "SmartTagHandler.hxx"
+#include "FormControlHelper.hxx"
+#include <map>
+
+namespace com::sun::star{
+ namespace awt{
+ struct Size;
+ }
+ namespace lang{
+ class XMultiServiceFactory;
+ struct Locale;
+ }
+ namespace text
+ {
+ class XTextField;
+ class XTextFrame;
+ class XFormField;
+ }
+ namespace beans{ class XPropertySet;}
+}
+
+namespace writerfilter::ooxml {
+ class OOXMLDocument;
+}
+
+namespace writerfilter::dmapper {
+
+class SdtHelper;
+
+struct PageMar
+{
+ sal_Int32 top;
+ sal_Int32 right;
+ sal_Int32 bottom;
+ sal_Int32 left;
+ sal_Int32 header;
+ sal_Int32 footer;
+ sal_Int32 gutter;
+ public:
+ PageMar();
+};
+enum PageMarElement
+{
+ PAGE_MAR_TOP,
+ PAGE_MAR_RIGHT,
+ PAGE_MAR_BOTTOM,
+ PAGE_MAR_LEFT,
+ PAGE_MAR_HEADER,
+ PAGE_MAR_FOOTER,
+ PAGE_MAR_GUTTER
+};
+
+/// property stack element
+enum ContextType
+{
+ CONTEXT_SECTION,
+ CONTEXT_PARAGRAPH,
+ CONTEXT_CHARACTER,
+ CONTEXT_STYLESHEET,
+ CONTEXT_LIST
+};
+enum { NUMBER_OF_CONTEXTS = CONTEXT_LIST + 1 };
+
+enum BreakType
+{
+ PAGE_BREAK,
+ COLUMN_BREAK,
+ LINE_BREAK
+};
+
+/**
+ * Two special footnotes are a separator line, and a continuation line.
+ * In MSOffice, these can contain text as well, but LO doesn't implement this
+ * rarely used feature, so the separator text needs to be skipped. (tdf#123262)
+ * Three-way logic is needed because there is no guaranteed on-off event.
+ * OFF == not in footnote separator
+ * ON == in footnote separator
+ * SKIPPING == ON status has been recognized.
+ */
+enum class SkipFootnoteSeparator
+{
+ OFF,
+ ON,
+ SKIPPING
+};
+
+// type of stored redlines
+enum StoredRedlines
+{
+ FRAME = 0,
+ FOOTNOTE,
+ ENDNOTE,
+ NONE
+};
+
+/**
+ * Storage for state that is relevant outside a header/footer, but not inside it.
+ *
+ * In case some state of DomainMapper_Impl should be reset before handling the
+ * header/footer and should be restored once handling of header/footer is done,
+ * then you can use this class to do so.
+ */
+class HeaderFooterContext
+{
+ bool m_bTextInserted;
+ sal_Int32 m_nTableDepth;
+
+public:
+ explicit HeaderFooterContext(bool bTextInserted, sal_Int32 nTableDepth);
+ bool getTextInserted() const;
+ sal_Int32 getTableDepth() const;
+};
+
+/// Information about a paragraph to be finished after a field end.
+struct FieldParagraph
+{
+ PropertyMapPtr m_pPropertyMap;
+ bool m_bRemove = false;
+};
+
+/// field stack element
+class FieldContext : public virtual SvRefBase
+{
+ bool m_bFieldCommandCompleted;
+ css::uno::Reference<css::text::XTextRange> m_xStartRange;
+
+ OUString m_sCommand;
+ OUString m_sResult;
+ OUString m_sVariableValue;
+ std::optional<FieldId> m_eFieldId;
+ bool m_bFieldLocked;
+
+ css::uno::Reference<css::text::XTextField> m_xTextField;
+ css::uno::Reference<css::text::XFormField> m_xFormField;
+ css::uno::Reference<css::beans::XPropertySet> m_xTOC;
+ css::uno::Reference<css::beans::XPropertySet> m_xTC; // TOX entry
+ css::uno::Reference<css::beans::XPropertySet> m_xCustomField;
+
+ OUString m_sHyperlinkURL;
+ /// A frame for the hyperlink when one exists.
+ OUString m_sHyperlinkTarget;
+
+ FFDataHandler::Pointer_t m_pFFDataHandler;
+ FormControlHelper::Pointer_t m_pFormControlHelper;
+ /// (Character) properties of the field itself.
+ PropertyMapPtr m_pProperties;
+
+ std::vector<FieldParagraph> m_aParagraphsToFinish;
+
+public:
+ explicit FieldContext(css::uno::Reference<css::text::XTextRange> const& xStart);
+ ~FieldContext() override;
+
+ const css::uno::Reference<css::text::XTextRange>& GetStartRange() const { return m_xStartRange; }
+
+ void AppendCommand(std::u16string_view rPart);
+ const OUString& GetCommand() const {return m_sCommand; }
+
+ void SetFieldId(FieldId eFieldId ) { m_eFieldId = eFieldId; }
+ std::optional<FieldId> const & GetFieldId() const { return m_eFieldId; }
+
+ void AppendResult(std::u16string_view rResult) { m_sResult += rResult; }
+ const OUString& GetResult() const { return m_sResult; }
+
+ void CacheVariableValue(const css::uno::Any& rAny);
+ const OUString& GetVariableValue() { return m_sVariableValue; }
+
+ void SetCommandCompleted() { m_bFieldCommandCompleted = true; }
+ bool IsCommandCompleted() const { return m_bFieldCommandCompleted; }
+
+ void SetFieldLocked() { m_bFieldLocked = true; }
+ bool IsFieldLocked() const { return m_bFieldLocked; }
+
+ const css::uno::Reference<css::beans::XPropertySet>& GetCustomField() const { return m_xCustomField; }
+ void SetCustomField(css::uno::Reference<css::beans::XPropertySet> const& xCustomField) { m_xCustomField = xCustomField; }
+ const css::uno::Reference<css::text::XTextField>& GetTextField() const { return m_xTextField;}
+ void SetTextField(css::uno::Reference<css::text::XTextField> const& xTextField);
+ const css::uno::Reference<css::text::XFormField>& GetFormField() const { return m_xFormField;}
+ void SetFormField(css::uno::Reference<css::text::XFormField> const& xFormField) { m_xFormField = xFormField;}
+
+ void SetTOC(css::uno::Reference<css::beans::XPropertySet> const& xTOC) { m_xTOC = xTOC; }
+ const css::uno::Reference<css::beans::XPropertySet>& GetTOC() const { return m_xTOC; }
+
+ void SetTC(css::uno::Reference<css::beans::XPropertySet> const& xTC) { m_xTC = xTC; }
+ const css::uno::Reference<css::beans::XPropertySet>& GetTC() const { return m_xTC; }
+
+ void SetHyperlinkURL( const OUString& rURL ) { m_sHyperlinkURL = rURL; }
+ const OUString& GetHyperlinkURL() const { return m_sHyperlinkURL; }
+ void SetHyperlinkTarget(const OUString& rTarget) { m_sHyperlinkTarget = rTarget; }
+ const OUString& GetHyperlinkTarget() const { return m_sHyperlinkTarget; }
+
+ void setFFDataHandler(FFDataHandler::Pointer_t pFFDataHandler) { m_pFFDataHandler = pFFDataHandler; }
+ const FFDataHandler::Pointer_t& getFFDataHandler() const { return m_pFFDataHandler; }
+
+ void setFormControlHelper(FormControlHelper::Pointer_t pFormControlHelper) { m_pFormControlHelper = pFormControlHelper; }
+ const FormControlHelper::Pointer_t& getFormControlHelper() const { return m_pFormControlHelper; }
+ const PropertyMapPtr& getProperties() const { return m_pProperties; }
+
+ ::std::vector<OUString> GetCommandParts() const;
+
+ std::vector<FieldParagraph>& GetParagraphsToFinish() { return m_aParagraphsToFinish; }
+};
+
+struct TextAppendContext
+{
+ css::uno::Reference<css::text::XTextAppend> xTextAppend;
+ css::uno::Reference<css::text::XTextRange> xInsertPosition;
+ css::uno::Reference<css::text::XParagraphCursor> xCursor;
+ ParagraphPropertiesPtr pLastParagraphProperties;
+
+ /**
+ * Objects anchored to the current paragraph, may affect the paragraph
+ * spacing.
+ */
+ std::vector<AnchoredObjectInfo> m_aAnchoredObjects;
+
+ inline TextAppendContext(const css::uno::Reference<css::text::XTextAppend>& xAppend, const css::uno::Reference<css::text::XTextCursor>& xCur);
+};
+
+struct AnchoredContext
+{
+ css::uno::Reference<css::text::XTextContent> xTextContent;
+ bool bToRemove;
+
+ explicit AnchoredContext(const css::uno::Reference<css::text::XTextContent>& xContent)
+ : xTextContent(xContent), bToRemove(false)
+ {
+ }
+};
+
+typedef tools::SvRef<FieldContext> FieldContextPtr;
+
+/*-------------------------------------------------------------------------
+ extended tab stop struct
+ -----------------------------------------------------------------------*/
+struct DeletableTabStop : public css::style::TabStop
+{
+ bool bDeleted;
+ explicit DeletableTabStop()
+ : bDeleted(false)
+ {
+ FillChar = ' '; // same default as SvxXMLTabStopContext_Impl
+ }
+ DeletableTabStop(const css::style::TabStop& rTabStop)
+ : TabStop(rTabStop),
+ bDeleted(false)
+ {
+ }
+};
+/// helper to remember bookmark start position
+struct BookmarkInsertPosition
+{
+ bool m_bIsStartOfText;
+ OUString m_sBookmarkName;
+ css::uno::Reference<css::text::XTextRange> m_xTextRange;
+ BookmarkInsertPosition(bool bIsStartOfText, const OUString& rName, css::uno::Reference<css::text::XTextRange> const& xTextRange):
+ m_bIsStartOfText( bIsStartOfText ),
+ m_sBookmarkName( rName ),
+ m_xTextRange( xTextRange )
+ {}
+};
+
+struct PermInsertPosition
+{
+ bool m_bIsStartOfText;
+ sal_Int32 m_Id;
+ OUString m_Ed;
+ OUString m_EdGrp;
+
+ css::uno::Reference<css::text::XTextRange> m_xTextRange;
+
+ PermInsertPosition(bool bIsStartOfText, sal_Int32 id, const OUString& ed, const OUString& edGrp, css::uno::Reference<css::text::XTextRange> const& xTextRange)
+ : m_bIsStartOfText(bIsStartOfText)
+ , m_Id(id)
+ , m_Ed(ed)
+ , m_EdGrp(edGrp)
+ , m_xTextRange(xTextRange)
+ {}
+
+ OUString createBookmarkName() const
+ {
+ OUString bookmarkName;
+
+ assert((!m_Ed.isEmpty()) || (!m_EdGrp.isEmpty()));
+
+ if (m_Ed.isEmpty())
+ {
+ bookmarkName += "permission-for-group:" +
+ OUString::number(m_Id) +
+ ":" +
+ m_EdGrp;
+ }
+ else
+ {
+ bookmarkName += "permission-for-user:" +
+ OUString::number(m_Id) +
+ ":" +
+ m_Ed;
+ }
+
+ //todo: make sure the name is not used already!
+ return bookmarkName;
+ }
+};
+
+/// Stores the start/end positions of an annotation before its insertion.
+struct AnnotationPosition
+{
+ css::uno::Reference<css::text::XTextRange> m_xStart;
+ css::uno::Reference<css::text::XTextRange> m_xEnd;
+};
+
+struct RubyInfo
+{
+ OUString sRubyText;
+ OUString sRubyStyle;
+ sal_uInt32 nSprmId;
+ sal_uInt32 nRubyAlign;
+ sal_uInt32 nHps;
+ sal_uInt32 nHpsBaseText;
+
+ RubyInfo():
+ nSprmId(0),
+ nRubyAlign(0),
+ nHps(0),
+ nHpsBaseText(0)
+ {
+ }
+};
+
+struct LineNumberSettings
+{
+ sal_Int32 nDistance;
+ sal_Int32 nInterval;
+ bool bRestartAtEachPage;
+ LineNumberSettings() :
+ nDistance(-1)
+ ,nInterval(0)
+ ,bRestartAtEachPage(true)
+ {}
+
+};
+
+/// Contains information about a table that will be potentially converted to a floating one at the section end.
+struct FloatingTableInfo
+{
+ css::uno::Reference<css::text::XTextRange> m_xStart;
+ css::uno::Reference<css::text::XTextRange> m_xEnd;
+ css::uno::Sequence<css::beans::PropertyValue> m_aFrameProperties;
+ sal_Int32 m_nTableWidth;
+ sal_Int32 m_nTableWidthType;
+ /// Break type of the section that contains this table.
+ sal_Int32 m_nBreakType = -1;
+ /// Tables in footnotes and endnotes are always floating
+ bool m_bConvertToFloatingInFootnote = false;
+
+ FloatingTableInfo(css::uno::Reference<css::text::XTextRange> const& xStart,
+ css::uno::Reference<css::text::XTextRange> const& xEnd,
+ const css::uno::Sequence<css::beans::PropertyValue>& aFrameProperties,
+ sal_Int32 nTableWidth, sal_Int32 nTableWidthType, bool bConvertToFloatingInFootnote)
+ : m_xStart(xStart),
+ m_xEnd(xEnd),
+ m_aFrameProperties(aFrameProperties),
+ m_nTableWidth(nTableWidth),
+ m_nTableWidthType(nTableWidthType),
+ m_bConvertToFloatingInFootnote(bConvertToFloatingInFootnote)
+ {
+ }
+ css::uno::Any getPropertyValue(std::u16string_view propertyName);
+};
+
+/// Stores original/in-file-format info about a single anchored object.
+struct AnchoredObjectInfo
+{
+ css::uno::Reference<css::text::XTextContent> m_xAnchoredObject;
+ sal_Int32 m_nLeftMargin = 0;
+ RedlineParamsPtr m_xRedlineForInline;
+};
+
+/// Stores info about objects anchored to a given paragraph.
+struct AnchoredObjectsInfo
+{
+ css::uno::Reference<css::text::XTextRange> m_xParagraph;
+ std::vector<AnchoredObjectInfo> m_aAnchoredObjects;
+};
+
+struct SymbolData
+{
+ sal_Unicode cSymbol;
+ OUString sFont;
+ SymbolData():
+ cSymbol(),
+ sFont()
+ { }
+};
+
+class DomainMapper;
+class DomainMapper_Impl final
+{
+public:
+ typedef std::map < OUString, BookmarkInsertPosition > BookmarkMap_t;
+ typedef std::map < sal_Int32, PermInsertPosition > PermMap_t;
+
+private:
+ SourceDocumentType m_eDocumentType;
+ DomainMapper& m_rDMapper;
+ writerfilter::ooxml::OOXMLDocument* m_pOOXMLDocument;
+ OUString m_aBaseUrl;
+ css::uno::Reference<css::text::XTextDocument> m_xTextDocument;
+ css::uno::Reference<css::beans::XPropertySet> m_xDocumentSettings;
+ css::uno::Reference<css::lang::XMultiServiceFactory> m_xTextFactory;
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+ css::uno::Reference<css::container::XNameContainer> m_xPageStyles1;
+ // cache next available number, expensive to repeatedly compute
+ std::optional<int> m_xNextUnusedPageStyleNo;
+ css::uno::Reference<css::container::XNameContainer> m_xCharacterStyles;
+ // cache next available number, expensive to repeatedly compute
+ std::optional<int> m_xNextUnusedCharacterStyleNo;
+ css::uno::Reference<css::text::XText> m_xBodyText;
+ css::uno::Reference<css::text::XTextContent> m_xEmbedded;
+
+ std::stack<TextAppendContext> m_aTextAppendStack;
+ std::stack<AnchoredContext> m_aAnchoredStack;
+ std::stack<HeaderFooterContext> m_aHeaderFooterStack;
+ std::stack<std::pair<TextAppendContext, bool>> m_aHeaderFooterTextAppendStack;
+ std::deque<FieldContextPtr> m_aFieldStack;
+ bool m_bForceGenericFields;
+ /// Type of decimal symbol associated to the document language in Writer locale definition
+ bool m_bIsDecimalComma;
+ bool m_bSetUserFieldContent;
+ bool m_bSetCitation;
+ bool m_bSetDateValue;
+ bool m_bIsFirstSection;
+ bool m_bIsColumnBreakDeferred;
+ bool m_bIsPageBreakDeferred;
+ sal_Int32 m_nLineBreaksDeferred;
+ /// If we want to set "sdt end" on the next character context.
+ bool m_bSdtEndDeferred;
+ /// If we want to set "paragraph sdt end" on the next paragraph context.
+ bool m_bParaSdtEndDeferred;
+ bool m_bStartTOC;
+ bool m_bStartTOCHeaderFooter;
+ /// If we got any text that is the pre-rendered result of the TOC field.
+ bool m_bStartedTOC;
+ bool m_bStartIndex;
+ bool m_bStartBibliography;
+ unsigned int m_nStartGenericField;
+ bool m_bTextInserted;
+ LineNumberSettings m_aLineNumberSettings;
+
+ BookmarkMap_t m_aBookmarkMap;
+ OUString m_sCurrentBkmkId;
+ OUString m_sCurrentBkmkName;
+ OUString m_sCurrentBkmkPrefix;
+
+ PermMap_t m_aPermMap;
+ sal_Int32 m_sCurrentPermId;
+ OUString m_sCurrentPermEd;
+ OUString m_sCurrentPermEdGrp;
+
+ PageMar m_aPageMargins;
+ SymbolData m_aSymbolData;
+
+ // TableManagers are stacked: one for each stream to avoid any confusion
+ std::stack< tools::SvRef< DomainMapperTableManager > > m_aTableManagers;
+ tools::SvRef<DomainMapperTableHandler> m_pTableHandler;
+ // List of document lists overrides. They are applied only once on first occurrence in document
+ o3tl::sorted_vector<sal_Int32> m_aListOverrideApplied;
+
+ //each context needs a stack of currently used attributes
+ std::stack<PropertyMapPtr> m_aPropertyStacks[NUMBER_OF_CONTEXTS];
+ std::stack<ContextType> m_aContextStack;
+ std::queue<std::optional<sal_Int16>> m_aFrameDirectionQueue;
+ bool m_bFrameDirectionSet;
+ FontTablePtr m_pFontTable;
+ ListsManager::Pointer m_pListTable;
+ std::deque< css::uno::Reference<css::drawing::XShape> > m_aPendingShapes;
+ StyleSheetTablePtr m_pStyleSheetTable;
+ ThemeTablePtr m_pThemeTable;
+ SettingsTablePtr m_pSettingsTable;
+ GraphicImportPtr m_pGraphicImport;
+
+
+ PropertyMapPtr m_pTopContext;
+ PropertyMapPtr m_pLastSectionContext;
+ PropertyMapPtr m_pLastCharacterContext;
+
+ ::std::vector<DeletableTabStop> m_aCurrentTabStops;
+ OUString m_sCurrentParaStyleName; //highly inaccurate. Overwritten by "overlapping" paragraphs like comments, flys.
+ OUString m_sDefaultParaStyleName; //caches the ConvertedStyleName of the default paragraph style
+ bool m_bInDocDefaultsImport;
+ bool m_bInStyleSheetImport; //in import of fonts, styles, lists or lfos
+ bool m_bInNumberingImport; //in import of numbering (i.e. numbering.xml)
+ bool m_bInAnyTableImport; //in import of fonts, styles, lists or lfos
+ enum class HeaderFooterImportState
+ {
+ none,
+ header,
+ footer,
+ } m_eInHeaderFooterImport;
+ bool m_bDiscardHeaderFooter;
+ bool m_bInFootOrEndnote;
+ bool m_bInFootnote;
+ PropertyMapPtr m_pFootnoteContext;
+ bool m_bHasFootnoteStyle;
+ bool m_bCheckFootnoteStyle;
+ /// Skip paragraphs from the <w:separator/> footnote
+ SkipFootnoteSeparator m_eSkipFootnoteState;
+ /// preload footnotes and endnotes
+ sal_Int32 m_nFootnotes; // footnote count
+ sal_Int32 m_nEndnotes; // endnote count
+ // these are the real first notes, use their content in the first notes
+ sal_Int32 m_nFirstFootnoteIndex;
+ sal_Int32 m_nFirstEndnoteIndex;
+
+ bool m_bLineNumberingSet;
+ bool m_bIsInFootnoteProperties;
+
+ RubyInfo m_aRubyInfo;
+ //registered frame properties
+ std::vector<css::beans::PropertyValue> m_aFrameProperties;
+ css::uno::Reference<css::text::XTextRange> m_xFrameStartRange;
+ css::uno::Reference<css::text::XTextRange> m_xFrameEndRange;
+
+ // Redline stack
+ std::stack< std::vector< RedlineParamsPtr > > m_aRedlines;
+ // The redline currently read, may be also stored by a context instead of m_aRedlines.
+ RedlineParamsPtr m_currentRedline;
+ RedlineParamsPtr m_previousRedline;
+ RedlineParamsPtr m_pParaMarkerRedline;
+ bool m_bIsParaMarkerChange;
+ bool m_bIsParaMarkerMove;
+ // redline data of the terminating run, if it's a moveFrom deletion or a moveTo insertion
+ RedlineParamsPtr m_pParaMarkerRedlineMove;
+ // This is for removing workaround (double ZWSPs around the anchoring point) for track
+ // changed images anchored *to* character, if it's followed by a redline text run immediately.
+ // (In that case, the image is part of a tracked text range, no need for the dummy
+ // text ZWSPs to keep the change tracking of the image in Writer.)
+ bool m_bRedlineImageInPreviousRun;
+
+ /// If the current paragraph has any runs.
+ bool m_bParaChanged;
+ bool m_bIsFirstParaInSection;
+ bool m_bIsFirstParaInSectionAfterRedline;
+ bool m_bIsFirstParaInShape = false;
+ bool m_bDummyParaAddedForTableInSection;
+ bool m_bDummyParaAddedForTableInSectionPage;
+ bool m_bTextFrameInserted;
+ bool m_bIsPreviousParagraphFramed;
+ bool m_bIsLastParaInSection;
+ bool m_bIsLastSectionGroup;
+ bool m_bIsInComments;
+ /// If the current paragraph contains section property definitions.
+ bool m_bParaSectpr;
+ bool m_bUsingEnhancedFields;
+ /// If the current paragraph is inside a structured document element.
+ bool m_bSdt;
+ bool m_bIsFirstRun;
+ bool m_bIsOutsideAParagraph;
+ /// This is a continuation of already finished paragraph - e.g., first in an index section
+ bool m_bRemoveThisParagraph = false;
+
+ css::uno::Reference< css::text::XTextCursor > xTOCMarkerCursor;
+
+ //annotation import
+ css::uno::Reference< css::beans::XPropertySet > m_xAnnotationField;
+ sal_Int32 m_nAnnotationId;
+ bool m_bAnnotationResolved = false;
+ std::unordered_map< sal_Int32, AnnotationPosition > m_aAnnotationPositions;
+
+ void SetNumberFormat(const OUString& rCommand, css::uno::Reference<css::beans::XPropertySet> const& xPropertySet, bool bDetectFormat = false);
+ /// @throws css::uno::Exception
+ css::uno::Reference<css::beans::XPropertySet> FindOrCreateFieldMaster(const char* pFieldMasterService, const OUString& rFieldMasterName);
+ css::uno::Reference<css::beans::XPropertySet> const & GetDocumentSettings();
+
+ std::map<sal_Int32, css::uno::Any> deferredCharacterProperties;
+ SmartTagHandler m_aSmartTagHandler;
+
+ css::uno::Reference<css::text::XTextRange> m_xGlossaryEntryStart;
+ css::uno::Reference<css::text::XTextRange> m_xSdtEntryStart;
+ std::stack<BookmarkInsertPosition> m_xSdtStarts;
+
+ std::queue< css::uno::Reference< css::text::XTextFrame > > m_xPendingTextBoxFrames;
+
+public:
+ css::uno::Reference<css::text::XTextRange> m_xInsertTextRange;
+ css::uno::Reference<css::text::XTextRange> m_xAltChunkStartingRange;
+ std::deque<sal_Int32> m_aFootnoteIds;
+ std::deque<sal_Int32> m_aEndnoteIds;
+
+ bool m_bIsInTextBox;
+private:
+ bool m_bIsNewDoc;
+ bool m_bIsAltChunk = false;
+ bool m_bIsReadGlossaries;
+ std::optional<sal_Int16> m_oLineBreakClear;
+
+public:
+ DomainMapper_Impl(
+ DomainMapper& rDMapper,
+ css::uno::Reference < css::uno::XComponentContext > const& xContext,
+ css::uno::Reference< css::lang::XComponent > const& xModel,
+ SourceDocumentType eDocumentType,
+ utl::MediaDescriptor const & rMediaDesc);
+ ~DomainMapper_Impl();
+
+ void setDocumentReference(writerfilter::ooxml::OOXMLDocument* pDocument) { if (!m_pOOXMLDocument) m_pOOXMLDocument = pDocument; };
+ writerfilter::ooxml::OOXMLDocument* getDocumentReference() const;
+
+ SectionPropertyMap* GetLastSectionContext( )
+ {
+ return dynamic_cast< SectionPropertyMap* >( m_pLastSectionContext.get( ) );
+ }
+
+ css::uno::Reference<css::container::XNameContainer> const & GetPageStyles();
+ OUString GetUnusedPageStyleName();
+ css::uno::Reference<css::container::XNameContainer> const & GetCharacterStyles();
+ OUString GetUnusedCharacterStyleName();
+ css::uno::Reference<css::text::XText> const & GetBodyText();
+ const css::uno::Reference<css::lang::XMultiServiceFactory>& GetTextFactory() const
+ {
+ return m_xTextFactory;
+ }
+ const css::uno::Reference<css::text::XTextDocument>& GetTextDocument() const
+ {
+ return m_xTextDocument;
+ }
+ void SetDocumentSettingsProperty( const OUString& rPropName, const css::uno::Any& rValue );
+
+ void CreateRedline(css::uno::Reference<css::text::XTextRange> const& xRange, const RedlineParamsPtr& pRedline);
+
+ void CheckParaMarkerRedline(css::uno::Reference<css::text::XTextRange> const& xRange);
+
+ void CheckRedline(css::uno::Reference<css::text::XTextRange> const& xRange);
+
+ void StartParaMarkerChange( );
+ void EndParaMarkerChange( );
+ void StartParaMarkerMove( );
+ void EndParaMarkerMove( );
+ void ChainTextFrames();
+
+ void PushTextBoxContent();
+ void PopTextBoxContent();
+ void AttachTextBoxContentToShape(css::uno::Reference<css::drawing::XShape> xShape);
+
+ void RemoveDummyParaForTableInSection();
+ void AddDummyParaForTableInSection();
+ void RemoveLastParagraph( );
+ void SetIsDecimalComma() { m_bIsDecimalComma = true; };
+ void SetIsLastParagraphInSection( bool bIsLast );
+ bool GetIsLastParagraphInSection() const { return m_bIsLastParaInSection;}
+ void SetRubySprmId( sal_uInt32 nSprmId) { m_aRubyInfo.nSprmId = nSprmId ; }
+ void SetRubyText( OUString const &sText, OUString const &sStyle) {
+ m_aRubyInfo.sRubyText = sText;
+ m_aRubyInfo.sRubyStyle = sStyle;
+ }
+ const RubyInfo & GetRubyInfo() const { return m_aRubyInfo;}
+ void SetRubyInfo(const RubyInfo & rInfo) { m_aRubyInfo = rInfo;}
+
+ void SetIsLastSectionGroup( bool bIsLast );
+ bool GetIsLastSectionGroup() const { return m_bIsLastSectionGroup;}
+ void SetIsFirstParagraphInSection( bool bIsFirst );
+ void SetIsFirstParagraphInSectionAfterRedline( bool bIsFirstAfterRedline );
+ bool GetIsFirstParagraphInSection( bool bAfterRedline = false ) const;
+ void SetIsFirstParagraphInShape(bool bIsFirst);
+ bool GetIsFirstParagraphInShape() const { return m_bIsFirstParaInShape; }
+ void SetIsDummyParaAddedForTableInSection( bool bIsAdded );
+ bool GetIsDummyParaAddedForTableInSection() const { return m_bDummyParaAddedForTableInSection;}
+ void SetIsDummyParaAddedForTableInSectionPage(bool bIsAdded);
+ bool GetIsDummyParaAddedForTableInSectionPage() const { return m_bDummyParaAddedForTableInSectionPage; }
+
+ /// Track if a textframe has been inserted into this section
+ void SetIsTextFrameInserted( bool bIsInserted );
+ bool GetIsTextFrameInserted() const { return m_bTextFrameInserted;}
+
+ void SetIsPreviousParagraphFramed( bool bIsFramed ) { m_bIsPreviousParagraphFramed = bIsFramed; }
+ bool GetIsPreviousParagraphFramed() const { return m_bIsPreviousParagraphFramed; }
+ void SetParaSectpr(bool bParaSectpr);
+ bool GetParaSectpr() const { return m_bParaSectpr;}
+
+ void SetSymbolChar( sal_Int32 nSymbol) { m_aSymbolData.cSymbol = sal_Unicode(nSymbol); }
+ void SetSymbolFont( OUString const &rName ) { m_aSymbolData.sFont = rName; }
+ const SymbolData & GetSymbolData() const { return m_aSymbolData;}
+
+ /// Setter method for m_bSdt.
+ void SetSdt(bool bSdt);
+
+ void PushSdt();
+ void PopSdt();
+ /// Gives access to the currently open run/inline SDTs.
+ const std::stack<BookmarkInsertPosition>& GetSdtStarts() const;
+
+ /// Getter method for m_bSdt.
+ bool GetSdt() const { return m_bSdt;}
+ bool GetParaChanged() const { return m_bParaChanged;}
+ bool GetParaHadField() const { return m_bParaHadField; }
+ bool GetRemoveThisPara() const { return m_bRemoveThisParagraph; }
+
+ void deferBreak( BreakType deferredBreakType );
+ bool isBreakDeferred( BreakType deferredBreakType );
+ void clearDeferredBreaks();
+ void clearDeferredBreak(BreakType deferredBreakType);
+
+ void setSdtEndDeferred(bool bSdtEndDeferred);
+ bool isSdtEndDeferred() const;
+ void setParaSdtEndDeferred(bool bParaSdtEndDeferred);
+ bool isParaSdtEndDeferred() const;
+
+ void finishParagraph( const PropertyMapPtr& pPropertyMap, const bool bRemove = false, const bool bNoNumbering = false);
+ void appendTextPortion( const OUString& rString, const PropertyMapPtr& pPropertyMap );
+ void appendTextContent(const css::uno::Reference<css::text::XTextContent>&, const css::uno::Sequence<css::beans::PropertyValue>&);
+ void appendOLE( const OUString& rStreamName, const std::shared_ptr<OLEHandler>& pOleHandler );
+ void appendStarMath( const Value& v);
+ void adjustLastPara(sal_Int8 nAlign);
+ css::uno::Reference<css::beans::XPropertySet> appendTextSectionAfter(css::uno::Reference<css::text::XTextRange> const & xBefore);
+
+ /// AutoText import: each entry is placed in the separate section
+ void appendGlossaryEntry();
+ /// Remember where entry was started
+ void setGlossaryEntryStart( css::uno::Reference<css::text::XTextRange> const & xStart )
+ {
+ m_xGlossaryEntryStart = xStart;
+ }
+
+ // push the new properties onto the stack and make it the 'current' property map
+ void PushProperties(ContextType eId);
+ void PushStyleProperties(const PropertyMapPtr& pStyleProperties);
+ void PushListProperties(const PropertyMapPtr& pListProperties);
+ void PopProperties(ContextType eId);
+
+ ContextType GetTopContextType() const { return m_aContextStack.top(); }
+ const PropertyMapPtr& GetTopContext() const
+ {
+ return m_pTopContext;
+ }
+ PropertyMapPtr GetTopContextOfType(ContextType eId);
+
+ bool HasTopText() const;
+ css::uno::Reference<css::text::XTextAppend> const & GetTopTextAppend();
+ FieldContextPtr const & GetTopFieldContext();
+
+ bool HasTopAnchoredObjects() const;
+
+ FontTablePtr const & GetFontTable()
+ {
+ if(!m_pFontTable)
+ m_pFontTable = new FontTable();
+ return m_pFontTable;
+ }
+ StyleSheetTablePtr const & GetStyleSheetTable()
+ {
+ if(!m_pStyleSheetTable)
+ m_pStyleSheetTable = new StyleSheetTable( m_rDMapper, m_xTextDocument, m_bIsNewDoc );
+ return m_pStyleSheetTable;
+ }
+ OUString GetListStyleName(sal_Int32 nListId);
+ ListsManager::Pointer const & GetListTable();
+ ThemeTablePtr const & GetThemeTable()
+ {
+ if(!m_pThemeTable)
+ m_pThemeTable = new ThemeTable;
+ return m_pThemeTable;
+ }
+
+ SettingsTablePtr const & GetSettingsTable()
+ {
+ if( !m_pSettingsTable )
+ m_pSettingsTable = new SettingsTable(m_rDMapper);
+ return m_pSettingsTable;
+ }
+
+ GraphicImportPtr const & GetGraphicImport( GraphicImportType eGraphicImportType );
+ void ResetGraphicImport();
+ // this method deletes the current m_pGraphicImport after import
+ void ImportGraphic(const writerfilter::Reference< Properties>::Pointer_t&, GraphicImportType eGraphicImportType );
+
+ void InitTabStopFromStyle(const css::uno::Sequence<css::style::TabStop>& rInitTabStops);
+ void IncorporateTabStop( const DeletableTabStop &aTabStop );
+ css::uno::Sequence<css::style::TabStop> GetCurrentTabStopAndClear();
+
+ void SetCurrentParaStyleName(const OUString& sStringValue) {m_sCurrentParaStyleName = sStringValue;}
+ OUString GetCurrentParaStyleName();
+ OUString GetDefaultParaStyleName();
+
+ // specified style - including inherited properties. Indicate whether paragraph defaults should be checked.
+ css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, bool* bIsDocDefault = nullptr);
+ // current paragraph style - including inherited properties
+ css::uno::Any GetPropertyFromParaStyleSheet(PropertyIds eId);
+ // context's character style - including inherited properties
+ css::uno::Any GetPropertyFromCharStyleSheet(PropertyIds eId, const PropertyMapPtr& rContext);
+ // get property first from the given context, or secondly via inheritance from styles/docDefaults
+ css::uno::Any GetAnyProperty(PropertyIds eId, const PropertyMapPtr& rContext);
+ void SetDocDefaultsImport( bool bSet ) { m_bInDocDefaultsImport = bSet;}
+ bool IsDocDefaultsImport()const { return m_bInDocDefaultsImport;}
+ void SetStyleSheetImport( bool bSet ) { m_bInStyleSheetImport = bSet;}
+ bool IsStyleSheetImport()const { return m_bInStyleSheetImport;}
+ void SetNumberingImport( bool bSet ) { m_bInNumberingImport = bSet;}
+ bool IsNumberingImport() const { return m_bInNumberingImport;}
+ void SetAnyTableImport( bool bSet ) { m_bInAnyTableImport = bSet;}
+ bool IsAnyTableImport()const { return m_bInAnyTableImport;}
+ bool IsInShape()const { return m_aAnchoredStack.size() > 0;}
+
+ void PushShapeContext(const css::uno::Reference<css::drawing::XShape>& xShape);
+ void PopShapeContext();
+ void UpdateEmbeddedShapeProps(const css::uno::Reference<css::drawing::XShape>& xShape);
+ /// Add a pending shape: it's currently inserted into the document, but it should be removed before the import finishes.
+ void PushPendingShape(const css::uno::Reference<css::drawing::XShape>& xShape);
+ /// Get the first pending shape, if there are any.
+ css::uno::Reference<css::drawing::XShape> PopPendingShape();
+
+ void PushPageHeader(SectionPropertyMap::PageType eType);
+ void PushPageFooter(SectionPropertyMap::PageType eType);
+
+ void PopPageHeaderFooter();
+ bool IsInHeaderFooter() const { return m_eInHeaderFooterImport != HeaderFooterImportState::none; }
+ void ConvertHeaderFooterToTextFrame(bool, bool);
+ static void fillEmptyFrameProperties(std::vector<css::beans::PropertyValue>& rFrameProperties, bool bSetAnchorToChar);
+
+ bool IsInTOC() const { return m_bStartTOC; }
+
+ void PushFootOrEndnote( bool bIsFootnote );
+ void PopFootOrEndnote();
+ bool IsInFootOrEndnote() const { return m_bInFootOrEndnote; }
+ bool IsInFootnote() const { return m_bInFootnote; }
+
+ void StartCustomFootnote(const PropertyMapPtr pContext);
+ void EndCustomFootnote();
+ bool IsInCustomFootnote() const { return m_bHasFootnoteStyle; }
+ bool CheckFootnoteStyle() const { return m_bCheckFootnoteStyle; }
+ void SetHasFootnoteStyle(bool bVal) { m_bHasFootnoteStyle = bVal; }
+ void SetCheckFootnoteStyle(bool bVal) { m_bCheckFootnoteStyle = bVal; }
+
+ const PropertyMapPtr& GetFootnoteContext() const { return m_pFootnoteContext; }
+
+ SkipFootnoteSeparator GetSkipFootnoteState() const { return m_eSkipFootnoteState; }
+ void SetSkipFootnoteState(SkipFootnoteSeparator eId) { m_eSkipFootnoteState = eId; }
+ sal_Int32 GetFootnoteCount() const { return m_nFootnotes; }
+ void IncrementFootnoteCount() { ++m_nFootnotes; }
+ sal_Int32 GetEndnoteCount() const { return m_nEndnotes; }
+ void IncrementEndnoteCount() { ++m_nEndnotes; }
+ bool CopyTemporaryNotes(
+ css::uno::Reference< css::text::XFootnote > xNoteSrc,
+ css::uno::Reference< css::text::XFootnote > xNoteDest );
+ void RemoveTemporaryFootOrEndnotes();
+
+ void PushAnnotation();
+ void PopAnnotation();
+
+ /// A field context starts with a cFieldStart.
+ void PushFieldContext();
+ //the current field context waits for the completion of the command
+ bool IsOpenFieldCommand() const;
+ bool IsOpenField() const;
+ //mark field in current context as locked (fixed)
+ void SetFieldLocked();
+ //collect the pieces of the command
+ void AppendFieldCommand(OUString const & rPartOfCommand);
+ void handleRubyEQField( const FieldContextPtr& pContext);
+ void handleFieldSet
+ (const FieldContextPtr& pContext,
+ css::uno::Reference< css::uno::XInterface > const & xFieldInterface,
+ css::uno::Reference< css::beans::XPropertySet > const& xFieldProperties);
+ void handleFieldAsk
+ (const FieldContextPtr& pContext,
+ css::uno::Reference< css::uno::XInterface > & xFieldInterface,
+ css::uno::Reference< css::beans::XPropertySet > const& xFieldProperties);
+ OUString convertFieldFormula(const OUString& input);
+ void handleFieldFormula
+ (const FieldContextPtr& pContext,
+ css::uno::Reference< css::beans::XPropertySet > const& xFieldProperties);
+ void handleAutoNum
+ (const FieldContextPtr& pContext,
+ css::uno::Reference< css::uno::XInterface > const & xFieldInterface,
+ css::uno::Reference< css::beans::XPropertySet > const& xFieldProperties);
+ static void handleAuthor
+ (std::u16string_view rFirstParam,
+ css::uno::Reference< css::beans::XPropertySet > const& xFieldProperties,
+ FieldId eFieldId);
+ void handleDocProperty
+ (const FieldContextPtr& pContext,
+ OUString const& rFirstParam,
+ css::uno::Reference< css::uno::XInterface > & xFieldInterface);
+ void handleToc
+ (const FieldContextPtr& pContext,
+ const OUString & sTOCServiceName);
+ void handleIndex
+ (const FieldContextPtr& pContext,
+ const OUString & sTOCServiceName);
+
+ void handleBibliography
+ (const FieldContextPtr& pContext,
+ const OUString & sTOCServiceName);
+ /// The field command has to be closed (cFieldSep appeared).
+ void CloseFieldCommand();
+ //the _current_ fields require a string type result while TOCs accept richt results
+ bool IsFieldResultAsString();
+ void AppendFieldResult(std::u16string_view rResult);
+ //apply the result text to the related field
+ void SetFieldResult(OUString const& rResult);
+ // set FFData of top field context
+ void SetFieldFFData( const FFDataHandler::Pointer_t& pFFDataHandler );
+ /// The end of field is reached (cFieldEnd appeared) - the command might still be open.
+ void PopFieldContext();
+
+ /// Returns title of the TOC placed in paragraph(s) before TOC field inside STD-frame
+ OUString extractTocTitle();
+ css::uno::Reference<css::beans::XPropertySet> createSectionForRange(css::uno::Reference< css::text::XTextRange > xStart, css::uno::Reference< css::text::XTextRange > xEnd, const OUString & sObjectType, bool stepLeft);
+
+ void SetBookmarkName( const OUString& rBookmarkName );
+ void StartOrEndBookmark( const OUString& rId );
+
+ void SetMoveBookmark( bool IsFrom );
+
+ void setPermissionRangeEd(const OUString& user);
+ void setPermissionRangeEdGrp(const OUString& group);
+ void startOrEndPermissionRange(sal_Int32 permissinId);
+
+ void AddAnnotationPosition(
+ const bool bStart,
+ const sal_Int32 nAnnotationId );
+
+ bool hasTableManager() const
+ {
+ return !m_aTableManagers.empty();
+ }
+
+ DomainMapperTableManager& getTableManager()
+ {
+ return *m_aTableManagers.top();
+ }
+
+ void appendTableManager( )
+ {
+ tools::SvRef<DomainMapperTableManager> pMngr(new DomainMapperTableManager());
+ m_aTableManagers.push( pMngr );
+ }
+
+ void appendTableHandler( )
+ {
+ if (m_pTableHandler)
+ m_aTableManagers.top()->setHandler(m_pTableHandler);
+ }
+
+ void popTableManager( )
+ {
+ if (hasTableManager())
+ m_aTableManagers.pop();
+ }
+
+ void SetLineNumbering( sal_Int32 nLnnMod, sal_uInt32 nLnc, sal_Int32 ndxaLnn );
+ bool IsLineNumberingSet() const {return m_bLineNumberingSet;}
+
+ DeletableTabStop m_aCurrentTabStop;
+
+ /// If we're right after the end of a table.
+ bool m_bConvertedTable = false;
+
+ bool IsOOXMLImport() const { return m_eDocumentType == SourceDocumentType::OOXML; }
+
+ bool IsRTFImport() const { return m_eDocumentType == SourceDocumentType::RTF; }
+
+ void InitPageMargins() { m_aPageMargins = PageMar(); }
+ void SetPageMarginTwip( PageMarElement eElement, sal_Int32 nValue );
+ const PageMar& GetPageMargins() const {return m_aPageMargins;}
+
+ const LineNumberSettings& GetLineNumberSettings() const { return m_aLineNumberSettings;}
+ void SetLineNumberSettings(const LineNumberSettings& rSet) { m_aLineNumberSettings = rSet;}
+
+ void SetInFootnoteProperties(bool bSet) { m_bIsInFootnoteProperties = bSet;}
+ bool IsInFootnoteProperties() const { return m_bIsInFootnoteProperties;}
+
+ bool IsInComments() const { return m_bIsInComments; };
+
+ void CheckUnregisteredFrameConversion( );
+
+ void RegisterFrameConversion(css::uno::Reference<css::text::XTextRange> const& xFrameStartRange,
+ css::uno::Reference<css::text::XTextRange> const& xFrameEndRange,
+ std::vector<css::beans::PropertyValue>&& aFrameProperties);
+ void ExecuteFrameConversion();
+
+ void AddNewRedline( sal_uInt32 sprmId );
+
+ sal_Int32 GetCurrentRedlineToken( ) const;
+ void SetCurrentRedlineAuthor( const OUString& sAuthor );
+ void SetCurrentRedlineDate( const OUString& sDate );
+ void SetCurrentRedlineId( sal_Int32 nId );
+ void SetCurrentRedlineToken( sal_Int32 nToken );
+ void SetCurrentRedlineRevertProperties( const css::uno::Sequence<css::beans::PropertyValue>& aProperties );
+ void SetCurrentRedlineIsRead();
+ void RemoveTopRedline( );
+ void SetCurrentRedlineInitials( const OUString& sInitials );
+ bool IsFirstRun() const { return m_bIsFirstRun;}
+ void SetIsFirstRun(bool bval) { m_bIsFirstRun = bval;}
+ bool IsOutsideAParagraph() const { return m_bIsOutsideAParagraph;}
+ void SetIsOutsideAParagraph(bool bval) { m_bIsOutsideAParagraph = bval;}
+
+ void ApplySettingsTable();
+
+ css::uno::Reference<css::text::XTextAppend> GetCurrentXText() {
+ return m_aTextAppendStack.empty() ? nullptr : m_aTextAppendStack.top().xTextAppend;
+ }
+
+ void NewFrameDirection() {
+ m_aFrameDirectionQueue.push(std::nullopt);
+ m_bFrameDirectionSet = false;
+ }
+ void SetFrameDirection(sal_Int16 nDirection) {
+ if (!m_bFrameDirectionSet && !m_aFrameDirectionQueue.empty()) {
+ m_aFrameDirectionQueue.back() = nDirection;
+ m_bFrameDirectionSet = true;
+ }
+ }
+ std::optional<sal_Int16> PopFrameDirection() {
+ if (m_aFrameDirectionQueue.empty())
+ return {};
+ const std::optional<sal_Int16> nDirection = m_aFrameDirectionQueue.front();
+ m_aFrameDirectionQueue.pop();
+ return nDirection;
+ }
+
+ SectionPropertyMap * GetSectionContext();
+
+ sal_Int16 GetListLevel(const StyleSheetEntryPtr& pEntry, const PropertyMapPtr& pParaContext = nullptr);
+ void ValidateListLevel(const OUString& sStyleIdentifierD);
+
+ /**
+ Used for attributes/sprms which cannot be evaluated immediately (e.g. they depend
+ on another one that comes in the same CONTEXT_CHARACTER). The property will be processed
+ again in DomainMapper::processDeferredCharacterProperties().
+ */
+ void deferCharacterProperty(sal_Int32 id, const css::uno::Any& value);
+ /**
+ Processes properties deferred using deferCharacterProperty(). To be called whenever the top
+ CONTEXT_CHARACTER is going to be used (e.g. by appendText()).
+ */
+ void processDeferredCharacterProperties();
+
+ sal_Int32 getNumberingProperty(const sal_Int32 nListId, sal_Int32 nListLevel, const OUString& aProp);
+ /// Get a property of the current numbering style's current level.
+ sal_Int32 getCurrentNumberingProperty(const OUString& aProp);
+
+ /// If we're importing into a new document, or just pasting to an existing one.
+ bool IsNewDoc() const { return m_bIsNewDoc;}
+
+ bool IsAltChunk() const { return m_bIsAltChunk;}
+
+ /// If we're importing autotext.
+ bool IsReadGlossaries() const { return m_bIsReadGlossaries;}
+
+ tools::SvRef<SdtHelper> m_pSdtHelper;
+
+ /// Document background color, applied to every page style.
+ std::optional<sal_Int32> m_oBackgroundColor;
+
+ /**
+ * This contains the raw table depth. m_nTableDepth > 0 is the same as
+ * getTableManager().isInTable(), unless we're in the first paragraph of a
+ * table, or first paragraph after a table, as the table manager is only
+ * updated once we ended the paragraph (and know if the para has the
+ * inTbl SPRM or not).
+ */
+ sal_Int32 m_nTableDepth;
+ /// Raw table cell depth.
+ sal_Int32 m_nTableCellDepth;
+ /// Table cell depth of the last finished paragraph.
+ sal_Int32 m_nLastTableCellParagraphDepth;
+
+ /// If the current section has footnotes.
+ bool m_bHasFtn;
+ /// If the current section has a footnote separator.
+ bool m_bHasFtnSep;
+
+ /// If the next tab should be ignored, used for footnotes.
+ bool m_bCheckFirstFootnoteTab;
+ bool m_bIgnoreNextTab;
+ /// Pending floating tables: they may be converted to text frames at the section end.
+ std::vector<FloatingTableInfo> m_aPendingFloatingTables;
+
+ /// Paragraphs with anchored objects in the current section.
+ std::vector<AnchoredObjectsInfo> m_aAnchoredObjectAnchors;
+
+ /// Append a property to a sub-grabbag if necessary (e.g. 'lineRule', 'auto')
+ void appendGrabBag(std::vector<css::beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, const OUString& aValue);
+ void appendGrabBag(std::vector<css::beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, std::vector<css::beans::PropertyValue>& rValue);
+
+ /// Enable, disable and check status of grabbags
+ void enableInteropGrabBag(const OUString& aName);
+ void disableInteropGrabBag();
+ bool isInteropGrabBagEnabled() const;
+
+ /// Name of m_aInteropGrabBag.
+ OUString m_aInteropGrabBagName;
+
+ /// A toplevel dmapper grabbag, like 'pPr'.
+ std::vector<css::beans::PropertyValue> m_aInteropGrabBag;
+
+ /// A sub-grabbag of m_aInteropGrabBag, like 'spacing'.
+ std::vector<css::beans::PropertyValue> m_aSubInteropGrabBag;
+
+ /// ST_PositionOffset values we received
+ std::pair<OUString, OUString> m_aPositionOffsets;
+ /// ST_AlignH/V values we received
+ std::pair<OUString, OUString> m_aAligns;
+ /// ST_PositivePercentage values we received
+ std::queue<OUString> m_aPositivePercentages;
+ bool isInIndexContext() const { return m_bStartIndex;}
+ bool isInBibliographyContext() const { return m_bStartBibliography;}
+ SmartTagHandler& getSmartTagHandler() { return m_aSmartTagHandler; }
+
+ void substream(Id rName, ::writerfilter::Reference<Stream>::Pointer_t const& ref);
+
+ /// If the document needs to split paragraph.
+ bool m_bIsSplitPara;
+
+ /// Check if "SdtEndBefore" property is set
+ bool IsSdtEndBefore();
+
+ bool IsDiscardHeaderFooter() const;
+
+ bool IsForceGenericFields() const { return m_bForceGenericFields; }
+
+ void SetParaAutoBefore(bool bParaAutoBefore) { m_bParaAutoBefore = bParaAutoBefore; }
+
+ /// Forget about the previous paragraph, as it's not inside the same
+ /// start/end node.
+ void ClearPreviousParagraph();
+
+ /// Check if previous paragraph has borders in between and do the border magic to it if so
+ bool handlePreviousParagraphBorderInBetween() const;
+
+ /// Handle redline text portions in a frame, footnotes and redlines:
+ /// store their data, and create them after frame creation or footnote/endnote copying
+ bool m_bIsActualParagraphFramed;
+ std::deque<css::uno::Any> m_aStoredRedlines[StoredRedlines::NONE];
+
+ bool IsParaWithInlineObject() const { return m_bParaWithInlineObject; }
+
+ css::uno::Reference< css::embed::XStorage > m_xDocumentStorage;
+
+ /// Handles <w:altChunk>.
+ void HandleAltChunk(const OUString& rStreamName);
+
+ /// Handles <w:ptab>.
+ void HandlePTab(sal_Int32 nAlignment);
+
+ /// Handles <w:br w:clear="...">.
+ void HandleLineBreakClear(sal_Int32 nClear);
+
+ /// Handles <w:br>.
+ void HandleLineBreak(const PropertyMapPtr& pPropertyMap);
+
+ void commentProps(const OUString& sId, const CommentProperties& rProps);
+
+private:
+ void PushPageHeaderFooter(bool bHeader, SectionPropertyMap::PageType eType);
+ // Start a new index section; if needed, finish current paragraph
+ css::uno::Reference<css::beans::XPropertySet> StartIndexSectionChecked(const OUString& sServiceName);
+ std::vector<css::uno::Reference< css::drawing::XShape > > m_vTextFramesForChaining ;
+ /// Current paragraph had at least one field in it.
+ bool m_bParaHadField;
+ bool m_bSaveParaHadField;
+ css::uno::Reference<css::beans::XPropertySet> m_xPreviousParagraph;
+ /// Current paragraph has automatic before spacing.
+ bool m_bParaAutoBefore;
+ /// Current paragraph in a table is first paragraph of a cell
+ bool m_bFirstParagraphInCell;
+ bool m_bSaveFirstParagraphInCell;
+ /// Current paragraph had at least one inline object in it.
+ bool m_bParaWithInlineObject;
+ /// SAXException was seen so document will be abandoned
+ bool m_bSaxError;
+
+ std::unordered_map<OUString, CommentProperties> m_aCommentProps;
+};
+
+TextAppendContext::TextAppendContext(const css::uno::Reference<css::text::XTextAppend>& xAppend, const css::uno::Reference<css::text::XTextCursor>& xCur)
+ : xTextAppend(xAppend)
+{
+ xCursor.set(xCur, css::uno::UNO_QUERY);
+ xInsertPosition = xCursor;
+}
+
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/FFDataHandler.cxx b/writerfilter/source/dmapper/FFDataHandler.cxx
new file mode 100644
index 000000000..507327cf8
--- /dev/null
+++ b/writerfilter/source/dmapper/FFDataHandler.cxx
@@ -0,0 +1,190 @@
+/* -*- 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 "FFDataHandler.hxx"
+#include "TagLogger.hxx"
+
+#include <ooxml/resourceids.hxx>
+
+namespace writerfilter::dmapper {
+
+/************************
+ * class: FFDataHandler *
+ ************************/
+
+FFDataHandler::FFDataHandler() :
+LoggedProperties("FFDataHandler"),
+m_nCheckboxHeight(0),
+m_bCheckboxAutoHeight(false),
+m_nCheckboxChecked(-1),
+m_nCheckboxDefault(-1),
+m_nTextMaxLength(0)
+{
+}
+
+
+FFDataHandler::~FFDataHandler()
+{
+}
+
+
+bool FFDataHandler::getCheckboxChecked() const
+{
+ if (m_nCheckboxChecked != -1)
+ return m_nCheckboxChecked;
+ else if (m_nCheckboxDefault != -1)
+ return m_nCheckboxDefault;
+ else
+ return false;
+}
+
+
+void FFDataHandler::lcl_sprm(Sprm & r_Sprm)
+{
+ switch(r_Sprm.getId())
+ {
+ case NS_ooxml::LN_CT_FFData_name:
+ {
+ m_sName = r_Sprm.getValue()->getString();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFData_helpText:
+ {
+ resolveSprm(r_Sprm);
+ }
+ break;
+ case NS_ooxml::LN_CT_FFData_statusText:
+ {
+ resolveSprm(r_Sprm);
+ }
+ break;
+ case NS_ooxml::LN_CT_FFData_entryMacro:
+ {
+ m_sEntryMacro = r_Sprm.getValue()->getString();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFData_exitMacro:
+ {
+ m_sExitMacro = r_Sprm.getValue()->getString();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFCheckBox_size:
+ {
+ m_nCheckboxHeight = r_Sprm.getValue()->getInt();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFCheckBox_sizeAuto:
+ {
+ m_bCheckboxAutoHeight = r_Sprm.getValue()->getInt();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFCheckBox_checked:
+ {
+ m_nCheckboxChecked = r_Sprm.getValue()->getInt();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFCheckBox_default:
+ {
+ m_nCheckboxDefault = r_Sprm.getValue()->getInt();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFData_checkBox:
+ {
+ resolveSprm(r_Sprm);
+ }
+ break;
+ case NS_ooxml::LN_CT_FFDDList_result:
+ {
+ m_sDropDownResult = r_Sprm.getValue()->getString();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFDDList_listEntry:
+ {
+ m_DropDownEntries.push_back(r_Sprm.getValue()->getString());
+ }
+ break;
+ case NS_ooxml::LN_CT_FFData_ddList:
+ {
+ resolveSprm(r_Sprm);
+ }
+ break;
+ case NS_ooxml::LN_CT_FFTextInput_type:
+ {
+ m_sTextType = r_Sprm.getValue()->getString();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFTextInput_default:
+ {
+ m_sTextDefault = r_Sprm.getValue()->getString();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFTextInput_maxLength:
+ {
+ m_nTextMaxLength = r_Sprm.getValue()->getInt();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFTextInput_format:
+ {
+ m_sTextFormat = r_Sprm.getValue()->getString();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFData_textInput:
+ {
+ resolveSprm(r_Sprm);
+ }
+ break;
+ default:
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ break;
+ }
+}
+
+void FFDataHandler::resolveSprm(Sprm & r_Sprm)
+{
+ writerfilter::Reference<Properties>::Pointer_t pProperties = r_Sprm.getProps();
+ if( pProperties)
+ pProperties->resolve(*this);
+}
+
+void FFDataHandler::lcl_attribute(Id name, Value & val)
+{
+ switch (name)
+ {
+ case NS_ooxml::LN_CT_FFHelpText_val:
+ {
+ m_sHelpText = val.getString();
+ }
+ break;
+ case NS_ooxml::LN_CT_FFStatusText_val:
+ {
+ m_sStatusText = val.getString();
+ }
+ break;
+ default:
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ break;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/FFDataHandler.hxx b/writerfilter/source/dmapper/FFDataHandler.hxx
new file mode 100644
index 000000000..f8a88f5c9
--- /dev/null
+++ b/writerfilter/source/dmapper/FFDataHandler.hxx
@@ -0,0 +1,99 @@
+/* -*- 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 "LoggedResources.hxx"
+#include <rtl/ustring.hxx>
+#include <vector>
+namespace writerfilter::dmapper
+{
+class FFDataHandler : public LoggedProperties
+{
+public:
+ // typedefs
+ typedef ::tools::SvRef<FFDataHandler> Pointer_t;
+ typedef ::std::vector<OUString> DropDownEntries_t;
+
+ // constructor
+ FFDataHandler();
+ // destructor
+ virtual ~FFDataHandler() override;
+
+ // member: name
+ const OUString& getName() const { return m_sName; }
+
+ // member: helpText
+ const OUString& getHelpText() const { return m_sHelpText; }
+
+ // member: statusText
+ const OUString& getStatusText() const { return m_sStatusText; }
+
+ const OUString& getEntryMacro() const { return m_sEntryMacro; }
+ const OUString& getExitMacro() const { return m_sExitMacro; }
+
+ // member: checkboxHeight
+ sal_uInt32 getCheckboxHeight() const { return m_nCheckboxHeight; }
+
+ // member: checkboxAutoHeight
+ bool getCheckboxAutoHeight() const { return m_bCheckboxAutoHeight; }
+
+ // member: checkboxChecked or checkboxDefault (if the previous is not set)
+ bool getCheckboxChecked() const;
+
+ // member: dropDownResult
+ const OUString& getDropDownResult() const { return m_sDropDownResult; }
+
+ // member: dropDownEntries
+ const DropDownEntries_t& getDropDownEntries() const { return m_DropDownEntries; }
+
+ // member: textDefault
+ const OUString& getTextDefault() const { return m_sTextDefault; }
+
+ const OUString& getTextType() const { return m_sTextType; }
+ const OUString& getTextFormat() const { return m_sTextFormat; }
+ sal_uInt16 getTextMaxLength() const { return m_nTextMaxLength; }
+
+ // sprm
+ void resolveSprm(Sprm& r_sprm);
+
+private:
+ OUString m_sName;
+ OUString m_sHelpText;
+ OUString m_sStatusText;
+ OUString m_sEntryMacro;
+ OUString m_sExitMacro;
+ sal_uInt32 m_nCheckboxHeight;
+ bool m_bCheckboxAutoHeight;
+ int m_nCheckboxChecked;
+ int m_nCheckboxDefault;
+ OUString m_sDropDownResult;
+ DropDownEntries_t m_DropDownEntries;
+ OUString m_sTextDefault;
+ OUString m_sTextType;
+ OUString m_sTextFormat;
+ sal_uInt16 m_nTextMaxLength;
+
+ // sprm
+ void lcl_sprm(Sprm& r_sprm) override;
+
+ // attribute
+ void lcl_attribute(Id name, Value& val) override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/FieldTypes.hxx b/writerfilter/source/dmapper/FieldTypes.hxx
new file mode 100644
index 000000000..a907a7af6
--- /dev/null
+++ b/writerfilter/source/dmapper/FieldTypes.hxx
@@ -0,0 +1,305 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+namespace writerfilter::dmapper {
+
+enum FieldId
+{
+ /* ADDRESSBLOCK \d \* MERGEFORMAT -> Addressblock completely unsupported*/
+ FIELD_ADDRESSBLOCK
+ /* ADVANCE \d downvalue \l leftvalue \r rightvalue \u upvalue \x xvalue \y yvalue -> unsupported*/
+ ,FIELD_ADVANCE
+ /* ASK bookmarkname "hint" \d defaultanswer \o \* MERGEFORMAT ->
+ the hint is not always quoted, inputfield with default answer, prompt before merge (\o)
+ */
+ ,FIELD_ASK
+ /* AUTONUM \* Numberingswitch ->
+ mapped to sequence field "AutoNr"
+ */
+ ,FIELD_AUTONUM
+ /* AUTONUMLGL \* Numberingswitch ->
+ mapped to sequence field "AutoNr"
+ */
+ ,FIELD_AUTONUMLGL
+ /* AUTONUMOUT \* Numberingswitch ->
+ mapped to sequence field "AutoNr"
+ */
+ ,FIELD_AUTONUMOUT
+ /* AUTHOR NewAuthor \* defaultswitch \* MERGEFORMAT ->
+ mapped to sequence field "AutoNr"
+ */
+ ,FIELD_AUTHOR
+ /* COMMENTS "comment" \* MERGEFORMAT ->
+ Docinfo-Comments
+ */
+ ,FIELD_COMMENTS
+ /* CREATEDATE \h \* MERGEFORMAT ->
+ docinfo-created-date
+ */
+ ,FIELD_CREATEDATE
+ /* DATE \@ "number format" \s \* MERGEFORMAT ->
+ ww8filterimprovement: multiple languages now supported
+ */
+ ,FIELD_DATE
+ /* DOCPROPERTY propertyname \* MERGEFORMAT ->
+ ww8filterimprovement: some fields imported as functionally equivalent fields if possible,
+ the others imported as UserField
+ */
+ ,FIELD_DOCPROPERTY
+ /* DOCVARIABLE Name \* MERGEFORMAT ->
+ ww8filterimprovement: now imported as user fields
+ */
+ ,FIELD_DOCVARIABLE
+ /* EDITTIME \# "displayformat" \* Numberingswitch \* MERGEFORMAT ->
+ DocInfo-Modified-Date
+ ww8filterimprovement: multiple languages now supported
+ */
+ ,FIELD_EDITTIME
+ ,FIELD_EQ
+ /* FILLIN "text to fill in" \d defaultanswer \o \* MERGEFORMAT ->
+ Function-InputField
+ */
+ ,FIELD_FILLIN
+ /* FILENAME \p \* * MERGEFORMAT ->
+ file name (\p with path)
+ */
+ ,FIELD_FILENAME
+ /* FILESIZE \* NumberingType \* MERGEFORMAT ->
+ not imported in old ww8 filter, see lcl_ParseNumberingType
+ todo find alternative field
+ */
+ ,FIELD_FILESIZE
+ /* =formula \# "number format"
+ todo find alternative field
+ */
+ ,FIELD_FORMULA
+ /* FORMCHECKBOX */
+ ,FIELD_FORMCHECKBOX
+ /* FORMDROPDOWN */
+ ,FIELD_FORMDROPDOWN
+ /* FORMTEXT */
+ ,FIELD_FORMTEXT
+ /* GOTOBUTTON text \* MERGEFORMAT ->
+ not imported in old ww8 filter
+ todo find alternative field
+ */
+ ,FIELD_GOTOBUTTON
+ /* HYPERLINK "link" \* MERGEFORMAT ->
+ not imported in old ww8 filter
+ ww8filterimprovement: now imported as hyperlink
+ */
+ ,FIELD_HYPERLINK
+ /* IF condition "then text" "else text" ->
+ not imported in old ww8 filter
+ ww8filterimprovement: now imported
+ todo: condition, if text, else text still missing
+ */
+ ,FIELD_IF
+ /* INFO NameOfInfo \* MERGEFORMAT -> old
+ todo: filter imports wrong?
+ */
+ ,FIELD_INFO
+ /* INCLUDEPICTURE path \* MERGEFORMAT->
+ old filter imports an embedded picture
+ */
+ ,FIELD_INCLUDEPICTURE
+ /* KEYWORDS keyword \* defaultswitch \* Numberingswitch \* MERGEFORMAT ->
+ DocInfo Keywords
+ */
+ ,FIELD_KEYWORDS
+ /* LASTSAVEDBY \* MERGEFORMAT ->
+ DocInfo-Modified-Author
+ */
+ ,FIELD_LASTSAVEDBY
+ /* MACROBUTTON MacroName quick help text ->
+ Macro field
+ */
+ ,FIELD_MACROBUTTON
+ /* MERGEFIELD ColumName \b prefix \f suffix \* MERGEFORMAT ->
+ ww8filterimprovement: column-only API now supported
+ */
+ ,FIELD_MERGEFIELD
+ /* MERGEREC \* MERGEFORMAT ->
+ RecordNumber field, maybe without db name
+ todo: currently unchecked
+ */
+ ,FIELD_MERGEREC
+ /* MERGESEQ \* MERGEFORMAT ->
+ not imported in old ww8 filter
+ ww8filterimprovement: now imported
+ todo: currently unchecked
+ */
+ ,FIELD_MERGESEQ
+ /* NEXT text ->
+ Next record
+ todo: currently unchecked
+ */
+ ,FIELD_NEXT
+ /* NEXTIF condition
+ todo: condition not imported
+ */
+ ,FIELD_NEXTIF
+ /* PAGE \* Numberingswitch \* MERGEFORMAT ->
+ see lcl_ParseNumberingType
+ */
+ ,FIELD_PAGE
+ ,FIELD_PAGEREF
+ ,FIELD_PRINTDATE
+ /* REF targetbkm \f \* MERGEFORMAT ->
+ imports a ShowVariable (bookmarkname)?
+ \h hyperlink to paragraph
+ \p relative to para above/below
+ \f reference number
+ \d separator number separator
+ \n paragraph number
+ \r paragraph number in relative context
+ \t suppress non delimiters
+ \w paragraph number in full context
+ \* Upper/Lower...
+ */
+ ,FIELD_REF
+ /* REVNUM \* Numberingswitch \* MERGEFORMAT ->
+ DocInfo-revision number
+ */
+ ,FIELD_REVNUM
+ /* SAVEDATE \@ "NumberFormat"\* MERGEFORMAT ->
+ DocInfo-modified-date
+ */
+ ,FIELD_SAVEDATE
+ /* SECTION \* NumberFormat \* MERGEFORMAT ->
+ not imported in old ww8 filter see lcl_ParseNumberingType
+ todo: find alternative
+ */
+ ,FIELD_SECTION
+ /* SECTIONPAGES \* NumberFormat \* MERGEFORMAT ->
+ not imported in old ww8 filter see lcl_ParseNumberingType
+ todo: find alternative
+ */
+ ,FIELD_SECTIONPAGES
+ /* SEQ sequencename \h \c \n \r \s \* MERGEFORMAT ->
+ number range name:sequencename value:sequencename+1
+ todo: only partially implemented, switches unsupported
+ */
+ ,FIELD_SEQ
+ /* SET bookmarkname newtext \* MERGEFORMAT ->
+ SetVariable bookmarkname = newtext
+ todo: not implemented yet
+ */
+ ,FIELD_SET
+ /* SKIPIF condition \* MERGEFORMAT ->
+ ??
+ todo: not implemented yet
+ */
+ ,FIELD_SKIPIF
+ /* STYLEREF stylename \* MERGEFORMAT ->
+ not imported in old ww8 filter
+ todo: add an equivalent field type
+ */
+ ,FIELD_STYLEREF
+ /* SUBJECT subject \* Defaultswitch \* MERGEFORMAT ->
+ DocInfo - subject
+ */
+ ,FIELD_SUBJECT
+ /* SYMBOL symbolnumber \* MERGEFORMAT ->
+ inserts a special char (symbolnumber)
+ todo: find alternative
+ */
+ ,FIELD_SYMBOL
+ /* TEMPLATE \* Defaultswitch \* MERGEFORMAT
+ TemplateName field
+ */
+ ,FIELD_TEMPLATE
+ /* TIME \@ "number format" \* MERGEFORMAT
+ ww8filterimprovement: multiple languages now supported
+ */
+ ,FIELD_TIME
+ /* TITLE \* Defaultswitch \* MERGEFORMAT ->
+ DocInfo-title
+ */
+ ,FIELD_TITLE
+ /* USERINITIALS newinitials \* MERGEFORMAT ->
+ ExtendedUser field (SHORTCUT)
+ */
+ ,FIELD_USERINITIALS
+ /* USERADDRESS \* MERGEFORMAT ->
+ not imported in old ww8 filter
+ todo: find alternative
+ */
+ ,FIELD_USERADDRESS
+ /* USERNAME newusername \* MERGEFORMAT ->
+ not imported in old ww8 filter
+ todo: import as extended user field(s)
+ */
+ ,FIELD_USERNAME
+ /*
+ TOC options:
+ \a Builds a table of figures but does not include the captions's label and number
+ \b Uses a bookmark to specify area of document from which to build table of contents
+ \c Builds a table of figures of the given label
+ \d Defines the separator between sequence and page numbers
+ \f Builds a table of contents using TC entries instead of outline levels
+ \h Hyperlinks the entries and page numbers within the table of contents
+ \l Defines the TC entries field level used to build a table of contents
+ \n Builds a table of contents or a range of entries, such as 1-9, in a table of contents without page numbers
+ \o Builds a table of contents by using outline levels instead of TC entries
+ \p Defines the separator between the table entry and its page number
+ \s Builds a table of contents by using a sequence type
+ \t Builds a table of contents by using style names other than the standard outline styles
+ \u Builds a table of contents by using the applied paragraph outline level
+ \w Preserve tab characters within table entries
+ \x Preserve newline characters within table entries
+ \z Hides page numbers within the table of contents when shown in Web Layout View
+ */
+ ,FIELD_TOC
+ /*
+ TOC entry: text
+ \f TC entry in doc with multiple tables
+ \l Outline Level
+ \n Suppress page numbers
+ example: TOC "EntryText \f \l 2 \n
+ */
+ ,FIELD_TC
+ /* document statistic - number of characters
+ */
+ ,FIELD_NUMCHARS
+ /* document statistic - number of words
+ */
+ ,FIELD_NUMWORDS
+ /* document statistic - number of pages
+ */
+ ,FIELD_NUMPAGES
+ /* Document alphabetical index
+ */
+ ,FIELD_INDEX
+ /* Document alphabetical index marks
+ */
+ ,FIELD_XE
+ /**
+ * Bibliography
+ */
+ ,FIELD_BIBLIOGRAPHY
+ /* Citation
+ */
+ ,FIELD_CITATION
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/FontTable.cxx b/writerfilter/source/dmapper/FontTable.cxx
new file mode 100644
index 000000000..470deff83
--- /dev/null
+++ b/writerfilter/source/dmapper/FontTable.cxx
@@ -0,0 +1,299 @@
+/* -*- 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 "FontTable.hxx"
+#include <o3tl/deleter.hxx>
+#include <ooxml/resourceids.hxx>
+#include <vector>
+#include <sal/log.hxx>
+#include <rtl/tencinfo.h>
+#include <vcl/embeddedfontshelper.hxx>
+#include <unotools/fontdefs.hxx>
+
+using namespace com::sun::star;
+
+namespace writerfilter::dmapper
+{
+
+struct FontTable_Impl
+{
+ std::unique_ptr<EmbeddedFontsHelper, o3tl::default_delete<EmbeddedFontsHelper>> xEmbeddedFontHelper;
+ std::vector< FontEntry::Pointer_t > aFontEntries;
+ FontEntry::Pointer_t pCurrentEntry;
+ FontTable_Impl() {}
+};
+
+FontTable::FontTable()
+: LoggedProperties("FontTable")
+, LoggedTable("FontTable")
+, LoggedStream("FontTable")
+, m_pImpl( new FontTable_Impl )
+{
+}
+
+FontTable::~FontTable()
+{
+}
+
+void FontTable::lcl_attribute(Id Name, Value & val)
+{
+ SAL_WARN_IF( !m_pImpl->pCurrentEntry, "writerfilter.dmapper", "current entry has to be set here" );
+ if(!m_pImpl->pCurrentEntry)
+ return ;
+ int nIntValue = val.getInt();
+ OUString sValue = val.getString();
+ switch(Name)
+ {
+ case NS_ooxml::LN_CT_Pitch_val:
+ if (static_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_Pitch_fixed)
+ ;
+ else if (static_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_Pitch_variable)
+ ;
+ else if (static_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_Pitch_default)
+ ;
+ else
+ SAL_WARN("writerfilter.dmapper", "FontTable::lcl_attribute: unhandled NS_ooxml::CT_Pitch_val: " << nIntValue);
+ break;
+ case NS_ooxml::LN_CT_Font_name:
+ m_pImpl->pCurrentEntry->sFontName = sValue;
+ break;
+ case NS_ooxml::LN_CT_Charset_val:
+ // w:characterSet has higher priority, set only if that one is not set
+ if( m_pImpl->pCurrentEntry->nTextEncoding == RTL_TEXTENCODING_DONTKNOW )
+ {
+ m_pImpl->pCurrentEntry->nTextEncoding = rtl_getTextEncodingFromWindowsCharset( nIntValue );
+ if( IsStarSymbol( m_pImpl->pCurrentEntry->sFontName ))
+ m_pImpl->pCurrentEntry->nTextEncoding = RTL_TEXTENCODING_SYMBOL;
+ }
+ break;
+ case NS_ooxml::LN_CT_Charset_characterSet:
+ {
+ OString tmp;
+ sValue.convertToString( &tmp, RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
+ m_pImpl->pCurrentEntry->nTextEncoding = rtl_getTextEncodingFromMimeCharset( tmp.getStr() );
+ // Older LO versions used to write incorrect character set for OpenSymbol, fix.
+ if( IsStarSymbol( m_pImpl->pCurrentEntry->sFontName ))
+ m_pImpl->pCurrentEntry->nTextEncoding = RTL_TEXTENCODING_SYMBOL;
+ break;
+ }
+ default: ;
+ }
+}
+
+void FontTable::lcl_sprm(Sprm& rSprm)
+{
+ SAL_WARN_IF( !m_pImpl->pCurrentEntry, "writerfilter.dmapper", "current entry has to be set here" );
+ if(!m_pImpl->pCurrentEntry)
+ return ;
+ sal_uInt32 nSprmId = rSprm.getId();
+
+ switch(nSprmId)
+ {
+ case NS_ooxml::LN_CT_Font_charset:
+ case NS_ooxml::LN_CT_Font_pitch:
+ resolveSprm( rSprm );
+ break;
+ case NS_ooxml::LN_CT_Font_embedRegular:
+ case NS_ooxml::LN_CT_Font_embedBold:
+ case NS_ooxml::LN_CT_Font_embedItalic:
+ case NS_ooxml::LN_CT_Font_embedBoldItalic:
+ {
+ writerfilter::Reference< Properties >::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ EmbeddedFontHandler handler(*this, m_pImpl->pCurrentEntry->sFontName,
+ nSprmId == NS_ooxml::LN_CT_Font_embedRegular ? ""
+ : nSprmId == NS_ooxml::LN_CT_Font_embedBold ? "b"
+ : nSprmId == NS_ooxml::LN_CT_Font_embedItalic ? "i"
+ : /*NS_ooxml::LN_CT_Font_embedBoldItalic*/ "bi" );
+ pProperties->resolve( handler );
+ }
+ break;
+ }
+ case NS_ooxml::LN_CT_Font_altName:
+ break;
+ case NS_ooxml::LN_CT_Font_panose1:
+ break;
+ case NS_ooxml::LN_CT_Font_family:
+ break;
+ case NS_ooxml::LN_CT_Font_sig:
+ break;
+ case NS_ooxml::LN_CT_Font_notTrueType:
+ break;
+ default:
+ SAL_WARN("writerfilter.dmapper", "FontTable::lcl_sprm: unhandled token: " << nSprmId);
+ break;
+ }
+}
+
+void FontTable::resolveSprm(Sprm & r_Sprm)
+{
+ writerfilter::Reference<Properties>::Pointer_t pProperties = r_Sprm.getProps();
+ if( pProperties )
+ pProperties->resolve(*this);
+}
+
+void FontTable::lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref)
+{
+ //create a new font entry
+ SAL_WARN_IF( m_pImpl->pCurrentEntry, "writerfilter.dmapper", "current entry has to be NULL here" );
+ m_pImpl->pCurrentEntry = new FontEntry;
+ ref->resolve(*this);
+ //append it to the table
+ m_pImpl->aFontEntries.push_back( m_pImpl->pCurrentEntry );
+ m_pImpl->pCurrentEntry.clear();
+}
+
+void FontTable::lcl_startSectionGroup()
+{
+}
+
+void FontTable::lcl_endSectionGroup()
+{
+}
+
+void FontTable::lcl_startParagraphGroup()
+{
+}
+
+void FontTable::lcl_endParagraphGroup()
+{
+}
+
+void FontTable::lcl_startCharacterGroup()
+{
+}
+
+void FontTable::lcl_endCharacterGroup()
+{
+}
+
+void FontTable::lcl_text(const sal_uInt8*, size_t )
+{
+}
+
+void FontTable::lcl_utext(const sal_uInt8* , size_t)
+{
+}
+
+void FontTable::lcl_props(writerfilter::Reference<Properties>::Pointer_t)
+{
+}
+
+void FontTable::lcl_table(Id, writerfilter::Reference<Table>::Pointer_t)
+{
+}
+
+void FontTable::lcl_substream(Id, ::writerfilter::Reference<Stream>::Pointer_t)
+{
+}
+
+void FontTable::lcl_startShape(uno::Reference<drawing::XShape> const&)
+{
+}
+
+void FontTable::lcl_endShape( )
+{
+}
+
+FontEntry::Pointer_t FontTable::getFontEntry(sal_uInt32 nIndex)
+{
+ return (m_pImpl->aFontEntries.size() > nIndex)
+ ? m_pImpl->aFontEntries[nIndex]
+ : FontEntry::Pointer_t();
+}
+
+sal_uInt32 FontTable::size()
+{
+ return m_pImpl->aFontEntries.size();
+}
+
+void FontTable::addEmbeddedFont(const css::uno::Reference<css::io::XInputStream>& stream,
+ const OUString& fontName, const char* extra,
+ std::vector<unsigned char> const & key)
+{
+ if (!m_pImpl->xEmbeddedFontHelper)
+ m_pImpl->xEmbeddedFontHelper.reset(new EmbeddedFontsHelper);
+ m_pImpl->xEmbeddedFontHelper->addEmbeddedFont(stream, fontName, extra, key);
+}
+
+EmbeddedFontHandler::EmbeddedFontHandler(FontTable& rFontTable, const OUString& _fontName, const char* _style )
+: LoggedProperties("EmbeddedFontHandler")
+, fontTable( rFontTable )
+, fontName( _fontName )
+, style( _style )
+{
+}
+
+EmbeddedFontHandler::~EmbeddedFontHandler()
+{
+ if( !inputStream.is())
+ return;
+ std::vector< unsigned char > key( 32 );
+ if( !fontKey.isEmpty())
+ { // key for unobfuscating
+ // 1 3 5 7 10 2 5 7 20 2 5 7 9 1 3 5
+ // {62E79491-959F-41E9-B76B-6B32631DEA5C}
+ static const int pos[ 16 ] = { 35, 33, 31, 29, 27, 25, 22, 20, 17, 15, 12, 10, 7, 5, 3, 1 };
+ for( int i = 0;
+ i < 16;
+ ++i )
+ {
+ int v1 = fontKey[ pos[ i ]];
+ int v2 = fontKey[ pos[ i ] + 1 ];
+ assert(( v1 >= '0' && v1 <= '9' ) || ( v1 >= 'A' && v1 <= 'F' ));
+ assert(( v2 >= '0' && v2 <= '9' ) || ( v2 >= 'A' && v2 <= 'F' ));
+ int val = ( v1 - ( v1 <= '9' ? '0' : 'A' - 10 )) * 16 + v2 - ( v2 <= '9' ? '0' : 'A' - 10 );
+ key[ i ] = val;
+ key[ i + 16 ] = val;
+ }
+ }
+ fontTable.addEmbeddedFont( inputStream, fontName, style, key );
+ inputStream->closeInput();
+}
+
+void EmbeddedFontHandler::lcl_attribute( Id name, Value& val )
+{
+ OUString sValue = val.getString();
+ switch( name )
+ {
+ case NS_ooxml::LN_CT_FontRel_fontKey:
+ fontKey = sValue;
+ break;
+ case NS_ooxml::LN_CT_Rel_id:
+ break;
+ case NS_ooxml::LN_CT_FontRel_subsetted:
+ break; // TODO? Let's just ignore this for now and hope
+ // it doesn't break anything.
+ case NS_ooxml::LN_inputstream: // the actual font data as stream
+ val.getAny() >>= inputStream;
+ break;
+ default:
+ break;
+ }
+}
+
+void EmbeddedFontHandler::lcl_sprm( Sprm& )
+{
+}
+
+
+}//namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/FontTable.hxx b/writerfilter/source/dmapper/FontTable.hxx
new file mode 100644
index 000000000..5f32776a2
--- /dev/null
+++ b/writerfilter/source/dmapper/FontTable.hxx
@@ -0,0 +1,106 @@
+/* -*- 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 <memory>
+#include <vector>
+#include "LoggedResources.hxx"
+#include <com/sun/star/io/XInputStream.hpp>
+
+namespace writerfilter::dmapper
+{
+
+struct FontTable_Impl;
+struct FontEntry : public virtual SvRefBase
+{
+ typedef tools::SvRef<FontEntry> Pointer_t;
+
+ OUString sFontName;
+ sal_Int32 nTextEncoding;
+ FontEntry() :
+ nTextEncoding( RTL_TEXTENCODING_DONTKNOW )
+ {}
+};
+
+class FontTable : public LoggedProperties, public LoggedTable
+ /*,public BinaryObj*/, public LoggedStream
+{
+ std::unique_ptr<FontTable_Impl> m_pImpl;
+
+ public:
+ FontTable();
+ virtual ~FontTable() override;
+
+ sal_uInt32 size();
+ FontEntry::Pointer_t getFontEntry(sal_uInt32 nIndex);
+
+ void addEmbeddedFont(const css::uno::Reference<css::io::XInputStream>& stream,
+ const OUString& fontName, const char* extra,
+ std::vector<unsigned char> const & key);
+
+ private:
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+ void resolveSprm(Sprm & r_sprm);
+
+ // Table
+ virtual void lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref) override;
+
+ // Stream
+ virtual void lcl_startSectionGroup() override;
+ virtual void lcl_endSectionGroup() override;
+ virtual void lcl_startParagraphGroup() override;
+ virtual void lcl_endParagraphGroup() override;
+ virtual void lcl_startCharacterGroup() override;
+ virtual void lcl_endCharacterGroup() override;
+ virtual void lcl_text(const sal_uInt8 * data, size_t len) override;
+ virtual void lcl_utext(const sal_uInt8 * data, size_t len) override;
+ virtual void lcl_props(writerfilter::Reference<Properties>::Pointer_t ref) override;
+ virtual void lcl_table(Id name,
+ writerfilter::Reference<Table>::Pointer_t ref) override;
+ virtual void lcl_substream(Id name,
+ ::writerfilter::Reference<Stream>::Pointer_t ref) override;
+ virtual void lcl_startShape(css::uno::Reference<css::drawing::XShape> const& xShape) override;
+ virtual void lcl_endShape( ) override;
+ virtual void lcl_startTextBoxContent() override {};
+ virtual void lcl_endTextBoxContent() override {};
+};
+typedef tools::SvRef< FontTable > FontTablePtr;
+
+class EmbeddedFontHandler : public LoggedProperties
+{
+public:
+ EmbeddedFontHandler(FontTable& rFontTable, const OUString& fontName, const char* style);
+ virtual ~EmbeddedFontHandler() override;
+private:
+ virtual void lcl_attribute( Id name, Value& val ) override;
+ virtual void lcl_sprm( Sprm& rSprm ) override;
+ FontTable& fontTable;
+ OUString fontName;
+ const char* const style;
+ OUString fontKey;
+ css::uno::Reference<css::io::XInputStream> inputStream;
+};
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/FormControlHelper.cxx b/writerfilter/source/dmapper/FormControlHelper.cxx
new file mode 100644
index 000000000..e00c4bebd
--- /dev/null
+++ b/writerfilter/source/dmapper/FormControlHelper.cxx
@@ -0,0 +1,378 @@
+/* -*- 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 <math.h>
+
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/uno/Any.hxx>
+
+#include <o3tl/safeint.hxx>
+
+#include "FormControlHelper.hxx"
+#include <xmloff/odffields.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+
+struct FormControlHelper::FormControlHelper_Impl : public virtual SvRefBase
+{
+ FieldId m_eFieldId;
+ awt::Size aSize;
+ uno::Reference<drawing::XDrawPage> rDrawPage;
+ uno::Reference<form::XForm> rForm;
+ uno::Reference<form::XFormComponent> rFormComponent;
+ uno::Reference<lang::XMultiServiceFactory> rServiceFactory;
+ uno::Reference<text::XTextDocument> rTextDocument;
+
+ uno::Reference<drawing::XDrawPage> const & getDrawPage();
+ uno::Reference<lang::XMultiServiceFactory> const & getServiceFactory();
+ uno::Reference<form::XForm> const & getForm();
+ uno::Reference<container::XIndexContainer> getFormComps();
+};
+
+uno::Reference<drawing::XDrawPage> const & FormControlHelper::FormControlHelper_Impl::getDrawPage()
+{
+ if (! rDrawPage.is())
+ {
+ uno::Reference<drawing::XDrawPageSupplier>
+ xDrawPageSupplier(rTextDocument, uno::UNO_QUERY);
+ if (xDrawPageSupplier.is())
+ rDrawPage = xDrawPageSupplier->getDrawPage();
+ }
+
+ return rDrawPage;
+}
+
+uno::Reference<lang::XMultiServiceFactory> const & FormControlHelper::FormControlHelper_Impl::getServiceFactory()
+{
+ if (! rServiceFactory.is())
+ rServiceFactory.set(rTextDocument, uno::UNO_QUERY);
+
+ return rServiceFactory;
+}
+
+uno::Reference<form::XForm> const & FormControlHelper::FormControlHelper_Impl::getForm()
+{
+ if (! rForm.is())
+ {
+ uno::Reference<form::XFormsSupplier> xFormsSupplier(getDrawPage(), uno::UNO_QUERY);
+
+ if (xFormsSupplier.is())
+ {
+ uno::Reference<container::XNameContainer> xFormsNamedContainer(xFormsSupplier->getForms());
+ static constexpr OUStringLiteral sDOCXForm = u"DOCX-Standard";
+
+ OUString sFormName(sDOCXForm);
+ sal_uInt16 nUnique = 0;
+
+ while (xFormsNamedContainer->hasByName(sFormName))
+ {
+ ++nUnique;
+ sFormName = sDOCXForm + OUString::number(nUnique);
+ }
+
+ uno::Reference<uno::XInterface> xForm(getServiceFactory()->createInstance("com.sun.star.form.component.Form"));
+ if (xForm.is())
+ {
+ uno::Reference<beans::XPropertySet>
+ xFormProperties(xForm, uno::UNO_QUERY);
+ uno::Any aAny(sFormName);
+ xFormProperties->setPropertyValue("Name", aAny);
+ }
+
+ rForm.set(xForm, uno::UNO_QUERY);
+
+ uno::Reference<container::XIndexContainer> xForms(xFormsNamedContainer, uno::UNO_QUERY);
+ uno::Any aAny(xForm);
+ xForms->insertByIndex(xForms->getCount(), aAny);
+ }
+ }
+
+ return rForm;
+}
+
+uno::Reference<container::XIndexContainer> FormControlHelper::FormControlHelper_Impl::getFormComps()
+{
+ uno::Reference<container::XIndexContainer> xIndexContainer(getForm(), uno::UNO_QUERY);
+
+ return xIndexContainer;
+}
+
+FormControlHelper::FormControlHelper(FieldId eFieldId,
+ uno::Reference<text::XTextDocument> const& xTextDocument,
+ FFDataHandler::Pointer_t const & pFFData)
+ : m_pFFData(pFFData), m_pImpl(new FormControlHelper_Impl)
+{
+ m_pImpl->m_eFieldId = eFieldId;
+ m_pImpl->rTextDocument = xTextDocument;
+}
+
+FormControlHelper::~FormControlHelper()
+{
+}
+
+bool FormControlHelper::createCheckbox(uno::Reference<text::XTextRange> const& xTextRange,
+ const OUString & rControlName)
+{
+ if ( !m_pFFData )
+ return false;
+ uno::Reference<lang::XMultiServiceFactory>
+ xServiceFactory(m_pImpl->getServiceFactory());
+
+ if (! xServiceFactory.is())
+ return false;
+
+ uno::Reference<uno::XInterface> xInterface = xServiceFactory->createInstance("com.sun.star.form.component.CheckBox");
+
+ if (!xInterface.is())
+ return false;
+
+ m_pImpl->rFormComponent.set(xInterface, uno::UNO_QUERY);
+ if (!m_pImpl->rFormComponent.is())
+ return false;
+
+ uno::Reference<beans::XPropertySet> xPropSet(xInterface, uno::UNO_QUERY);
+
+ sal_uInt32 nCheckBoxHeight = 16 * m_pFFData->getCheckboxHeight();
+
+ if (m_pFFData->getCheckboxAutoHeight())
+ {
+ uno::Reference<beans::XPropertySet> xTextRangeProps(xTextRange, uno::UNO_QUERY);
+
+ try
+ {
+ float fCheckBoxHeight = 0.0;
+ xTextRangeProps->getPropertyValue("CharHeight") >>= fCheckBoxHeight;
+ nCheckBoxHeight = static_cast<sal_uInt32>(floor(fCheckBoxHeight * 35.3));
+ }
+ catch (beans::UnknownPropertyException &)
+ {
+ }
+ }
+
+ m_pImpl->aSize.Width = nCheckBoxHeight;
+ m_pImpl->aSize.Height = m_pImpl->aSize.Width;
+
+ if (!m_pFFData->getStatusText().isEmpty())
+ {
+ xPropSet->setPropertyValue("HelpText", uno::Any(m_pFFData->getStatusText()));
+ }
+
+ xPropSet->setPropertyValue("DefaultState", uno::Any(m_pFFData->getCheckboxChecked()));
+
+ if (!m_pFFData->getHelpText().isEmpty())
+ {
+ xPropSet->setPropertyValue("HelpF1Text", uno::Any(m_pFFData->getHelpText()));
+ }
+
+ xPropSet->setPropertyValue("Name", uno::Any(rControlName));
+
+ return true;
+}
+
+void FormControlHelper::processField(uno::Reference<text::XFormField> const& xFormField)
+{
+ // Set field type first before adding parameters.
+ if (m_pImpl->m_eFieldId == FIELD_FORMTEXT )
+ {
+ xFormField->setFieldType(ODF_FORMTEXT);
+ }
+ else if (m_pImpl->m_eFieldId == FIELD_FORMCHECKBOX )
+ {
+ xFormField->setFieldType(ODF_FORMCHECKBOX);
+ }
+ else if (m_pImpl->m_eFieldId == FIELD_FORMDROPDOWN )
+ {
+ xFormField->setFieldType(ODF_FORMDROPDOWN);
+ }
+
+ uno::Reference<container::XNameContainer> xNameCont = xFormField->getParameters();
+ uno::Reference<container::XNamed> xNamed( xFormField, uno::UNO_QUERY );
+ if ( !(m_pFFData && xNamed.is() && xNameCont.is()) )
+ return;
+
+ OUString sTmp = m_pFFData->getEntryMacro();
+ if ( !sTmp.isEmpty() )
+ xNameCont->insertByName( "EntryMacro", uno::Any(sTmp) );
+ sTmp = m_pFFData->getExitMacro();
+ if ( !sTmp.isEmpty() )
+ xNameCont->insertByName( "ExitMacro", uno::Any(sTmp) );
+
+ sTmp = m_pFFData->getHelpText();
+ if ( !sTmp.isEmpty() )
+ xNameCont->insertByName( "Help", uno::Any(sTmp) );
+
+ sTmp = m_pFFData->getStatusText();
+ if ( !sTmp.isEmpty() )
+ xNameCont->insertByName( "Hint", uno::Any(sTmp) );
+
+ if (m_pImpl->m_eFieldId == FIELD_FORMTEXT )
+ {
+ sTmp = m_pFFData->getName();
+ try
+ {
+ if ( !sTmp.isEmpty() )
+ xNamed->setName( sTmp );
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("writerfilter", "Set Formfield name failed");
+ }
+
+ sTmp = m_pFFData->getTextType();
+ if ( !sTmp.isEmpty() )
+ xNameCont->insertByName( "Type", uno::Any(sTmp) );
+
+ const sal_uInt16 nMaxLength = m_pFFData->getTextMaxLength();
+ if ( nMaxLength )
+ {
+ xNameCont->insertByName( "MaxLength", uno::Any(nMaxLength) );
+ }
+
+ sTmp = m_pFFData->getTextDefault();
+ if ( !sTmp.isEmpty() )
+ xNameCont->insertByName( "Content", uno::Any(sTmp) );
+
+ sTmp = m_pFFData->getTextFormat();
+ if ( !sTmp.isEmpty() )
+ xNameCont->insertByName( "Format", uno::Any(sTmp) );
+ }
+ else if (m_pImpl->m_eFieldId == FIELD_FORMCHECKBOX )
+ {
+ uno::Reference<beans::XPropertySet> xPropSet(xFormField, uno::UNO_QUERY);
+ uno::Any aAny;
+ aAny <<= m_pFFData->getCheckboxChecked();
+ if ( xPropSet.is() )
+ xPropSet->setPropertyValue("Checked", aAny);
+ }
+ else if (m_pImpl->m_eFieldId == FIELD_FORMDROPDOWN )
+ {
+ const FFDataHandler::DropDownEntries_t& rEntries = m_pFFData->getDropDownEntries();
+ if (!rEntries.empty())
+ {
+ if ( xNameCont->hasByName(ODF_FORMDROPDOWN_LISTENTRY) )
+ xNameCont->replaceByName(ODF_FORMDROPDOWN_LISTENTRY, uno::Any(comphelper::containerToSequence(rEntries)));
+ else
+ xNameCont->insertByName(ODF_FORMDROPDOWN_LISTENTRY, uno::Any(comphelper::containerToSequence(rEntries)));
+
+ sal_Int32 nResult = m_pFFData->getDropDownResult().toInt32();
+ // 0 is valid, but also how toInt32 reports parse error, but it's a sensible default...
+ if (0 <= nResult && o3tl::make_unsigned(nResult) < rEntries.size())
+ {
+ if ( xNameCont->hasByName(ODF_FORMDROPDOWN_RESULT) )
+ xNameCont->replaceByName(ODF_FORMDROPDOWN_RESULT, uno::Any( nResult ) );
+ else
+ xNameCont->insertByName(ODF_FORMDROPDOWN_RESULT, uno::Any( nResult ) );
+ }
+ }
+ }
+}
+
+void FormControlHelper::insertControl(uno::Reference<text::XTextRange> const& xTextRange)
+{
+ bool bCreated = false;
+ if ( !m_pFFData )
+ return;
+ uno::Reference<container::XNameContainer> xFormCompsByName(m_pImpl->getForm(), uno::UNO_QUERY);
+ uno::Reference<container::XIndexContainer> xFormComps(m_pImpl->getFormComps());
+ if (! xFormComps.is())
+ return;
+
+ sal_Int32 nControl = 0;
+ bool bDone = false;
+ OUString sControlName;
+
+ do
+ {
+ OUString sTmp = "Control" + OUString::number(nControl);
+
+ nControl++;
+ if (! xFormCompsByName->hasByName(sTmp))
+ {
+ sControlName = sTmp;
+ bDone = true;
+ }
+ }
+ while (! bDone);
+
+ switch (m_pImpl->m_eFieldId)
+ {
+ case FIELD_FORMCHECKBOX:
+ bCreated = createCheckbox(xTextRange, sControlName);
+ break;
+ default:
+ break;
+ }
+
+ if (!bCreated)
+ return;
+
+ uno::Any aAny(m_pImpl->rFormComponent);
+ xFormComps->insertByIndex(xFormComps->getCount(), aAny);
+
+ if (! m_pImpl->getServiceFactory().is())
+ return;
+
+ uno::Reference<uno::XInterface> xInterface = m_pImpl->getServiceFactory()->createInstance("com.sun.star.drawing.ControlShape");
+
+ if (! xInterface.is())
+ return;
+
+ uno::Reference<drawing::XShape> xShape(xInterface, uno::UNO_QUERY);
+
+ if (! xShape.is())
+ return;
+
+ xShape->setSize(m_pImpl->aSize);
+
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+
+ sal_uInt16 nTmp = sal_uInt16(text::TextContentAnchorType_AS_CHARACTER);
+ xShapeProps->setPropertyValue("AnchorType", uno::Any(sal_uInt16(nTmp)));
+
+ nTmp = text::VertOrientation::CENTER;
+ xShapeProps->setPropertyValue("VertOrient", uno::Any(sal_uInt16(nTmp)));
+
+ xShapeProps->setPropertyValue("TextRange", uno::Any(xTextRange));
+
+ uno::Reference<drawing::XControlShape> xControlShape(xShape, uno::UNO_QUERY);
+ uno::Reference<awt::XControlModel> xControlModel(m_pImpl->rFormComponent, uno::UNO_QUERY);
+ xControlShape->setControl(xControlModel);
+
+ m_pImpl->getDrawPage()->add(xShape);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/FormControlHelper.hxx b/writerfilter/source/dmapper/FormControlHelper.hxx
new file mode 100644
index 000000000..b1f262024
--- /dev/null
+++ b/writerfilter/source/dmapper/FormControlHelper.hxx
@@ -0,0 +1,52 @@
+/* -*- 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 "FFDataHandler.hxx"
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XFormField.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include "FieldTypes.hxx"
+
+namespace writerfilter::dmapper
+{
+class FormControlHelper : public virtual SvRefBase
+{
+public:
+ typedef tools::SvRef<FormControlHelper> Pointer_t;
+ FormControlHelper(FieldId eFieldId,
+ css::uno::Reference<css::text::XTextDocument> const& rTextDocument,
+ FFDataHandler::Pointer_t const& pFFData);
+ ~FormControlHelper() override;
+
+ void insertControl(css::uno::Reference<css::text::XTextRange> const& xTextRange);
+ void processField(css::uno::Reference<css::text::XFormField> const& xFormField);
+ bool hasFFDataHandler() const { return (m_pFFData != nullptr); }
+
+private:
+ FFDataHandler::Pointer_t m_pFFData;
+ struct FormControlHelper_Impl;
+ tools::SvRef<FormControlHelper_Impl> m_pImpl;
+
+ bool createCheckbox(css::uno::Reference<css::text::XTextRange> const& xTextRange,
+ const OUString& rControlName);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/GraphicHelpers.cxx b/writerfilter/source/dmapper/GraphicHelpers.cxx
new file mode 100644
index 000000000..d4fc4b9a8
--- /dev/null
+++ b/writerfilter/source/dmapper/GraphicHelpers.cxx
@@ -0,0 +1,348 @@
+/* -*- 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 "GraphicHelpers.hxx"
+#include "TagLogger.hxx"
+#include <dmapper/GraphicZOrderHelper.hxx>
+#include "PropertyIds.hxx"
+
+#include <ooxml/resourceids.hxx>
+
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+
+#include <oox/drawingml/drawingmltypes.hxx>
+#include <sal/log.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <tools/diagnose_ex.h>
+
+#include <iostream>
+
+namespace writerfilter::dmapper {
+
+using namespace com::sun::star;
+
+PositionHandler::PositionHandler( std::pair<OUString, OUString>& rPositionOffsets, std::pair<OUString, OUString>& rAligns ) :
+LoggedProperties("PositionHandler"),
+m_nOrient(text::VertOrientation::NONE),
+m_nRelation(text::RelOrientation::FRAME),
+m_nPosition(0),
+m_rPositionOffsets(rPositionOffsets),
+m_rAligns(rAligns)
+{
+}
+
+PositionHandler::~PositionHandler( )
+{
+}
+
+void PositionHandler::lcl_attribute( Id aName, Value& rVal )
+{
+ sal_Int32 nIntValue = rVal.getInt( );
+ switch ( aName )
+ {
+ case NS_ooxml::LN_CT_PosV_relativeFrom:
+ {
+ switch ( nIntValue )
+ {
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_margin:
+ m_nRelation = text::RelOrientation::PAGE_PRINT_AREA;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_page:
+ m_nRelation = text::RelOrientation::PAGE_FRAME;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_topMargin:
+ m_nRelation = text::RelOrientation::PAGE_PRINT_AREA_TOP;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_bottomMargin:
+ m_nRelation = text::RelOrientation::PAGE_PRINT_AREA_BOTTOM;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_paragraph:
+ m_nRelation = text::RelOrientation::FRAME;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_line:
+ m_nRelation = text::RelOrientation::TEXT_LINE;
+ break;
+
+ // TODO There are some other unhandled values
+ default:
+ SAL_WARN("writerfilter", "unhandled case (" << nIntValue << ") in NS_ooxml::LN_CT_PosV_relativeFrom");
+ }
+ }
+ break;
+
+ case NS_ooxml::LN_CT_PosH_relativeFrom:
+ {
+ switch ( nIntValue )
+ {
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_margin:
+ m_nRelation = text::RelOrientation::PAGE_PRINT_AREA;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_page:
+ m_nRelation = text::RelOrientation::PAGE_FRAME;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_insideMargin:
+ m_nRelation = text::RelOrientation::PAGE_FRAME;
+ m_bPageToggle = true;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_column:
+ m_nRelation = text::RelOrientation::FRAME;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_character:
+ m_nRelation = text::RelOrientation::CHAR;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_leftMargin:
+ m_nRelation = text::RelOrientation::PAGE_LEFT;
+ break;
+
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_rightMargin:
+ m_nRelation = text::RelOrientation::PAGE_RIGHT;
+ break;
+
+ // TODO There are some other unhandled values
+ default:
+ SAL_WARN("writerfilter", "unhandled case (" << nIntValue << ") in NS_ooxml::LN_CT_PosH_relativeFrom");
+ }
+ }
+ break;
+ default:
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ break;
+ }
+}
+
+void PositionHandler::lcl_sprm(Sprm& rSprm)
+{
+ sal_uInt32 nSprmId = rSprm.getId();
+
+ switch (nSprmId)
+ {
+ case NS_ooxml::LN_CT_PosH_posOffset:
+ m_nPosition = oox::drawingml::convertEmuToHmm(m_rPositionOffsets.first.toInt32());
+ m_rPositionOffsets.first.clear();
+ break;
+ case NS_ooxml::LN_CT_PosV_posOffset:
+ m_nPosition = oox::drawingml::convertEmuToHmm(m_rPositionOffsets.second.toInt32());
+ m_rPositionOffsets.second.clear();
+ break;
+ case NS_ooxml::LN_CT_PosH_align:
+ {
+ OUString& rAlign = m_rAligns.first;
+ if (rAlign == "left")
+ m_nOrient = text::HoriOrientation::LEFT;
+ else if (rAlign == "right")
+ m_nOrient = text::HoriOrientation::RIGHT;
+ else if (rAlign == "center")
+ m_nOrient = text::HoriOrientation::CENTER;
+ else if (rAlign == "inside")
+ m_nOrient = text::HoriOrientation::INSIDE;
+ else if (rAlign == "outside")
+ m_nOrient = text::HoriOrientation::OUTSIDE;
+ rAlign.clear();
+ break;
+ }
+ case NS_ooxml::LN_CT_PosV_align:
+ {
+ OUString& rAlign = m_rAligns.second;
+ if (rAlign == "top")
+ m_nOrient = text::VertOrientation::TOP;
+ else if (rAlign == "bottom")
+ m_nOrient = text::VertOrientation::BOTTOM;
+ else if (rAlign == "center")
+ m_nOrient = text::VertOrientation::CENTER;
+ else if (rAlign == "inside" && m_nRelation == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)
+ m_nOrient = text::VertOrientation::TOP;
+ else if (rAlign == "outside" && m_nRelation == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)
+ m_nOrient = text::VertOrientation::BOTTOM;
+ rAlign.clear();
+ break;
+ }
+ }
+}
+
+sal_Int16 PositionHandler::orientation() const
+{
+ if( m_nRelation == text::RelOrientation::TEXT_LINE )
+ { // It appears that to 'line of text' alignment is backwards to other alignments,
+ // 'top' meaning putting on top of the line instead of having top at the line.
+ if( m_nOrient == text::VertOrientation::TOP )
+ return text::VertOrientation::BOTTOM;
+ else if( m_nOrient == text::VertOrientation::BOTTOM )
+ return text::VertOrientation::TOP;
+ }
+ return m_nOrient;
+}
+
+WrapHandler::WrapHandler( ) :
+LoggedProperties("WrapHandler"),
+ m_nType( 0 ),
+ m_nSide( 0 )
+{
+}
+
+WrapHandler::~WrapHandler( )
+{
+}
+
+void WrapHandler::lcl_attribute( Id aName, Value& rVal )
+{
+ switch ( aName )
+ {
+ case NS_ooxml::LN_CT_Wrap_type:
+ m_nType = sal_Int32( rVal.getInt( ) );
+ break;
+ case NS_ooxml::LN_CT_Wrap_side:
+ m_nSide = sal_Int32( rVal.getInt( ) );
+ break;
+ default:;
+ }
+}
+
+void WrapHandler::lcl_sprm( Sprm& )
+{
+}
+
+text::WrapTextMode WrapHandler::getWrapMode( ) const
+{
+ // The wrap values do not map directly to our wrap mode,
+ // e.g. none in .docx actually means through in LO.
+ text::WrapTextMode nMode = text::WrapTextMode_THROUGH;
+
+ switch ( m_nType )
+ {
+ case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_square:
+ // through and tight are somewhat complicated, approximate
+ case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_tight:
+ case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_through:
+ {
+ switch ( m_nSide )
+ {
+ case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapSide_left:
+ nMode = text::WrapTextMode_LEFT;
+ break;
+ case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapSide_right:
+ nMode = text::WrapTextMode_RIGHT;
+ break;
+ default:
+ nMode = text::WrapTextMode_PARALLEL;
+ }
+ }
+ break;
+ case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_topAndBottom:
+ nMode = text::WrapTextMode_NONE;
+ break;
+ case NS_ooxml::LN_Value_vml_wordprocessingDrawing_ST_WrapType_none:
+ default:
+ nMode = text::WrapTextMode_THROUGH;
+ }
+
+ return nMode;
+}
+
+
+void GraphicZOrderHelper::addItem(uno::Reference<beans::XPropertySet> const& props, sal_Int32 const relativeHeight)
+{
+ items[ relativeHeight ] = props;
+}
+
+// The relativeHeight value in .docx is an arbitrary number, where only the relative ordering matters.
+// But in Writer, the z-order is index in 0..(numitems-1) range, so whenever a new item needs to be
+// added in the proper z-order, it is necessary to find the proper index.
+sal_Int32 GraphicZOrderHelper::findZOrder( sal_Int32 relativeHeight, bool bOldStyle )
+{
+ // std::map is iterated sorted by key
+ auto it = std::find_if(items.cbegin(), items.cend(),
+ [relativeHeight, bOldStyle](const Items::value_type& rItem) {
+ // Old-style ordering differs in what should happen when there is already an item with the same z-order:
+ // we belong under it in case of new-style, but we belong above it in case of old-style.
+ return bOldStyle ? (rItem.first > relativeHeight) : (rItem.first >= relativeHeight);
+ }
+ );
+ sal_Int32 itemZOrderOffset(0); // before the item
+ if( it == items.end()) // we're topmost
+ {
+ if( items.empty())
+ return 0;
+ --it;
+ itemZOrderOffset = 1; // after the topmost
+
+ // Check if this shape has a textbox. If so, the textbox will have its own ZOrder, so
+ // suggest a larger offset.
+ bool bTextBox = false;
+ uno::Reference<beans::XPropertySet> xShape = it->second;
+ uno::Reference<beans::XPropertySetInfo> xInfo = xShape->getPropertySetInfo();
+ if (xInfo->hasPropertyByName("TextBox"))
+ {
+ xShape->getPropertyValue("TextBox") >>= bTextBox;
+ }
+ if (bTextBox)
+ {
+ ++itemZOrderOffset;
+ }
+ }
+ // SwXFrame::getPropertyValue throws uno::RuntimeException
+ // when its GetFrameFormat() returns nullptr
+ try {
+ sal_Int32 itemZOrder(0);
+ if( it->second->getPropertyValue(getPropertyName( PROP_Z_ORDER )) >>= itemZOrder )
+ return itemZOrder + itemZOrderOffset;
+ }
+ catch (const uno::RuntimeException&) {
+ TOOLS_WARN_EXCEPTION("writerfilter", "Exception when getting item z-order");
+ }
+ SAL_WARN( "writerfilter", "findZOrder() didn't find item z-order" );
+ return 0; // this should not(?) happen
+}
+
+GraphicNamingHelper::GraphicNamingHelper()
+ : m_nCounter(0)
+{
+}
+
+OUString GraphicNamingHelper::NameGraphic(const OUString& rTemplate)
+{
+ OUString aRet = rTemplate;
+
+ if (aRet.isEmpty())
+ {
+ // Empty template: then auto-generate a unique name.
+ OUString aPrefix(SvxResId(STR_ObjNameSingulGRAF));
+ aRet += aPrefix + OUString::number(++m_nCounter);
+ }
+
+ return aRet;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/GraphicHelpers.hxx b/writerfilter/source/dmapper/GraphicHelpers.hxx
new file mode 100644
index 000000000..4d9c46394
--- /dev/null
+++ b/writerfilter/source/dmapper/GraphicHelpers.hxx
@@ -0,0 +1,79 @@
+/* -*- 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 "LoggedResources.hxx"
+#include <com/sun/star/text/WrapTextMode.hpp>
+
+#include <utility>
+
+namespace writerfilter::dmapper
+{
+class PositionHandler : public LoggedProperties
+{
+public:
+ PositionHandler(std::pair<OUString, OUString>& rPositionOffsets,
+ std::pair<OUString, OUString>& rAligns);
+ virtual ~PositionHandler() override;
+ sal_Int16 orientation() const;
+ sal_Int16 relation() const { return m_nRelation; }
+ sal_Int32 position() const { return m_nPosition; }
+ bool GetPageToggle() const { return m_bPageToggle; }
+
+private:
+ virtual void lcl_attribute(Id aName, Value& rVal) override;
+ virtual void lcl_sprm(Sprm& rSprm) override;
+ sal_Int16 m_nOrient;
+ sal_Int16 m_nRelation;
+ sal_Int32 m_nPosition;
+ std::pair<OUString, OUString>& m_rPositionOffsets;
+ std::pair<OUString, OUString>& m_rAligns;
+ bool m_bPageToggle = false;
+};
+
+class WrapHandler : public LoggedProperties
+{
+public:
+ WrapHandler();
+ virtual ~WrapHandler() override;
+
+ css::text::WrapTextMode getWrapMode() const;
+
+private:
+ virtual void lcl_attribute(Id aName, Value& rVal) override;
+ virtual void lcl_sprm(Sprm& rSprm) override;
+
+ sal_Int32 m_nType;
+ sal_Int32 m_nSide;
+};
+
+/// Keeps track of the next available unique automatic name.
+class GraphicNamingHelper
+{
+ int m_nCounter;
+
+public:
+ GraphicNamingHelper();
+ /// Name a graphic based on rTemplate.
+ OUString NameGraphic(const OUString& rTemplate);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx
new file mode 100644
index 000000000..88a6be9ac
--- /dev/null
+++ b/writerfilter/source/dmapper/GraphicImport.cxx
@@ -0,0 +1,2029 @@
+/* -*- 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 <string.h>
+
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/ColorMode.hpp>
+#include <com/sun/star/drawing/PointSequenceSequence.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/graphic/XGraphicProvider.hpp>
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/text/GraphicCrop.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/table/ShadowFormat.hpp>
+
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/unoapi.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <oox/drawingml/drawingmltypes.hxx>
+
+#include "DomainMapper.hxx"
+#include <dmapper/GraphicZOrderHelper.hxx>
+#include <ooxml/resourceids.hxx>
+
+#include "ConversionHelper.hxx"
+#include "GraphicHelpers.hxx"
+#include "GraphicImport.hxx"
+#include "PropertyMap.hxx"
+#include "TagLogger.hxx"
+#include "WrapPolygonHandler.hxx"
+#include "util.hxx"
+
+#include <comphelper/propertysequence.hxx>
+#include <algorithm>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <oox/export/drawingml.hxx>
+
+using namespace css;
+
+namespace
+{
+bool isTopGroupObj(const uno::Reference<drawing::XShape>& xShape)
+{
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape(xShape);
+ if (!pObject)
+ return false;
+
+ if (pObject->getParentSdrObjectFromSdrObject())
+ return false;
+
+ return pObject->IsGroupObject();
+}
+}
+
+namespace writerfilter::dmapper
+{
+
+namespace {
+
+class XInputStreamHelper : public cppu::WeakImplHelper<io::XInputStream>
+{
+ const sal_uInt8* m_pBuffer;
+ const sal_Int32 m_nLength;
+ sal_Int32 m_nPosition;
+public:
+ XInputStreamHelper(const sal_uInt8* buf, size_t len);
+
+ virtual ::sal_Int32 SAL_CALL readBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead ) override;
+ virtual ::sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( ::sal_Int32 nBytesToSkip ) override;
+ virtual ::sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+};
+
+}
+
+XInputStreamHelper::XInputStreamHelper(const sal_uInt8* buf, size_t len) :
+ m_pBuffer( buf ),
+ m_nLength( len ),
+ m_nPosition( 0 )
+{
+}
+
+sal_Int32 XInputStreamHelper::readBytes( uno::Sequence<sal_Int8>& aData, sal_Int32 nBytesToRead )
+{
+ return readSomeBytes( aData, nBytesToRead );
+}
+
+sal_Int32 XInputStreamHelper::readSomeBytes( uno::Sequence<sal_Int8>& aData, sal_Int32 nMaxBytesToRead )
+{
+ sal_Int32 nRet = 0;
+ if( nMaxBytesToRead > 0 )
+ {
+ if( nMaxBytesToRead > m_nLength - m_nPosition )
+ nRet = m_nLength - m_nPosition;
+ else
+ nRet = nMaxBytesToRead;
+ aData.realloc( nRet );
+ sal_Int8* pData = aData.getArray();
+ if( nRet )
+ {
+ memcpy( pData, m_pBuffer + m_nPosition, nRet );
+ m_nPosition += nRet;
+ }
+ }
+ return nRet;
+}
+
+
+void XInputStreamHelper::skipBytes( sal_Int32 nBytesToSkip )
+{
+ if( nBytesToSkip < 0 || m_nPosition + nBytesToSkip > m_nLength)
+ throw io::BufferSizeExceededException();
+ m_nPosition += nBytesToSkip;
+}
+
+
+sal_Int32 XInputStreamHelper::available( )
+{
+ return m_nLength - m_nPosition;
+}
+
+
+void XInputStreamHelper::closeInput( )
+{
+}
+
+namespace {
+
+struct GraphicBorderLine
+{
+ sal_Int32 nLineWidth;
+ bool bHasShadow;
+
+ GraphicBorderLine() :
+ nLineWidth(0)
+ ,bHasShadow(false)
+ {}
+
+ bool isEmpty() const
+ {
+ return nLineWidth == 0 && !bHasShadow;
+ }
+
+};
+
+}
+
+class GraphicImport_Impl
+{
+private:
+ sal_Int32 nXSize;
+ bool bXSizeValid;
+ sal_Int32 nYSize;
+ bool bYSizeValid;
+
+public:
+ GraphicImportType eGraphicImportType;
+ DomainMapper& rDomainMapper;
+
+ sal_Int32 nLeftPosition;
+ sal_Int32 nTopPosition;
+
+ bool bUseSimplePos;
+ sal_Int32 zOrder;
+
+ sal_Int16 nHoriOrient;
+ sal_Int16 nHoriRelation;
+ bool bPageToggle = false;
+ sal_Int16 nVertOrient;
+ sal_Int16 nVertRelation;
+ text::WrapTextMode nWrap;
+ bool bLayoutInCell;
+ bool bCompatForcedLayoutInCell;
+ bool bAllowOverlap = true;
+ bool bOpaque;
+ bool bBehindDoc;
+ bool bContour;
+ bool bContourOutside;
+ WrapPolygon::Pointer_t mpWrapPolygon;
+
+ sal_Int32 nLeftMargin;
+ sal_Int32 nLeftMarginOrig = 0;
+ sal_Int32 nRightMargin;
+ sal_Int32 nTopMargin;
+ sal_Int32 nBottomMargin;
+
+ bool bShadow;
+ sal_Int32 nShadowXDistance;
+ sal_Int32 nShadowYDistance;
+ sal_Int32 nShadowColor;
+ sal_Int32 nShadowTransparence;
+
+ sal_Int32 nContrast;
+ sal_Int32 nBrightness;
+
+ static constexpr sal_Int32 nFillColor = 0xffffffff;
+
+ drawing::ColorMode eColorMode;
+
+ GraphicBorderLine aBorders[4];
+
+ bool bIsGraphic;
+
+ bool bSizeProtected;
+ bool bPositionProtected;
+ bool bHidden;
+
+ sal_Int32 nShapeOptionType;
+
+ OUString sName;
+ OUString sAlternativeText;
+ OUString title;
+ OUString sHyperlinkURL;
+ std::pair<OUString, OUString>& m_rPositionOffsets;
+ std::pair<OUString, OUString>& m_rAligns;
+ std::queue<OUString>& m_rPositivePercentages;
+ OUString sAnchorId;
+ comphelper::SequenceAsHashMap m_aInteropGrabBag;
+ std::optional<sal_Int32> m_oEffectExtentLeft;
+ std::optional<sal_Int32> m_oEffectExtentTop;
+ std::optional<sal_Int32> m_oEffectExtentRight;
+ std::optional<sal_Int32> m_oEffectExtentBottom;
+
+ GraphicImport_Impl(GraphicImportType eImportType, DomainMapper& rDMapper, std::pair<OUString, OUString>& rPositionOffsets, std::pair<OUString, OUString>& rAligns, std::queue<OUString>& rPositivePercentages) :
+ nXSize(0)
+ ,bXSizeValid(false)
+ ,nYSize(0)
+ ,bYSizeValid(false)
+ ,eGraphicImportType( eImportType )
+ ,rDomainMapper( rDMapper )
+ ,nLeftPosition(0)
+ ,nTopPosition(0)
+ ,bUseSimplePos(false)
+ ,zOrder(-1)
+ ,nHoriOrient( text::HoriOrientation::NONE )
+ ,nHoriRelation( text::RelOrientation::FRAME )
+ ,nVertOrient( text::VertOrientation::NONE )
+ ,nVertRelation( text::RelOrientation::FRAME )
+ ,nWrap(text::WrapTextMode_NONE)
+ ,bLayoutInCell(true)
+ ,bCompatForcedLayoutInCell(false)
+ ,bOpaque( !rDMapper.IsInHeaderFooter() )
+ ,bBehindDoc(false)
+ ,bContour(false)
+ ,bContourOutside(true)
+ ,nLeftMargin(319)
+ ,nRightMargin(319)
+ ,nTopMargin(0)
+ ,nBottomMargin(0)
+ ,bShadow(false)
+ ,nShadowXDistance(0)
+ ,nShadowYDistance(0)
+ ,nShadowColor(0)
+ ,nShadowTransparence(0)
+ ,nContrast(0)
+ ,nBrightness(0)
+ ,eColorMode( drawing::ColorMode_STANDARD )
+ ,bIsGraphic(false)
+ ,bSizeProtected(false)
+ ,bPositionProtected(false)
+ ,bHidden(false)
+ ,nShapeOptionType(0)
+ ,m_rPositionOffsets(rPositionOffsets)
+ ,m_rAligns(rAligns)
+ ,m_rPositivePercentages(rPositivePercentages)
+ {
+ if (eGraphicImportType == GraphicImportType::IMPORT_AS_DETECTED_INLINE
+ && !rDMapper.IsInShape())
+ {
+ zOrder = 0;
+ }
+ }
+
+ void setXSize(sal_Int32 _nXSize)
+ {
+ nXSize = _nXSize;
+ bXSizeValid = true;
+ }
+
+ sal_uInt32 getXSize() const
+ {
+ return nXSize;
+ }
+
+ bool isXSizeValid() const
+ {
+ return bXSizeValid;
+ }
+
+ void setYSize(sal_Int32 _nYSize)
+ {
+ nYSize = _nYSize;
+ bYSizeValid = true;
+ }
+
+ sal_uInt32 getYSize() const
+ {
+ return nYSize;
+ }
+
+ bool isYSizeValid() const
+ {
+ return bYSizeValid;
+ }
+
+ void applyMargins(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties) const
+ {
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ), uno::Any(nLeftMargin));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ), uno::Any(nRightMargin));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ), uno::Any(nTopMargin));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ), uno::Any(nBottomMargin));
+ }
+
+ void applyPosition(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties) const
+ {
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT ),
+ uno::Any(nHoriOrient));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT ),
+ uno::Any(nVertOrient));
+ }
+
+ void applyRelativePosition(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties, bool bRelativeOnly = false) const
+ {
+ if (!bRelativeOnly)
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT_POSITION),
+ uno::Any(nLeftPosition));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT_RELATION ),
+ uno::Any(nHoriRelation));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_PAGE_TOGGLE),
+ uno::Any(bPageToggle));
+ if (!bRelativeOnly)
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT_POSITION),
+ uno::Any(nTopPosition));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT_RELATION ),
+ uno::Any(nVertRelation));
+ }
+
+ void applyZOrder(uno::Reference<beans::XPropertySet> const & xGraphicObjectProperties) const
+ {
+ if (zOrder >= 0)
+ {
+ // tdf#120760 Send objects with behinddoc=true to the back.
+ sal_Int32 nZOrder = zOrder;
+ if (bBehindDoc && rDomainMapper.IsInHeaderFooter())
+ nZOrder -= SAL_MAX_INT32;
+ GraphicZOrderHelper* pZOrderHelper = rDomainMapper.graphicZOrderHelper();
+ bool bOldStyle = eGraphicImportType == GraphicImportType::IMPORT_AS_DETECTED_INLINE;
+ xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_Z_ORDER),
+ uno::Any(pZOrderHelper->findZOrder(nZOrder, bOldStyle)));
+ pZOrderHelper->addItem(xGraphicObjectProperties, nZOrder);
+ }
+ }
+
+ void applyName(uno::Reference<beans::XPropertySet> const & xGraphicObjectProperties) const
+ {
+ try
+ {
+ if (!sName.isEmpty())
+ {
+ uno::Reference<container::XNamed> const xNamed(xGraphicObjectProperties, uno::UNO_QUERY_THROW);
+ xNamed->setName(sName);
+ }
+ // else: name is automatically generated by SwDoc::MakeFlySection_()
+
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_DESCRIPTION ),
+ uno::Any( sAlternativeText ));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_TITLE ),
+ uno::Any( title ));
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter", "failed");
+ }
+ }
+
+ void applyHyperlink(uno::Reference<beans::XPropertySet> const & xShapeProps, bool bIsShape)
+ {
+ // Graphic objects have a different hyperlink prop than shapes
+ auto aHyperlinkProp = bIsShape ? PROP_HYPERLINK : PROP_HYPER_LINK_U_R_L;
+ if (!sHyperlinkURL.isEmpty())
+ {
+ xShapeProps->setPropertyValue(
+ getPropertyName(aHyperlinkProp), uno::Any(sHyperlinkURL));
+ }
+ }
+
+ /// Getter for m_aInteropGrabBag, but also merges in the values from other members if they are set.
+ comphelper::SequenceAsHashMap const & getInteropGrabBag()
+ {
+ comphelper::SequenceAsHashMap aEffectExtent;
+ if (m_oEffectExtentLeft)
+ aEffectExtent["l"] <<= *m_oEffectExtentLeft;
+ if (m_oEffectExtentTop)
+ aEffectExtent["t"] <<= *m_oEffectExtentTop;
+ if (m_oEffectExtentRight)
+ aEffectExtent["r"] <<= *m_oEffectExtentRight;
+ if (m_oEffectExtentBottom)
+ aEffectExtent["b"] <<= *m_oEffectExtentBottom;
+ if (!aEffectExtent.empty())
+ m_aInteropGrabBag["CT_EffectExtent"] <<= aEffectExtent.getAsConstPropertyValueList();
+ return m_aInteropGrabBag;
+ }
+};
+
+GraphicImport::GraphicImport(uno::Reference<uno::XComponentContext> const& xComponentContext,
+ uno::Reference<lang::XMultiServiceFactory> const& xTextFactory,
+ DomainMapper& rDMapper,
+ GraphicImportType eImportType,
+ std::pair<OUString, OUString>& rPositionOffsets,
+ std::pair<OUString, OUString>& rAligns,
+ std::queue<OUString>& rPositivePercentages)
+: LoggedProperties("GraphicImport")
+, LoggedTable("GraphicImport")
+, LoggedStream("GraphicImport")
+, m_pImpl(new GraphicImport_Impl(eImportType, rDMapper, rPositionOffsets, rAligns, rPositivePercentages))
+, m_xComponentContext(xComponentContext)
+, m_xTextFactory(xTextFactory)
+{
+}
+
+GraphicImport::~GraphicImport()
+{
+}
+
+com::sun::star::awt::Point GraphicImport::GetGraphicObjectPosition() const
+{
+ return (com::sun::star::awt::Point(m_pImpl->nLeftPosition, m_pImpl->nTopPosition));
+}
+
+bool GraphicImport::GetLayoutInCell() const
+{
+ return m_pImpl->bLayoutInCell;
+}
+
+void GraphicImport::handleWrapTextValue(sal_uInt32 nVal)
+{
+ switch (nVal)
+ {
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_bothSides: // 90920;
+ m_pImpl->nWrap = text::WrapTextMode_PARALLEL;
+ break;
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_left: // 90921;
+ m_pImpl->nWrap = text::WrapTextMode_LEFT;
+ break;
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_right: // 90922;
+ m_pImpl->nWrap = text::WrapTextMode_RIGHT;
+ break;
+ case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_largest: // 90923;
+ m_pImpl->nWrap = text::WrapTextMode_DYNAMIC;
+ break;
+ default:;
+ }
+}
+
+void GraphicImport::putPropertyToFrameGrabBag( const OUString& sPropertyName, const uno::Any& aPropertyValue )
+{
+ beans::PropertyValue aProperty;
+ aProperty.Name = sPropertyName;
+ aProperty.Value = aPropertyValue;
+
+ if (!m_xShape.is())
+ return;
+
+ uno::Reference< beans::XPropertySet > xSet(m_xShape, uno::UNO_QUERY_THROW);
+
+ uno::Reference< beans::XPropertySetInfo > xSetInfo(xSet->getPropertySetInfo());
+ if (!xSetInfo.is())
+ return;
+
+ OUString aGrabBagPropName;
+ uno::Reference<lang::XServiceInfo> xServiceInfo(m_xShape, uno::UNO_QUERY_THROW);
+ if (xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
+ aGrabBagPropName = "FrameInteropGrabBag";
+ else
+ aGrabBagPropName = "InteropGrabBag";
+
+ if (xSetInfo->hasPropertyByName(aGrabBagPropName))
+ {
+ //Add pProperty to the end of the Sequence for aGrabBagPropName
+ uno::Sequence<beans::PropertyValue> aTmp;
+ xSet->getPropertyValue(aGrabBagPropName) >>= aTmp;
+ std::vector<beans::PropertyValue> aGrabBag(comphelper::sequenceToContainer<std::vector<beans::PropertyValue> >(aTmp));
+ aGrabBag.push_back(aProperty);
+
+ xSet->setPropertyValue(aGrabBagPropName, uno::Any(comphelper::containerToSequence(aGrabBag)));
+ }
+}
+
+static bool lcl_bHasGroupSlantedChild(const SdrObject* pObj)
+{
+ // Returns true, if a child object differs more than 0.02deg from horizontal or vertical.
+ // Because lines sometimes are imported as customshapes, a horizontal or vertical line
+ // might not have exactly 0, 90, 180, or 270 degree as rotate angle.
+ if (!pObj)
+ return false;
+ if (!pObj->IsGroupObject())
+ return false;
+ SdrObjList* pSubList = pObj->GetSubList();
+ if (!pSubList)
+ return false;
+ SdrObjListIter aIterator(pSubList, SdrIterMode::DeepNoGroups);
+ while (aIterator.IsMore())
+ {
+ const SdrObject* pSubObj = aIterator.Next();
+ const Degree100 nRotateAngle = NormAngle36000(pSubObj->GetRotateAngle());
+ const sal_uInt16 nRot = nRotateAngle.get();
+ if ((3 < nRot && nRot < 8997) || (9003 < nRot && nRot < 17997)
+ || (18003 < nRot && nRot < 26997) || (27003 < nRot && nRot < 35997))
+ return true;
+ }
+ return false;
+}
+
+void GraphicImport::lcl_correctWord2007EffectExtent(const sal_Int32 nMSOAngle)
+{
+ // Word versions older than 14 do not swap width and height (see lcl_doMSOWidthHeightSwap)
+ // and therefore generate different effectExtent. We correct them here.
+ sal_Int16 nAngleDeg = (nMSOAngle / 60000) % 180;
+ if (nAngleDeg < 45 || nAngleDeg >= 135)
+ return;
+
+ sal_Int32 nDiff = o3tl::convert(
+ (double(m_pImpl->getXSize()) - double(m_pImpl->getYSize())) / 2.0,
+ o3tl::Length::mm100, o3tl::Length::emu);
+ if (m_pImpl->m_oEffectExtentLeft)
+ *m_pImpl->m_oEffectExtentLeft += nDiff;
+ if (m_pImpl->m_oEffectExtentRight)
+ *m_pImpl->m_oEffectExtentRight += nDiff;
+ if (m_pImpl->m_oEffectExtentTop)
+ *m_pImpl->m_oEffectExtentTop -= nDiff;
+ if (m_pImpl->m_oEffectExtentBottom)
+ *m_pImpl->m_oEffectExtentBottom -= nDiff;
+}
+
+static void lcl_doMSOWidthHeightSwap(awt::Point& rLeftTop, awt::Size& rSize,
+ const sal_Int32 nMSOAngle)
+{
+ if (nMSOAngle == 0)
+ return;
+ // convert nMSOAngle to degree in [0°,180°[
+ sal_Int16 nAngleDeg = (nMSOAngle / 60000) % 180;
+ if (nAngleDeg >= 45 && nAngleDeg < 135)
+ {
+ // keep center of rectangle given in rLeftTop and rSize
+ sal_Int32 aTemp = rSize.Width - rSize.Height;
+ rLeftTop.X += aTemp / 2;
+ rLeftTop.Y -= aTemp / 2;
+ std::swap(rSize.Width, rSize.Height);
+ }
+ return;
+}
+
+void GraphicImport::lcl_expandRectangleByEffectExtent(awt::Point& rLeftTop, awt::Size& rSize)
+{
+ sal_Int32 nEffectExtent = (m_pImpl->m_oEffectExtentLeft)
+ ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentLeft)
+ : 0;
+ rLeftTop.X -= nEffectExtent;
+ rSize.Width += nEffectExtent;
+ nEffectExtent = (m_pImpl->m_oEffectExtentRight)
+ ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentRight)
+ : 0;
+ rSize.Width += nEffectExtent;
+ nEffectExtent = (m_pImpl->m_oEffectExtentTop)
+ ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentTop)
+ : 0;
+ rLeftTop.Y -= nEffectExtent;
+ rSize.Height += nEffectExtent;
+ nEffectExtent = (m_pImpl->m_oEffectExtentBottom)
+ ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentBottom)
+ : 0;
+ rSize.Height += nEffectExtent;
+}
+
+void GraphicImport::lcl_attribute(Id nName, Value& rValue)
+{
+ sal_Int32 nIntValue = rValue.getInt();
+ switch( nName )
+ {
+ case NS_ooxml::LN_CT_Hyperlink_URL://90682;
+ m_pImpl->sHyperlinkURL = rValue.getString();
+ break;
+ case NS_ooxml::LN_blip: //the binary graphic data in a shape
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rValue.getProperties();
+ if( pProperties )
+ {
+ pProperties->resolve(*this);
+ }
+ }
+ break;
+ case NS_ooxml::LN_payload :
+ {
+ writerfilter::Reference<BinaryObj>::Pointer_t pPictureData = rValue.getBinary();
+ if( pPictureData )
+ pPictureData->resolve(*this);
+ }
+ break;
+
+ //border properties
+ case NS_ooxml::LN_CT_Border_sz:
+ m_pImpl->aBorders[BORDER_TOP].nLineWidth = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Border_val:
+ //graphic borders don't support different line types
+ break;
+ case NS_ooxml::LN_CT_Border_space:
+ break;
+ case NS_ooxml::LN_CT_Border_shadow:
+ m_pImpl->aBorders[BORDER_TOP].bHasShadow = nIntValue != 0;
+ break;
+ case NS_ooxml::LN_CT_Border_frame:
+ break;
+ case NS_ooxml::LN_CT_PositiveSize2D_cx:
+ case NS_ooxml::LN_CT_PositiveSize2D_cy:
+ {
+ sal_Int32 nDim = oox::drawingml::convertEmuToHmm(nIntValue);
+ // drawingML equivalent of oox::vml::ShapeType::getAbsRectangle():
+ // make sure a shape isn't hidden implicitly just because it has
+ // zero height or width.
+ if (nDim == 0)
+ nDim = 1;
+
+ if( nName == NS_ooxml::LN_CT_PositiveSize2D_cx )
+ m_pImpl->setXSize(nDim);
+ else
+ m_pImpl->setYSize(nDim);
+ }
+ break;
+ case NS_ooxml::LN_CT_EffectExtent_l:
+ m_pImpl->m_oEffectExtentLeft = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_EffectExtent_t:
+ m_pImpl->m_oEffectExtentTop = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_EffectExtent_r:
+ m_pImpl->m_oEffectExtentRight = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_EffectExtent_b:
+ m_pImpl->m_oEffectExtentBottom = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_NonVisualDrawingProps_id:// 90650;
+ //id of the object - ignored
+ break;
+ case NS_ooxml::LN_CT_NonVisualDrawingProps_name:// 90651;
+ //name of the object
+ m_pImpl->sName = rValue.getString();
+ break;
+ case NS_ooxml::LN_CT_NonVisualDrawingProps_descr:// 90652;
+ //alternative text
+ m_pImpl->sAlternativeText = rValue.getString();
+ break;
+ case NS_ooxml::LN_CT_NonVisualDrawingProps_title:
+ //alternative text
+ m_pImpl->title = rValue.getString();
+ break;
+ case NS_ooxml::LN_CT_NonVisualDrawingProps_hidden:
+ m_pImpl->bHidden = (nIntValue == 1);
+ break;
+ case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noChangeAspect://90644;
+ //disallow aspect ratio change - ignored
+ break;
+ case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noMove:// 90645;
+ m_pImpl->bPositionProtected = true;
+ break;
+ case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noResize: // 90646;
+ m_pImpl->bSizeProtected = true;
+ break;
+ case NS_ooxml::LN_CT_Anchor_distT: // 90983;
+ case NS_ooxml::LN_CT_Anchor_distB: // 90984;
+ case NS_ooxml::LN_CT_Anchor_distL: // 90985;
+ case NS_ooxml::LN_CT_Anchor_distR: // 90986;
+ {
+ m_pImpl->nShapeOptionType = nName;
+ ProcessShapeOptions(rValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_Anchor_simplePos_attr: // 90987;
+ m_pImpl->bUseSimplePos = nIntValue > 0;
+ break;
+ case NS_ooxml::LN_CT_Anchor_relativeHeight: // 90988;
+ m_pImpl->zOrder = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Anchor_behindDoc: // 90989; - in background
+ if (nIntValue > 0)
+ {
+ m_pImpl->bOpaque = false;
+ m_pImpl->bBehindDoc = true;
+ }
+ break;
+ case NS_ooxml::LN_CT_Anchor_locked: // 90990; - ignored
+ break;
+ case NS_ooxml::LN_CT_Anchor_layoutInCell: // 90991; - ignored
+ // Starting in MSO 2013, anchors are ALWAYS considered to be laid out in table cell.
+ m_pImpl->bCompatForcedLayoutInCell = !nIntValue
+ && m_pImpl->rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14
+ && m_pImpl->rDomainMapper.IsInTable();
+ m_pImpl->bLayoutInCell = m_pImpl->bCompatForcedLayoutInCell || nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Anchor_hidden: // 90992; - ignored
+ break;
+ case NS_ooxml::LN_CT_Anchor_allowOverlap:
+ m_pImpl->bAllowOverlap = nIntValue != 0;
+ break;
+ case NS_ooxml::LN_CT_Anchor_wp14_anchorId:
+ case NS_ooxml::LN_CT_Inline_wp14_anchorId:
+ {
+ OUStringBuffer aBuffer = OUString::number(nIntValue, 16);
+ OUStringBuffer aString;
+ comphelper::string::padToLength(aString, 8 - aBuffer.getLength(), '0');
+ aString.append(aBuffer.getStr());
+ m_pImpl->sAnchorId = aString.makeStringAndClear().toAsciiUpperCase();
+ }
+ break;
+ case NS_ooxml::LN_CT_Point2D_x: // 90405;
+ m_pImpl->nLeftPosition = ConversionHelper::convertTwipToMM100(nIntValue);
+ m_pImpl->nHoriRelation = text::RelOrientation::PAGE_FRAME;
+ m_pImpl->nHoriOrient = text::HoriOrientation::NONE;
+ break;
+ case NS_ooxml::LN_CT_Point2D_y: // 90406;
+ m_pImpl->nTopPosition = ConversionHelper::convertTwipToMM100(nIntValue);
+ m_pImpl->nVertRelation = text::RelOrientation::PAGE_FRAME;
+ m_pImpl->nVertOrient = text::VertOrientation::NONE;
+ break;
+ case NS_ooxml::LN_CT_WrapTight_wrapText: // 90934;
+ m_pImpl->bContour = true;
+ m_pImpl->bContourOutside = true;
+
+ handleWrapTextValue(rValue.getInt());
+
+ break;
+ case NS_ooxml::LN_CT_WrapThrough_wrapText:
+ m_pImpl->bContour = true;
+ m_pImpl->bContourOutside = false;
+
+ handleWrapTextValue(rValue.getInt());
+
+ break;
+ case NS_ooxml::LN_CT_WrapSquare_wrapText: //90928;
+ handleWrapTextValue(rValue.getInt());
+ break;
+ case NS_ooxml::LN_shape:
+ {
+ uno::Reference< drawing::XShape> xShape;
+ rValue.getAny( ) >>= xShape;
+ if ( xShape.is( ) )
+ {
+ // Is it a graphic image
+ bool bUseShape = true;
+ try
+ {
+ uno::Reference< beans::XPropertySet > xShapeProps
+ ( xShape, uno::UNO_QUERY_THROW );
+
+ uno::Reference<graphic::XGraphic> xGraphic;
+ xShapeProps->getPropertyValue("Graphic") >>= xGraphic;
+
+ sal_Int32 nRotation = 0;
+ xShapeProps->getPropertyValue("RotateAngle") >>= nRotation;
+
+ css::beans::PropertyValues aGrabBag;
+ xShapeProps->getPropertyValue("InteropGrabBag") >>= aGrabBag;
+ // if the shape contains effects in the grab bag, we should not transform it
+ // in a XTextContent so those effects can be preserved
+ bool bContainsEffects = std::any_of(std::cbegin(aGrabBag), std::cend(aGrabBag), [](const auto& rProp) {
+ return rProp.Name == "EffectProperties"
+ || rProp.Name == "3DEffectProperties"
+ || rProp.Name == "ArtisticEffectProperties";
+ });
+
+ xShapeProps->getPropertyValue("Shadow") >>= m_pImpl->bShadow;
+ if (m_pImpl->bShadow)
+ {
+ xShapeProps->getPropertyValue("ShadowXDistance") >>= m_pImpl->nShadowXDistance;
+ xShapeProps->getPropertyValue("ShadowYDistance") >>= m_pImpl->nShadowYDistance;
+ xShapeProps->getPropertyValue("ShadowColor") >>= m_pImpl->nShadowColor;
+ xShapeProps->getPropertyValue("ShadowTransparence") >>= m_pImpl->nShadowTransparence;
+ }
+
+ xShapeProps->getPropertyValue("GraphicColorMode") >>= m_pImpl->eColorMode;
+ xShapeProps->getPropertyValue("AdjustLuminance") >>= m_pImpl->nBrightness;
+ xShapeProps->getPropertyValue("AdjustContrast") >>= m_pImpl->nContrast;
+
+ // fdo#70457: transform XShape into a SwXTextGraphicObject only if there's no rotation
+ if ( nRotation == 0 && !bContainsEffects )
+ m_xGraphicObject = createGraphicObject( xGraphic, xShapeProps );
+
+ bUseShape = !m_xGraphicObject.is( );
+
+ if ( !bUseShape )
+ {
+ // Define the object size
+ uno::Reference< beans::XPropertySet > xGraphProps( m_xGraphicObject,
+ uno::UNO_QUERY );
+ awt::Size aSize = xShape->getSize( );
+ xGraphProps->setPropertyValue("Height",
+ uno::Any( aSize.Height ) );
+ xGraphProps->setPropertyValue("Width",
+ uno::Any( aSize.Width ) );
+
+ text::GraphicCrop aGraphicCrop( 0, 0, 0, 0 );
+ uno::Reference< beans::XPropertySet > xSourceGraphProps( xShape, uno::UNO_QUERY );
+ uno::Any aAny = xSourceGraphProps->getPropertyValue("GraphicCrop");
+ if(aAny >>= aGraphicCrop) {
+ xGraphProps->setPropertyValue("GraphicCrop",
+ uno::Any( aGraphicCrop ) );
+ }
+
+ // We need to drop the shape here somehow
+ uno::Reference< lang::XComponent > xShapeComponent( xShape, uno::UNO_QUERY );
+ xShapeComponent->dispose( );
+ }
+ }
+ catch( const beans::UnknownPropertyException & )
+ {
+ // It isn't a graphic image
+ }
+
+ if ( bUseShape )
+ m_xShape = xShape;
+
+ if ( m_xShape.is( ) )
+ {
+ uno::Reference< beans::XPropertySet > xShapeProps
+ (m_xShape, uno::UNO_QUERY_THROW);
+
+
+ xShapeProps->setPropertyValue
+ (getPropertyName(PROP_ANCHOR_TYPE),
+ uno::Any
+ (text::TextContentAnchorType_AS_CHARACTER));
+
+ // In Word, if a shape is anchored inline, that
+ // excludes being in the background.
+ xShapeProps->setPropertyValue("Opaque", uno::Any(true));
+
+ uno::Reference<lang::XServiceInfo> xServiceInfo(m_xShape, uno::UNO_QUERY_THROW);
+
+ // TextFrames can't be rotated. But for anything else,
+ // make sure that setting size doesn't affect rotation,
+ // that would not match Word's definition of rotation.
+ bool bKeepRotation = false;
+ if (!xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
+ {
+ bKeepRotation = true;
+ xShapeProps->setPropertyValue
+ (getPropertyName(PROP_TEXT_RANGE),
+ uno::Any
+ (m_pImpl->rDomainMapper.GetCurrentTextRange()));
+ }
+
+ awt::Size aSize(m_xShape->getSize());
+
+ // One purpose of the next part is, to set the logic rectangle of the SdrObject
+ // to nXSize and nYSize from import. That doesn't work for groups or lines,
+ // because they do not have a logic rectangle and m_xShape->getSize and
+ // m_xShape->setSize would work on the snap rectangle. In case a shape is
+ // rotated, non-uniform scaling the snap rectangle will introduce shearing on
+ // the shape. In case group or line is rotated, nXSize and nYSize contain the
+ // unrotated size from oox. The rotation is already incorporated into group
+ // children and line points. We must not scale them to unrotated size. Exclude
+ // those shapes here.
+
+ // Get MSO rotation angle. GetRotateAngle from SdrObject is not suitable
+ // here, because it returns the rotate angle of the first child for groups
+ // and slope angle for lines, even if line or group had not been rotated.
+ // Import in oox has put the rotation from oox file into InteropGrabBag.
+ comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue("InteropGrabBag"));
+ sal_Int32 nOOXAngle(0);
+ aInteropGrabBag.getValue("mso-rotation-angle") >>= nOOXAngle; // 1/60000 deg
+ // tdf#143455: A diagram is imported as group, but has no valid object list
+ // and contour wrap is different to Word. As workaround diagrams are excluded
+ // here in various places.
+ const SdrObject* pDiagramCandidate(SdrObject::getSdrObjectFromXShape(m_xShape));
+ const bool bIsDiagram(nullptr != pDiagramCandidate && pDiagramCandidate->isDiagram());
+ // tdf#143476: A lockedCanvas (Word2007) is imported as group, but has not
+ // got size and position. Values from m_Impl has to be used.
+ bool bIsLockedCanvas(false);
+ aInteropGrabBag.getValue("LockedCanvas") >>= bIsLockedCanvas;
+ const bool bIsGroupOrLine = (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape")
+ && !bIsDiagram && !bIsLockedCanvas)
+ || xServiceInfo->supportsService("com.sun.star.drawing.LineShape");
+ SdrObject* pShape = SdrObject::getSdrObjectFromXShape(m_xShape);
+ if ((bIsGroupOrLine && !lcl_bHasGroupSlantedChild(pShape) && nOOXAngle == 0)
+ || !bIsGroupOrLine)
+ {
+ if (m_pImpl->isXSizeValid())
+ aSize.Width = m_pImpl->getXSize();
+ if (m_pImpl->isYSizeValid())
+ aSize.Height = m_pImpl->getYSize();
+ }
+
+ Degree100 nRotation;
+ if (bKeepRotation)
+ {
+ // Use internal API, getPropertyValue("RotateAngle")
+ // would use GetObjectRotation(), which is not what
+ // we want.
+ if (pShape)
+ nRotation = pShape->GetRotateAngle();
+ }
+ m_xShape->setSize(aSize);
+ if (bKeepRotation)
+ {
+ xShapeProps->setPropertyValue("RotateAngle", uno::Any(nRotation.get()));
+ }
+
+ m_pImpl->bIsGraphic = true;
+
+ if (!m_pImpl->sAnchorId.isEmpty())
+ {
+ putPropertyToFrameGrabBag("AnchorId", uno::Any(m_pImpl->sAnchorId));
+ }
+
+ // Calculate mso unrotated rectangle and its center, needed below
+ awt::Size aImportSize(m_xShape->getSize()); // here only fallback
+ if (m_pImpl->isXSizeValid())
+ aImportSize.Width = m_pImpl->getXSize(); // Hmm
+ if (m_pImpl->isYSizeValid())
+ aImportSize.Height = m_pImpl->getYSize(); // Hmm
+ const awt::Point aImportPosition(GetGraphicObjectPosition()); // Hmm
+ double fCentrumX = aImportPosition.X + aImportSize.Width / 2.0;
+ double fCentrumY = aImportPosition.Y + aImportSize.Height / 2.0;
+
+ // In case of group and lines, transformations are incorporated in the child
+ // shapes or points respectively in LO. MSO has rotation as separate property.
+ // The position refers to the unrotated rectangle of MSO. We need to adapt it
+ // to the left-top of the transformed shape.
+ awt::Size aLOSize(m_xShape->getSize()); // LO snap rectangle size in Hmm
+ if (bIsGroupOrLine && !(m_pImpl->mpWrapPolygon))
+ {
+ // Set LO position. MSO rotation is done on shape center.
+ if(pShape && pShape->IsGroupObject())
+ {
+ tools::Rectangle aSnapRect = pShape->GetSnapRect(); // Twips
+ m_pImpl->nLeftPosition = ConversionHelper::convertTwipToMM100(aSnapRect.Left());
+ m_pImpl->nTopPosition = ConversionHelper::convertTwipToMM100(aSnapRect.Top());
+ aLOSize.Width = ConversionHelper::convertTwipToMM100(aSnapRect.getWidth());
+ aLOSize.Height = ConversionHelper::convertTwipToMM100(aSnapRect.getHeight());
+ }
+ else
+ {
+ m_pImpl->nLeftPosition = fCentrumX - aLOSize.Width / 2.0;
+ m_pImpl->nTopPosition = fCentrumY - aLOSize.Height / 2.0;
+ }
+ m_xShape->setPosition(GetGraphicObjectPosition());
+ }
+ // ToDo: Rotated shapes with position type "Alignment" (UI of Word) have
+ // wrong position. Word aligns the unrotated logic rectangle, LO the rotated
+ // snap rectangle.
+
+ // Margin correction
+
+ // tdf#143475: Word 2007 (vers 12) calculates effectExtent for rotated images
+ // based on the unrotated image without width-height-swap. We correct this to
+ // those values, which would be calculated if width-height-swap was used.
+ if (m_pImpl->rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() < 14
+ && xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape")
+ && nOOXAngle != 0)
+ {
+ lcl_correctWord2007EffectExtent(nOOXAngle);
+ }
+
+ if (m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_INLINE)
+ {
+ if (nOOXAngle == 0)
+ {
+ // EffectExtent contains all needed additional space, including fat
+ // stroke and shadow. Simple add it to the margins.
+ sal_Int32 nEffectExtent = (m_pImpl->m_oEffectExtentLeft)
+ ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentLeft)
+ : 0;
+ m_pImpl->nLeftMargin += nEffectExtent;
+ nEffectExtent = (m_pImpl->m_oEffectExtentRight)
+ ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentRight) : 0;
+ m_pImpl->nRightMargin += nEffectExtent;
+ nEffectExtent = (m_pImpl->m_oEffectExtentTop)
+ ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentTop) : 0;
+ m_pImpl->nTopMargin += nEffectExtent;
+ nEffectExtent = (m_pImpl->m_oEffectExtentBottom)
+ ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentBottom) : 0;
+ m_pImpl->nBottomMargin += nEffectExtent;
+ }
+ else
+ {
+ // As of June 2021 LibreOffice uses an area, which is large enough to
+ // contain the rotated snap rectangle. MSO uses a smaller area, so
+ // that the rotated snap rectangle covers text.
+ awt::Point aMSOBaseLeftTop = aImportPosition;
+ awt::Size aMSOBaseSize = aImportSize;
+ lcl_doMSOWidthHeightSwap(aMSOBaseLeftTop, aMSOBaseSize, nOOXAngle);
+ lcl_expandRectangleByEffectExtent(aMSOBaseLeftTop, aMSOBaseSize);
+
+ // Get LO SnapRect from SdrObject if possible
+ awt::Rectangle aLOSnapRect;
+ // For case we have no SdrObject, initialize with values from m_pImpl
+ aLOSnapRect.X = m_pImpl->nLeftPosition;
+ aLOSnapRect.Y = m_pImpl->nTopPosition;
+ aLOSnapRect.Width = aLOSize.Width;
+ aLOSnapRect.Height = aLOSize.Height;
+ if (pShape)
+ {
+ tools::Rectangle aSnapRect = pShape->GetSnapRect(); // Twip
+ aLOSnapRect.X = ConversionHelper::convertTwipToMM100(aSnapRect.Left());
+ aLOSnapRect.Y = ConversionHelper::convertTwipToMM100(aSnapRect.Top());
+ aLOSnapRect.Width = ConversionHelper::convertTwipToMM100(aSnapRect.getWidth());
+ aLOSnapRect.Height = ConversionHelper::convertTwipToMM100(aSnapRect.getHeight());
+ }
+
+ m_pImpl->nLeftMargin += aLOSnapRect.X - aMSOBaseLeftTop.X;
+ m_pImpl->nRightMargin += aMSOBaseLeftTop.X + aMSOBaseSize.Width
+ - (aLOSnapRect.X + aLOSnapRect.Width);
+ m_pImpl->nTopMargin += aLOSnapRect.Y - aMSOBaseLeftTop.Y;
+ m_pImpl->nBottomMargin += aMSOBaseLeftTop.Y + aMSOBaseSize.Height
+ - (aLOSnapRect.Y + aLOSnapRect.Height);
+ // tdf#141880 LibreOffice cannot handle negative vertical margins.
+ // Those cases are caught below at common place.
+ }
+ } // end IMPORT_AS_DETECTED_INLINE
+ else if ((m_pImpl->nWrap == text::WrapTextMode_PARALLEL
+ || m_pImpl->nWrap == text::WrapTextMode_DYNAMIC
+ || m_pImpl->nWrap == text::WrapTextMode_LEFT
+ || m_pImpl->nWrap == text::WrapTextMode_RIGHT
+ || m_pImpl->nWrap == text::WrapTextMode_NONE)
+ && !(m_pImpl->mpWrapPolygon) && !bIsDiagram)
+ {
+ // For wrap "Square" an area is defined around which the text wraps. MSO
+ // describes the area by a base rectangle and effectExtent. LO uses the
+ // shape bounding box and margins. We adapt the margins to get the same
+ // area as MSO.
+ awt::Point aMSOBaseLeftTop = aImportPosition;
+ awt::Size aMSOBaseSize = aImportSize;
+ lcl_doMSOWidthHeightSwap(aMSOBaseLeftTop, aMSOBaseSize, nOOXAngle);
+ lcl_expandRectangleByEffectExtent(aMSOBaseLeftTop, aMSOBaseSize);
+
+ // Get LO bound rectangle from SdrObject if possible
+ awt::Rectangle aLOBoundRect;
+ // For case we have no SdrObject, initialize with values from m_pImpl
+ aLOBoundRect.X = m_pImpl->nLeftPosition;
+ aLOBoundRect.Y = m_pImpl->nTopPosition;
+ aLOBoundRect.Width = aLOSize.Width;
+ aLOBoundRect.Height = aLOSize.Height;
+ if (pShape)
+ {
+ tools::Rectangle aBoundRect = pShape->GetCurrentBoundRect(); // Twip
+ aLOBoundRect.X = ConversionHelper::convertTwipToMM100(aBoundRect.Left());
+ aLOBoundRect.Y = ConversionHelper::convertTwipToMM100(aBoundRect.Top());
+ aLOBoundRect.Width = ConversionHelper::convertTwipToMM100(aBoundRect.getWidth());
+ aLOBoundRect.Height = ConversionHelper::convertTwipToMM100(aBoundRect.getHeight());
+ }
+
+ m_pImpl->nLeftMargin += aLOBoundRect.X - aMSOBaseLeftTop.X;
+ m_pImpl->nRightMargin += aMSOBaseLeftTop.X + aMSOBaseSize.Width
+ - (aLOBoundRect.X + aLOBoundRect.Width);
+ m_pImpl->nTopMargin += aLOBoundRect.Y - aMSOBaseLeftTop.Y;
+ m_pImpl->nBottomMargin += aMSOBaseLeftTop.Y + aMSOBaseSize.Height
+ - (aLOBoundRect.Y + aLOBoundRect.Height);
+ }
+ else if (m_pImpl->mpWrapPolygon && !bIsDiagram)
+ {
+ // Word uses a wrap polygon, LibreOffice has no explicit wrap polygon
+ // but creates the wrap contour based on the shape geometry, without
+ // stroke width and shadow, but with rotation and flip. The concepts
+ // are not compatible. We approximate Word's rendering by setting
+ // wrap margins.
+
+ // Build a range from the wrap polygon from Word.
+ const drawing::PointSequenceSequence aWrapPolygon
+ = m_pImpl->mpWrapPolygon->getPointSequenceSequence();
+ basegfx::B2DPolyPolygon aB2DWrapPolyPolygon
+ = basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(
+ aWrapPolygon);
+ // Wrap polygon values are relative to 0..21600|0..21600.
+ // Scale to shape size (in Hmm).
+ basegfx::B2DHomMatrix aMatrix = basegfx::utils::createScaleB2DHomMatrix(
+ aImportSize.Width / 21600.0, aImportSize.Height / 21600.0);
+ aB2DWrapPolyPolygon.transform(aMatrix);
+
+ // Shape geometry will be rotated, rotate wrap polygon too.
+ if (nOOXAngle != 0)
+ {
+ aMatrix = basegfx::utils::createRotateAroundPoint(
+ aImportSize.Width / 2.0, aImportSize.Height / 2.0,
+ basegfx::deg2rad<60000>(nOOXAngle));
+ aB2DWrapPolyPolygon.transform(aMatrix);
+ }
+ basegfx::B2DRange aB2DWrapRange = aB2DWrapPolyPolygon.getB2DRange();
+
+ // Build a range from shape geometry
+ basegfx::B2DRange aShapeRange;
+ if (pShape)
+ {
+ basegfx::B2DPolyPolygon aShapePolygon = pShape->TakeXorPoly(); // Twips
+ aMatrix = basegfx::utils::createScaleB2DHomMatrix(
+ o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100),
+ o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100));
+ aShapePolygon.transform(aMatrix);
+ // Wrap polygon treats left/top of shape as origin, shift shape polygon accordingly
+ aMatrix = basegfx::utils::createTranslateB2DHomMatrix(
+ -aImportPosition.X, -aImportPosition.Y);
+ aShapePolygon.transform(aMatrix);
+ aShapeRange = aShapePolygon.getB2DRange();
+ }
+ else // can this happen?
+ {
+ aShapeRange
+ = basegfx::B2DRange(0, 0, aImportSize.Width, aImportSize.Height);
+ if (nOOXAngle != 0)
+ {
+ aMatrix = basegfx::utils::createRotateB2DHomMatrix(
+ basegfx::deg2rad<60000>(nOOXAngle));
+ aShapeRange.transform(aMatrix);
+ }
+ }
+
+ // Add difference between shape and wrap range to margin and remember
+ // difference in Twips for export.
+ comphelper::SequenceAsHashMap aAnchorDistDiff;
+
+ const double fTopDiff = aShapeRange.getMinY() - aB2DWrapRange.getMinY();
+ m_pImpl->nTopMargin += basegfx::fround(fTopDiff);
+ aAnchorDistDiff["distTDiff"] <<= basegfx::fround(
+ o3tl::convert(fTopDiff, o3tl::Length::mm100, o3tl::Length::twip));
+
+ const double fBottomDiff = aB2DWrapRange.getMaxY() - aShapeRange.getMaxY();
+ m_pImpl->nBottomMargin += basegfx::fround(fBottomDiff);
+ aAnchorDistDiff["distBDiff"] <<= basegfx::fround(
+ o3tl::convert(fBottomDiff, o3tl::Length::mm100, o3tl::Length::twip));
+
+ const double fLeftDiff = aShapeRange.getMinX() - aB2DWrapRange.getMinX();
+ m_pImpl->nLeftMargin += basegfx::fround(fLeftDiff);
+ aAnchorDistDiff["distLDiff"] <<= basegfx::fround(
+ o3tl::convert(fLeftDiff, o3tl::Length::mm100, o3tl::Length::twip));
+
+ const double fRightDiff = aB2DWrapRange.getMaxX() - aShapeRange.getMaxX();
+ m_pImpl->nRightMargin += basegfx::fround(fRightDiff);
+ aAnchorDistDiff["distRDiff"] <<= basegfx::fround(
+ o3tl::convert(fRightDiff, o3tl::Length::mm100, o3tl::Length::twip));
+
+ m_pImpl->m_aInteropGrabBag["AnchorDistDiff"]
+ <<= aAnchorDistDiff.getAsConstPropertyValueList();
+
+ // FixMe: tdf#141880. LibreOffice cannot handle negative horizontal margin in contour wrap
+ if (m_pImpl->nLeftMargin < 0)
+ m_pImpl->nLeftMargin = 0;
+ if (m_pImpl->nRightMargin < 0)
+ m_pImpl->nRightMargin = 0;
+ }
+ else if (!bIsDiagram) // text::WrapTextMode_THROUGH
+ {
+ // Word writes and evaluates the effectExtent in case of position
+ // type 'Alignment' (UI). We move these values to margin to approximate
+ // Word's rendering.
+ if (m_pImpl->m_oEffectExtentLeft)
+ {
+ m_pImpl->nLeftMargin
+ += oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentLeft);
+ }
+ if (m_pImpl->m_oEffectExtentTop)
+ {
+ m_pImpl->nTopMargin
+ += oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentTop);
+ }
+ if (m_pImpl->m_oEffectExtentRight)
+ {
+ m_pImpl->nRightMargin
+ += oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentRight);
+ }
+ if (m_pImpl->m_oEffectExtentBottom)
+ {
+ m_pImpl->nBottomMargin
+ += oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentBottom);
+ }
+ }
+
+ // FixMe: tdf#141880 LibreOffice cannot handle negative vertical margins
+ // although they are allowed in ODF.
+ if (m_pImpl->nTopMargin < 0)
+ m_pImpl->nTopMargin = 0;
+ if (m_pImpl->nBottomMargin < 0)
+ m_pImpl->nBottomMargin = 0;
+ }
+
+ if (bUseShape && m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
+ {
+ // If we are here, this is a drawingML shape. For those, only dmapper (and not oox) knows the anchoring infos (just like for Writer pictures).
+ // But they aren't Writer pictures, either (which are already handled above).
+ uno::Reference< beans::XPropertySet > xShapeProps(m_xShape, uno::UNO_QUERY_THROW);
+
+ if (m_pImpl->nWrap == text::WrapTextMode_THROUGH && m_pImpl->nHoriRelation == text::RelOrientation::FRAME)
+ {
+ // text::RelOrientation::FRAME is OOXML's "column", which behaves as if
+ // layout-in-cell would be always off.
+ m_pImpl->bLayoutInCell = false;
+ }
+
+ // Anchored: Word only supports at-char in that case.
+ text::TextContentAnchorType eAnchorType = text::TextContentAnchorType_AT_CHARACTER;
+
+ if (m_pImpl->bHidden)
+ {
+ xShapeProps->setPropertyValue("Visible", uno::Any(false));
+ xShapeProps->setPropertyValue("Printable", uno::Any(false));
+ }
+
+ // Avoid setting AnchorType for TextBoxes till SwTextBoxHelper::syncProperty() doesn't handle transition.
+ bool bTextBox = false;
+ xShapeProps->getPropertyValue("TextBox") >>= bTextBox;
+
+ // The positioning change caused by LayoutInCell doesn't sync well
+ // in the text / frame duo. So the compatibility fix only correctly
+ // positions the frame and not the text currently.
+ // tdf#135943: Instead of half-fixing and making a complete mess,
+ // just avoid until layout's repositioning is sync'd to the text frame.
+ if (m_pImpl->bLayoutInCell && bTextBox)
+ m_pImpl->bLayoutInCell = !m_pImpl->bCompatForcedLayoutInCell;
+
+ xShapeProps->setPropertyValue("AnchorType", uno::Any(eAnchorType));
+
+ if (m_pImpl->nVertRelation == text::RelOrientation::TEXT_LINE)
+ {
+ // Word's "line" is "below the bottom of the line", our TEXT_LINE is
+ // "towards top, from the bottom of the line", so invert the vertical
+ // position.
+ awt::Point aPoint = xShape->getPosition();
+ aPoint.Y *= -1;
+ xShape->setPosition(aPoint);
+ }
+
+ if (m_pImpl->bLayoutInCell && bTextBox && m_pImpl->rDomainMapper.IsInTable()
+ && m_pImpl->nHoriRelation == text::RelOrientation::PAGE_FRAME)
+ m_pImpl->nHoriRelation = text::RelOrientation::FRAME;
+ if(m_pImpl->rDomainMapper.IsInTable())
+ xShapeProps->setPropertyValue(getPropertyName(PROP_FOLLOW_TEXT_FLOW),
+ uno::Any(m_pImpl->bLayoutInCell));
+ //only the position orientation is handled in applyPosition()
+ m_pImpl->applyPosition(xShapeProps);
+
+ uno::Reference<lang::XServiceInfo> xServiceInfo(m_xShape, uno::UNO_QUERY_THROW);
+ if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape") ||
+ xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
+ {
+ // You would expect that position and rotation are
+ // independent, but they are not. Till we are not
+ // there yet to handle all scaling, translation and
+ // rotation with a single transformation matrix,
+ // make sure there is no graphic rotation set when we set
+ // the position.
+ sal_Int32 nRotation = 0;
+ if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
+ {
+ xShapeProps->getPropertyValue("RotateAngle") >>= nRotation;
+ }
+ if (nRotation)
+ xShapeProps->setPropertyValue("RotateAngle", uno::Any(sal_Int32(0)));
+
+ // Position of the groupshape should be set after children have been added.
+ // Long-term we should get rid of positioning group
+ // shapes, though. Do it for top-level ones with
+ // absolute page position as a start.
+ // fdo#80555: also set position for graphic shapes here
+ if (!isTopGroupObj(m_xShape)
+ || m_pImpl->nHoriRelation != text::RelOrientation::PAGE_FRAME
+ || m_pImpl->nVertRelation != text::RelOrientation::PAGE_FRAME)
+ m_xShape->setPosition(
+ awt::Point(m_pImpl->nLeftPosition, m_pImpl->nTopPosition));
+
+ if (nRotation)
+ xShapeProps->setPropertyValue("RotateAngle", uno::Any(nRotation));
+ }
+
+
+ m_pImpl->applyRelativePosition(xShapeProps, /*bRelativeOnly=*/true);
+
+ xShapeProps->setPropertyValue("SurroundContour", uno::Any(m_pImpl->bContour));
+ xShapeProps->setPropertyValue("ContourOutside", uno::Any(m_pImpl->bContourOutside));
+ m_pImpl->applyMargins(xShapeProps);
+ xShapeProps->setPropertyValue("Opaque", uno::Any(m_pImpl->bOpaque));
+ xShapeProps->setPropertyValue("Surround", uno::Any(static_cast<sal_Int32>(m_pImpl->nWrap)));
+ m_pImpl->applyZOrder(xShapeProps);
+ m_pImpl->applyName(xShapeProps);
+ m_pImpl->applyHyperlink(xShapeProps, bUseShape);
+ xShapeProps->setPropertyValue("AllowOverlap",
+ uno::Any(m_pImpl->bAllowOverlap));
+
+ // Get the grab-bag set by oox, merge with our one and then put it back.
+ comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue("InteropGrabBag"));
+ aInteropGrabBag.update(m_pImpl->getInteropGrabBag());
+ xShapeProps->setPropertyValue("InteropGrabBag", uno::Any(aInteropGrabBag.getAsConstPropertyValueList()));
+ }
+ else if (bUseShape && m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_INLINE)
+ {
+ uno::Reference< beans::XPropertySet > xShapeProps(m_xShape, uno::UNO_QUERY_THROW);
+ m_pImpl->applyMargins(xShapeProps);
+ m_pImpl->applyZOrder(xShapeProps);
+ comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue("InteropGrabBag"));
+ aInteropGrabBag.update(m_pImpl->getInteropGrabBag());
+ xShapeProps->setPropertyValue("InteropGrabBag", uno::Any(aInteropGrabBag.getAsConstPropertyValueList()));
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Inline_distT:
+ m_pImpl->nTopMargin = 0;
+ break;
+ case NS_ooxml::LN_CT_Inline_distB:
+ m_pImpl->nBottomMargin = 0;
+ break;
+ case NS_ooxml::LN_CT_Inline_distL:
+ m_pImpl->nLeftMargin = 0;
+ break;
+ case NS_ooxml::LN_CT_Inline_distR:
+ m_pImpl->nRightMargin = 0;
+ break;
+ case NS_ooxml::LN_CT_GraphicalObjectData_uri:
+ rValue.getString();
+ //TODO: does it need to be handled?
+ break;
+ case NS_ooxml::LN_CT_SizeRelH_relativeFrom:
+ {
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_ST_SizeRelFromH_margin:
+ if (m_xShape.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RelativeWidthRelation", uno::Any(text::RelOrientation::FRAME));
+ }
+ break;
+ case NS_ooxml::LN_ST_SizeRelFromH_leftMargin:
+ case NS_ooxml::LN_ST_SizeRelFromH_outsideMargin:
+ if (m_xShape.is())
+ {
+ // Here we handle the relative size of the width of some shape.
+ // The size of the shape's width is going to be relative to the size of the left margin.
+ // E.g.: (left margin = 8 && relative size = 150%) -> width of some shape = 12.
+ uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RelativeWidthRelation", uno::Any(text::RelOrientation::PAGE_LEFT));
+ }
+ break;
+ case NS_ooxml::LN_ST_SizeRelFromH_rightMargin:
+ case NS_ooxml::LN_ST_SizeRelFromH_insideMargin:
+ if (m_xShape.is())
+ {
+ // Same as the left margin above.
+ uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RelativeWidthRelation", uno::Any(text::RelOrientation::PAGE_RIGHT));
+ }
+ break;
+ case NS_ooxml::LN_ST_SizeRelFromH_page:
+ if (m_xShape.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RelativeWidthRelation", uno::Any(text::RelOrientation::PAGE_FRAME));
+ }
+ break;
+ default:
+ SAL_WARN("writerfilter", "GraphicImport::lcl_attribute: unhandled NS_ooxml::LN_CT_SizeRelH_relativeFrom value: " << nIntValue);
+ break;
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_SizeRelV_relativeFrom:
+ {
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_ST_SizeRelFromV_margin:
+ if (m_xShape.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RelativeHeightRelation", uno::Any(text::RelOrientation::FRAME));
+ }
+ break;
+ case NS_ooxml::LN_ST_SizeRelFromV_page:
+ if (m_xShape.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RelativeHeightRelation", uno::Any(text::RelOrientation::PAGE_FRAME));
+ }
+ break;
+ case NS_ooxml::LN_ST_SizeRelFromV_topMargin:
+ if (m_xShape.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RelativeHeightRelation", uno::Any(text::RelOrientation::PAGE_PRINT_AREA));
+ }
+ break;
+ case NS_ooxml::LN_ST_SizeRelFromV_bottomMargin:
+ if (m_xShape.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("RelativeHeightRelation", uno::Any(text::RelOrientation::PAGE_PRINT_AREA_BOTTOM));
+ }
+ break;
+ default:
+ SAL_WARN("writerfilter", "GraphicImport::lcl_attribute: unhandled NS_ooxml::LN_CT_SizeRelV_relativeFrom value: " << nIntValue);
+ break;
+ }
+ }
+ break;
+ default:
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ break;
+ }
+}
+
+uno::Reference<text::XTextContent> GraphicImport::GetGraphicObject()
+{
+ uno::Reference<text::XTextContent> xResult;
+
+ if (m_xGraphicObject.is())
+ xResult = m_xGraphicObject;
+ else if (m_xShape.is())
+ {
+ xResult.set(m_xShape, uno::UNO_QUERY_THROW);
+ }
+
+ return xResult;
+}
+
+
+void GraphicImport::ProcessShapeOptions(Value const & rValue)
+{
+ sal_Int32 nIntValue = rValue.getInt();
+ switch( m_pImpl->nShapeOptionType )
+ {
+ case NS_ooxml::LN_CT_Anchor_distL:
+ m_pImpl->nLeftMargin = nIntValue / 360;
+ m_pImpl->nLeftMarginOrig = m_pImpl->nLeftMargin;
+ break;
+ case NS_ooxml::LN_CT_Anchor_distT:
+ //todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustULWrapForWordMargins()
+ m_pImpl->nTopMargin = nIntValue / 360;
+ break;
+ case NS_ooxml::LN_CT_Anchor_distR:
+ //todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustLRWrapForWordMargins()
+ m_pImpl->nRightMargin = nIntValue / 360;
+ break;
+ case NS_ooxml::LN_CT_Anchor_distB:
+ //todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustULWrapForWordMargins()
+ m_pImpl->nBottomMargin = nIntValue / 360;
+ break;
+ default:
+ OSL_FAIL( "shape option unsupported?");
+ }
+}
+
+
+void GraphicImport::lcl_sprm(Sprm& rSprm)
+{
+ sal_uInt32 nSprmId = rSprm.getId();
+
+ switch(nSprmId)
+ {
+ case NS_ooxml::LN_CT_Inline_extent: // 90911;
+ case NS_ooxml::LN_CT_Inline_effectExtent: // 90912;
+ case NS_ooxml::LN_CT_Inline_docPr: // 90913;
+ case NS_ooxml::LN_CT_Inline_cNvGraphicFramePr: // 90914;
+ case NS_ooxml::LN_CT_NonVisualGraphicFrameProperties_graphicFrameLocks:// 90657
+ case NS_ooxml::LN_CT_Inline_a_graphic:// 90915
+ case NS_ooxml::LN_CT_Anchor_simplePos_elem: // 90975;
+ case NS_ooxml::LN_CT_Anchor_extent: // 90978;
+ case NS_ooxml::LN_CT_Anchor_effectExtent: // 90979;
+ case NS_ooxml::LN_EG_WrapType_wrapSquare: // 90945;
+ case NS_ooxml::LN_EG_WrapType_wrapTight: // 90946;
+ case NS_ooxml::LN_EG_WrapType_wrapThrough:
+ case NS_ooxml::LN_CT_Anchor_docPr: // 90980;
+ case NS_ooxml::LN_CT_Anchor_cNvGraphicFramePr: // 90981;
+ case NS_ooxml::LN_CT_Anchor_a_graphic: // 90982;
+ case NS_ooxml::LN_CT_WrapPath_start: // 90924;
+ case NS_ooxml::LN_CT_WrapPath_lineTo: // 90925;
+ case NS_ooxml::LN_graphic_graphic:
+ case NS_ooxml::LN_pic_pic:
+ case NS_ooxml::LN_dgm_relIds:
+ case NS_ooxml::LN_lc_lockedCanvas:
+ case NS_ooxml::LN_c_chart:
+ case NS_ooxml::LN_wps_wsp:
+ case NS_ooxml::LN_wpg_wgp:
+ case NS_ooxml::LN_sizeRelH_sizeRelH:
+ case NS_ooxml::LN_sizeRelV_sizeRelV:
+ case NS_ooxml::LN_hlinkClick_hlinkClick:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ pProperties->resolve(*this);
+ }
+
+ // We'll map these to PARALLEL, save the original wrap type.
+ if (nSprmId == NS_ooxml::LN_EG_WrapType_wrapTight)
+ m_pImpl->m_aInteropGrabBag["EG_WrapType"] <<= OUString("wrapTight");
+ else if (nSprmId == NS_ooxml::LN_EG_WrapType_wrapThrough)
+ m_pImpl->m_aInteropGrabBag["EG_WrapType"] <<= OUString("wrapThrough");
+
+ switch (nSprmId)
+ {
+ case NS_ooxml::LN_EG_WrapType_wrapSquare:
+ case NS_ooxml::LN_EG_WrapType_wrapThrough:
+ case NS_ooxml::LN_EG_WrapType_wrapTight:
+ {
+ // tdf#137850: Word >= 2013 seems to ignore bBehindDoc except for wrapNone, but older versions honour it.
+ if (m_pImpl->bBehindDoc && m_pImpl->rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14)
+ m_pImpl->bOpaque = true;
+ }
+ break;
+ }
+
+ }
+ break;
+ case NS_ooxml::LN_CT_WrapTight_wrapPolygon:
+ case NS_ooxml::LN_CT_WrapThrough_wrapPolygon:
+ {
+ WrapPolygonHandler aHandler;
+
+ resolveSprmProps(aHandler, rSprm);
+
+ m_pImpl->mpWrapPolygon = aHandler.getPolygon();
+
+ // Save the wrap path in case we can't handle it natively: drawinglayer shapes, TextFrames.
+ m_pImpl->m_aInteropGrabBag["CT_WrapPath"] <<= m_pImpl->mpWrapPolygon->getPointSequenceSequence();
+ }
+ break;
+ case NS_ooxml::LN_CT_Anchor_positionH: // 90976;
+ {
+ // Use a special handler for the positioning
+ auto pHandler = std::make_shared<PositionHandler>( m_pImpl->m_rPositionOffsets, m_pImpl->m_rAligns );
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ pProperties->resolve( *pHandler );
+ if( !m_pImpl->bUseSimplePos )
+ {
+ m_pImpl->nHoriRelation = pHandler->relation();
+ m_pImpl->bPageToggle = pHandler->GetPageToggle();
+ m_pImpl->nHoriOrient = pHandler->orientation();
+ m_pImpl->nLeftPosition = pHandler->position();
+
+ // Left adjustments: if horizontally aligned to left of margin, then remove the
+ // left wrapping.
+ if (m_pImpl->nHoriOrient == text::HoriOrientation::LEFT)
+ {
+ if (m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA)
+ {
+ m_pImpl->nLeftMargin = 0;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Anchor_positionV: // 90977;
+ {
+ // Use a special handler for the positioning
+ auto pHandler = std::make_shared<PositionHandler>( m_pImpl->m_rPositionOffsets, m_pImpl->m_rAligns);
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ pProperties->resolve( *pHandler );
+ if( !m_pImpl->bUseSimplePos )
+ {
+ m_pImpl->nVertRelation = pHandler->relation();
+ m_pImpl->nVertOrient = pHandler->orientation();
+ m_pImpl->nTopPosition = pHandler->position();
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_SizeRelH_pctWidth:
+ case NS_ooxml::LN_CT_SizeRelV_pctHeight:
+ if (m_pImpl->m_rPositivePercentages.empty())
+ break;
+
+ if (m_xShape.is())
+ {
+ sal_Int16 nPositivePercentage = rtl::math::round(m_pImpl->m_rPositivePercentages.front().toDouble() / oox::drawingml::PER_PERCENT);
+
+ if (nPositivePercentage)
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
+ OUString aProperty = nSprmId == NS_ooxml::LN_CT_SizeRelH_pctWidth ? OUString("RelativeWidth") : OUString("RelativeHeight");
+
+ sal_Int32 nTextPreRotateAngle = 0;
+ uno::Any aAny;
+ if (xPropertySet->getPropertySetInfo()->hasPropertyByName(
+ "CustomShapeGeometry"))
+ {
+ aAny = xPropertySet->getPropertyValue("CustomShapeGeometry");
+ }
+ comphelper::SequenceAsHashMap aCustomShapeGeometry(aAny);
+ auto it = aCustomShapeGeometry.find("TextPreRotateAngle");
+ if (it != aCustomShapeGeometry.end())
+ {
+ nTextPreRotateAngle = it->second.get<sal_Int32>();
+ }
+ if (nTextPreRotateAngle == 0)
+ {
+ xPropertySet->setPropertyValue(aProperty,
+ uno::Any(nPositivePercentage));
+ }
+ }
+ }
+
+ // Make sure the token is consumed even if xShape is an empty
+ // reference.
+ m_pImpl->m_rPositivePercentages.pop();
+ break;
+ case NS_ooxml::LN_EG_WrapType_wrapNone: // 90944; - doesn't contain attributes
+ //depending on the behindDoc attribute text wraps through behind or in front of the object
+ m_pImpl->nWrap = text::WrapTextMode_THROUGH;
+
+ // Wrap though means the margins defined earlier should not be
+ // respected.
+ m_pImpl->nLeftMargin = 0;
+ m_pImpl->nTopMargin = 0;
+ m_pImpl->nRightMargin = 0;
+ m_pImpl->nBottomMargin = 0;
+ break;
+ case NS_ooxml::LN_EG_WrapType_wrapTopAndBottom: // 90948;
+ // tdf#137850: Word >= 2013 seems to ignore bBehindDoc except for wrapNone, but older versions honour it.
+ if (m_pImpl->bBehindDoc && m_pImpl->rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14)
+ m_pImpl->bOpaque = true;
+ m_pImpl->nWrap = text::WrapTextMode_NONE;
+ break;
+ case NS_ooxml::LN_CT_GraphicalObject_graphicData:// 90660;
+ {
+ m_pImpl->bIsGraphic = true;
+
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_NonVisualDrawingProps_a_hlinkClick: // 90689;
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ pProperties->resolve( *this );
+ }
+ break;
+ default:
+ SAL_WARN("writerfilter", "GraphicImport::lcl_sprm: unhandled token: " << nSprmId);
+ break;
+ }
+}
+
+void GraphicImport::lcl_entry(writerfilter::Reference<Properties>::Pointer_t /*ref*/)
+{
+}
+
+uno::Reference<text::XTextContent> GraphicImport::createGraphicObject(uno::Reference<graphic::XGraphic> const & rxGraphic,
+ uno::Reference<beans::XPropertySet> const & xShapeProps)
+{
+ uno::Reference<text::XTextContent> xGraphicObject;
+ try
+ {
+ if (rxGraphic.is())
+ {
+ uno::Reference< beans::XPropertySet > xGraphicObjectProperties(
+ m_xTextFactory->createInstance("com.sun.star.text.TextGraphicObject"),
+ uno::UNO_QUERY_THROW);
+ xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_GRAPHIC), uno::Any(rxGraphic));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE),
+ uno::Any( m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR ?
+ text::TextContentAnchorType_AT_CHARACTER :
+ text::TextContentAnchorType_AS_CHARACTER ));
+ xGraphicObject.set( xGraphicObjectProperties, uno::UNO_QUERY_THROW );
+
+ //shapes have only one border
+ table::BorderLine2 aBorderLine;
+ GraphicBorderLine& rBorderLine = m_pImpl->aBorders[0];
+ if (rBorderLine.isEmpty() && xShapeProps.is() && xShapeProps->getPropertyValue("LineStyle").get<drawing::LineStyle>() != drawing::LineStyle_NONE)
+ {
+ // In case we got no border tokens and we have the
+ // original shape, then use its line properties as the
+ // border.
+ aBorderLine.Color = xShapeProps->getPropertyValue("LineColor").get<sal_Int32>();
+ aBorderLine.LineWidth = xShapeProps->getPropertyValue("LineWidth").get<sal_Int32>();
+ }
+ else
+ {
+ aBorderLine.Color = 0;
+ aBorderLine.InnerLineWidth = 0;
+ aBorderLine.OuterLineWidth = static_cast<sal_Int16>(rBorderLine.nLineWidth);
+ aBorderLine.LineDistance = 0;
+ }
+ PropertyIds const aBorderProps[] =
+ {
+ PROP_LEFT_BORDER,
+ PROP_RIGHT_BORDER,
+ PROP_TOP_BORDER,
+ PROP_BOTTOM_BORDER
+ };
+
+ for(PropertyIds const & rBorderProp : aBorderProps)
+ xGraphicObjectProperties->setPropertyValue(getPropertyName(rBorderProp), uno::Any(aBorderLine));
+
+ // setting graphic object shadow properties
+ if (m_pImpl->bShadow)
+ {
+ // Shadow width is approximated by average of X and Y
+ table::ShadowFormat aShadow;
+ sal_uInt32 nShadowColor = m_pImpl->nShadowColor & 0x00FFFFFF; // The shadow color we get is RGB only.
+ sal_Int32 nShadowWidth = (abs(m_pImpl->nShadowXDistance)
+ + abs(m_pImpl->nShadowYDistance)) / 2;
+
+ aShadow.ShadowWidth = nShadowWidth;
+ sal_uInt8 nShadowTransparence = float(m_pImpl->nShadowTransparence) * 2.55;
+ nShadowColor |= (nShadowTransparence << 24); // Add transparence to the color.
+ aShadow.Color = nShadowColor;
+ // Distances -ve for top and right, +ve for bottom and left
+ if (m_pImpl->nShadowXDistance > 0)
+ {
+ if (m_pImpl->nShadowYDistance > 0)
+ aShadow.Location = table::ShadowLocation_BOTTOM_RIGHT;
+ else
+ aShadow.Location = table::ShadowLocation_TOP_RIGHT;
+ }
+ else
+ {
+ if (m_pImpl->nShadowYDistance > 0)
+ aShadow.Location = table::ShadowLocation_BOTTOM_LEFT;
+ else
+ aShadow.Location = table::ShadowLocation_TOP_LEFT;
+ }
+
+ xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_SHADOW_FORMAT), uno::Any(aShadow));
+ }
+
+ // setting properties for all types
+ if( m_pImpl->bPositionProtected )
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_POSITION_PROTECTED ),
+ uno::Any(true));
+ if( m_pImpl->bSizeProtected )
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_SIZE_PROTECTED ),
+ uno::Any(true));
+
+ sal_Int32 nWidth = - m_pImpl->nLeftPosition;
+ if (m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
+ {
+ //adjust margins
+ if( (m_pImpl->nHoriOrient == text::HoriOrientation::LEFT &&
+ (m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA ||
+ m_pImpl->nHoriRelation == text::RelOrientation::FRAME) ) ||
+ (m_pImpl->nHoriOrient == text::HoriOrientation::INSIDE &&
+ m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA ))
+ m_pImpl->nLeftMargin = 0;
+ if((m_pImpl->nHoriOrient == text::HoriOrientation::RIGHT &&
+ (m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA ||
+ m_pImpl->nHoriRelation == text::RelOrientation::FRAME) ) ||
+ (m_pImpl->nHoriOrient == text::HoriOrientation::INSIDE &&
+ m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA ))
+ m_pImpl->nRightMargin = 0;
+ // adjust top/bottom margins
+ if( m_pImpl->nVertOrient == text::VertOrientation::TOP &&
+ ( m_pImpl->nVertRelation == text::RelOrientation::PAGE_PRINT_AREA ||
+ m_pImpl->nVertRelation == text::RelOrientation::PAGE_FRAME))
+ m_pImpl->nTopMargin = 0;
+ if( m_pImpl->nVertOrient == text::VertOrientation::BOTTOM &&
+ ( m_pImpl->nVertRelation == text::RelOrientation::PAGE_PRINT_AREA ||
+ m_pImpl->nVertRelation == text::RelOrientation::PAGE_FRAME))
+ m_pImpl->nBottomMargin = 0;
+ if( m_pImpl->nVertOrient == text::VertOrientation::BOTTOM &&
+ m_pImpl->nVertRelation == text::RelOrientation::PAGE_PRINT_AREA )
+ m_pImpl->nBottomMargin = 0;
+ //adjust alignment
+ if( m_pImpl->nHoriOrient == text::HoriOrientation::INSIDE &&
+ m_pImpl->nHoriRelation == text::RelOrientation::PAGE_FRAME )
+ {
+ // convert 'left to page' to 'from left -<width> to page text area'
+ m_pImpl->nHoriOrient = text::HoriOrientation::NONE;
+ m_pImpl->nHoriRelation = text::RelOrientation::PAGE_PRINT_AREA;
+ m_pImpl->nLeftPosition = - nWidth;
+ }
+ else if( m_pImpl->nHoriOrient == text::HoriOrientation::OUTSIDE &&
+ m_pImpl->nHoriRelation == text::RelOrientation::PAGE_FRAME )
+ {
+ // convert 'right to page' to 'from left 0 to right page border'
+ m_pImpl->nHoriOrient = text::HoriOrientation::NONE;
+ m_pImpl->nHoriRelation = text::RelOrientation::PAGE_RIGHT;
+ m_pImpl->nLeftPosition = 0;
+ }
+
+ m_pImpl->applyPosition(xGraphicObjectProperties);
+ m_pImpl->applyRelativePosition(xGraphicObjectProperties);
+ if( !m_pImpl->bOpaque )
+ {
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::Any(m_pImpl->bOpaque));
+ }
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_SURROUND ),
+ uno::Any(static_cast<sal_Int32>(m_pImpl->nWrap)));
+ if( m_pImpl->rDomainMapper.IsInTable())
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_FOLLOW_TEXT_FLOW ),
+ uno::Any(m_pImpl->bLayoutInCell));
+
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_SURROUND_CONTOUR ),
+ uno::Any(m_pImpl->bContour));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_CONTOUR_OUTSIDE ),
+ uno::Any(m_pImpl->bContourOutside));
+ m_pImpl->applyMargins(xGraphicObjectProperties);
+ }
+
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_ADJUST_CONTRAST ),
+ uno::Any(static_cast<sal_Int16>(m_pImpl->nContrast)));
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_ADJUST_LUMINANCE ),
+ uno::Any(static_cast<sal_Int16>(m_pImpl->nBrightness)));
+ if(m_pImpl->eColorMode != drawing::ColorMode_STANDARD)
+ {
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_GRAPHIC_COLOR_MODE ),
+ uno::Any(m_pImpl->eColorMode));
+ }
+
+ xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_BACK_COLOR ),
+ uno::Any( GraphicImport_Impl::nFillColor ));
+ m_pImpl->applyZOrder(xGraphicObjectProperties);
+
+ //there seems to be no way to detect the original size via _real_ API
+ uno::Reference< beans::XPropertySet > xGraphicProperties(rxGraphic, uno::UNO_QUERY_THROW);
+
+ if (m_pImpl->mpWrapPolygon)
+ {
+ uno::Any aContourPolyPolygon;
+ awt::Size aGraphicSize;
+ WrapPolygon::Pointer_t pCorrected;
+ xGraphicProperties->getPropertyValue(getPropertyName(PROP_SIZE100th_M_M)) >>= aGraphicSize;
+ if (aGraphicSize.Width && aGraphicSize.Height)
+ {
+ pCorrected = m_pImpl->mpWrapPolygon->correctWordWrapPolygon(aGraphicSize);
+ }
+ else
+ {
+ xGraphicProperties->getPropertyValue(getPropertyName(PROP_SIZE_PIXEL)) >>= aGraphicSize;
+ if (aGraphicSize.Width && aGraphicSize.Height)
+ {
+ pCorrected = m_pImpl->mpWrapPolygon->correctWordWrapPolygonPixel(aGraphicSize);
+ }
+ }
+
+ text::GraphicCrop aGraphicCrop;
+ xShapeProps->getPropertyValue("GraphicCrop") >>= aGraphicCrop;
+ if (aGraphicCrop.Top != 0 || aGraphicCrop.Bottom != 0 || aGraphicCrop.Left != 0
+ || aGraphicCrop.Right != 0)
+ {
+ // Word's wrap polygon deals with a canvas which has the size of the already
+ // cropped graphic, correct our polygon to have the same render result.
+ pCorrected = pCorrected->correctCrop(aGraphicSize, aGraphicCrop);
+ }
+
+ if (pCorrected)
+ {
+ aContourPolyPolygon <<= pCorrected->getPointSequenceSequence();
+ xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_CONTOUR_POLY_POLYGON),
+ aContourPolyPolygon);
+ // We should bring it to front, even if wp:anchor's behindDoc="1",
+ // because otherwise paragraph background (if set) overlaps the graphic
+ // TODO: if paragraph's background becomes bottommost, then remove this hack
+ xGraphicObjectProperties->setPropertyValue("Opaque", uno::Any(true));
+ }
+ }
+
+
+ if(m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_INLINE || m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
+ {
+ if( m_pImpl->getXSize() && m_pImpl->getYSize() )
+ xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_SIZE),
+ uno::Any( awt::Size( m_pImpl->getXSize(), m_pImpl->getYSize() )));
+ m_pImpl->applyMargins(xGraphicObjectProperties);
+ m_pImpl->applyName(xGraphicObjectProperties);
+ m_pImpl->applyHyperlink(xGraphicObjectProperties, false);
+ }
+
+ // Handle horizontal flip.
+ bool bMirrored = false;
+ xShapeProps->getPropertyValue("IsMirrored") >>= bMirrored;
+ if (bMirrored)
+ {
+ xGraphicObjectProperties->setPropertyValue("HoriMirroredOnEvenPages",
+ uno::Any(true));
+ xGraphicObjectProperties->setPropertyValue("HoriMirroredOnOddPages",
+ uno::Any(true));
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter", "");
+ }
+ return xGraphicObject;
+}
+
+
+void GraphicImport::data(const sal_uInt8* buf, size_t len)
+{
+ uno::Reference< io::XInputStream > xIStream = new XInputStreamHelper( buf, len );
+ beans::PropertyValues aMediaProperties{ comphelper::makePropertyValue(
+ getPropertyName(PROP_INPUT_STREAM), xIStream) };
+
+ uno::Reference<beans::XPropertySet> xPropertySet;
+ uno::Reference<graphic::XGraphicProvider> xGraphicProvider(graphic::GraphicProvider::create(m_xComponentContext));
+ uno::Reference<graphic::XGraphic> xGraphic = xGraphicProvider->queryGraphic(aMediaProperties);
+ m_xGraphicObject = createGraphicObject(xGraphic, xPropertySet);
+}
+
+
+void GraphicImport::lcl_startSectionGroup()
+{
+}
+
+
+void GraphicImport::lcl_endSectionGroup()
+{
+}
+
+
+void GraphicImport::lcl_startParagraphGroup()
+{
+}
+
+
+void GraphicImport::lcl_endParagraphGroup()
+{
+}
+
+
+void GraphicImport::lcl_startCharacterGroup()
+{
+}
+
+
+void GraphicImport::lcl_endCharacterGroup()
+{
+}
+
+
+void GraphicImport::lcl_text(const sal_uInt8 * /*_data*/, size_t /*len*/)
+{
+}
+
+
+void GraphicImport::lcl_utext(const sal_uInt8 * /*_data*/, size_t /*len*/)
+{
+}
+
+
+void GraphicImport::lcl_props(writerfilter::Reference<Properties>::Pointer_t /*ref*/)
+{
+}
+
+
+void GraphicImport::lcl_table(Id /*name*/, writerfilter::Reference<Table>::Pointer_t /*ref*/)
+{
+}
+
+
+void GraphicImport::lcl_substream(Id /*name*/, ::writerfilter::Reference<Stream>::Pointer_t /*ref*/)
+{
+}
+
+void GraphicImport::lcl_startShape(uno::Reference<drawing::XShape> const&)
+{
+}
+
+void GraphicImport::lcl_endShape( )
+{
+}
+
+bool GraphicImport::IsGraphic() const
+{
+ return m_pImpl->bIsGraphic;
+}
+
+sal_Int32 GraphicImport::GetLeftMarginOrig() const
+{
+ return m_pImpl->nLeftMarginOrig;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/GraphicImport.hxx b/writerfilter/source/dmapper/GraphicImport.hxx
new file mode 100644
index 000000000..93be2df3f
--- /dev/null
+++ b/writerfilter/source/dmapper/GraphicImport.hxx
@@ -0,0 +1,140 @@
+/* -*- 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 <queue>
+#include <memory>
+
+#include "LoggedResources.hxx"
+
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+
+namespace com::sun::star {
+ namespace uno
+ {
+ class XComponentContext;
+ }
+ namespace lang
+ {
+ class XMultiServiceFactory;
+ }
+ namespace text
+ {
+ class XTextContent;
+ }
+ namespace drawing
+ {
+ class XShape;
+ }
+ namespace beans
+ {
+ struct PropertyValue;
+ }
+}
+
+namespace writerfilter::dmapper
+{
+class GraphicImport_Impl;
+class DomainMapper;
+
+enum GraphicImportType
+{
+ IMPORT_AS_DETECTED_INLINE,
+ IMPORT_AS_DETECTED_ANCHOR
+};
+
+class GraphicImport : public LoggedProperties, public LoggedTable
+ ,public BinaryObj, public LoggedStream
+{
+ std::unique_ptr<GraphicImport_Impl> m_pImpl;
+
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+ css::uno::Reference<css::lang::XMultiServiceFactory> m_xTextFactory;
+
+ css::uno::Reference<css::text::XTextContent> m_xGraphicObject;
+
+ css::uno::Reference<css::drawing::XShape> m_xShape;
+ void ProcessShapeOptions(Value const & val);
+
+ css::uno::Reference<css::text::XTextContent>
+ createGraphicObject(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
+ css::uno::Reference<css::beans::XPropertySet> const & xShapeProps);
+
+ void putPropertyToFrameGrabBag( const OUString& sPropertyName, const css::uno::Any& aPropertyValue );
+
+public:
+ explicit GraphicImport( css::uno::Reference<css::uno::XComponentContext> const& xComponentContext,
+ css::uno::Reference<css::lang::XMultiServiceFactory> const& xTextFactory,
+ DomainMapper& rDomainMapper,
+ GraphicImportType eGraphicImportType,
+ std::pair<OUString, OUString>& rPositionOffsets,
+ std::pair<OUString, OUString>& rAligns,
+ std::queue<OUString>& rPositivePercentages);
+ virtual ~GraphicImport() override;
+
+ // BinaryObj
+ virtual void data(const sal_uInt8* buffer, size_t len) override;
+
+ css::uno::Reference<css::text::XTextContent> GetGraphicObject();
+ const css::uno::Reference<css::drawing::XShape>& GetXShapeObject() const { return m_xShape;}
+ bool IsGraphic() const;
+ sal_Int32 GetLeftMarginOrig() const;
+
+ com::sun::star::awt::Point GetGraphicObjectPosition() const;
+
+ bool GetLayoutInCell() const;
+
+ private:
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+ // Table
+ virtual void lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref) override;
+
+ // Stream
+ virtual void lcl_startSectionGroup() override;
+ virtual void lcl_endSectionGroup() override;
+ virtual void lcl_startParagraphGroup() override;
+ virtual void lcl_endParagraphGroup() override;
+ virtual void lcl_startCharacterGroup() override;
+ virtual void lcl_endCharacterGroup() override;
+ virtual void lcl_text(const sal_uInt8 * data, size_t len) override;
+ virtual void lcl_utext(const sal_uInt8 * data, size_t len) override;
+ virtual void lcl_props(writerfilter::Reference<Properties>::Pointer_t ref) override;
+ virtual void lcl_table(Id name,
+ writerfilter::Reference<Table>::Pointer_t ref) override;
+ virtual void lcl_substream(Id name, writerfilter::Reference<Stream>::Pointer_t ref) override;
+ virtual void lcl_startShape(css::uno::Reference<css::drawing::XShape> const& xShape) override;
+ virtual void lcl_startTextBoxContent() override {};
+ virtual void lcl_endTextBoxContent() override {};
+ virtual void lcl_endShape() override;
+
+ void handleWrapTextValue(sal_uInt32 nVal);
+ void lcl_expandRectangleByEffectExtent(css::awt::Point& rLeftTop, css::awt::Size& rSize);
+ void lcl_correctWord2007EffectExtent(const sal_Int32 nMSOAngle);
+};
+
+typedef tools::SvRef<GraphicImport> GraphicImportPtr;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/LatentStyleHandler.cxx b/writerfilter/source/dmapper/LatentStyleHandler.cxx
new file mode 100644
index 000000000..bc381d21f
--- /dev/null
+++ b/writerfilter/source/dmapper/LatentStyleHandler.cxx
@@ -0,0 +1,71 @@
+/* -*- 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 "LatentStyleHandler.hxx"
+#include "TagLogger.hxx"
+#include <ooxml/resourceids.hxx>
+
+namespace writerfilter::dmapper
+{
+using namespace ::com::sun::star;
+
+LatentStyleHandler::LatentStyleHandler()
+ : LoggedProperties("LatentStyleHandler")
+{
+}
+
+LatentStyleHandler::~LatentStyleHandler() = default;
+
+void LatentStyleHandler::lcl_attribute(Id nId, Value& rVal)
+{
+ beans::PropertyValue aValue;
+ bool bFound = true;
+ switch (nId)
+ {
+ case NS_ooxml::LN_CT_LsdException_name:
+ aValue.Name = "name";
+ break;
+ case NS_ooxml::LN_CT_LsdException_locked:
+ aValue.Name = "locked";
+ break;
+ case NS_ooxml::LN_CT_LsdException_uiPriority:
+ aValue.Name = "uiPriority";
+ break;
+ case NS_ooxml::LN_CT_LsdException_semiHidden:
+ aValue.Name = "semiHidden";
+ break;
+ case NS_ooxml::LN_CT_LsdException_unhideWhenUsed:
+ aValue.Name = "unhideWhenUsed";
+ break;
+ case NS_ooxml::LN_CT_LsdException_qFormat:
+ aValue.Name = "qFormat";
+ break;
+ default:
+ bFound = false;
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ break;
+ }
+ if (bFound)
+ {
+ aValue.Value <<= rVal.getString();
+ m_aAttributes.push_back(aValue);
+ }
+}
+
+void LatentStyleHandler::lcl_sprm(Sprm& /*rSprm*/) {}
+
+const std::vector<beans::PropertyValue>& LatentStyleHandler::getAttributes() const
+{
+ return m_aAttributes;
+}
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/LatentStyleHandler.hxx b/writerfilter/source/dmapper/LatentStyleHandler.hxx
new file mode 100644
index 000000000..52a4d9e7c
--- /dev/null
+++ b/writerfilter/source/dmapper/LatentStyleHandler.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include "LoggedResources.hxx"
+#include <vector>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+namespace writerfilter::dmapper
+{
+/// Handler for a latent style (w:lsdException element)
+class LatentStyleHandler : public LoggedProperties
+{
+ std::vector<css::beans::PropertyValue> m_aAttributes;
+
+ // Properties
+ void lcl_attribute(Id nId, Value& rVal) override;
+ void lcl_sprm(Sprm& sprm) override;
+
+public:
+ LatentStyleHandler();
+ ~LatentStyleHandler() override;
+
+ const std::vector<css::beans::PropertyValue>& getAttributes() const;
+};
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/LoggedResources.cxx b/writerfilter/source/dmapper/LoggedResources.cxx
new file mode 100644
index 000000000..4eeb5db10
--- /dev/null
+++ b/writerfilter/source/dmapper/LoggedResources.cxx
@@ -0,0 +1,400 @@
+/* -*- 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 "LoggedResources.hxx"
+#include "TagLogger.hxx"
+#include <ooxml/QNameToString.hxx>
+
+using namespace ::com::sun::star;
+
+namespace writerfilter
+{
+#ifdef DBG_UTIL
+
+LoggedResourcesHelper::LoggedResourcesHelper(const std::string& sPrefix)
+ : msPrefix(sPrefix)
+{
+}
+
+LoggedResourcesHelper::~LoggedResourcesHelper() {}
+
+void LoggedResourcesHelper::startElement(const std::string& sElement)
+{
+ TagLogger::getInstance().startElement(msPrefix + "." + sElement);
+}
+
+void LoggedResourcesHelper::endElement() { TagLogger::getInstance().endElement(); }
+
+void LoggedResourcesHelper::chars(std::u16string_view rChars)
+{
+ TagLogger::getInstance().chars(rChars);
+}
+
+void LoggedResourcesHelper::chars(const std::string& rChars)
+{
+ TagLogger::getInstance().chars(rChars);
+}
+
+void LoggedResourcesHelper::attribute(const std::string& rName, const std::string& rValue)
+{
+ TagLogger::getInstance().attribute(rName, rValue);
+}
+
+void LoggedResourcesHelper::attribute(const std::string& rName, sal_uInt32 nValue)
+{
+ TagLogger::getInstance().attribute(rName, nValue);
+}
+
+#endif
+
+LoggedStream::LoggedStream(
+#ifdef DBG_UTIL
+ const std::string& sPrefix)
+ : mHelper(sPrefix)
+#else
+ const std::string&)
+#endif
+{
+}
+
+LoggedStream::~LoggedStream() {}
+
+void LoggedStream::startSectionGroup()
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("section");
+#endif
+
+ lcl_startSectionGroup();
+}
+
+void LoggedStream::endSectionGroup()
+{
+ lcl_endSectionGroup();
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::startParagraphGroup()
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("paragraph");
+#endif
+
+ lcl_startParagraphGroup();
+}
+
+void LoggedStream::endParagraphGroup()
+{
+ lcl_endParagraphGroup();
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::startCharacterGroup()
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("charactergroup");
+#endif
+
+ lcl_startCharacterGroup();
+}
+
+void LoggedStream::endCharacterGroup()
+{
+ lcl_endCharacterGroup();
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::startShape(uno::Reference<drawing::XShape> const& xShape)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("shape");
+#endif
+
+ lcl_startShape(xShape);
+}
+
+void LoggedStream::endShape()
+{
+ lcl_endShape();
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::startTextBoxContent() { lcl_startTextBoxContent(); }
+
+void LoggedStream::endTextBoxContent() { lcl_endTextBoxContent(); }
+
+void LoggedStream::text(const sal_uInt8* data, size_t len)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("text");
+
+ OUString sText(reinterpret_cast<const char*>(data), len, RTL_TEXTENCODING_MS_1252);
+
+ mHelper.startElement("data");
+ LoggedResourcesHelper::chars(sText);
+ LoggedResourcesHelper::endElement();
+#endif
+
+ lcl_text(data, len);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::utext(const sal_uInt8* data, size_t len)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("utext");
+ mHelper.startElement("data");
+
+ OUString sText(reinterpret_cast<const sal_Unicode*>(data), len);
+
+ LoggedResourcesHelper::chars(sText);
+
+ LoggedResourcesHelper::endElement();
+#endif
+
+ lcl_utext(data, len);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::positionOffset(const OUString& rText, bool bVertical)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("positionOffset");
+ LoggedResourcesHelper::attribute("vertical", static_cast<int>(bVertical));
+ LoggedResourcesHelper::chars(rText);
+#endif
+
+ lcl_positionOffset(rText, bVertical);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::align(const OUString& rText, bool bVertical)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("align");
+ LoggedResourcesHelper::attribute("vertical", static_cast<int>(bVertical));
+ LoggedResourcesHelper::chars(rText);
+#endif
+
+ lcl_align(rText, bVertical);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::positivePercentage(const OUString& rText)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("positivePercentage");
+ LoggedResourcesHelper::chars(rText);
+#endif
+
+ lcl_positivePercentage(rText);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::props(writerfilter::Reference<Properties>::Pointer_t ref)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("props");
+#endif
+
+ lcl_props(ref);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::table(Id name, writerfilter::Reference<Table>::Pointer_t ref)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("table");
+ LoggedResourcesHelper::attribute("name", QNameToString(name));
+#endif
+
+ lcl_table(name, ref);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::substream(Id name, writerfilter::Reference<Stream>::Pointer_t ref)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("substream");
+ LoggedResourcesHelper::attribute("name", QNameToString(name));
+#endif
+
+ lcl_substream(name, ref);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::info(const std::string& _info)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("info");
+ LoggedResourcesHelper::attribute("text", _info);
+#else
+ (void)_info;
+#endif
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::startGlossaryEntry()
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("startGlossaryEntry");
+#endif
+
+ lcl_startGlossaryEntry();
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::endGlossaryEntry()
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("endGlossaryEntry");
+#endif
+
+ lcl_endGlossaryEntry();
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+void LoggedStream::checkId(const sal_Int32 nId)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("checkId");
+ LoggedResourcesHelper::chars(OUString::number(nId));
+#endif
+
+ lcl_checkId(nId);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+LoggedProperties::LoggedProperties(
+#ifdef DBG_UTIL
+ const std::string& sPrefix)
+ : mHelper(sPrefix)
+#else
+ const std::string&)
+#endif
+{
+}
+
+LoggedProperties::~LoggedProperties() {}
+
+void LoggedProperties::attribute(Id name, Value& val)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("attribute");
+ LoggedResourcesHelper::attribute("name", QNameToString(name));
+ LoggedResourcesHelper::attribute("value", val.toString());
+ LoggedResourcesHelper::endElement();
+#endif
+
+ lcl_attribute(name, val);
+}
+
+void LoggedProperties::sprm(Sprm& rSprm)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("sprm");
+ LoggedResourcesHelper::attribute("name", QNameToString(rSprm.getId()));
+ LoggedResourcesHelper::chars(rSprm.toString());
+#endif
+
+ lcl_sprm(rSprm);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+
+LoggedTable::LoggedTable(
+#ifdef DBG_UTIL
+ const std::string& sPrefix)
+ : mHelper(sPrefix)
+#else
+ const std::string&)
+#endif
+{
+}
+
+LoggedTable::~LoggedTable() {}
+
+void LoggedTable::entry(int pos, writerfilter::Reference<Properties>::Pointer_t ref)
+{
+#ifdef DBG_UTIL
+ mHelper.startElement("entry");
+ LoggedResourcesHelper::attribute("pos", pos);
+#else
+ (void)pos;
+#endif
+
+ lcl_entry(ref);
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper::endElement();
+#endif
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/LoggedResources.hxx b/writerfilter/source/dmapper/LoggedResources.hxx
new file mode 100644
index 000000000..76efbe5c4
--- /dev/null
+++ b/writerfilter/source/dmapper/LoggedResources.hxx
@@ -0,0 +1,144 @@
+/* -*- 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 <sal/config.h>
+
+#include <string_view>
+
+#include <dmapper/resourcemodel.hxx>
+
+namespace writerfilter
+{
+#ifdef DBG_UTIL
+class LoggedResourcesHelper final
+{
+public:
+ explicit LoggedResourcesHelper(const std::string& sPrefix);
+ ~LoggedResourcesHelper();
+
+ void startElement(const std::string& sElement);
+ static void endElement();
+ static void chars(std::u16string_view rChars);
+ static void chars(const std::string& rChars);
+ static void attribute(const std::string& rName, const std::string& rValue);
+ static void attribute(const std::string& rName, sal_uInt32 nValue);
+
+private:
+ std::string msPrefix;
+};
+#endif
+
+class LoggedStream : public Stream
+{
+public:
+ explicit LoggedStream(const std::string& sPrefix);
+ virtual ~LoggedStream() override;
+
+ void startSectionGroup() override;
+ void endSectionGroup() override;
+ void startParagraphGroup() override;
+ void endParagraphGroup() override;
+ void startCharacterGroup() override;
+ void endCharacterGroup() override;
+ void startShape(css::uno::Reference<css::drawing::XShape> const& xShape) override;
+ void endShape() override;
+ void startTextBoxContent() override;
+ void endTextBoxContent() override;
+ void text(const sal_uInt8* data, size_t len) override;
+ void utext(const sal_uInt8* data, size_t len) override;
+ void positionOffset(const OUString& rText, bool bVertical) override;
+ void align(const OUString& rText, bool bVertical) override;
+ void positivePercentage(const OUString& rText) override;
+ void props(writerfilter::Reference<Properties>::Pointer_t ref) override;
+ void table(Id name, writerfilter::Reference<Table>::Pointer_t ref) override;
+ void substream(Id name, writerfilter::Reference<Stream>::Pointer_t ref) override;
+ void info(const std::string& info) override;
+ void startGlossaryEntry() override;
+ void endGlossaryEntry() override;
+ void checkId(const sal_Int32 nId) override;
+
+ virtual void setDocumentReference(writerfilter::ooxml::OOXMLDocument* /*pDocument*/) override{};
+
+protected:
+ virtual void lcl_startSectionGroup() = 0;
+ virtual void lcl_endSectionGroup() = 0;
+ virtual void lcl_startParagraphGroup() = 0;
+ virtual void lcl_endParagraphGroup() = 0;
+ virtual void lcl_startCharacterGroup() = 0;
+ virtual void lcl_endCharacterGroup() = 0;
+ virtual void lcl_startShape(css::uno::Reference<css::drawing::XShape> const& xShape) = 0;
+ virtual void lcl_endShape() = 0;
+ virtual void lcl_startTextBoxContent() = 0;
+ virtual void lcl_endTextBoxContent() = 0;
+ virtual void lcl_text(const sal_uInt8* data, size_t len) = 0;
+ virtual void lcl_utext(const sal_uInt8* data, size_t len) = 0;
+ virtual void lcl_positionOffset(const OUString& /*rText*/, bool /*bVertical*/) {}
+ virtual css::awt::Point getPositionOffset() override { return css::awt::Point(); }
+ virtual void lcl_align(const OUString& /*rText*/, bool /*bVertical*/) {}
+ virtual void lcl_positivePercentage(const OUString& /*rText*/) {}
+ virtual void lcl_props(writerfilter::Reference<Properties>::Pointer_t ref) = 0;
+ virtual void lcl_table(Id name, writerfilter::Reference<Table>::Pointer_t ref) = 0;
+ virtual void lcl_substream(Id name, writerfilter::Reference<Stream>::Pointer_t ref) = 0;
+ virtual void lcl_startGlossaryEntry() {}
+ virtual void lcl_endGlossaryEntry() {}
+ virtual void lcl_checkId(const sal_Int32) {}
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper mHelper;
+#endif
+};
+
+class LoggedProperties : public Properties
+{
+public:
+ explicit LoggedProperties(const std::string& sPrefix);
+ virtual ~LoggedProperties() override;
+
+ void attribute(Id name, Value& val) override;
+ void sprm(Sprm& sprm) override;
+
+protected:
+ virtual void lcl_attribute(Id name, Value& val) = 0;
+ virtual void lcl_sprm(Sprm& sprm) = 0;
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper mHelper;
+#endif
+};
+
+class LoggedTable : public Table
+{
+public:
+ explicit LoggedTable(const std::string& sPrefix);
+ virtual ~LoggedTable() override;
+
+ void entry(int pos, writerfilter::Reference<Properties>::Pointer_t ref) override;
+
+protected:
+ virtual void lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref) = 0;
+
+#ifdef DBG_UTIL
+ LoggedResourcesHelper mHelper;
+#endif
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/MeasureHandler.cxx b/writerfilter/source/dmapper/MeasureHandler.cxx
new file mode 100644
index 000000000..5eea9a5c6
--- /dev/null
+++ b/writerfilter/source/dmapper/MeasureHandler.cxx
@@ -0,0 +1,136 @@
+/* -*- 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 "MeasureHandler.hxx"
+#include "ConversionHelper.hxx"
+#include <ooxml/resourceids.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/text/SizeType.hpp>
+#include <comphelper/sequence.hxx>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+
+
+MeasureHandler::MeasureHandler() :
+LoggedProperties("MeasureHandler"),
+m_nMeasureValue( 0 ),
+m_nUnit( -1 ),
+m_nRowHeightSizeType( text::SizeType::MIN )
+{
+}
+
+
+MeasureHandler::~MeasureHandler()
+{
+}
+
+
+void MeasureHandler::lcl_attribute(Id rName, Value & rVal)
+{
+ sal_Int32 nIntValue = rVal.getInt();
+ switch( rName )
+ {
+ case NS_ooxml::LN_CT_TblWidth_type:
+ {
+ //can be: NS_ooxml::LN_Value_ST_TblWidth_nil, NS_ooxml::LN_Value_ST_TblWidth_pct,
+ // NS_ooxml::LN_Value_ST_TblWidth_dxa, NS_ooxml::LN_Value_ST_TblWidth_auto;
+ m_nUnit = nIntValue;
+
+ if (!m_aInteropGrabBagName.isEmpty())
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "type";
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_TblWidth_nil: aValue.Value <<= OUString("nil"); break;
+ case NS_ooxml::LN_Value_ST_TblWidth_pct: aValue.Value <<= OUString("pct"); break;
+ case NS_ooxml::LN_Value_ST_TblWidth_dxa: aValue.Value <<= OUString("dxa"); break;
+ case NS_ooxml::LN_Value_ST_TblWidth_auto: aValue.Value <<= OUString("auto"); break;
+ }
+ m_aInteropGrabBag.push_back(aValue);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Height_hRule:
+ {
+ OUString sHeightType = rVal.getString();
+ if ( sHeightType == "exact" )
+ m_nRowHeightSizeType = text::SizeType::FIX;
+ }
+ break;
+ case NS_ooxml::LN_CT_TblWidth_w:
+ m_nMeasureValue = nIntValue;
+ if (!m_aInteropGrabBagName.isEmpty())
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "w";
+ aValue.Value <<= nIntValue;
+ m_aInteropGrabBag.push_back(aValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_Height_val: // a string value
+ {
+ m_nUnit = NS_ooxml::LN_Value_ST_TblWidth_dxa;
+ OUString sHeight = rVal.getString();
+ m_nMeasureValue = sHeight.toInt32();
+ }
+ break;
+ default:
+ OSL_FAIL( "unknown attribute");
+ }
+}
+
+
+void MeasureHandler::lcl_sprm(Sprm &) {}
+
+
+sal_Int32 MeasureHandler::getMeasureValue() const
+{
+ sal_Int32 nRet = 0;
+ if( m_nMeasureValue != 0 && m_nUnit >= 0 )
+ {
+ // TODO m_nUnit 3 - twip, other values unknown :-(
+ if( m_nUnit == 3 || sal::static_int_cast<Id>(m_nUnit) == NS_ooxml::LN_Value_ST_TblWidth_dxa)
+ {
+ nRet = ConversionHelper::convertTwipToMM100( m_nMeasureValue );
+ }
+ //todo: handle additional width types:
+ //NS_ooxml::LN_Value_ST_TblWidth_nil, NS_ooxml::LN_Value_ST_TblWidth_pct,
+ //NS_ooxml::LN_Value_ST_TblWidth_dxa, NS_ooxml::LN_Value_ST_TblWidth_auto;
+ }
+ return nRet;
+}
+
+void MeasureHandler::enableInteropGrabBag(const OUString& aName)
+{
+ m_aInteropGrabBagName = aName;
+}
+
+beans::PropertyValue MeasureHandler::getInteropGrabBag()
+{
+ beans::PropertyValue aRet;
+ aRet.Name = m_aInteropGrabBagName;
+ aRet.Value <<= comphelper::containerToSequence(m_aInteropGrabBag);
+ return aRet;
+}
+
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/MeasureHandler.hxx b/writerfilter/source/dmapper/MeasureHandler.hxx
new file mode 100644
index 000000000..8a6f9639e
--- /dev/null
+++ b/writerfilter/source/dmapper/MeasureHandler.hxx
@@ -0,0 +1,60 @@
+/* -*- 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 "LoggedResources.hxx"
+#include <vector>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+namespace writerfilter::dmapper
+{
+/** Handler for sprms that contain a measure and a unit
+ - Left indent of tables
+ - Preferred width of tables
+ */
+class MeasureHandler : public LoggedProperties
+{
+ sal_Int32 m_nMeasureValue;
+ sal_Int32 m_nUnit;
+ sal_Int16 m_nRowHeightSizeType; //table row height type
+
+ OUString m_aInteropGrabBagName;
+ std::vector<css::beans::PropertyValue> m_aInteropGrabBag;
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value& val) override;
+ virtual void lcl_sprm(Sprm& sprm) override;
+
+public:
+ MeasureHandler();
+ virtual ~MeasureHandler() override;
+
+ sal_Int32 getMeasureValue() const;
+
+ sal_Int32 getValue() const { return m_nMeasureValue; }
+ sal_Int32 getUnit() const { return m_nUnit; }
+
+ sal_Int16 GetRowHeightSizeType() const { return m_nRowHeightSizeType; }
+ void enableInteropGrabBag(const OUString& aName);
+ css::beans::PropertyValue getInteropGrabBag();
+};
+typedef tools::SvRef<MeasureHandler> MeasureHandlerPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/ModelEventListener.cxx b/writerfilter/source/dmapper/ModelEventListener.cxx
new file mode 100644
index 000000000..8e20f7ad9
--- /dev/null
+++ b/writerfilter/source/dmapper/ModelEventListener.cxx
@@ -0,0 +1,117 @@
+/* -*- 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 "ModelEventListener.hxx"
+#include "PropertyIds.hxx"
+#include <rtl/ustring.hxx>
+#include <com/sun/star/document/XEventBroadcaster.hpp>
+#include <com/sun/star/text/XTextFieldsSupplier.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/ReferenceFieldPart.hpp>
+#include <com/sun/star/text/ReferenceFieldSource.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/view/XFormLayerAccess.hpp>
+#include <tools/diagnose_ex.h>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+
+
+ModelEventListener::ModelEventListener(bool bIndexes, bool bControls)
+ : m_bIndexes(bIndexes),
+ m_bControls(bControls)
+{
+}
+
+
+ModelEventListener::~ModelEventListener()
+{
+}
+
+
+void ModelEventListener::notifyEvent( const document::EventObject& rEvent )
+{
+ if ( rEvent.EventName == "OnFocus" && m_bIndexes)
+ {
+ try
+ {
+ //remove listener
+ uno::Reference<document::XEventBroadcaster>(rEvent.Source, uno::UNO_QUERY_THROW )->removeEventListener(
+ uno::Reference<document::XEventListener>(this));
+
+ // If we have PAGEREF fields, update fields as well.
+ uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(rEvent.Source, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xEnumeration = xTextFieldsSupplier->getTextFields()->createEnumeration();
+ sal_Int32 nIndex = 0;
+ while(xEnumeration->hasMoreElements())
+ {
+ try
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xEnumeration->nextElement(), uno::UNO_QUERY);
+ sal_Int16 nSource = 0;
+ xPropertySet->getPropertyValue(getPropertyName(PROP_REFERENCE_FIELD_SOURCE)) >>= nSource;
+ sal_Int16 nPart = 0;
+ xPropertySet->getPropertyValue(getPropertyName(PROP_REFERENCE_FIELD_PART)) >>= nPart;
+ if (nSource == text::ReferenceFieldSource::BOOKMARK && nPart == text::ReferenceFieldPart::PAGE)
+ ++nIndex;
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ // doesn't even have such a property? ignore
+ }
+ }
+ if (nIndex)
+ {
+ uno::Reference<util::XRefreshable> xRefreshable(xTextFieldsSupplier->getTextFields(), uno::UNO_QUERY);
+ xRefreshable->refresh();
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "exception while updating indexes");
+ }
+ }
+
+ if ( rEvent.EventName == "OnFocus" && m_bControls)
+ {
+
+ // Form design mode is enabled by default in Writer, not in Word.
+ uno::Reference<frame::XModel> xModel(rEvent.Source, uno::UNO_QUERY);
+ uno::Reference<view::XFormLayerAccess> xFormLayerAccess(xModel->getCurrentController(), uno::UNO_QUERY);
+ xFormLayerAccess->setFormDesignMode(false);
+ }
+}
+
+
+void ModelEventListener::disposing( const lang::EventObject& rEvent )
+{
+ try
+ {
+ uno::Reference<document::XEventBroadcaster>(rEvent.Source, uno::UNO_QUERY_THROW )->removeEventListener(
+ uno::Reference<document::XEventListener>(this));
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/ModelEventListener.hxx b/writerfilter/source/dmapper/ModelEventListener.hxx
new file mode 100644
index 000000000..47cd94b80
--- /dev/null
+++ b/writerfilter/source/dmapper/ModelEventListener.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <com/sun/star/document/XEventListener.hpp>
+#include <cppuhelper/implbase.hxx>
+
+namespace writerfilter::dmapper
+{
+class ModelEventListener : public cppu::WeakImplHelper<css::document::XEventListener>
+{
+ bool m_bIndexes;
+ bool m_bControls;
+
+public:
+ ModelEventListener(bool bIndexes, bool bControls);
+ virtual ~ModelEventListener() override;
+
+ virtual void SAL_CALL notifyEvent(const css::document::EventObject& Event) override;
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+};
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/NumberingManager.cxx b/writerfilter/source/dmapper/NumberingManager.cxx
new file mode 100644
index 000000000..cb97f59ff
--- /dev/null
+++ b/writerfilter/source/dmapper/NumberingManager.cxx
@@ -0,0 +1,1210 @@
+/* -*- 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 "ConversionHelper.hxx"
+#include "NumberingManager.hxx"
+#include "StyleSheetTable.hxx"
+#include "PropertyIds.hxx"
+
+#include <ooxml/resourceids.hxx>
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/PositionAndSpaceMode.hpp>
+#include <com/sun/star/text/XChapterNumberingSupplier.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/UnitConversion.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/string.hxx>
+#include <regex>
+
+using namespace com::sun::star;
+
+namespace writerfilter::dmapper {
+
+//--------------------------------------------------- Utility functions
+template <typename T>
+static beans::PropertyValue lcl_makePropVal(PropertyIds nNameID, T const & aValue)
+{
+ return {getPropertyName(nNameID), 0, uno::Any(aValue), beans::PropertyState_DIRECT_VALUE};
+}
+
+static sal_Int32 lcl_findProperty( const uno::Sequence< beans::PropertyValue >& aProps, std::u16string_view sName )
+{
+ sal_Int32 i = 0;
+ sal_Int32 nLen = aProps.getLength( );
+ sal_Int32 nPos = -1;
+
+ while ( nPos == -1 && i < nLen )
+ {
+ if ( aProps[i].Name == sName )
+ nPos = i;
+ else
+ i++;
+ }
+
+ return nPos;
+}
+
+static void lcl_mergeProperties( const uno::Sequence< beans::PropertyValue >& aSrc,
+ uno::Sequence< beans::PropertyValue >& aDst )
+{
+ for ( const auto& rProp : aSrc )
+ {
+ // Look for the same property in aDst
+ sal_Int32 nPos = lcl_findProperty( aDst, rProp.Name );
+ if ( nPos >= 0 )
+ {
+ // Replace the property value by the one in aSrc
+ aDst.getArray()[nPos] = rProp;
+ }
+ else
+ {
+ // Simply add the new value
+ aDst.realloc( aDst.getLength( ) + 1 );
+ aDst.getArray()[ aDst.getLength( ) - 1 ] = rProp;
+ }
+ }
+}
+
+//-------------------------------------------- ListLevel implementation
+void ListLevel::SetValue( Id nId, sal_Int32 nValue )
+{
+ switch( nId )
+ {
+ case NS_ooxml::LN_CT_Lvl_start:
+ m_nIStartAt = nValue;
+ break;
+ case NS_ooxml::LN_CT_NumLvl_startOverride:
+ m_nStartOverride = nValue;
+ break;
+ case NS_ooxml::LN_CT_NumFmt_val:
+ m_nNFC = nValue;
+ break;
+ case NS_ooxml::LN_CT_Lvl_isLgl:
+ break;
+ case NS_ooxml::LN_CT_Lvl_legacy:
+ break;
+ case NS_ooxml::LN_CT_Lvl_suff:
+ m_nXChFollow = nValue;
+ break;
+ case NS_ooxml::LN_CT_TabStop_pos:
+ if (nValue < 0)
+ {
+ SAL_INFO("writerfilter",
+ "unsupported list tab stop position " << nValue);
+ }
+ else
+ m_nTabstop = nValue;
+ break;
+ default:
+ OSL_FAIL( "this line should never be reached");
+ }
+ m_bHasValues = true;
+}
+
+void ListLevel::SetCustomNumberFormat(const OUString& rValue) { m_aCustomNumberFormat = rValue; }
+
+sal_Int16 ListLevel::GetNumberingType(sal_Int16 nDefault) const
+{
+ return ConversionHelper::ConvertNumberingType(m_nNFC, nDefault);
+}
+
+bool ListLevel::HasValues() const
+{
+ return m_bHasValues;
+}
+
+void ListLevel::SetParaStyle( const tools::SvRef< StyleSheetEntry >& pStyle )
+{
+ if (!pStyle)
+ return;
+ m_pParaStyle = pStyle;
+}
+
+uno::Sequence<beans::PropertyValue> ListLevel::GetProperties(bool bDefaults)
+{
+ uno::Sequence<beans::PropertyValue> aLevelProps = GetLevelProperties(bDefaults);
+ if (m_pParaStyle)
+ AddParaProperties( &aLevelProps );
+ return aLevelProps;
+}
+
+static bool IgnoreForCharStyle(std::u16string_view aStr, const bool bIsSymbol)
+{
+ //Names found in PropertyIds.cxx, Lines 56-396
+ return (aStr==u"Adjust" || aStr==u"IndentAt" || aStr==u"FirstLineIndent"
+ || aStr==u"FirstLineOffset" || aStr==u"LeftMargin"
+ // We need font names when they are different for the bullet and for the text.
+ // But leave symbols alone, we only want to keep the font style for letters and numbers.
+ || (bIsSymbol && aStr==u"CharFontName")
+ );
+}
+uno::Sequence< beans::PropertyValue > ListLevel::GetCharStyleProperties( )
+{
+ PropertyValueVector_t rProperties;
+
+ const uno::Sequence< beans::PropertyValue > vPropVals = PropertyMap::GetPropertyValues();
+ const bool bIsSymbol(GetBulletChar().getLength() <= 1);
+ for( const auto& rPropNal : vPropVals )
+ if (! IgnoreForCharStyle(rPropNal.Name, bIsSymbol))
+ rProperties.emplace_back(rPropNal.Name, 0, rPropNal.Value, beans::PropertyState_DIRECT_VALUE);
+
+ return comphelper::containerToSequence(rProperties);
+}
+
+uno::Sequence<beans::PropertyValue> ListLevel::GetLevelProperties(bool bDefaults)
+{
+ std::vector<beans::PropertyValue> aNumberingProperties;
+
+ if (m_nIStartAt >= 0)
+ aNumberingProperties.push_back(lcl_makePropVal<sal_Int16>(PROP_START_WITH, m_nIStartAt) );
+ else if (bDefaults)
+ aNumberingProperties.push_back(lcl_makePropVal<sal_Int16>(PROP_START_WITH, 0));
+
+ sal_Int16 nNumberFormat = -1;
+ if (m_nNFC == NS_ooxml::LN_Value_ST_NumberFormat_custom)
+ {
+ nNumberFormat = ConversionHelper::ConvertCustomNumberFormat(m_aCustomNumberFormat);
+ }
+ else
+ {
+ nNumberFormat = ConversionHelper::ConvertNumberingType(m_nNFC);
+ }
+ if( m_nNFC >= 0)
+ {
+ if (m_xGraphicBitmap.is())
+ nNumberFormat = style::NumberingType::BITMAP;
+ aNumberingProperties.push_back(lcl_makePropVal(PROP_NUMBERING_TYPE, nNumberFormat));
+ }
+
+ // todo: this is not the bullet char
+ if( nNumberFormat == style::NumberingType::CHAR_SPECIAL )
+ {
+ if (!GetBulletChar().isEmpty())
+ {
+ aNumberingProperties.push_back(lcl_makePropVal(PROP_BULLET_CHAR, m_sBulletChar->copy(0, 1)));
+ }
+ else
+ {
+ // If w:lvlText's value is null - set bullet char to zero.
+ aNumberingProperties.push_back(lcl_makePropVal<sal_Unicode>(PROP_BULLET_CHAR, 0));
+ }
+ }
+ if (m_xGraphicBitmap.is())
+ {
+ aNumberingProperties.push_back(lcl_makePropVal(PROP_GRAPHIC_BITMAP, m_xGraphicBitmap));
+ aNumberingProperties.push_back(lcl_makePropVal(PROP_GRAPHIC_SIZE, m_aGraphicSize));
+ }
+
+ if (m_nTabstop.has_value())
+ aNumberingProperties.push_back(lcl_makePropVal(PROP_LISTTAB_STOP_POSITION, *m_nTabstop));
+ else if (bDefaults)
+ aNumberingProperties.push_back(lcl_makePropVal<sal_Int16>(PROP_LISTTAB_STOP_POSITION, 0));
+
+ //TODO: handling of nFLegal?
+ //TODO: nFNoRestart lower levels do not restart when higher levels are incremented, like:
+ //1.
+ //1.1
+ //2.2
+ //2.3
+ //3.4
+
+// TODO: sRGBXchNums; array of inherited numbers
+
+// nXChFollow; following character 0 - tab, 1 - space, 2 - nothing
+ if (bDefaults || m_nXChFollow != SvxNumberFormat::LISTTAB)
+ aNumberingProperties.push_back(lcl_makePropVal(PROP_LEVEL_FOLLOW, m_nXChFollow));
+
+ PropertyIds const aReadIds[] =
+ {
+ PROP_ADJUST, PROP_INDENT_AT, PROP_FIRST_LINE_INDENT,
+ PROP_FIRST_LINE_OFFSET, PROP_LEFT_MARGIN
+ };
+ for(PropertyIds const & rReadId : aReadIds) {
+ std::optional<PropertyMap::Property> aProp = getProperty(rReadId);
+ if (aProp)
+ aNumberingProperties.emplace_back( getPropertyName(aProp->first), 0, aProp->second, beans::PropertyState_DIRECT_VALUE );
+ else if (rReadId == PROP_FIRST_LINE_INDENT && bDefaults)
+ // Writer default is -360 twips, Word default seems to be 0.
+ aNumberingProperties.emplace_back("FirstLineIndent", 0, uno::Any(static_cast<sal_Int32>(0)), beans::PropertyState_DIRECT_VALUE);
+ else if (rReadId == PROP_INDENT_AT && bDefaults)
+ // Writer default is 720 twips, Word default seems to be 0.
+ aNumberingProperties.emplace_back("IndentAt", 0,
+ uno::Any(static_cast<sal_Int32>(0)),
+ beans::PropertyState_DIRECT_VALUE);
+ }
+
+ std::optional<PropertyMap::Property> aPropFont = getProperty(PROP_CHAR_FONT_NAME);
+ if (aPropFont)
+ aNumberingProperties.emplace_back( getPropertyName(PROP_BULLET_FONT_NAME), 0, aPropFont->second, beans::PropertyState_DIRECT_VALUE );
+
+ return comphelper::containerToSequence(aNumberingProperties);
+}
+
+// Add the properties only if they do not already exist in the sequence.
+void ListLevel::AddParaProperties( uno::Sequence< beans::PropertyValue >* props )
+{
+ uno::Sequence< beans::PropertyValue >& aProps = *props;
+
+ OUString sFirstLineIndent = getPropertyName(
+ PROP_FIRST_LINE_INDENT );
+ OUString sIndentAt = getPropertyName(
+ PROP_INDENT_AT );
+
+ bool hasFirstLineIndent = lcl_findProperty( aProps, sFirstLineIndent );
+ bool hasIndentAt = lcl_findProperty( aProps, sIndentAt );
+
+ if( hasFirstLineIndent && hasIndentAt )
+ return; // has them all, nothing to add
+
+ const uno::Sequence< beans::PropertyValue > aParaProps = m_pParaStyle->pProperties->GetPropertyValues( );
+
+ // ParaFirstLineIndent -> FirstLineIndent
+ // ParaLeftMargin -> IndentAt
+
+ OUString sParaIndent = getPropertyName(
+ PROP_PARA_FIRST_LINE_INDENT );
+ OUString sParaLeftMargin = getPropertyName(
+ PROP_PARA_LEFT_MARGIN );
+
+ for ( const auto& rParaProp : aParaProps )
+ {
+ if ( !hasFirstLineIndent && rParaProp.Name == sParaIndent )
+ {
+ aProps.realloc( aProps.getLength() + 1 );
+ auto pProps = aProps.getArray();
+ pProps[aProps.getLength( ) - 1] = rParaProp;
+ pProps[aProps.getLength( ) - 1].Name = sFirstLineIndent;
+ }
+ else if ( !hasIndentAt && rParaProp.Name == sParaLeftMargin )
+ {
+ aProps.realloc( aProps.getLength() + 1 );
+ auto pProps = aProps.getArray();
+ pProps[aProps.getLength( ) - 1] = rParaProp;
+ pProps[aProps.getLength( ) - 1].Name = sIndentAt;
+ }
+
+ }
+}
+
+NumPicBullet::NumPicBullet()
+ : m_nId(0)
+{
+}
+
+NumPicBullet::~NumPicBullet()
+{
+}
+
+void NumPicBullet::SetId(sal_Int32 nId)
+{
+ m_nId = nId;
+}
+
+void NumPicBullet::SetShape(uno::Reference<drawing::XShape> const& xShape)
+{
+ m_xShape = xShape;
+}
+
+
+//--------------------------------------- AbstractListDef implementation
+
+AbstractListDef::AbstractListDef( ) :
+ m_nId( -1 )
+{
+}
+
+AbstractListDef::~AbstractListDef( )
+{
+}
+
+void AbstractListDef::SetValue( sal_uInt32 nSprmId )
+{
+ switch( nSprmId )
+ {
+ case NS_ooxml::LN_CT_AbstractNum_tmpl:
+ break;
+ default:
+ OSL_FAIL( "this line should never be reached");
+ }
+}
+
+ListLevel::Pointer AbstractListDef::GetLevel( sal_uInt16 nLvl )
+{
+ ListLevel::Pointer pLevel;
+ if ( m_aLevels.size( ) > nLvl )
+ pLevel = m_aLevels[ nLvl ];
+ return pLevel;
+}
+
+void AbstractListDef::AddLevel( sal_uInt16 nLvl )
+{
+ if ( nLvl >= m_aLevels.size() )
+ m_aLevels.resize( nLvl+1 );
+
+ if (!m_aLevels[nLvl])
+ {
+ m_aLevels[nLvl] = new ListLevel;
+ }
+
+ m_pCurrentLevel = m_aLevels[nLvl];
+}
+
+uno::Sequence<uno::Sequence<beans::PropertyValue>> AbstractListDef::GetPropertyValues(bool bDefaults)
+{
+ uno::Sequence< uno::Sequence< beans::PropertyValue > > result( sal_Int32( m_aLevels.size( ) ) );
+ uno::Sequence< beans::PropertyValue >* aResult = result.getArray( );
+
+ int nLevels = m_aLevels.size( );
+ for ( int i = 0; i < nLevels; i++ )
+ {
+ if (m_aLevels[i])
+ aResult[i] = m_aLevels[i]->GetProperties(bDefaults);
+ }
+
+ return result;
+}
+
+const OUString& AbstractListDef::MapListId(OUString const& rId)
+{
+ if (!m_oListId)
+ {
+ m_oListId = rId;
+ }
+ return *m_oListId;
+}
+
+//---------------------------------------------- ListDef implementation
+
+ListDef::ListDef( )
+{
+}
+
+ListDef::~ListDef( )
+{
+}
+
+const OUString & ListDef::GetStyleName(sal_Int32 const nId,
+ uno::Reference<container::XNameContainer> const& xStyles)
+{
+ if (xStyles.is())
+ {
+ OUString sStyleName = "WWNum" + OUString::number( nId );
+
+ while (xStyles->hasByName(sStyleName)) // unique
+ {
+ sStyleName += "a";
+ }
+
+ m_StyleName = sStyleName;
+ }
+ else
+ {
+// fails in rtftok test assert(!m_StyleName.isEmpty()); // must be inited first
+ }
+
+ return m_StyleName;
+}
+
+uno::Sequence<uno::Sequence<beans::PropertyValue>> ListDef::GetMergedPropertyValues()
+{
+ if (!m_pAbstractDef)
+ return uno::Sequence< uno::Sequence< beans::PropertyValue > >();
+
+ // [1] Call the same method on the abstract list
+ uno::Sequence<uno::Sequence<beans::PropertyValue>> aAbstract
+ = m_pAbstractDef->GetPropertyValues(/*bDefaults=*/true);
+ auto aAbstractRange = asNonConstRange(aAbstract);
+
+ // [2] Call the upper class method
+ uno::Sequence<uno::Sequence<beans::PropertyValue>> aThis
+ = AbstractListDef::GetPropertyValues(/*bDefaults=*/false);
+
+ // Merge the results of [2] in [1]
+ sal_Int32 nThisCount = aThis.getLength( );
+ sal_Int32 nAbstractCount = aAbstract.getLength( );
+ for ( sal_Int32 i = 0; i < nThisCount && i < nAbstractCount; i++ )
+ {
+ uno::Sequence< beans::PropertyValue > level = aThis[i];
+ if (level.hasElements() && GetLevel(i)->HasValues())
+ {
+ // If the element contains something, merge it, but ignore stub overrides.
+ lcl_mergeProperties( level, aAbstractRange[i] );
+ }
+ }
+
+ return aAbstract;
+}
+
+static uno::Reference< container::XNameContainer > lcl_getUnoNumberingStyles(
+ uno::Reference<lang::XMultiServiceFactory> const& xFactory)
+{
+ uno::Reference< container::XNameContainer > xStyles;
+
+ try
+ {
+ uno::Reference< style::XStyleFamiliesSupplier > xFamilies( xFactory, uno::UNO_QUERY_THROW );
+ uno::Any oFamily = xFamilies->getStyleFamilies( )->getByName("NumberingStyles");
+
+ oFamily >>= xStyles;
+ }
+ catch ( const uno::Exception & )
+ {
+ }
+
+ return xStyles;
+}
+
+/// Rank the list in terms of suitability for becoming the Outline numbering rule in LO.
+sal_uInt16 ListDef::GetChapterNumberingWeight() const
+{
+ sal_Int16 nWeight = 0;
+ const sal_Int8 nAbstLevels = m_pAbstractDef ? m_pAbstractDef->Size() : 0;
+ for (sal_Int8 nLevel = 0; nLevel < nAbstLevels; ++nLevel)
+ {
+ const ListLevel::Pointer pAbsLevel = m_pAbstractDef->GetLevel(nLevel);
+ if (!pAbsLevel)
+ continue;
+ const StyleSheetEntryPtr pParaStyle = pAbsLevel->GetParaStyle();
+ if (!pParaStyle)
+ continue;
+ const StyleSheetPropertyMap& rProps = *pParaStyle->pProperties;
+ // In LO, the level's paraStyle outlineLevel always matches this listLevel.
+ // An undefined listLevel is treated as the first level.
+ sal_Int8 nListLevel = std::clamp<sal_Int8>(rProps.GetListLevel(), 0, 9);
+ if (nListLevel != nLevel || rProps.GetOutlineLevel() != nLevel)
+ return 0;
+ else if (pAbsLevel->GetNumberingType(style::NumberingType::NUMBER_NONE)
+ != style::NumberingType::NUMBER_NONE)
+ {
+ // Arbitrarily chosen weighting factors - trying to round-trip LO choices if possible.
+ // LibreOffice always saves Outline rule (usually containing heading styles) as numId 1.
+ sal_uInt16 nWeightingFactor = GetId() == 1 ? 8 : 1;
+ if (pParaStyle->sStyleIdentifierD.startsWith("Heading") )
+ ++nWeightingFactor;
+ nWeight += nWeightingFactor;
+ }
+ }
+ return nWeight;
+}
+
+void ListDef::CreateNumberingRules( DomainMapper& rDMapper,
+ uno::Reference<lang::XMultiServiceFactory> const& xFactory, sal_Int16 nOutline)
+{
+ // Get the UNO Numbering styles
+ uno::Reference< container::XNameContainer > xStyles = lcl_getUnoNumberingStyles( xFactory );
+
+ // Do the whole thing
+ if( !(!m_xNumRules.is() && xFactory.is() && xStyles.is( )) )
+ return;
+
+ try
+ {
+ // Create the numbering style
+ uno::Reference< beans::XPropertySet > xStyle (
+ xFactory->createInstance("com.sun.star.style.NumberingStyle"),
+ uno::UNO_QUERY_THROW );
+
+ if (GetId() == nOutline)
+ m_StyleName = "Outline"; //SwNumRule.GetOutlineRuleName()
+ else
+ xStyles->insertByName(GetStyleName(GetId(), xStyles), css::uno::Any(xStyle));
+
+ uno::Any oStyle = xStyles->getByName(GetStyleName());
+ xStyle.set( oStyle, uno::UNO_QUERY_THROW );
+
+ // Get the default OOo Numbering style rules
+ uno::Any aRules = xStyle->getPropertyValue( getPropertyName( PROP_NUMBERING_RULES ) );
+ aRules >>= m_xNumRules;
+
+ uno::Sequence<uno::Sequence<beans::PropertyValue>> aProps = GetMergedPropertyValues();
+
+ sal_Int32 nAbstLevels = m_pAbstractDef ? m_pAbstractDef->Size() : 0;
+ sal_Int32 nLevel = 0;
+ while ( nLevel < nAbstLevels )
+ {
+ ListLevel::Pointer pAbsLevel = m_pAbstractDef->GetLevel( nLevel );
+ ListLevel::Pointer pLevel = GetLevel( nLevel );
+
+ // Get the merged level properties
+ auto aLvlProps = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aProps[nLevel]);
+
+ // Get the char style
+ auto aAbsCharStyleProps = pAbsLevel
+ ? pAbsLevel->GetCharStyleProperties()
+ : uno::Sequence<beans::PropertyValue>();
+ if ( pLevel )
+ {
+ uno::Sequence< beans::PropertyValue >& rAbsCharStyleProps = aAbsCharStyleProps;
+ uno::Sequence< beans::PropertyValue > aCharStyleProps =
+ pLevel->GetCharStyleProperties( );
+ uno::Sequence< beans::PropertyValue >& rCharStyleProps = aCharStyleProps;
+ lcl_mergeProperties( rAbsCharStyleProps, rCharStyleProps );
+ }
+
+ // Change the sequence into a vector
+ auto aStyleProps
+ = comphelper::sequenceToContainer<PropertyValueVector_t>(aAbsCharStyleProps);
+
+ //create (or find) a character style containing the character
+ // attributes of the symbol and apply it to the numbering level
+ OUString sStyle = rDMapper.getOrCreateCharStyle(aStyleProps, /*bAlwaysCreate=*/true);
+ aLvlProps.push_back(
+ comphelper::makePropertyValue(getPropertyName(PROP_CHAR_STYLE_NAME), sStyle));
+
+ OUString sText = pAbsLevel
+ ? pAbsLevel->GetBulletChar()
+ : OUString();
+ // Inherit <w:lvlText> from the abstract level in case the override would be empty.
+ if (pLevel && pLevel->HasBulletChar())
+ sText = pLevel->GetBulletChar( );
+
+ aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LIST_FORMAT), sText));
+
+ aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_POSITION_AND_SPACE_MODE), sal_Int16(text::PositionAndSpaceMode::LABEL_ALIGNMENT)));
+
+ // Replace the numbering rules for the level
+ m_xNumRules->replaceByIndex(nLevel, uno::Any(comphelper::containerToSequence(aLvlProps)));
+
+ // Handle the outline level here
+ if (GetId() == nOutline && pAbsLevel && pAbsLevel->GetParaStyle())
+ {
+ uno::Reference< text::XChapterNumberingSupplier > xOutlines (
+ xFactory, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexReplace > xOutlineRules =
+ xOutlines->getChapterNumberingRules( );
+
+ StyleSheetEntryPtr pParaStyle = pAbsLevel->GetParaStyle( );
+ pParaStyle->bAssignedAsChapterNumbering = true;
+ aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEADING_STYLE_NAME), pParaStyle->sConvertedStyleName));
+
+ xOutlineRules->replaceByIndex(nLevel, uno::Any(comphelper::containerToSequence(aLvlProps)));
+ }
+
+ nLevel++;
+ }
+
+ // Create the numbering style for these rules
+ OUString sNumRulesName = getPropertyName( PROP_NUMBERING_RULES );
+ xStyle->setPropertyValue( sNumRulesName, uno::Any( m_xNumRules ) );
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "" );
+ assert( !"Incorrect argument to UNO call" );
+ }
+ catch( const uno::RuntimeException& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "" );
+ assert( !"Incorrect argument to UNO call" );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "" );
+ }
+
+}
+
+//------------------------------------- NumberingManager implementation
+
+
+ListsManager::ListsManager(DomainMapper& rDMapper,
+ const uno::Reference<lang::XMultiServiceFactory> & xFactory)
+ : LoggedProperties("ListsManager")
+ , LoggedTable("ListsManager")
+ , m_rDMapper(rDMapper)
+ , m_xFactory(xFactory)
+{
+}
+
+ListsManager::~ListsManager( )
+{
+ DisposeNumPicBullets();
+}
+
+void ListsManager::DisposeNumPicBullets( )
+{
+ uno::Reference<drawing::XShape> xShape;
+ for (const auto& rNumPicBullet : m_aNumPicBullets)
+ {
+ xShape = rNumPicBullet->GetShape();
+ if (xShape.is())
+ {
+ uno::Reference<lang::XComponent> xShapeComponent(xShape, uno::UNO_QUERY);
+ xShapeComponent->dispose();
+ }
+ }
+}
+
+void ListsManager::lcl_attribute( Id nName, Value& rVal )
+{
+ ListLevel::Pointer pCurrentLvl;
+
+ if (nName != NS_ooxml::LN_CT_NumPicBullet_numPicBulletId)
+ {
+ OSL_ENSURE( m_pCurrentDefinition, "current entry has to be set here");
+ if(!m_pCurrentDefinition)
+ return ;
+ pCurrentLvl = m_pCurrentDefinition->GetCurrentLevel( );
+ }
+ else
+ {
+ SAL_WARN_IF(!m_pCurrentNumPicBullet, "writerfilter", "current entry has to be set here");
+ if (!m_pCurrentNumPicBullet)
+ return;
+ }
+ int nIntValue = rVal.getInt();
+
+
+ switch(nName)
+ {
+ case NS_ooxml::LN_CT_LevelText_val:
+ {
+ if(pCurrentLvl)
+ {
+ //if the BulletChar is a soft-hyphen (0xad)
+ //replace it with a hard-hyphen (0x2d)
+ //-> this fixes missing hyphen export in PDF etc.
+ // see tdf#101626
+ std::string sLevelText = rVal.getString().replace(0xad, 0x2d).toUtf8().getStr();
+
+ // DOCX level-text contains levels definition in format "%1.%2.%3"
+ // we need to convert it to LO internal representation: "%1%.%2%.%3%"
+ static const std::regex aTokenRegex("(%\\d)");
+ sLevelText = std::regex_replace(sLevelText, aTokenRegex, "$1%");
+ pCurrentLvl->SetBulletChar( OUString::fromUtf8(sLevelText) );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Lvl_start:
+ case NS_ooxml::LN_CT_Lvl_numFmt:
+ case NS_ooxml::LN_CT_NumFmt_format:
+ case NS_ooxml::LN_CT_NumFmt_val:
+ case NS_ooxml::LN_CT_Lvl_isLgl:
+ case NS_ooxml::LN_CT_Lvl_legacy:
+ if ( pCurrentLvl )
+ {
+ if (nName == NS_ooxml::LN_CT_NumFmt_format)
+ {
+ pCurrentLvl->SetCustomNumberFormat(rVal.getString());
+ }
+ else
+ {
+ pCurrentLvl->SetValue(nName, sal_Int32(nIntValue));
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Num_numId:
+ m_pCurrentDefinition->SetId( rVal.getString().toInt32( ) );
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_nsid:
+ m_pCurrentDefinition->SetId( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_tmpl:
+ AbstractListDef::SetValue( nName );
+ break;
+ case NS_ooxml::LN_CT_NumLvl_ilvl:
+ //add a new level to the level vector and make it the current one
+ m_pCurrentDefinition->AddLevel(rVal.getString().toUInt32());
+ break;
+ case NS_ooxml::LN_CT_Lvl_ilvl:
+ m_pCurrentDefinition->AddLevel(rVal.getString().toUInt32());
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_abstractNumId:
+ {
+ // This one corresponds to the AbstractNum Id definition
+ // The reference to the abstract num is in the sprm method
+ sal_Int32 nVal = rVal.getString().toInt32();
+ m_pCurrentDefinition->SetId( nVal );
+ }
+ break;
+ case NS_ooxml::LN_CT_Ind_start:
+ case NS_ooxml::LN_CT_Ind_left:
+ if ( pCurrentLvl )
+ pCurrentLvl->Insert(
+ PROP_INDENT_AT, uno::Any( ConversionHelper::convertTwipToMM100( nIntValue ) ));
+ break;
+ case NS_ooxml::LN_CT_Ind_hanging:
+ if ( pCurrentLvl )
+ pCurrentLvl->Insert(
+ PROP_FIRST_LINE_INDENT, uno::Any( - ConversionHelper::convertTwipToMM100( nIntValue ) ));
+ break;
+ case NS_ooxml::LN_CT_Ind_firstLine:
+ if ( pCurrentLvl )
+ pCurrentLvl->Insert(
+ PROP_FIRST_LINE_INDENT, uno::Any( ConversionHelper::convertTwipToMM100( nIntValue ) ));
+ break;
+ case NS_ooxml::LN_CT_Lvl_tplc: //template code - unsupported
+ case NS_ooxml::LN_CT_Lvl_tentative: //marks level as unused in the document - unsupported
+ break;
+ case NS_ooxml::LN_CT_TabStop_pos:
+ {
+ //no paragraph attributes in ListTable char style sheets
+ if ( pCurrentLvl )
+ pCurrentLvl->SetValue( nName,
+ ConversionHelper::convertTwipToMM100( nIntValue ) );
+ }
+ break;
+ case NS_ooxml::LN_CT_TabStop_val:
+ {
+ // TODO Do something of that
+ }
+ break;
+ case NS_ooxml::LN_CT_NumPicBullet_numPicBulletId:
+ m_pCurrentNumPicBullet->SetId(rVal.getString().toInt32());
+ break;
+ default:
+ SAL_WARN("writerfilter", "ListsManager::lcl_attribute: unhandled token: " << nName);
+ }
+}
+
+void ListsManager::lcl_sprm( Sprm& rSprm )
+{
+ //fill the attributes of the style sheet
+ sal_uInt32 nSprmId = rSprm.getId();
+ if( !(m_pCurrentDefinition ||
+ nSprmId == NS_ooxml::LN_CT_Numbering_abstractNum ||
+ nSprmId == NS_ooxml::LN_CT_Numbering_num ||
+ (nSprmId == NS_ooxml::LN_CT_NumPicBullet_pict && m_pCurrentNumPicBullet) ||
+ nSprmId == NS_ooxml::LN_CT_Numbering_numPicBullet))
+ return;
+
+ static bool bIsStartVisited = false;
+ sal_Int32 nIntValue = rSprm.getValue()->getInt();
+ switch( nSprmId )
+ {
+ case NS_ooxml::LN_CT_Numbering_abstractNum:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if(pProperties)
+ {
+ //create a new Abstract list entry
+ OSL_ENSURE( !m_pCurrentDefinition, "current entry has to be NULL here");
+ m_pCurrentDefinition = new AbstractListDef;
+ pProperties->resolve( *this );
+ //append it to the table
+ m_aAbstractLists.push_back( m_pCurrentDefinition );
+ m_pCurrentDefinition = AbstractListDef::Pointer();
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Numbering_num:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if(pProperties)
+ {
+ // Create a new list entry
+ OSL_ENSURE( !m_pCurrentDefinition, "current entry has to be NULL here");
+ ListDef::Pointer listDef( new ListDef );
+ m_pCurrentDefinition = listDef.get();
+ pProperties->resolve( *this );
+ //append it to the table
+ m_aLists.push_back( listDef );
+
+ m_pCurrentDefinition = AbstractListDef::Pointer();
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Numbering_numPicBullet:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ NumPicBullet::Pointer numPicBullet(new NumPicBullet());
+ m_pCurrentNumPicBullet = numPicBullet;
+ pProperties->resolve(*this);
+ m_aNumPicBullets.push_back(numPicBullet);
+ m_pCurrentNumPicBullet = NumPicBullet::Pointer();
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_NumPicBullet_pict:
+ {
+ uno::Reference<drawing::XShape> xShape = m_rDMapper.PopPendingShape();
+
+ m_pCurrentNumPicBullet->SetShape(xShape);
+ }
+ break;
+ case NS_ooxml::LN_CT_Lvl_lvlPicBulletId:
+ if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
+ {
+ uno::Reference<drawing::XShape> xShape;
+ for (const auto& rNumPicBullet : m_aNumPicBullets)
+ {
+ if (rNumPicBullet->GetId() == nIntValue)
+ {
+ xShape = rNumPicBullet->GetShape();
+ break;
+ }
+ }
+ if (xShape.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
+ try
+ {
+ uno::Any aAny = xPropertySet->getPropertyValue("Graphic");
+ if (aAny.has<uno::Reference<graphic::XGraphic>>() && pCurrentLevel)
+ {
+ auto xGraphic = aAny.get<uno::Reference<graphic::XGraphic>>();
+ if (xGraphic.is())
+ {
+ uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
+ pCurrentLevel->SetGraphicBitmap(xBitmap);
+ }
+ }
+ }
+ catch (const beans::UnknownPropertyException&)
+ {}
+
+ // Respect only the aspect ratio of the picture, not its size.
+ awt::Size aPrefSize = xShape->getSize();
+ if ( aPrefSize.Height * aPrefSize.Width != 0 )
+ {
+ // See SwDefBulletConfig::InitFont(), default height is 14.
+ const int nFontHeight = 14;
+ // Point -> mm100.
+ const int nHeight = nFontHeight * 35;
+ int nWidth = (nHeight * aPrefSize.Width) / aPrefSize.Height;
+
+ awt::Size aSize( o3tl::toTwips(nWidth, o3tl::Length::mm100), o3tl::toTwips(nHeight, o3tl::Length::mm100) );
+ pCurrentLevel->SetGraphicSize( aSize );
+ }
+ else
+ {
+ awt::Size aSize( o3tl::toTwips(aPrefSize.Width, o3tl::Length::mm100), o3tl::toTwips(aPrefSize.Height, o3tl::Length::mm100) );
+ pCurrentLevel->SetGraphicSize( aSize );
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Num_abstractNumId:
+ {
+ sal_Int32 nAbstractNumId = rSprm.getValue()->getInt();
+ ListDef* pListDef = dynamic_cast< ListDef* >( m_pCurrentDefinition.get( ) );
+ if ( pListDef != nullptr )
+ {
+ // The current def should be a ListDef
+ pListDef->SetAbstractDefinition(
+ GetAbstractList( nAbstractNumId ) );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_multiLevelType:
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_tmpl:
+ AbstractListDef::SetValue( nSprmId );
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_lvl:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if(pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_Lvl_start:
+ if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
+ pCurrentLevel->SetValue( nSprmId, nIntValue );
+ bIsStartVisited = true;
+ break;
+ case NS_ooxml::LN_CT_Lvl_numFmt:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+ }
+ if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
+ {
+ if( !bIsStartVisited )
+ {
+ pCurrentLevel->SetValue( NS_ooxml::LN_CT_Lvl_start, 0 );
+ bIsStartVisited = true;
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Lvl_isLgl:
+ case NS_ooxml::LN_CT_Lvl_legacy:
+ if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
+ {
+ pCurrentLevel->SetValue(nSprmId, nIntValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_Lvl_suff:
+ {
+ if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
+ {
+ SvxNumberFormat::LabelFollowedBy value = SvxNumberFormat::LISTTAB;
+ if( rSprm.getValue()->getString() == "tab" )
+ value = SvxNumberFormat::LISTTAB;
+ else if( rSprm.getValue()->getString() == "space" )
+ value = SvxNumberFormat::SPACE;
+ else if( rSprm.getValue()->getString() == "nothing" )
+ value = SvxNumberFormat::NOTHING;
+ else
+ SAL_WARN( "writerfilter", "Unknown ST_LevelSuffix value "
+ << rSprm.getValue()->getString());
+ pCurrentLevel->SetValue( nSprmId, value );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Lvl_lvlText:
+ case NS_ooxml::LN_CT_Lvl_rPr : //contains LN_EG_RPrBase_rFonts
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if(pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_NumLvl_lvl:
+ {
+ // overwrite level
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if(pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_Lvl_lvlJc:
+ {
+ sal_Int16 nValue = text::HoriOrientation::NONE;
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_Jc_left:
+ case NS_ooxml::LN_Value_ST_Jc_start:
+ nValue = text::HoriOrientation::LEFT;
+ break;
+ case NS_ooxml::LN_Value_ST_Jc_center:
+ nValue = text::HoriOrientation::CENTER;
+ break;
+ case NS_ooxml::LN_Value_ST_Jc_right:
+ case NS_ooxml::LN_Value_ST_Jc_end:
+ nValue = text::HoriOrientation::RIGHT;
+ break;
+ }
+
+ if (nValue != text::HoriOrientation::NONE)
+ {
+ if (ListLevel::Pointer pLevel = m_pCurrentDefinition->GetCurrentLevel())
+ {
+ pLevel->Insert(
+ PROP_ADJUST, uno::Any( nValue ) );
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Lvl_pPr:
+ case NS_ooxml::LN_CT_PPrBase_ind:
+ {
+ //todo: how to handle paragraph properties within numbering levels (except LeftIndent and FirstLineIndent)?
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if(pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_PPrBase_tabs:
+ case NS_ooxml::LN_CT_Tabs_tab:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if(pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_Lvl_pStyle:
+ {
+ OUString sStyleName = rSprm.getValue( )->getString( );
+ if (ListLevel::Pointer pLevel = m_pCurrentDefinition->GetCurrentLevel())
+ {
+ StyleSheetTablePtr pStylesTable = m_rDMapper.GetStyleSheetTable( );
+ const StyleSheetEntryPtr pStyle = pStylesTable->FindStyleSheetByISTD( sStyleName );
+ pLevel->SetParaStyle( pStyle );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Num_lvlOverride:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_NumLvl_startOverride:
+ {
+ if(m_pCurrentDefinition)
+ {
+ if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
+ {
+ pCurrentLevel->SetValue(NS_ooxml::LN_CT_NumLvl_startOverride, nIntValue);
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_numStyleLink:
+ {
+ OUString sStyleName = rSprm.getValue( )->getString( );
+ m_pCurrentDefinition->SetNumStyleLink(sStyleName);
+ }
+ break;
+ case NS_ooxml::LN_CT_AbstractNum_styleLink:
+ {
+ OUString sStyleName = rSprm.getValue()->getString();
+ m_pCurrentDefinition->SetStyleLink(sStyleName);
+ }
+ break;
+ case NS_ooxml::LN_EG_RPrBase_rFonts: //contains font properties
+ case NS_ooxml::LN_EG_RPrBase_color:
+ case NS_ooxml::LN_EG_RPrBase_u:
+ case NS_ooxml::LN_EG_RPrBase_sz:
+ case NS_ooxml::LN_EG_RPrBase_lang:
+ case NS_ooxml::LN_EG_RPrBase_eastAsianLayout:
+ //no break!
+ default:
+ if (ListLevel::Pointer pCurrentLevel = m_pCurrentDefinition->GetCurrentLevel())
+ {
+ m_rDMapper.PushListProperties(pCurrentLevel.get());
+ m_rDMapper.sprm( rSprm );
+ m_rDMapper.PopListProperties();
+ }
+ }
+}
+
+void ListsManager::lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref )
+{
+ if( m_rDMapper.IsOOXMLImport() || m_rDMapper.IsRTFImport() )
+ {
+ ref->resolve(*this);
+ }
+ else
+ {
+ // Create AbstractListDef's
+ OSL_ENSURE( !m_pCurrentDefinition, "current entry has to be NULL here");
+ m_pCurrentDefinition = new AbstractListDef( );
+ ref->resolve(*this);
+ //append it to the table
+ m_aAbstractLists.push_back( m_pCurrentDefinition );
+ m_pCurrentDefinition = AbstractListDef::Pointer();
+ }
+}
+
+AbstractListDef::Pointer ListsManager::GetAbstractList( sal_Int32 nId )
+{
+ for (const auto& listDef : m_aAbstractLists)
+ {
+ if (listDef->GetId( ) == nId)
+ {
+ if (listDef->GetNumStyleLink().getLength() > 0)
+ {
+ // If the abstract num has a style linked, check the linked style's number id.
+ StyleSheetTablePtr pStylesTable = m_rDMapper.GetStyleSheetTable( );
+
+ const StyleSheetEntryPtr pStyleSheetEntry =
+ pStylesTable->FindStyleSheetByISTD(listDef->GetNumStyleLink() );
+
+ const StyleSheetPropertyMap* pStyleSheetProperties =
+ pStyleSheetEntry ? pStyleSheetEntry->pProperties.get() : nullptr;
+
+ if( pStyleSheetProperties && pStyleSheetProperties->GetListId() >= 0 )
+ {
+ ListDef::Pointer pList = GetList( pStyleSheetProperties->GetListId() );
+ if ( pList!=nullptr )
+ return pList->GetAbstractDefinition();
+ }
+
+ // In stylesheet we did not found anything useful. Try to find base abstractnum having this stylelink
+ for (const auto & baseListDef : m_aAbstractLists)
+ {
+ if (baseListDef->GetStyleLink() == listDef->GetNumStyleLink())
+ {
+ return baseListDef;
+ }
+ }
+ }
+
+ // Standalone abstract list
+ return listDef;
+ }
+ }
+
+ return nullptr;
+}
+
+ListDef::Pointer ListsManager::GetList( sal_Int32 nId )
+{
+ ListDef::Pointer pList;
+ if (nId == -1)
+ return pList;
+
+ int nLen = m_aLists.size( );
+ int i = 0;
+ while ( !pList && i < nLen )
+ {
+ if ( m_aLists[i]->GetId( ) == nId )
+ pList = m_aLists[i];
+ i++;
+ }
+
+ // nId 0 is only valid for abstractNum, not numId (which has an abstract definition)
+ assert(!pList || nId || !pList->GetAbstractDefinition() || m_rDMapper.IsRTFImport());
+
+ return pList;
+}
+
+void ListsManager::CreateNumberingRules( )
+{
+ // Try to determine which numId would best work as LO's Chapter Numbering Outline rule.
+ // (The best fix for many import bugs is just to prevent ANY assignment as chapter numbering.)
+ sal_Int16 nChosenAsChapterNumberingId = -1;
+ sal_uInt16 nHighestWeight = 5; // arbitrarily chosen minimum threshold
+ for (const auto& rList : m_aLists)
+ {
+ sal_uInt16 nWeight = rList->GetChapterNumberingWeight();
+ if (nWeight > nHighestWeight)
+ {
+ nHighestWeight = nWeight;
+ nChosenAsChapterNumberingId = rList->GetId();
+ //Optimization: if the weight cannot be beaten anymore, then quit early
+ if (nHighestWeight > 17)
+ break;
+ }
+ }
+
+ // Loop over the definitions
+ for ( const auto& rList : m_aLists )
+ {
+ rList->CreateNumberingRules(m_rDMapper, m_xFactory, nChosenAsChapterNumberingId);
+ }
+ m_rDMapper.GetStyleSheetTable()->ReApplyInheritedOutlineLevelFromChapterNumbering();
+ m_rDMapper.GetStyleSheetTable()->ApplyNumberingStyleNameToParaStyles();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/NumberingManager.hxx b/writerfilter/source/dmapper/NumberingManager.hxx
new file mode 100644
index 000000000..2c1e0af3c
--- /dev/null
+++ b/writerfilter/source/dmapper/NumberingManager.hxx
@@ -0,0 +1,250 @@
+/* -*- 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 "PropertyMap.hxx"
+
+#include "DomainMapper.hxx"
+#include "LoggedResources.hxx"
+#include "StyleSheetTable.hxx"
+
+#include <editeng/numitem.hxx>
+
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+
+namespace writerfilter::dmapper {
+
+class DomainMapper;
+class StyleSheetEntry;
+
+
+/** Class representing the numbering level properties.
+ */
+class ListLevel : public PropertyMap
+{
+ sal_Int32 m_nIStartAt; //LN_CT_Lvl_start
+ sal_Int32 m_nStartOverride;
+ sal_Int32 m_nNFC; //LN_CT_Lvl_numFmt
+ /// LN_CT_NumFmt_format, in case m_nNFC is custom.
+ OUString m_aCustomNumberFormat;
+ sal_Int16 m_nXChFollow; //LN_IXCHFOLLOW
+ std::optional<OUString> m_sBulletChar;
+ css::awt::Size m_aGraphicSize;
+ css::uno::Reference<css::awt::XBitmap> m_xGraphicBitmap;
+ std::optional<sal_Int32> m_nTabstop;
+ tools::SvRef< StyleSheetEntry > m_pParaStyle;
+ bool m_bHasValues = false;
+
+public:
+
+ typedef tools::SvRef< ListLevel > Pointer;
+
+ ListLevel() :
+ m_nIStartAt(-1)
+ ,m_nStartOverride(-1)
+ ,m_nNFC(-1)
+ ,m_nXChFollow(SvxNumberFormat::LISTTAB)
+ {}
+
+ // Setters for the import
+ void SetValue( Id nId, sal_Int32 nValue );
+ void SetCustomNumberFormat(const OUString& rValue);
+ void SetBulletChar( const OUString& sValue ) { m_sBulletChar = sValue; };
+ void SetGraphicSize( const css::awt::Size& aValue ) { m_aGraphicSize = aValue; };
+
+ void SetGraphicBitmap(css::uno::Reference<css::awt::XBitmap> const& xGraphicBitmap)
+ { m_xGraphicBitmap = xGraphicBitmap; }
+ void SetParaStyle( const tools::SvRef< StyleSheetEntry >& pStyle );
+
+ // Getters
+ sal_Int16 GetNumberingType(sal_Int16 nDefault) const;
+ bool HasBulletChar() const { return m_sBulletChar.has_value(); };
+ OUString GetBulletChar( ) const { return m_sBulletChar.has_value()? *m_sBulletChar : OUString(); };
+ const tools::SvRef< StyleSheetEntry >& GetParaStyle( ) const { return m_pParaStyle; };
+ sal_Int32 GetStartOverride() const { return m_nStartOverride; };
+ /// Determines if SetValue() was called at least once.
+ bool HasValues() const;
+
+ // UNO mapping functions
+ css::uno::Sequence<css::beans::PropertyValue> GetProperties(bool bDefaults);
+
+ css::uno::Sequence<css::beans::PropertyValue> GetCharStyleProperties();
+private:
+
+ css::uno::Sequence<css::beans::PropertyValue> GetLevelProperties(bool bDefaults);
+
+ void AddParaProperties(css::uno::Sequence<css::beans::PropertyValue>* pProps);
+};
+
+/// Represents a numbering picture bullet: an id and a graphic.
+class NumPicBullet final : public virtual SvRefBase
+{
+public:
+ typedef tools::SvRef<NumPicBullet> Pointer;
+ NumPicBullet();
+ ~NumPicBullet() override;
+
+ void SetId(sal_Int32 nId);
+ sal_Int32 GetId() const { return m_nId;}
+ void SetShape(css::uno::Reference<css::drawing::XShape> const& xShape);
+ const css::uno::Reference<css::drawing::XShape>& GetShape() const { return m_xShape; }
+private:
+ sal_Int32 m_nId;
+ css::uno::Reference<css::drawing::XShape> m_xShape;
+};
+
+class AbstractListDef : public virtual SvRefBase
+{
+private:
+ // The ID member reflects either the abstractNumId or the numId
+ // depending on the use of the class
+ sal_Int32 m_nId;
+
+ // Properties of each level. This can also reflect the overridden
+ // levels of a numbering.
+ ::std::vector< ListLevel::Pointer > m_aLevels;
+
+ // Only used during the numbering import
+ ListLevel::Pointer m_pCurrentLevel;
+
+ // The style name linked to.
+ OUString m_sNumStyleLink;
+
+ // This abstract numbering is a base definition for this style
+ OUString m_sStyleLink;
+
+ /// list id to use for all derived numbering definitions
+ std::optional<OUString> m_oListId;
+
+public:
+ typedef tools::SvRef< AbstractListDef > Pointer;
+
+ AbstractListDef( );
+ virtual ~AbstractListDef( ) override;
+
+ // Setters using during the import
+ void SetId( sal_Int32 nId ) { m_nId = nId; };
+ static void SetValue( sal_uInt32 nSprmId );
+
+ // Accessors
+ sal_Int32 GetId( ) const { return m_nId; };
+
+ sal_Int16 Size( ) { return sal_Int16( m_aLevels.size( ) ); };
+ ListLevel::Pointer GetLevel( sal_uInt16 nLvl );
+ void AddLevel( sal_uInt16 nLvl );
+
+ const ListLevel::Pointer& GetCurrentLevel( ) const { return m_pCurrentLevel; };
+
+ css::uno::Sequence< css::uno::Sequence<css::beans::PropertyValue> > GetPropertyValues(bool bDefaults);
+
+ void SetNumStyleLink(const OUString& sValue) { m_sNumStyleLink = sValue; };
+ const OUString& GetNumStyleLink() const { return m_sNumStyleLink; };
+
+ void SetStyleLink(const OUString& sValue) { m_sStyleLink = sValue; };
+ const OUString& GetStyleLink() const { return m_sStyleLink; };
+
+ const OUString& MapListId(OUString const& rId);
+};
+
+class ListDef : public AbstractListDef
+{
+private:
+ // Pointer to the abstract numbering
+ AbstractListDef::Pointer m_pAbstractDef;
+
+ // Cache for the UNO numbering rules
+ css::uno::Reference< css::container::XIndexReplace > m_xNumRules;
+
+ /// mapped list style name
+ OUString m_StyleName;
+
+public:
+ typedef tools::SvRef< ListDef > Pointer;
+
+ ListDef( );
+ virtual ~ListDef( ) override;
+
+ // Accessors
+ void SetAbstractDefinition( AbstractListDef::Pointer pAbstract ) { m_pAbstractDef = pAbstract; };
+ const AbstractListDef::Pointer& GetAbstractDefinition( ) const { return m_pAbstractDef; };
+
+ // Mapping functions
+ const OUString & GetStyleName() const { return m_StyleName; };
+ const OUString & GetStyleName(sal_Int32 nId, css::uno::Reference<css::container::XNameContainer> const& xStyles);
+
+ css::uno::Sequence< css::uno::Sequence<css::beans::PropertyValue> > GetMergedPropertyValues();
+
+ sal_uInt16 GetChapterNumberingWeight() const;
+ void CreateNumberingRules(DomainMapper& rDMapper, css::uno::Reference<css::lang::XMultiServiceFactory> const& xFactory, sal_Int16 nOutline);
+
+ const css::uno::Reference<css::container::XIndexReplace>& GetNumberingRules() const { return m_xNumRules; }
+
+};
+
+/** This class provides access to the defined numbering styles.
+ */
+class ListsManager :
+ public LoggedProperties,
+ public LoggedTable
+{
+private:
+
+ DomainMapper& m_rDMapper;
+ css::uno::Reference<css::lang::XMultiServiceFactory> m_xFactory;
+
+ // The numbering entries
+ std::vector< NumPicBullet::Pointer > m_aNumPicBullets;
+ std::vector< AbstractListDef::Pointer > m_aAbstractLists;
+ std::vector< ListDef::Pointer > m_aLists;
+
+
+ // These members are used for import only
+ AbstractListDef::Pointer m_pCurrentDefinition;
+ NumPicBullet::Pointer m_pCurrentNumPicBullet;
+
+ AbstractListDef::Pointer GetAbstractList( sal_Int32 nId );
+
+ // Properties
+ virtual void lcl_attribute( Id nName, Value & rVal ) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+ // Table
+ virtual void lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref) override;
+
+public:
+
+ ListsManager(DomainMapper& rDMapper, const css::uno::Reference<css::lang::XMultiServiceFactory>& xFactory);
+ virtual ~ListsManager() override;
+
+ typedef tools::SvRef< ListsManager > Pointer;
+
+ ListDef::Pointer GetList( sal_Int32 nId );
+
+ // Mapping methods
+ void CreateNumberingRules( );
+
+ // Dispose the NumPicBullets
+ void DisposeNumPicBullets( );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/OLEHandler.cxx b/writerfilter/source/dmapper/OLEHandler.cxx
new file mode 100644
index 000000000..8a495e32f
--- /dev/null
+++ b/writerfilter/source/dmapper/OLEHandler.cxx
@@ -0,0 +1,331 @@
+/* -*- 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 "OLEHandler.hxx"
+#include "DomainMapper.hxx"
+#include "GraphicHelpers.hxx"
+
+#include <oox/ole/oleobjecthelper.hxx>
+#include <ooxml/resourceids.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+#include <unotools/mediadescriptor.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/document/XEmbeddedObjectResolver.hpp>
+#include <com/sun/star/document/XEmbeddedObjectSupplier.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XImporter.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+
+
+OLEHandler::OLEHandler(DomainMapper& rDomainMapper) :
+LoggedProperties("OLEHandler"),
+ m_nWrapMode(text::WrapTextMode_THROUGHT),
+ m_rDomainMapper(rDomainMapper)
+{
+}
+
+
+OLEHandler::~OLEHandler()
+{
+}
+
+
+void OLEHandler::lcl_attribute(Id rName, Value & rVal)
+{
+ OUString sStringValue = rVal.getString();
+ switch( rName )
+ {
+ case NS_ooxml::LN_CT_OLEObject_Type:
+ break;
+ case NS_ooxml::LN_CT_OLEObject_ProgID:
+ m_sProgId = sStringValue;
+ break;
+ case NS_ooxml::LN_CT_OLEObject_ShapeID:
+ break;
+ case NS_ooxml::LN_CT_OLEObject_DrawAspect:
+ m_sDrawAspect = sStringValue;
+ break;
+ case NS_ooxml::LN_CT_OLEObject_ObjectID:
+ break;
+ case NS_ooxml::LN_CT_OLEObject_r_id:
+ break;
+ case NS_ooxml::LN_inputstream:
+ rVal.getAny() >>= m_xInputStream;
+ break;
+ case NS_ooxml::LN_CT_Object_dxaOrig:
+ m_sVisAreaWidth = sStringValue;
+ break;
+ case NS_ooxml::LN_CT_Object_dyaOrig:
+ m_sVisAreaHeight = sStringValue;
+ break;
+ case NS_ooxml::LN_shape:
+ {
+ uno::Reference< drawing::XShape > xTempShape;
+ rVal.getAny() >>= xTempShape;
+
+ // Control shape is handled on a different code path
+ uno::Reference< lang::XServiceInfo > xSInfo( xTempShape, uno::UNO_QUERY_THROW );
+ if(xSInfo->supportsService("com.sun.star.drawing.ControlShape"))
+ {
+ m_rDomainMapper.hasControls(true);
+ break;
+ }
+
+ if( xTempShape.is() )
+ {
+ m_xShape.set( xTempShape );
+
+ // No need to set the wrapping here as it's either set in oox or will be set later
+
+ // Shapes in the header or footer should be in the background, since the default is WrapTextMode_THROUGH.
+ if (m_rDomainMapper.IsInHeaderFooter())
+ {
+ try
+ {
+ uno::Reference<beans::XPropertySet> xShapeProps(m_xShape, uno::UNO_QUERY);
+ xShapeProps->setPropertyValue("Opaque", uno::Any(false));
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter", "Exception in OLE Handler");
+ }
+ }
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "unknown attribute");
+ }
+}
+
+css::awt::Size OLEHandler::getSize() const
+{
+ if (!m_xShape)
+ return css::awt::Size();
+ return m_xShape->getSize();
+}
+
+css::uno::Reference<css::graphic::XGraphic> OLEHandler::getReplacement() const
+{
+ if (!m_xShape)
+ return nullptr;
+ uno::Reference<beans::XPropertySet> xShapeProps(m_xShape, uno::UNO_QUERY);
+ css::uno::Reference<css::graphic::XGraphic> xReplacement;
+ xShapeProps->getPropertyValue(getPropertyName(PROP_BITMAP)) >>= xReplacement;
+ return xReplacement;
+}
+
+void OLEHandler::lcl_sprm(Sprm & rSprm)
+{
+ sal_uInt32 nSprmId = rSprm.getId();
+ switch( nSprmId )
+ {
+ case NS_ooxml::LN_OLEObject_OLEObject:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ pProperties->resolve(*this);
+ }
+ }
+ break;
+ case NS_ooxml::LN_wrap_wrap:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if ( pProperties )
+ {
+ tools::SvRef<WrapHandler> pHandler( new WrapHandler );
+ pProperties->resolve( *pHandler );
+
+ m_nWrapMode = pHandler->getWrapMode( );
+
+ try
+ {
+ uno::Reference< beans::XPropertySet > xShapeProps( m_xShape, uno::UNO_QUERY_THROW );
+
+ xShapeProps->setPropertyValue(
+ getPropertyName( PROP_SURROUND ),
+ uno::Any( static_cast<sal_Int32>(m_nWrapMode) ) );
+
+ // Through shapes in the header or footer(that spill into the body) should be in the background.
+ // It is just assumed that all shapes will spill into the body.
+ if( m_rDomainMapper.IsInHeaderFooter() )
+ xShapeProps->setPropertyValue("Opaque", uno::Any(m_nWrapMode != text::WrapTextMode_THROUGH));
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter", "Exception in OLE Handler");
+ }
+ }
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "unknown attribute");
+ }
+ }
+}
+
+void OLEHandler::importStream(const uno::Reference<uno::XComponentContext>& xComponentContext, const uno::Reference<text::XTextDocument>& xTextDocument, const uno::Reference<text::XTextContent>& xOLE)
+{
+ OUString aFilterService;
+ if (m_sProgId == "Word.Document.12")
+ aFilterService = "com.sun.star.comp.Writer.WriterFilter";
+ else if (m_sProgId == "Excel.Sheet.12")
+ aFilterService = "com.sun.star.comp.oox.xls.ExcelFilter";
+ else if (m_sProgId == "Equation.3")
+ aFilterService = "com.sun.star.comp.Math.MathTypeFilter";
+ else
+ SAL_WARN("writerfilter", "OLEHandler::importStream: unhandled m_sProgId: " << m_sProgId);
+
+ if (!m_xInputStream.is() || aFilterService.isEmpty())
+ return;
+
+ // Create the filter service.
+ uno::Reference<uno::XInterface> xInterface = xComponentContext->getServiceManager()->createInstanceWithContext(aFilterService, xComponentContext);
+
+ // Set target document.
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY);
+ uno::Reference<document::XEmbeddedObjectSupplier> xSupplier(xOLE, uno::UNO_QUERY);
+ uno::Reference<lang::XComponent> xEmbeddedObject = xSupplier->getEmbeddedObject();
+ if (!xEmbeddedObject.is())
+ return;
+ xImporter->setTargetDocument( xEmbeddedObject );
+
+ // Import the input stream.
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["InputStream"] <<= m_xInputStream;
+ uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY);
+ xFilter->filter(aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Now that the data is imported, update the (typically) changed stream name.
+ uno::Reference<beans::XPropertySet> xPropertySet(xOLE, uno::UNO_QUERY);
+ ::oox::ole::SaveInteropProperties(xTextDocument,
+ xPropertySet->getPropertyValue("StreamName").get<OUString>(), &m_aURL,
+ m_sProgId);
+}
+
+OUString OLEHandler::getCLSID() const
+{
+ OUString aRet;
+
+ // See officecfg/registry/data/org/openoffice/Office/Embedding.xcu.
+ if (m_sProgId == "Word.Document.12")
+ {
+ if (officecfg::Office::Common::Filter::Microsoft::Import::WinWordToWriter::get())
+ aRet = "8BC6B165-B1B2-4EDD-aa47-dae2ee689dd6";
+ }
+ else if (m_sProgId == "Excel.Sheet.12")
+ {
+ if (officecfg::Office::Common::Filter::Microsoft::Import::ExcelToCalc::get())
+ aRet = "47BBB4CB-CE4C-4E80-A591-42D9AE74950F";
+ }
+ else if (m_sProgId == "Equation.3")
+ {
+ if (officecfg::Office::Common::Filter::Microsoft::Import::MathTypeToMath::get())
+ aRet = "078B7ABA-54FC-457F-8551-6147E776A997";
+ }
+ else
+ SAL_WARN("writerfilter", "OLEHandler::getCLSID: unhandled m_sProgId: " << m_sProgId);
+
+ return aRet;
+}
+
+OUString const & OLEHandler::GetDrawAspect() const
+{
+ return m_sDrawAspect;
+}
+
+OUString const & OLEHandler::GetVisAreaWidth() const
+{
+ return m_sVisAreaWidth;
+}
+
+OUString const & OLEHandler::GetVisAreaHeight() const
+{
+ return m_sVisAreaHeight;
+}
+
+OUString OLEHandler::copyOLEOStream(
+ uno::Reference<text::XTextDocument> const& xTextDocument)
+{
+ OUString sRet;
+ if( !m_xInputStream.is( ) )
+ return sRet;
+ try
+ {
+ uno::Reference < lang::XMultiServiceFactory > xFactory(xTextDocument, uno::UNO_QUERY_THROW);
+ uno::Reference< document::XEmbeddedObjectResolver > xEmbeddedResolver(
+ xFactory->createInstance("com.sun.star.document.ImportEmbeddedObjectResolver"), uno::UNO_QUERY_THROW );
+ //hack to work with the ImportEmbeddedObjectResolver
+ static sal_Int32 nObjectCount = 100;
+ uno::Reference< container::XNameAccess > xNA( xEmbeddedResolver, uno::UNO_QUERY_THROW );
+ OUString aURL = "Obj" + OUString::number( nObjectCount++ );
+ uno::Reference < io::XOutputStream > xOLEStream;
+ if( (xNA->getByName( aURL ) >>= xOLEStream) && xOLEStream.is() )
+ {
+ const sal_Int32 nReadRequest = 0x1000;
+ uno::Sequence< sal_Int8 > aData;
+
+ while( true )
+ {
+ sal_Int32 nRead = m_xInputStream->readBytes( aData, nReadRequest );
+ xOLEStream->writeBytes( aData );
+ if( nRead < nReadRequest )
+ {
+ xOLEStream->closeOutput();
+ break;
+ }
+ }
+
+ ::oox::ole::SaveInteropProperties(xTextDocument, aURL, nullptr, m_sProgId);
+
+ OUString aPersistName( xEmbeddedResolver->resolveEmbeddedObjectURL( aURL ) );
+ sRet = aPersistName.copy( strlen("vnd.sun.star.EmbeddedObject:") );
+
+ }
+ uno::Reference< lang::XComponent > xComp( xEmbeddedResolver, uno::UNO_QUERY_THROW );
+ xComp->dispose();
+ m_aURL = aURL;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "OLEHandler::createOLEObject");
+ }
+ return sRet;
+}
+
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/OLEHandler.hxx b/writerfilter/source/dmapper/OLEHandler.hxx
new file mode 100644
index 000000000..67fed0128
--- /dev/null
+++ b/writerfilter/source/dmapper/OLEHandler.hxx
@@ -0,0 +1,94 @@
+/* -*- 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 "LoggedResources.hxx"
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+
+namespace com::sun::star{
+ namespace graphic{
+ class XGraphic;
+ }
+ namespace io{
+ class XInputStream;
+ }
+ namespace text{
+ class XTextContent;
+ class XTextDocument;
+ }
+ namespace uno {
+ class XComponentContext;
+ }
+}
+namespace writerfilter::dmapper
+{
+class DomainMapper;
+/** Handler for OLE objects
+ */
+class OLEHandler : public LoggedProperties
+{
+ OUString m_sProgId;
+ OUString m_sDrawAspect;
+ OUString m_sVisAreaWidth;
+ OUString m_sVisAreaHeight;
+ /// The stream URL right after the import of the raw data.
+ OUString m_aURL;
+
+ css::text::WrapTextMode m_nWrapMode;
+
+ css::uno::Reference<css::drawing::XShape> m_xShape;
+
+ css::uno::Reference<css::io::XInputStream> m_xInputStream;
+ DomainMapper& m_rDomainMapper;
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+public:
+ explicit OLEHandler(DomainMapper& rDomainMapper);
+ virtual ~OLEHandler() override;
+
+ const css::uno::Reference<css::drawing::XShape>& getShape() const { return m_xShape; };
+
+ bool isOLEObject() const { return m_xInputStream.is(); }
+
+ /// In case of a valid CLSID, import the native data to the previously created empty OLE object.
+ void importStream(const css::uno::Reference<css::uno::XComponentContext>& xComponentContext,
+ const css::uno::Reference<css::text::XTextDocument>& xTextDocument,
+ const css::uno::Reference<css::text::XTextContent>& xOLE);
+
+ /// Get the CLSID of the OLE object, in case we can find one based on m_sProgId.
+ OUString getCLSID() const;
+
+ OUString const & GetDrawAspect() const;
+ OUString const & GetVisAreaWidth() const;
+ OUString const & GetVisAreaHeight() const;
+
+ OUString copyOLEOStream(css::uno::Reference<css::text::XTextDocument> const& xTextDocument);
+
+ css::awt::Size getSize() const;
+ css::uno::Reference<css::graphic::XGraphic> getReplacement() const;
+
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/PageBordersHandler.cxx b/writerfilter/source/dmapper/PageBordersHandler.cxx
new file mode 100644
index 000000000..89548eb35
--- /dev/null
+++ b/writerfilter/source/dmapper/PageBordersHandler.cxx
@@ -0,0 +1,145 @@
+/* -*- 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 "BorderHandler.hxx"
+#include "PageBordersHandler.hxx"
+
+#include <ooxml/resourceids.hxx>
+
+namespace writerfilter::dmapper {
+
+PgBorder::PgBorder( ) :
+ m_nDistance( 0 ),
+ m_ePos( BORDER_RIGHT ),
+ m_bShadow(false)
+{
+}
+
+PageBordersHandler::PageBordersHandler( ) :
+LoggedProperties("PageBordersHandler"),
+m_eBorderApply(SectionPropertyMap::BorderApply::ToAllInSection),
+m_eOffsetFrom(SectionPropertyMap::BorderOffsetFrom::Text)
+{
+}
+
+PageBordersHandler::~PageBordersHandler( )
+{
+}
+
+void PageBordersHandler::lcl_attribute( Id eName, Value& rVal )
+{
+ int nIntValue = rVal.getInt( );
+ switch ( eName )
+ {
+ case NS_ooxml::LN_CT_PageBorders_display:
+ {
+ switch ( nIntValue )
+ {
+ default:
+ case NS_ooxml::LN_Value_doc_ST_PageBorderDisplay_allPages:
+ m_eBorderApply = SectionPropertyMap::BorderApply::ToAllInSection;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_PageBorderDisplay_firstPage:
+ m_eBorderApply = SectionPropertyMap::BorderApply::ToFirstPageInSection;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_PageBorderDisplay_notFirstPage:
+ m_eBorderApply = SectionPropertyMap::BorderApply::ToAllButFirstInSection;
+ break;
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_PageBorders_offsetFrom:
+ {
+ switch ( nIntValue )
+ {
+ default:
+ case NS_ooxml::LN_Value_doc_ST_PageBorderOffset_page:
+ m_eOffsetFrom = SectionPropertyMap::BorderOffsetFrom::Edge;
+ break;
+ case NS_ooxml::LN_Value_doc_ST_PageBorderOffset_text:
+ m_eOffsetFrom = SectionPropertyMap::BorderOffsetFrom::Text;
+ break;
+ }
+ }
+ break;
+ default:;
+ }
+}
+
+void PageBordersHandler::lcl_sprm( Sprm& rSprm )
+{
+ switch ( rSprm.getId( ) )
+ {
+ case NS_ooxml::LN_CT_PageBorders_top:
+ case NS_ooxml::LN_CT_PageBorders_left:
+ case NS_ooxml::LN_CT_PageBorders_bottom:
+ case NS_ooxml::LN_CT_PageBorders_right:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pBorderHandler = std::make_shared<BorderHandler>( true );
+ pProperties->resolve(*pBorderHandler);
+ BorderPosition ePos = BorderPosition( 0 );
+ switch( rSprm.getId( ) )
+ {
+ case NS_ooxml::LN_CT_PageBorders_top:
+ ePos = BORDER_TOP;
+ break;
+ case NS_ooxml::LN_CT_PageBorders_left:
+ ePos = BORDER_LEFT;
+ break;
+ case NS_ooxml::LN_CT_PageBorders_bottom:
+ ePos = BORDER_BOTTOM;
+ break;
+ case NS_ooxml::LN_CT_PageBorders_right:
+ ePos = BORDER_RIGHT;
+ break;
+ default:;
+ }
+
+ PgBorder aPgBorder;
+ aPgBorder.m_rLine = pBorderHandler->getBorderLine( );
+ aPgBorder.m_nDistance = pBorderHandler->getLineDistance( );
+ aPgBorder.m_ePos = ePos;
+ aPgBorder.m_bShadow = pBorderHandler->getShadow();
+ if (pBorderHandler->getLineType() != NS_ooxml::LN_Value_ST_Border_none)
+ {
+ m_aBorders.push_back( aPgBorder );
+ }
+ }
+ }
+ break;
+ default:;
+ }
+}
+
+void PageBordersHandler::SetBorders( SectionPropertyMap* pSectContext )
+{
+ for (const PgBorder& rBorder : m_aBorders)
+ {
+ pSectContext->SetBorder( rBorder.m_ePos, rBorder.m_nDistance, rBorder.m_rLine, rBorder.m_bShadow );
+ }
+ pSectContext->SetBorderApply(m_eBorderApply);
+ pSectContext->SetBorderOffsetFrom(m_eOffsetFrom);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/PageBordersHandler.hxx b/writerfilter/source/dmapper/PageBordersHandler.hxx
new file mode 100644
index 000000000..537d34b81
--- /dev/null
+++ b/writerfilter/source/dmapper/PageBordersHandler.hxx
@@ -0,0 +1,62 @@
+/* -*- 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 "PropertyMap.hxx"
+
+#include "LoggedResources.hxx"
+
+#include <com/sun/star/table/BorderLine2.hpp>
+
+#include <vector>
+
+namespace writerfilter::dmapper
+{
+class PgBorder
+{
+public:
+ css::table::BorderLine2 m_rLine;
+ sal_Int32 m_nDistance;
+ BorderPosition m_ePos;
+ bool m_bShadow;
+
+ PgBorder();
+};
+
+class PageBordersHandler : public LoggedProperties
+{
+private:
+ // See implementation of SectionPropertyMap::ApplyBorderToPageStyles
+ SectionPropertyMap::BorderApply m_eBorderApply;
+ SectionPropertyMap::BorderOffsetFrom m_eOffsetFrom;
+ std::vector<PgBorder> m_aBorders;
+
+ // Properties
+ virtual void lcl_attribute(Id eName, Value& rVal) override;
+ virtual void lcl_sprm(Sprm& rSprm) override;
+
+public:
+ PageBordersHandler();
+ virtual ~PageBordersHandler() override;
+
+ void SetBorders(SectionPropertyMap* pSectContext);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx
new file mode 100644
index 000000000..658a73040
--- /dev/null
+++ b/writerfilter/source/dmapper/PropertyIds.cxx
@@ -0,0 +1,386 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <rtl/ustring.hxx>
+#include "PropertyIds.hxx"
+
+namespace writerfilter::dmapper{
+
+OUString getPropertyName( PropertyIds eId )
+{
+ OUString sName;
+ switch(eId) {
+ case PROP_CHAR_WEIGHT: sName = "CharWeight"; break;
+ case PROP_CHAR_POSTURE: sName = "CharPosture"; break;
+ case PROP_CHAR_STRIKEOUT: sName = "CharStrikeout"; break;
+ case PROP_CHAR_CONTOURED: sName = "CharContoured"; break;
+ case PROP_CHAR_SHADOWED: sName = "CharShadowed"; break;
+ case PROP_CHAR_CASE_MAP: sName = "CharCaseMap"; break;
+ case PROP_CHAR_COLOR: sName = "CharColor"; break;
+ case PROP_CHAR_RELIEF: sName = "CharRelief"; break;
+ case PROP_CHAR_UNDERLINE: sName = "CharUnderline"; break;
+ case PROP_CHAR_UNDERLINE_COLOR: sName = "CharUnderlineColor"; break;
+ case PROP_CHAR_UNDERLINE_HAS_COLOR: sName = "CharUnderlineHasColor"; break;
+ case PROP_CHAR_WORD_MODE: sName = "CharWordMode"; break;
+ case PROP_CHAR_ESCAPEMENT : sName = "CharEscapement"; break;
+ case PROP_CHAR_ESCAPEMENT_HEIGHT: sName = "CharEscapementHeight"; break;
+ case PROP_CHAR_HEIGHT: sName = "CharHeight"; break;
+ case PROP_CHAR_HEIGHT_COMPLEX: sName = "CharHeightComplex"; break;
+ case PROP_CHAR_LOCALE: sName = "CharLocale"; break;
+ case PROP_CHAR_LOCALE_ASIAN: sName = "CharLocaleAsian"; break;
+ case PROP_CHAR_LOCALE_COMPLEX: sName = "CharLocaleComplex"; break;
+ case PROP_CHAR_WEIGHT_COMPLEX : sName = "CharWeightComplex"; break;
+ case PROP_CHAR_POSTURE_COMPLEX: sName = "CharPostureComplex"; break;
+ case PROP_CHAR_CHAR_KERNING: sName = "CharKerning"; break;
+ case PROP_CHAR_AUTO_KERNING: sName = "CharAutoKerning"; break;
+ case PROP_CHAR_SCALE_WIDTH: sName = "CharScaleWidth"; break;
+ case PROP_CHAR_STYLE_NAME: sName = "CharStyleName"; break;
+ case PROP_CHAR_FONT_NAME: sName = "CharFontName"; break;
+ case PROP_CHAR_FONT_CHAR_SET: sName = "CharFontCharSet"; break;
+ case PROP_CHAR_FONT_NAME_ASIAN : sName = "CharFontNameAsian"; break;
+ case PROP_CHAR_HEIGHT_ASIAN : sName = "CharHeightAsian"; break;
+ case PROP_CHAR_FONT_NAME_COMPLEX : sName = "CharFontNameComplex"; break;
+ case PROP_CHAR_HIDDEN : sName = "CharHidden"; break;
+ case PROP_CHAR_WEIGHT_ASIAN : sName = "CharWeightAsian"; break;
+ case PROP_CHAR_POSTURE_ASIAN : sName = "CharPostureAsian"; break;
+ case PROP_CHAR_BACK_COLOR: sName = "CharBackColor"; break;
+ case PROP_CHAR_EMPHASIS: sName = "CharEmphasis"; break;
+ case PROP_CHAR_COMBINE_IS_ON: sName = "CharCombineIsOn"; break;
+ case PROP_CHAR_COMBINE_PREFIX: sName = "CharCombinePrefix"; break;
+ case PROP_CHAR_COMBINE_SUFFIX: sName = "CharCombineSuffix"; break;
+ case PROP_CHAR_ROTATION: sName = "CharRotation"; break;
+ case PROP_CHAR_ROTATION_IS_FIT_TO_LINE: sName = "CharRotationIsFitToLine"; break;
+ case PROP_CHAR_FLASH: sName = "CharFlash"; break;
+ case PROP_CHAR_LEFT_BORDER: sName = "CharLeftBorder";break;
+ case PROP_CHAR_RIGHT_BORDER: sName = "CharRightBorder";break;
+ case PROP_CHAR_TOP_BORDER: sName = "CharTopBorder";break;
+ case PROP_CHAR_BOTTOM_BORDER: sName = "CharBottomBorder";break;
+ case PROP_CHAR_LEFT_BORDER_DISTANCE: sName = "CharLeftBorderDistance"; break;
+ case PROP_CHAR_RIGHT_BORDER_DISTANCE: sName = "CharRightBorderDistance"; break;
+ case PROP_CHAR_TOP_BORDER_DISTANCE: sName = "CharTopBorderDistance";break;
+ case PROP_CHAR_BOTTOM_BORDER_DISTANCE: sName = "CharBottomBorderDistance"; break;
+ case PROP_CHAR_SHADOW_FORMAT: sName = "CharShadowFormat"; break;
+ case PROP_CHAR_HIGHLIGHT: sName = "CharHighlight"; break;
+ case PROP_PARA_STYLE_NAME: sName = "ParaStyleName"; break;
+ case PROP_PARA_ADJUST: sName = "ParaAdjust"; break;
+ case PROP_PARA_VERT_ALIGNMENT: sName = "ParaVertAlignment"; break;
+ case PROP_PARA_LAST_LINE_ADJUST: sName = "ParaLastLineAdjust"; break;
+ case PROP_PARA_RIGHT_MARGIN : sName = "ParaRightMargin"; break;
+ case PROP_PARA_LEFT_MARGIN : sName = "ParaLeftMargin"; break;
+ case PROP_PARA_FIRST_LINE_INDENT: sName = "ParaFirstLineIndent"; break;
+ case PROP_PARA_KEEP_TOGETHER: sName = "ParaKeepTogether"; break;
+ case PROP_PARA_TOP_MARGIN: sName = "ParaTopMargin"; break;
+ case PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING: sName = "ParaTopMarginBeforeAutoSpacing"; break;
+ case PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING: sName = "ParaBottomMarginAfterAutoSpacing"; break;
+ case PROP_PARA_CONTEXT_MARGIN: sName = "ParaContextMargin"; break;
+ case PROP_PARA_BOTTOM_MARGIN: sName = "ParaBottomMargin"; break;
+ case PROP_PARA_IS_HYPHENATION: sName = "ParaIsHyphenation"; break;
+ case PROP_PARA_HYPHENATION_NO_CAPS: sName = "ParaHyphenationNoCaps"; break;
+ case PROP_PARA_HYPHENATION_ZONE: sName = "ParaHyphenationZone"; break;
+ case PROP_PARA_LINE_NUMBER_COUNT: sName = "ParaLineNumberCount"; break;
+ case PROP_PARA_IS_HANGING_PUNCTUATION: sName = "ParaIsHangingPunctuation"; break;
+ case PROP_PARA_LINE_SPACING: sName = "ParaLineSpacing"; break;
+ case PROP_PARA_TAB_STOPS: sName = "ParaTabStops"; break;
+ case PROP_PARA_WIDOWS: sName = "ParaWidows"; break;
+ case PROP_PARA_ORPHANS: sName = "ParaOrphans"; break;
+ case PROP_PARA_LINE_NUMBER_START_VALUE: sName = "ParaLineNumberStartValue"; break;
+ case PROP_NUMBERING_LEVEL: sName = "NumberingLevel"; break;
+ case PROP_NUMBERING_RULES: sName = "NumberingRules"; break;
+ case PROP_NUMBERING_TYPE: sName = "NumberingType"; break;
+ case PROP_START_WITH: sName = "StartWith"; break;
+ case PROP_ADJUST: sName = "Adjust"; break;
+ case PROP_PARENT_NUMBERING: sName = "ParentNumbering"; break;
+ case PROP_RIGHT_MARGIN : sName = "RightMargin"; break;
+ case PROP_LEFT_MARGIN : sName = "LeftMargin"; break;
+ case PROP_TOP_MARGIN : sName = "TopMargin"; break;
+ case PROP_BOTTOM_MARGIN : sName = "BottomMargin"; break;
+ case PROP_FIRST_LINE_OFFSET: sName = "FirstLineOffset"; break;
+ case PROP_LEFT_BORDER : sName = "LeftBorder";break;
+ case PROP_RIGHT_BORDER : sName = "RightBorder";break;
+ case PROP_TOP_BORDER : sName = "TopBorder";break;
+ case PROP_BOTTOM_BORDER : sName = "BottomBorder";break;
+ case PROP_TABLE_BORDER : sName = "TableBorder";break;
+ case PROP_TABLE_ROW_DELETE : sName = "TableRowDelete"; break;
+ case PROP_TABLE_ROW_INSERT : sName = "TableRowInsert"; break;
+ case PROP_TABLE_CELL_DELETE : sName = "TableCellDelete"; break;
+ case PROP_TABLE_CELL_INSERT : sName = "TableCellInsert"; break;
+ case PROP_LEFT_BORDER_DISTANCE : sName = "LeftBorderDistance"; break;
+ case PROP_RIGHT_BORDER_DISTANCE : sName = "RightBorderDistance"; break;
+ case PROP_TOP_BORDER_DISTANCE : sName = "TopBorderDistance";break;
+ case PROP_BOTTOM_BORDER_DISTANCE: sName = "BottomBorderDistance"; break;
+ case PROP_CURRENT_PRESENTATION : sName = "CurrentPresentation"; break;
+ case PROP_IS_FIXED : sName = "IsFixed"; break;
+ case PROP_SUB_TYPE : sName = "SubType"; break;
+ case PROP_FILE_FORMAT : sName = "FileFormat"; break;
+ case PROP_HYPER_LINK_U_R_L : sName = "HyperLinkURL"; break;
+ case PROP_HYPERLINK : sName = "Hyperlink"; break;
+ case PROP_NUMBER_FORMAT : sName = "NumberFormat"; break;
+ case PROP_NAME : sName = "Name"; break;
+ case PROP_IS_INPUT : sName = "IsInput"; break;
+ case PROP_HINT : sName = "Hint"; break;
+ case PROP_FULL_NAME : sName = "FullName"; break;
+ case PROP_DESCRIPTION : sName = "Description"; break;
+ case PROP_MACRO_NAME : sName = "MacroName"; break;
+ case PROP_TITLE : sName = "Title"; break;
+ case PROP_CONTENT : sName = "Content"; break;
+ case PROP_INPUT_STREAM : sName = "InputStream"; break;
+ case PROP_GRAPHIC : sName = "Graphic"; break;
+ case PROP_ANCHOR_TYPE : sName = "AnchorType"; break;
+ case PROP_SIZE : sName = "Size"; break;
+ case PROP_HORI_ORIENT : sName = "HoriOrient"; break;
+ case PROP_HORI_ORIENT_POSITION : sName = "HoriOrientPosition"; break;
+ case PROP_HORI_ORIENT_RELATION : sName = "HoriOrientRelation"; break;
+ case PROP_VERT_ORIENT : sName = "VertOrient"; break;
+ case PROP_VERT_ORIENT_POSITION : sName = "VertOrientPosition"; break;
+ case PROP_VERT_ORIENT_RELATION : sName = "VertOrientRelation"; break;
+ case PROP_SIZE100th_M_M : sName = "Size100thMM"; break;
+ case PROP_SIZE_PIXEL : sName = "SizePixel"; break;
+ case PROP_SURROUND : sName = "Surround"; break;
+ case PROP_SURROUND_CONTOUR : sName = "SurroundContour"; break;
+ case PROP_ADJUST_CONTRAST : sName = "AdjustContrast"; break;
+ case PROP_ADJUST_LUMINANCE : sName = "AdjustLuminance"; break;
+ case PROP_GRAPHIC_COLOR_MODE : sName = "GraphicColorMode"; break;
+ case PROP_CONTOUR_OUTSIDE : sName = "ContourOutside"; break;
+ case PROP_CONTOUR_POLY_POLYGON : sName = "ContourPolyPolygon"; break;
+ case PROP_PAGE_TOGGLE : sName = "PageToggle"; break;
+ case PROP_BACK_COLOR : sName = "BackColor"; break;
+ case PROP_BACK_COLOR_TRANSPARENCY: sName = "BackColorTransparency"; break;
+ case PROP_ALTERNATIVE_TEXT : sName = "AlternativeText"; break;
+ case PROP_HEADER_TEXT_LEFT : sName = "HeaderTextLeft"; break;
+ case PROP_HEADER_TEXT : sName = "HeaderText"; break;
+ case PROP_HEADER_IS_SHARED : sName = "HeaderIsShared"; break;
+ case PROP_HEADER_IS_ON : sName = "HeaderIsOn"; break;
+ case PROP_FOOTER_TEXT_LEFT : sName = "FooterTextLeft"; break;
+ case PROP_FOOTER_TEXT : sName = "FooterText"; break;
+ case PROP_FOOTER_IS_SHARED : sName = "FooterIsShared"; break;
+ case PROP_FOOTER_IS_ON : sName = "FooterIsOn"; break;
+ case PROP_FOOTNOTE_COUNTING : sName = "FootnoteCounting"; break;
+ case PROP_FOOTNOTE_LINE_ADJUST : sName = "FootnoteLineAdjust"; break;
+ case PROP_WIDTH : sName = "Width"; break;
+ case PROP_HEIGHT : sName = "Height"; break;
+ case PROP_TEXT_COLUMNS : sName = "TextColumns"; break;
+ case PROP_AUTOMATIC_DISTANCE : sName = "AutomaticDistance"; break;
+ case PROP_IS_LANDSCAPE : sName = "IsLandscape"; break;
+ case PROP_FIRST_PAGE : sName = "First Page"; break;
+ case PROP_PAGE_DESC_NAME : sName = "PageDescName"; break;
+ case PROP_PAGE_NUMBER_OFFSET: sName = "PageNumberOffset"; break;
+ case PROP_BREAK_TYPE : sName = "BreakType"; break;
+ case PROP_FOOTER_IS_DYNAMIC_HEIGHT: sName = "FooterIsDynamicHeight"; break;
+ case PROP_FOOTER_DYNAMIC_SPACING: sName = "FooterDynamicSpacing"; break;
+ case PROP_FOOTER_HEIGHT : sName = "FooterHeight"; break;
+ case PROP_FOOTER_BODY_DISTANCE : sName = "FooterBodyDistance"; break;
+ case PROP_HEADER_IS_DYNAMIC_HEIGHT: sName = "HeaderIsDynamicHeight"; break;
+ case PROP_HEADER_DYNAMIC_SPACING: sName = "HeaderDynamicSpacing"; break;
+ case PROP_HEADER_HEIGHT : sName = "HeaderHeight"; break;
+ case PROP_HEADER_BODY_DISTANCE : sName = "HeaderBodyDistance"; break;
+ case PROP_WRITING_MODE : sName = "WritingMode"; break;
+ case PROP_GRID_MODE : sName = "GridMode"; break;
+ case PROP_GRID_DISPLAY : sName = "GridDisplay"; break;
+ case PROP_GRID_PRINT : sName = "GridPrint"; break;
+ case PROP_GRID_LINES : sName = "GridLines"; break;
+ case PROP_GRID_BASE_HEIGHT : sName = "GridBaseHeight"; break;
+ case PROP_GRID_BASE_WIDTH : sName = "GridBaseWidth"; break;
+ case PROP_GRID_RUBY_HEIGHT : sName = "GridRubyHeight"; break;
+ case PROP_GRID_STANDARD_MODE : sName = "StandardPageMode"; break;
+ case PROP_IS_ON : sName = "IsOn"; break;
+ case PROP_RESTART_AT_EACH_PAGE : sName = "RestartAtEachPage"; break;
+ case PROP_COUNT_EMPTY_LINES : sName = "CountEmptyLines"; break;
+ case PROP_COUNT_LINES_IN_FRAMES : sName = "CountLinesInFrames"; break;
+ case PROP_INTERVAL : sName = "Interval"; break;
+ case PROP_DISTANCE : sName = "Distance"; break;
+ case PROP_NUMBER_POSITION : sName = "NumberPosition"; break;
+ case PROP_LEVEL : sName = "Level"; break;
+ case PROP_LEVEL_FOLLOW : sName = "LabelFollowedBy"; break;
+ case PROP_LEVEL_PARAGRAPH_STYLES : sName = "LevelParagraphStyles"; break;
+ case PROP_LEVEL_FORMAT : sName = "LevelFormat"; break;
+ case PROP_LIST_FORMAT : sName = "ListFormat"; break;
+ case PROP_TOKEN_TYPE : sName = "TokenType"; break;
+ case PROP_TOKEN_HYPERLINK_START : sName = "TokenHyperlinkStart"; break;
+ case PROP_TOKEN_HYPERLINK_END : sName = "TokenHyperlinkEnd"; break;
+ case PROP_TOKEN_CHAPTER_INFO : sName = "TokenChapterInfo"; break;
+ case PROP_CHAPTER_FORMAT : sName = "ChapterFormat"; break;
+ case PROP_TOKEN_TEXT : sName = "TokenText"; break;
+ case PROP_TEXT : sName = "Text"; break;
+ case PROP_CREATE_FROM_OUTLINE : sName = "CreateFromOutline"; break;
+ case PROP_CREATE_FROM_MARKS : sName = "CreateFromMarks"; break;
+ case PROP_STANDARD : sName = "Standard"; break;
+ case PROP_SPLIT : sName = "Split"; break;
+ case PROP_IS_SPLIT_ALLOWED : sName = "IsSplitAllowed"; break;
+ case META_PROP_VERTICAL_BORDER : sName = "VerticalBorder"; break;
+ case META_PROP_HORIZONTAL_BORDER : sName = "HorizontalBorder"; break;
+ case PROP_HEADER_ROW_COUNT : sName = "HeaderRowCount"; break;
+ case PROP_SIZE_TYPE : sName = "SizeType"; break;
+ case PROP_TABLE_COLUMN_SEPARATORS: sName = "TableColumnSeparators"; break;
+ case META_PROP_TABLE_STYLE_NAME : sName = "TableStyleName"; break;
+ case PROP_TABLE_REDLINE_PARAMS : sName = "TableRedlineParams"; break;
+ case PROP_REDLINE_AUTHOR : sName = "RedlineAuthor"; break;
+ case PROP_REDLINE_DATE_TIME : sName = "RedlineDateTime"; break;
+ case PROP_REDLINE_TYPE : sName = "RedlineType"; break;
+ case PROP_REDLINE_REVERT_PROPERTIES: sName = "RedlineRevertProperties"; break;
+ case PROP_IS_PROTECTED : sName = "IsProtected"; break;
+ case PROP_SIZE_PROTECTED : sName = "SizeProtected"; break;
+ case PROP_POSITION_PROTECTED : sName = "PositionProtected"; break;
+ case PROP_OPAQUE : sName = "Opaque"; break;
+ case PROP_VERTICAL_MERGE : sName = "VerticalMerge"; break;
+ case PROP_BULLET_CHAR : sName = "BulletChar"; break;
+ case PROP_BULLET_FONT_NAME : sName = "BulletFontName"; break;
+ case PROP_TABS_RELATIVE_TO_INDENT: sName = "TabsRelativeToIndent"; break;
+ case PROP_PREFIX : sName = "Prefix"; break;
+ case PROP_SUFFIX : sName = "Suffix"; break;
+ case PROP_CREATE_FROM_LEVEL_PARAGRAPH_STYLES: sName = "CreateFromLevelParagraphStyles"; break;
+ case PROP_DROP_CAP_FORMAT : sName = "DropCapFormat"; break;
+ case PROP_REFERENCE_FIELD_PART : sName = "ReferenceFieldPart"; break;
+ case PROP_SOURCE_NAME: sName = "SourceName"; break;
+ case PROP_REFERENCE_FIELD_SOURCE : sName = "ReferenceFieldSource"; break;
+ case PROP_WIDTH_TYPE : sName = "WidthType"; break;
+ case PROP_TBL_LOOK : sName = "TblLook"; break;
+ case PROP_TEXT_RANGE: sName = "TextRange"; break;
+ case PROP_TEXT_VERTICAL_ADJUST : sName = "TextVerticalAdjust"; break;
+ case PROP_SERVICE_CHAR_STYLE : sName = "com.sun.star.style.CharacterStyle"; break;
+ case PROP_SERVICE_PARA_STYLE : sName = "com.sun.star.style.ParagraphStyle"; break;
+ case PROP_CHARACTER_STYLES : sName = "CharacterStyles"; break;
+ case PROP_PARAGRAPH_STYLES : sName = "ParagraphStyles"; break;
+ case PROP_TABLE_BORDER_DISTANCES: sName = "TableBorderDistances"; break;
+ case META_PROP_CELL_MAR_TOP : sName = "MetaPropCellMarTop"; break;
+ case META_PROP_CELL_MAR_BOTTOM : sName = "MetaPropCellMarBottom"; break;
+ case META_PROP_CELL_MAR_LEFT : sName = "MetaPropCellMarLeft"; break;
+ case META_PROP_CELL_MAR_RIGHT : sName = "MetaPropCellMarRight"; break;
+ case PROP_START_AT : sName = "StartAt"; break;
+ case PROP_CHAR_PROP_HEIGHT : sName = "CharPropHeight"; break;
+ case PROP_CHAR_PROP_HEIGHT_ASIAN : sName = "CharPropHeightAsian"; break;
+ case PROP_CHAR_PROP_HEIGHT_COMPLEX: sName = "CharPropHeightComplex"; break;
+ case PROP_FORMAT : sName = "Format"; break;
+ case PROP_INSERT : sName = "Insert"; break;
+ case PROP_DELETE : sName = "Delete"; break;
+ case PROP_PARAGRAPH_FORMAT : sName = "ParagraphFormat"; break;
+ case PROP_STREAM_NAME: sName = "StreamName"; break;
+ case PROP_BITMAP : sName = "Bitmap"; break;
+ case PROP_IS_DATE : sName = "IsDate"; break;
+ case PROP_TAB_STOP_DISTANCE : sName = "TabStopDistance"; break;
+ case PROP_INDENT_AT : sName = "IndentAt"; break;
+ case PROP_FIRST_LINE_INDENT : sName = "FirstLineIndent"; break;
+ case PROP_NUMBERING_STYLE_NAME : sName = "NumberingStyleName"; break;
+ case PROP_OUTLINE_LEVEL : sName = "OutlineLevel"; break;
+ case PROP_LISTTAB_STOP_POSITION : sName = "ListtabStopPosition"; break;
+ case PROP_POSITION_AND_SPACE_MODE : sName = "PositionAndSpaceMode"; break;
+ case PROP_PARA_SPLIT: sName = "ParaSplit"; break;
+ case PROP_HELP: sName = "Help"; break;
+ case PROP_HEADING_STYLE_NAME: sName = "HeadingStyleName"; break;
+ case PROP_FRM_DIRECTION: sName = "FRMDirection"; break;
+ case PROP_EMBEDDED_OBJECT : sName = "EmbeddedObject"; break;
+ case PROP_IS_VISIBLE: sName = "IsVisible"; break;
+ case PROP_PAGE_STYLE_LAYOUT: sName = "PageStyleLayout"; break;
+ case PROP_Z_ORDER: sName = "ZOrder"; break;
+ case PROP_EMBED_FONTS: sName = "EmbedFonts"; break;
+ case PROP_EMBED_SYSTEM_FONTS: sName = "EmbedSystemFonts"; break;
+ case PROP_SHADOW_FORMAT: sName = "ShadowFormat"; break;
+ case PROP_RELATIVE_WIDTH: sName = "RelativeWidth"; break;
+ case PROP_IS_WIDTH_RELATIVE: sName = "IsWidthRelative"; break;
+ case PROP_GRAPHIC_BITMAP: sName = "GraphicBitmap"; break;
+ case PROP_GRAPHIC_SIZE: sName = "GraphicSize"; break;
+ case PROP_CHAR_SHADING_VALUE: sName = "CharShadingValue"; break;
+ case PROP_CHAR_SHADING_MARKER: sName = "CharShadingMarker"; break;
+ case PROP_LABEL_CATEGORY: sName = "LabelCategory"; break;
+ case PROP_MIRROR_INDENTS : sName = "MirrorIndents"; break;
+ case PROP_SURROUND_TEXT_WRAP_SMALL: sName = "SurroundTextWrapSmall"; break;
+ case PROP_PARA_SHADOW_FORMAT: sName = "ParaShadowFormat"; break;
+ case PROP_FOOTNOTE_LINE_RELATIVE_WIDTH: sName = "FootnoteLineRelativeWidth"; break;
+ case PROP_TBL_HEADER: sName = "TblHeader"; break;
+ case PROP_CHAR_THEME_NAME_ASCII : sName = "CharThemeNameAscii"; break;
+ case PROP_CHAR_THEME_NAME_CS : sName = "CharThemeNameCs"; break;
+ case PROP_CHAR_THEME_NAME_H_ANSI : sName = "CharThemeNameHAnsi"; break;
+ case PROP_CHAR_THEME_NAME_EAST_ASIA : sName = "CharThemeNameEastAsia"; break;
+ case PROP_CHAR_THEME_FONT_NAME_ASCII : sName = "CharThemeFontNameAscii"; break;
+ case PROP_CHAR_THEME_FONT_NAME_CS : sName = "CharThemeFontNameCs"; break;
+ case PROP_CHAR_THEME_FONT_NAME_EAST_ASIA: sName = "CharThemeFontNameEastAsia"; break;
+ case PROP_CHAR_THEME_COLOR : sName = "CharThemeColor"; break;
+ case PROP_CHAR_THEME_ORIGINAL_COLOR : sName = "CharThemeOriginalColor"; break;
+ case PROP_CHAR_THEME_COLOR_SHADE : sName = "CharThemeColorShade"; break;
+ case PROP_CHAR_THEME_FILL : sName = "CharThemeFill"; break;
+ case PROP_HORIZONTAL_MERGE: sName = "HorizontalMerge"; break;
+ case PROP_HIDE_TAB_LEADER_AND_PAGE_NUMBERS : sName = "HideTabLeaderAndPageNumber" ; break ;
+ case PROP_TAB_IN_TOC : sName = "TabInTOC"; break ;
+ case PROP_TOC_BOOKMARK: sName = "TOCBookmark"; break;
+ case PROP_TOC_NEW_LINE: sName = "TOCNewLine"; break;
+ case PROP_TOC_PARAGRAPH_OUTLINE_LEVEL : sName = "TOCParagraphOutlineLevel"; break;
+ case PROP_CHAR_THEME_COLOR_TINT : sName = "CharThemeColorTint"; break;
+ case PROP_CHAR_GLOW_TEXT_EFFECT : sName = "CharGlowTextEffect"; break;
+ case PROP_CHAR_SHADOW_TEXT_EFFECT : sName = "CharShadowTextEffect"; break;
+ case PROP_CHAR_REFLECTION_TEXT_EFFECT : sName = "CharReflectionTextEffect"; break;
+ case PROP_CHAR_TEXTOUTLINE_TEXT_EFFECT : sName = "CharTextOutlineTextEffect"; break;
+ case PROP_CHAR_TEXTFILL_TEXT_EFFECT : sName = "CharTextFillTextEffect"; break;
+ case PROP_CHAR_SCENE3D_TEXT_EFFECT : sName = "CharScene3DTextEffect"; break;
+ case PROP_CHAR_PROPS3D_TEXT_EFFECT : sName = "CharProps3DTextEffect"; break;
+ case PROP_CHAR_LIGATURES_TEXT_EFFECT : sName = "CharLigaturesTextEffect"; break;
+ case PROP_CHAR_NUMFORM_TEXT_EFFECT : sName = "CharNumFormTextEffect"; break;
+ case PROP_CHAR_NUMSPACING_TEXT_EFFECT : sName = "CharNumSpacingTextEffect"; break;
+ case PROP_CHAR_STYLISTICSETS_TEXT_EFFECT : sName = "CharStylisticSetsTextEffect"; break;
+ case PROP_CHAR_CNTXTALTS_TEXT_EFFECT : sName = "CharCntxtAltsTextEffect"; break;
+ case PROP_SDTPR : sName = "SdtPr"; break;
+ case PROP_CELL_INTEROP_GRAB_BAG : sName = "CellInteropGrabBag"; break;
+ case PROP_TABLE_INTEROP_GRAB_BAG : sName = "TableInteropGrabBag"; break;
+ case PROP_APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING : sName = "ApplyParagraphMarkFormatToNumbering"; break;
+ case PROP_SDT_END_BEFORE: sName = "SdtEndBefore"; break;
+ case PROP_PARA_SDT_END_BEFORE: sName = "ParaSdtEndBefore"; break;
+ case META_PROP_TABLE_LOOK: sName = "TableStyleLook"; break;
+ case PROP_PARA_CNF_STYLE: sName = "ParaCnfStyle"; break;
+ case PROP_CELL_CNF_STYLE: sName = "CellCnfStyle"; break;
+ case PROP_ROW_CNF_STYLE: sName = "RowCnfStyle"; break;
+ case PROP_CELL_HIDE_MARK: sName = "CellHideMark"; break;
+ case PROP_FOLLOW_TEXT_FLOW: sName = "IsFollowingTextFlow"; break;
+ case PROP_FILL_STYLE: sName = "FillStyle"; break;
+ case PROP_FILL_COLOR: sName = "FillColor"; break;
+ case PROP_SNAP_TO_GRID: sName = "SnapToGrid"; break;
+ case PROP_GRID_SNAP_TO_CHARS: sName = "GridSnapToChars"; break;
+ case PROP_RUBY_STYLE: sName = "RubyCharStyleName"; break;
+ case PROP_RUBY_TEXT: sName = "RubyText"; break;
+ case PROP_RUBY_ADJUST: sName = "RubyAdjust"; break;
+ case PROP_RUBY_POSITION: sName = "RubyPosition"; break;
+ case PROP_DATABASE_NAME: sName = "DataBaseName"; break;
+ case PROP_COMMAND_TYPE: sName = "DataCommandType"; break;
+ case PROP_DATATABLE_NAME: sName = "DataTableName"; break;
+ case PROP_DATACOLUMN_NAME: sName = "DataColumnName"; break;
+ case PROP_CHAR_TRANSPARENCE: sName = "CharTransparence"; break;
+ case PROP_CELL_FORMULA: sName = "CellFormula"; break;
+ case PROP_CELL_FORMULA_CONVERTED: sName = "CellFormulaConverted"; break;
+ case PROP_GUTTER_MARGIN:
+ sName = "GutterMargin";
+ break;
+ case PROP_RTL_GUTTER:
+ sName = "RtlGutter";
+ break;
+ case PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF: sName = "CursorNotIgnoreTables"; break;
+ case PROP_PARA_CONNECT_BORDERS: sName= "ParaIsConnectBorder"; break;
+ }
+ assert(sName.getLength()>0);
+ return sName;
+}
+
+bool isCharacterProperty( const PropertyIds eId )
+{
+ return eId > PROP_CHARACTER_STYLES && eId < PROP_CHARACTER_END;
+}
+
+bool isParagraphProperty( const PropertyIds eId )
+{
+ return (eId >= PROP_PARA_ADJUST && eId <= PROP_PARA_WIDOWS) || eId == PROP_FILL_COLOR;
+}
+
+} //namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx
new file mode 100644
index 000000000..20e4a1cc0
--- /dev/null
+++ b/writerfilter/source/dmapper/PropertyIds.hxx
@@ -0,0 +1,377 @@
+/* -*- 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 <rtl/ustring.hxx>
+
+namespace writerfilter::dmapper{
+// Ensure that Character Properties are placed between PROP_CHARACTER_STYLES and PROP_CHARACTER_END
+enum PropertyIds
+ {
+ PROP_ID_START = 1
+ ,META_PROP_CELL_MAR_BOTTOM = PROP_ID_START
+ ,META_PROP_CELL_MAR_LEFT
+ ,META_PROP_CELL_MAR_RIGHT
+ ,META_PROP_CELL_MAR_TOP
+ ,META_PROP_HORIZONTAL_BORDER
+ ,META_PROP_TABLE_STYLE_NAME
+ ,META_PROP_VERTICAL_BORDER
+ ,PROP_ADJUST
+ ,PROP_ADJUST_CONTRAST
+ ,PROP_ADJUST_LUMINANCE
+ ,PROP_ALTERNATIVE_TEXT
+ ,PROP_ANCHOR_TYPE
+ ,PROP_AUTOMATIC_DISTANCE
+ ,PROP_BACK_COLOR
+ ,PROP_BACK_COLOR_TRANSPARENCY
+ ,PROP_BITMAP
+ ,PROP_BOTTOM_BORDER
+ ,PROP_BOTTOM_BORDER_DISTANCE
+ ,PROP_BOTTOM_MARGIN
+ ,PROP_BREAK_TYPE
+ ,PROP_BULLET_CHAR
+ ,PROP_BULLET_FONT_NAME
+ ,PROP_CHAPTER_FORMAT
+ ,PROP_CHARACTER_STYLES
+ ,PROP_CHAR_AUTO_KERNING
+ ,PROP_CHAR_BACK_COLOR
+ ,PROP_CHAR_CASE_MAP
+ ,PROP_CHAR_CHAR_KERNING
+ ,PROP_CHAR_COLOR
+ ,PROP_CHAR_COMBINE_IS_ON
+ ,PROP_CHAR_COMBINE_PREFIX
+ ,PROP_CHAR_COMBINE_SUFFIX
+ ,PROP_CHAR_CONTOURED
+ ,PROP_CHAR_LEFT_BORDER
+ ,PROP_CHAR_RIGHT_BORDER
+ ,PROP_CHAR_TOP_BORDER
+ ,PROP_CHAR_BOTTOM_BORDER
+ ,PROP_CHAR_LEFT_BORDER_DISTANCE
+ ,PROP_CHAR_RIGHT_BORDER_DISTANCE
+ ,PROP_CHAR_TOP_BORDER_DISTANCE
+ ,PROP_CHAR_BOTTOM_BORDER_DISTANCE
+ ,PROP_CHAR_EMPHASIS
+ ,PROP_CHAR_ESCAPEMENT
+ ,PROP_CHAR_ESCAPEMENT_HEIGHT
+ ,PROP_CHAR_FLASH
+ ,PROP_CHAR_FONT_CHAR_SET
+ ,PROP_CHAR_FONT_NAME
+ ,PROP_CHAR_FONT_NAME_ASIAN
+ ,PROP_CHAR_FONT_NAME_COMPLEX
+ ,PROP_CHAR_HEIGHT
+ ,PROP_CHAR_HEIGHT_ASIAN
+ ,PROP_CHAR_HEIGHT_COMPLEX
+ ,PROP_CHAR_HIDDEN
+ ,PROP_CHAR_HIGHLIGHT
+ ,PROP_CHAR_LOCALE
+ ,PROP_CHAR_LOCALE_ASIAN
+ ,PROP_CHAR_LOCALE_COMPLEX
+ ,PROP_CHAR_POSTURE
+ ,PROP_CHAR_POSTURE_ASIAN
+ ,PROP_CHAR_POSTURE_COMPLEX
+ ,PROP_CHAR_PROP_HEIGHT
+ ,PROP_CHAR_PROP_HEIGHT_ASIAN
+ ,PROP_CHAR_PROP_HEIGHT_COMPLEX
+ ,PROP_CHAR_RELIEF
+ ,PROP_CHAR_ROTATION
+ ,PROP_CHAR_ROTATION_IS_FIT_TO_LINE
+ ,PROP_CHAR_SCALE_WIDTH
+ ,PROP_CHAR_SHADOW_FORMAT
+ ,PROP_CHAR_SHADING_MARKER
+ ,PROP_CHAR_SHADING_VALUE
+ ,PROP_CHAR_SHADOWED
+ ,PROP_CHAR_STRIKEOUT
+ ,PROP_CHAR_STYLE_NAME
+ ,PROP_CHAR_TEXTOUTLINE_TEXT_EFFECT
+ ,PROP_CHAR_TEXTFILL_TEXT_EFFECT
+ ,PROP_CHAR_THEME_NAME_ASCII
+ ,PROP_CHAR_THEME_NAME_CS
+ ,PROP_CHAR_THEME_NAME_H_ANSI
+ ,PROP_CHAR_THEME_NAME_EAST_ASIA
+ ,PROP_CHAR_THEME_FONT_NAME_ASCII
+ ,PROP_CHAR_THEME_FONT_NAME_CS
+ ,PROP_CHAR_THEME_FONT_NAME_EAST_ASIA
+ ,PROP_CHAR_THEME_COLOR
+ ,PROP_CHAR_THEME_ORIGINAL_COLOR
+ ,PROP_CHAR_THEME_COLOR_SHADE
+ ,PROP_CHAR_THEME_FILL
+ ,PROP_CHAR_THEME_COLOR_TINT
+ ,PROP_CHAR_UNDERLINE
+ ,PROP_CHAR_UNDERLINE_COLOR
+ ,PROP_CHAR_UNDERLINE_HAS_COLOR
+ ,PROP_CHAR_WEIGHT
+ ,PROP_CHAR_WEIGHT_ASIAN
+ ,PROP_CHAR_WEIGHT_COMPLEX
+ ,PROP_CHAR_WORD_MODE
+ ,PROP_CHAR_GLOW_TEXT_EFFECT
+ ,PROP_CHAR_SHADOW_TEXT_EFFECT
+ ,PROP_CHAR_REFLECTION_TEXT_EFFECT
+ ,PROP_CHAR_SCENE3D_TEXT_EFFECT
+ ,PROP_CHAR_PROPS3D_TEXT_EFFECT
+ ,PROP_CHAR_LIGATURES_TEXT_EFFECT
+ ,PROP_CHAR_NUMFORM_TEXT_EFFECT
+ ,PROP_CHAR_NUMSPACING_TEXT_EFFECT
+ ,PROP_CHAR_STYLISTICSETS_TEXT_EFFECT
+ ,PROP_CHAR_CNTXTALTS_TEXT_EFFECT
+ ,PROP_CHARACTER_END
+ ,PROP_CONTENT = PROP_CHARACTER_END
+ ,PROP_CONTOUR_OUTSIDE
+ ,PROP_CONTOUR_POLY_POLYGON
+ ,PROP_COUNT_EMPTY_LINES
+ ,PROP_COUNT_LINES_IN_FRAMES
+ ,PROP_CREATE_FROM_LEVEL_PARAGRAPH_STYLES
+ ,PROP_CREATE_FROM_MARKS
+ ,PROP_CREATE_FROM_OUTLINE
+ ,PROP_CURRENT_PRESENTATION
+ ,PROP_DELETE
+ ,PROP_DESCRIPTION
+ ,PROP_DISTANCE
+ ,PROP_DROP_CAP_FORMAT
+ ,PROP_FILE_FORMAT
+ ,PROP_FIRST_LINE_INDENT
+ ,PROP_FIRST_LINE_OFFSET
+ ,PROP_FIRST_PAGE
+ ,PROP_FOOTER_BODY_DISTANCE
+ ,PROP_FOOTER_DYNAMIC_SPACING
+ ,PROP_FOOTER_HEIGHT
+ ,PROP_FOOTER_IS_DYNAMIC_HEIGHT
+ ,PROP_FOOTER_IS_ON
+ ,PROP_FOOTER_IS_SHARED
+ ,PROP_FOOTER_TEXT
+ ,PROP_FOOTER_TEXT_LEFT
+ ,PROP_FOOTNOTE_COUNTING
+ ,PROP_FOOTNOTE_LINE_ADJUST
+ ,PROP_FORMAT
+ ,PROP_FULL_NAME
+ ,PROP_GRAPHIC
+ ,PROP_GRAPHIC_COLOR_MODE
+ ,PROP_GRID_BASE_HEIGHT
+ ,PROP_GRID_BASE_WIDTH
+ ,PROP_GRID_DISPLAY
+ ,PROP_GRID_LINES
+ ,PROP_GRID_MODE
+ ,PROP_GRID_PRINT
+ ,PROP_GRID_RUBY_HEIGHT
+ ,PROP_HEADER_BODY_DISTANCE
+ ,PROP_HEADER_DYNAMIC_SPACING
+ ,PROP_HEADER_HEIGHT
+ ,PROP_HEADER_IS_DYNAMIC_HEIGHT
+ ,PROP_HEADER_IS_ON
+ ,PROP_HEADER_IS_SHARED
+ ,PROP_HEADER_ROW_COUNT
+ ,PROP_HEADER_TEXT
+ ,PROP_HEADER_TEXT_LEFT
+ ,PROP_HEADING_STYLE_NAME
+ ,PROP_HEIGHT
+ ,PROP_HELP
+ ,PROP_HINT
+ ,PROP_HORI_ORIENT
+ ,PROP_HORI_ORIENT_POSITION
+ ,PROP_HORI_ORIENT_RELATION
+ ,PROP_HYPER_LINK_U_R_L
+ ,PROP_HYPERLINK
+ ,PROP_INDENT_AT
+ ,PROP_INPUT_STREAM
+ ,PROP_INSERT
+ ,PROP_INTERVAL
+ ,PROP_IS_DATE
+ ,PROP_IS_FIXED
+ ,PROP_IS_INPUT
+ ,PROP_IS_LANDSCAPE
+ ,PROP_IS_ON
+ ,PROP_IS_SPLIT_ALLOWED
+ ,PROP_IS_VISIBLE
+ ,PROP_LABEL_CATEGORY
+ ,PROP_LEFT_BORDER
+ ,PROP_LEFT_BORDER_DISTANCE
+ ,PROP_LEFT_MARGIN
+ ,PROP_LEVEL
+ ,PROP_LEVEL_FOLLOW
+ ,PROP_LEVEL_FORMAT
+ ,PROP_LEVEL_PARAGRAPH_STYLES
+ ,PROP_LISTTAB_STOP_POSITION
+ ,PROP_LIST_FORMAT
+ ,PROP_MACRO_NAME
+ ,PROP_NAME
+ ,PROP_NUMBERING_LEVEL
+ ,PROP_NUMBERING_RULES
+ ,PROP_NUMBERING_STYLE_NAME
+ ,PROP_NUMBERING_TYPE
+ ,PROP_NUMBER_FORMAT
+ ,PROP_NUMBER_POSITION
+ ,PROP_OPAQUE
+ ,PROP_OUTLINE_LEVEL
+ ,PROP_PAGE_DESC_NAME
+ ,PROP_PAGE_NUMBER_OFFSET
+ ,PROP_PAGE_TOGGLE
+ ,PROP_PARAGRAPH_FORMAT
+ ,PROP_PARAGRAPH_STYLES
+ ,PROP_PARA_ADJUST
+ ,PROP_PARA_BOTTOM_MARGIN
+ ,PROP_PARA_FIRST_LINE_INDENT
+ ,PROP_PARA_IS_HANGING_PUNCTUATION
+ ,PROP_PARA_IS_HYPHENATION
+ ,PROP_PARA_HYPHENATION_NO_CAPS
+ ,PROP_PARA_HYPHENATION_ZONE
+ ,PROP_PARA_KEEP_TOGETHER
+ ,PROP_PARA_LAST_LINE_ADJUST
+ ,PROP_PARA_LEFT_MARGIN
+ ,PROP_PARA_LINE_NUMBER_COUNT
+ ,PROP_PARA_LINE_NUMBER_START_VALUE
+ ,PROP_PARA_LINE_SPACING
+ ,PROP_PARA_ORPHANS
+ ,PROP_PARA_RIGHT_MARGIN
+ ,PROP_PARA_SPLIT
+ ,PROP_PARA_STYLE_NAME
+ ,PROP_PARA_TAB_STOPS
+ ,PROP_PARA_TOP_MARGIN
+ ,PROP_PARA_VERT_ALIGNMENT
+ ,PROP_PARA_WIDOWS
+ ,PROP_PARENT_NUMBERING
+ ,PROP_POSITION_AND_SPACE_MODE
+ ,PROP_POSITION_PROTECTED
+ ,PROP_IS_PROTECTED
+ ,PROP_PREFIX
+ ,PROP_REDLINE_AUTHOR
+ ,PROP_REDLINE_DATE_TIME
+ ,PROP_REDLINE_TYPE
+ ,PROP_REDLINE_REVERT_PROPERTIES
+ ,PROP_REFERENCE_FIELD_PART
+ ,PROP_REFERENCE_FIELD_SOURCE
+ ,PROP_RESTART_AT_EACH_PAGE
+ ,PROP_RIGHT_BORDER
+ ,PROP_RIGHT_BORDER_DISTANCE
+ ,PROP_RIGHT_MARGIN
+ ,PROP_SERVICE_CHAR_STYLE
+ ,PROP_SERVICE_PARA_STYLE
+ ,PROP_SIZE
+ ,PROP_SIZE100th_M_M
+ ,PROP_SIZE_PIXEL
+ ,PROP_SIZE_PROTECTED
+ ,PROP_SIZE_TYPE
+ ,PROP_SOURCE_NAME
+ ,PROP_SPLIT
+ ,PROP_STANDARD
+ ,PROP_START_AT
+ ,PROP_START_WITH
+ ,PROP_STREAM_NAME
+ ,PROP_SUB_TYPE
+ ,PROP_SUFFIX
+ ,PROP_SURROUND
+ ,PROP_SURROUND_CONTOUR
+ ,PROP_TABLE_BORDER
+ ,PROP_TABLE_BORDER_DISTANCES
+ ,PROP_TABLE_COLUMN_SEPARATORS
+ ,PROP_TABLE_REDLINE_PARAMS
+ ,PROP_TABLE_ROW_DELETE
+ ,PROP_TABLE_ROW_INSERT
+ ,PROP_TABLE_CELL_DELETE
+ ,PROP_TABLE_CELL_INSERT
+ ,PROP_TABS_RELATIVE_TO_INDENT
+ ,PROP_TAB_STOP_DISTANCE
+ ,PROP_TEXT
+ ,PROP_TEXT_COLUMNS
+ ,PROP_TEXT_RANGE
+ ,PROP_TEXT_VERTICAL_ADJUST
+ ,PROP_TITLE
+ ,PROP_TOKEN_CHAPTER_INFO
+ ,PROP_TOKEN_HYPERLINK_END
+ ,PROP_TOKEN_HYPERLINK_START
+ ,PROP_TOKEN_TEXT
+ ,PROP_TOKEN_TYPE
+ ,PROP_TOP_BORDER
+ ,PROP_TOP_BORDER_DISTANCE
+ ,PROP_TOP_MARGIN
+ ,PROP_VERTICAL_MERGE
+ ,PROP_GRID_STANDARD_MODE
+ ,PROP_VERT_ORIENT
+ ,PROP_VERT_ORIENT_POSITION
+ ,PROP_VERT_ORIENT_RELATION
+ ,PROP_WIDTH
+ ,PROP_WIDTH_TYPE
+ ,PROP_TBL_LOOK
+ ,PROP_WRITING_MODE
+ ,PROP_FRM_DIRECTION
+ ,PROP_EMBEDDED_OBJECT
+ ,PROP_PARA_CONTEXT_MARGIN
+ ,PROP_PAGE_STYLE_LAYOUT
+ ,PROP_Z_ORDER
+ ,PROP_EMBED_FONTS
+ ,PROP_EMBED_SYSTEM_FONTS
+ ,PROP_SHADOW_FORMAT
+ ,PROP_RELATIVE_WIDTH
+ ,PROP_IS_WIDTH_RELATIVE
+ ,PROP_GRAPHIC_BITMAP
+ ,PROP_GRAPHIC_SIZE
+ ,PROP_MIRROR_INDENTS
+ ,PROP_SURROUND_TEXT_WRAP_SMALL
+ ,PROP_PARA_SHADOW_FORMAT
+ ,PROP_FOOTNOTE_LINE_RELATIVE_WIDTH
+ ,PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING
+ ,PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING
+ ,PROP_TBL_HEADER
+ ,PROP_HORIZONTAL_MERGE
+ ,PROP_HIDE_TAB_LEADER_AND_PAGE_NUMBERS
+ ,PROP_TAB_IN_TOC
+ ,PROP_TOC_BOOKMARK
+ ,PROP_TOC_NEW_LINE
+ ,PROP_TOC_PARAGRAPH_OUTLINE_LEVEL
+ ,PROP_SDTPR
+ ,PROP_CELL_INTEROP_GRAB_BAG
+ ,PROP_TABLE_INTEROP_GRAB_BAG
+ ,PROP_APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING
+ ,PROP_SDT_END_BEFORE
+ ,PROP_PARA_SDT_END_BEFORE
+ ,META_PROP_TABLE_LOOK
+ ,PROP_PARA_CNF_STYLE
+ ,PROP_CELL_CNF_STYLE
+ ,PROP_ROW_CNF_STYLE
+ ,PROP_CELL_HIDE_MARK
+ ,PROP_FOLLOW_TEXT_FLOW
+ ,PROP_FILL_STYLE
+ ,PROP_FILL_COLOR
+ ,PROP_SNAP_TO_GRID
+ ,PROP_GRID_SNAP_TO_CHARS
+ ,PROP_RUBY_STYLE
+ ,PROP_RUBY_TEXT
+ ,PROP_RUBY_ADJUST
+ ,PROP_RUBY_POSITION
+ ,PROP_DATABASE_NAME
+ ,PROP_COMMAND_TYPE
+ ,PROP_DATATABLE_NAME
+ ,PROP_DATACOLUMN_NAME
+ ,PROP_CHAR_TRANSPARENCE
+ ,PROP_CELL_FORMULA
+ ,PROP_CELL_FORMULA_CONVERTED
+ ,PROP_GUTTER_MARGIN
+ ,PROP_RTL_GUTTER
+ ,PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF
+ ,PROP_PARA_CONNECT_BORDERS
+ };
+
+//Returns the UNO string equivalent to eId.
+OUString getPropertyName(PropertyIds eId);
+
+bool isCharacterProperty(const PropertyIds eId);
+
+bool isParagraphProperty(const PropertyIds eId);
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx
new file mode 100644
index 000000000..1ae28759d
--- /dev/null
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -0,0 +1,2219 @@
+/* -*- 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 "PropertyMap.hxx"
+#include "TagLogger.hxx"
+#include <ooxml/resourceids.hxx>
+#include "DomainMapper_Impl.hxx"
+#include "ConversionHelper.hxx"
+#include <editeng/boxitem.hxx>
+#include <i18nutil/paper.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/style/BreakType.hpp>
+#include <com/sun/star/style/PageStyleLayout.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/table/ShadowFormat.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/HorizontalAdjust.hpp>
+#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/text/XRedline.hpp>
+#include <com/sun/star/text/XTextColumns.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
+#include <com/sun/star/text/XTextTablesSupplier.hpp>
+#include <com/sun/star/text/TextGridMode.hpp>
+#include <com/sun/star/text/XTextCopy.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <comphelper/sequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <tools/diagnose_ex.h>
+#include "PropertyMapHelper.hxx"
+#include <o3tl/sorted_vector.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+using namespace com::sun::star;
+
+namespace writerfilter::dmapper {
+
+uno::Sequence< beans::PropertyValue > PropertyMap::GetPropertyValues( bool bCharGrabBag )
+{
+ using comphelper::makePropertyValue;
+
+ if ( !m_aValues.empty() || m_vMap.empty() )
+ return comphelper::containerToSequence( m_aValues );
+
+ size_t nCharGrabBag = 0;
+ size_t nParaGrabBag = 0;
+ size_t nCellGrabBag = 0;
+ size_t nRowGrabBag = 0;
+
+ const PropValue* pParaStyleProp = nullptr;
+ const PropValue* pCharStyleProp = nullptr;
+ const PropValue* pNumRuleProp = nullptr;
+
+ m_aValues.reserve( m_vMap.size() );
+ for ( const auto& rPropPair : m_vMap )
+ {
+ if ( rPropPair.second.getGrabBagType() == CHAR_GRAB_BAG )
+ nCharGrabBag++;
+ else if ( rPropPair.second.getGrabBagType() == PARA_GRAB_BAG )
+ nParaGrabBag++;
+ else if ( rPropPair.second.getGrabBagType() == CELL_GRAB_BAG )
+ nCellGrabBag++;
+ else if ( rPropPair.first == PROP_CELL_INTEROP_GRAB_BAG )
+ {
+ uno::Sequence< beans::PropertyValue > aSeq;
+ rPropPair.second.getValue() >>= aSeq;
+ nCellGrabBag += aSeq.getLength();
+ }
+ else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
+ nRowGrabBag++;
+
+ if ( rPropPair.first == PROP_PARA_STYLE_NAME ) pParaStyleProp = &rPropPair.second;
+ if ( rPropPair.first == PROP_CHAR_STYLE_NAME ) pCharStyleProp = &rPropPair.second;
+ if ( rPropPair.first == PROP_NUMBERING_RULES ) pNumRuleProp = &rPropPair.second;
+ }
+
+ // Style names have to be the first elements within the property sequence
+ // otherwise they will overwrite 'hard' attributes
+ if ( pParaStyleProp != nullptr )
+ m_aValues.push_back( makePropertyValue( getPropertyName( PROP_PARA_STYLE_NAME ), pParaStyleProp->getValue() ) );
+ if ( pCharStyleProp != nullptr )
+ m_aValues.push_back( makePropertyValue( getPropertyName( PROP_CHAR_STYLE_NAME ), pCharStyleProp->getValue() ) );
+ if ( pNumRuleProp != nullptr )
+ m_aValues.push_back( makePropertyValue(getPropertyName( PROP_NUMBERING_RULES ), pNumRuleProp->getValue() ) );
+
+ // If there are any grab bag properties, we need one slot for them.
+ uno::Sequence< beans::PropertyValue > aCharGrabBagValues( nCharGrabBag );
+ uno::Sequence< beans::PropertyValue > aParaGrabBagValues( nParaGrabBag );
+ uno::Sequence< beans::PropertyValue > aCellGrabBagValues( nCellGrabBag );
+ uno::Sequence< beans::PropertyValue > aRowGrabBagValues ( nRowGrabBag );
+ beans::PropertyValue* pCharGrabBagValues = aCharGrabBagValues.getArray();
+ beans::PropertyValue* pParaGrabBagValues = aParaGrabBagValues.getArray();
+ beans::PropertyValue* pCellGrabBagValues = aCellGrabBagValues.getArray();
+ beans::PropertyValue* pRowGrabBagValues = aRowGrabBagValues.getArray();
+ // Record index for the next property to be added in each grab bag.
+ sal_Int32 nRowGrabBagValue = 0;
+ sal_Int32 nCellGrabBagValue = 0;
+ sal_Int32 nParaGrabBagValue = 0;
+ sal_Int32 nCharGrabBagValue = 0;
+
+ for ( const auto& rPropPair : m_vMap )
+ {
+ if ( rPropPair.first != PROP_PARA_STYLE_NAME &&
+ rPropPair.first != PROP_CHAR_STYLE_NAME &&
+ rPropPair.first != PROP_NUMBERING_RULES )
+ {
+ if ( rPropPair.second.getGrabBagType() == CHAR_GRAB_BAG )
+ {
+ if ( bCharGrabBag )
+ {
+ pCharGrabBagValues[nCharGrabBagValue].Name = getPropertyName( rPropPair.first );
+ pCharGrabBagValues[nCharGrabBagValue].Value = rPropPair.second.getValue();
+ ++nCharGrabBagValue;
+ }
+ }
+ else if ( rPropPair.second.getGrabBagType() == PARA_GRAB_BAG )
+ {
+ pParaGrabBagValues[nParaGrabBagValue].Name = getPropertyName( rPropPair.first );
+ pParaGrabBagValues[nParaGrabBagValue].Value = rPropPair.second.getValue();
+ ++nParaGrabBagValue;
+ }
+ else if ( rPropPair.second.getGrabBagType() == CELL_GRAB_BAG )
+ {
+ pCellGrabBagValues[nCellGrabBagValue].Name = getPropertyName( rPropPair.first );
+ pCellGrabBagValues[nCellGrabBagValue].Value = rPropPair.second.getValue();
+ ++nCellGrabBagValue;
+ }
+ else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
+ {
+ pRowGrabBagValues[nRowGrabBagValue].Name = getPropertyName( rPropPair.first );
+ pRowGrabBagValues[nRowGrabBagValue].Value = rPropPair.second.getValue();
+ ++nRowGrabBagValue;
+ }
+ else if ( rPropPair.first == PROP_CELL_INTEROP_GRAB_BAG )
+ {
+ uno::Sequence< beans::PropertyValue > aSeq;
+ rPropPair.second.getValue() >>= aSeq;
+ std::copy(std::cbegin(aSeq), std::cend(aSeq), pCellGrabBagValues + nCellGrabBagValue);
+ nCellGrabBagValue += aSeq.getLength();
+ }
+ else
+ {
+ m_aValues.push_back( makePropertyValue( getPropertyName( rPropPair.first ), rPropPair.second.getValue() ) );
+ }
+ }
+ }
+
+ if ( nCharGrabBag && bCharGrabBag )
+ m_aValues.push_back( makePropertyValue( "CharInteropGrabBag", uno::Any( aCharGrabBagValues ) ) );
+
+ if ( nParaGrabBag )
+ m_aValues.push_back( makePropertyValue( "ParaInteropGrabBag", uno::Any( aParaGrabBagValues ) ) );
+
+ if ( nCellGrabBag )
+ m_aValues.push_back( makePropertyValue( "CellInteropGrabBag", uno::Any( aCellGrabBagValues ) ) );
+
+ if ( nRowGrabBag )
+ m_aValues.push_back( makePropertyValue( "RowInteropGrabBag", uno::Any( aRowGrabBagValues ) ) );
+
+ return comphelper::containerToSequence( m_aValues );
+}
+
+std::vector< PropertyIds > PropertyMap::GetPropertyIds()
+{
+ std::vector< PropertyIds > aRet;
+ for ( const auto& rPropPair : m_vMap )
+ aRet.push_back( rPropPair.first );
+ return aRet;
+}
+
+#ifdef DBG_UTIL
+static void lcl_AnyToTag( const uno::Any& rAny )
+{
+ try {
+ sal_Int32 aInt = 0;
+ if ( rAny >>= aInt )
+ {
+ TagLogger::getInstance().attribute( "value", rAny );
+ }
+ else
+ {
+ TagLogger::getInstance().attribute( "unsignedValue", 0 );
+ }
+
+ sal_uInt32 auInt = 0;
+ rAny >>= auInt;
+ TagLogger::getInstance().attribute( "unsignedValue", auInt );
+
+ float aFloat = 0.0f;
+ if ( rAny >>= aFloat )
+ {
+ TagLogger::getInstance().attribute( "floatValue", rAny );
+ }
+ else
+ {
+ TagLogger::getInstance().attribute( "unsignedValue", 0 );
+ }
+
+ OUString aStr;
+ rAny >>= aStr;
+ TagLogger::getInstance().attribute( "stringValue", aStr );
+ }
+ catch ( ... )
+ {
+ }
+}
+#endif
+
+void PropertyMap::Insert( PropertyIds eId, const uno::Any& rAny, bool bOverwrite, GrabBagType i_GrabBagType, bool bDocDefault )
+{
+#ifdef DBG_UTIL
+ const OUString& rInsert = getPropertyName(eId);
+
+ TagLogger::getInstance().startElement("propertyMap.insert");
+ TagLogger::getInstance().attribute("name", rInsert);
+ lcl_AnyToTag(rAny);
+ TagLogger::getInstance().endElement();
+#endif
+
+ if ( !bOverwrite )
+ m_vMap.insert(std::make_pair(eId, PropValue(rAny, i_GrabBagType, bDocDefault)));
+ else
+ m_vMap[eId] = PropValue(rAny, i_GrabBagType);
+
+ Invalidate();
+}
+
+void PropertyMap::Erase( PropertyIds eId )
+{
+ // Safe call to erase, it throws no exceptions, even if eId is not in m_vMap
+ m_vMap.erase(eId);
+
+ Invalidate();
+}
+
+std::optional< PropertyMap::Property > PropertyMap::getProperty( PropertyIds eId ) const
+{
+ std::map< PropertyIds, PropValue >::const_iterator aIter = m_vMap.find( eId );
+ if ( aIter == m_vMap.end() )
+ return std::optional<Property>();
+ else
+ return std::make_pair( eId, aIter->second.getValue() );
+}
+
+bool PropertyMap::isSet( PropertyIds eId) const
+{
+ return m_vMap.find( eId ) != m_vMap.end();
+}
+
+bool PropertyMap::isDocDefault( PropertyIds eId ) const
+{
+ std::map< PropertyIds, PropValue >::const_iterator aIter = m_vMap.find( eId );
+ if ( aIter == m_vMap.end() )
+ return false;
+ else
+ return aIter->second.getIsDocDefault();
+}
+
+#ifdef DBG_UTIL
+void PropertyMap::dumpXml() const
+{
+ TagLogger::getInstance().startElement( "PropertyMap" );
+
+ for ( const auto& rPropPair : m_vMap )
+ {
+ TagLogger::getInstance().startElement( "property" );
+
+ TagLogger::getInstance().attribute( "name", getPropertyName( rPropPair.first ) );
+
+ switch ( rPropPair.first )
+ {
+ case PROP_TABLE_COLUMN_SEPARATORS:
+ lcl_DumpTableColumnSeparators( rPropPair.second.getValue() );
+ break;
+ default:
+ {
+ try
+ {
+ sal_Int32 aInt = 0;
+ rPropPair.second.getValue() >>= aInt;
+ TagLogger::getInstance().attribute( "value", aInt );
+
+ sal_uInt32 auInt = 0;
+ rPropPair.second.getValue() >>= auInt;
+ TagLogger::getInstance().attribute( "unsignedValue", auInt );
+
+ float aFloat = 0.0;
+ rPropPair.second.getValue() >>= aFloat;
+ TagLogger::getInstance().attribute( "floatValue", aFloat );
+
+ rPropPair.second.getValue() >>= auInt;
+ TagLogger::getInstance().attribute( "stringValue", std::u16string_view() );
+ }
+ catch ( ... )
+ {
+ }
+ }
+ break;
+ }
+
+ TagLogger::getInstance().endElement();
+ }
+
+ TagLogger::getInstance().endElement();
+}
+#endif
+
+void PropertyMap::InsertProps( const PropertyMapPtr& rMap, const bool bOverwrite )
+{
+ if ( !rMap )
+ return;
+
+ for ( const auto& rPropPair : rMap->m_vMap )
+ {
+ if ( bOverwrite || !m_vMap.count(rPropPair.first) )
+ {
+ if ( !bOverwrite && !rPropPair.second.getIsDocDefault() )
+ m_vMap.insert(std::make_pair(rPropPair.first, PropValue(rPropPair.second.getValue(), rPropPair.second.getGrabBagType(), true)));
+ else
+ m_vMap[rPropPair.first] = rPropPair.second;
+ }
+ }
+
+ insertTableProperties( rMap.get(), bOverwrite );
+
+ Invalidate();
+}
+
+void PropertyMap::insertTableProperties( const PropertyMap*, const bool )
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element( "PropertyMap.insertTableProperties" );
+#endif
+}
+
+void PropertyMap::printProperties()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement( "properties" );
+
+ for ( const auto& rPropPair : m_vMap )
+ {
+ SAL_INFO( "writerfilter", getPropertyName( rPropPair.first ) );
+
+ table::BorderLine2 aLine;
+ sal_Int32 nColor;
+ if ( rPropPair.second.getValue() >>= aLine )
+ {
+ TagLogger::getInstance().startElement( "borderline" );
+ TagLogger::getInstance().attribute( "color", aLine.Color );
+ TagLogger::getInstance().attribute( "inner", aLine.InnerLineWidth );
+ TagLogger::getInstance().attribute( "outer", aLine.OuterLineWidth );
+ TagLogger::getInstance().endElement();
+ }
+ else if ( rPropPair.second.getValue() >>= nColor )
+ {
+ TagLogger::getInstance().startElement( "color" );
+ TagLogger::getInstance().attribute( "number", nColor );
+ TagLogger::getInstance().endElement();
+ }
+ }
+
+ TagLogger::getInstance().endElement();
+#else
+ (void) this; // avoid loplugin:staticmethods
+#endif
+}
+
+SectionPropertyMap::SectionPropertyMap( bool bIsFirstSection )
+ : m_bIsFirstSection( bIsFirstSection )
+ , m_eBorderApply( BorderApply::ToAllInSection )
+ , m_eBorderOffsetFrom( BorderOffsetFrom::Text )
+ , m_bTitlePage( false )
+ , m_nColumnCount( 0 )
+ , m_nColumnDistance( 1249 )
+ , m_bSeparatorLineIsOn( false )
+ , m_bEvenlySpaced( false )
+ , m_nPageNumber( -1 )
+ , m_nPageNumberType( -1 )
+ , m_nBreakType( -1 )
+ , m_nLeftMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
+ , m_nRightMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
+ , m_nGutterMargin(0)
+ , m_nTopMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
+ , m_nBottomMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
+ , m_nHeaderTop( o3tl::convert(0.5, o3tl::Length::in, o3tl::Length::mm100) )
+ , m_nHeaderBottom( o3tl::convert(0.5, o3tl::Length::in, o3tl::Length::mm100) )
+ , m_nGridType( 0 )
+ , m_nGridLinePitch( 1 )
+ , m_nDxtCharSpace( 0 )
+ , m_bGridSnapToChars( true )
+ , m_nLnnMod( 0 )
+ , m_nLnc(NS_ooxml::LN_Value_ST_LineNumberRestart_newPage)
+ , m_ndxaLnn( 0 )
+ , m_nLnnMin( 0 )
+ , m_bDynamicHeightTop( true )
+ , m_bDynamicHeightBottom( true )
+ , m_bDefaultHeaderLinkToPrevious( true )
+ , m_bEvenPageHeaderLinkToPrevious( true )
+ , m_bFirstPageHeaderLinkToPrevious( true )
+ , m_bDefaultFooterLinkToPrevious( true )
+ , m_bEvenPageFooterLinkToPrevious( true )
+ , m_bFirstPageFooterLinkToPrevious( true )
+{
+#ifdef DBG_UTIL
+ static sal_Int32 nNumber = 0;
+ m_nDebugSectionNumber = nNumber++;
+#endif
+
+ for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
+ {
+ m_nBorderDistances[nBorder] = -1;
+ m_bBorderShadows[nBorder] = false;
+ }
+ // todo: set defaults in ApplyPropertiesToPageStyles
+ // initialize defaults
+ PaperInfo aLetter( PAPER_LETTER );
+ // page height, 1/100mm
+ Insert( PROP_HEIGHT, uno::Any( static_cast<sal_Int32>(aLetter.getHeight()) ) );
+ // page width, 1/100mm
+ Insert( PROP_WIDTH, uno::Any( static_cast<sal_Int32>(aLetter.getWidth()) ) );
+ // page left margin, 1/100 mm
+ Insert( PROP_LEFT_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
+ // page right margin, 1/100 mm
+ Insert( PROP_RIGHT_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
+ // page top margin, 1/100 mm
+ Insert( PROP_TOP_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
+ // page bottom margin, 1/100 mm
+ Insert( PROP_BOTTOM_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
+ // page style layout
+ Insert( PROP_PAGE_STYLE_LAYOUT, uno::Any( style::PageStyleLayout_ALL ) );
+ uno::Any aFalse( uno::Any( false ) );
+ Insert( PROP_GRID_DISPLAY, aFalse );
+ Insert( PROP_GRID_PRINT, aFalse );
+ Insert( PROP_GRID_MODE, uno::Any( text::TextGridMode::NONE ) );
+
+ if ( m_bIsFirstSection )
+ {
+ m_sFirstPageStyleName = getPropertyName( PROP_FIRST_PAGE );
+ m_sFollowPageStyleName = getPropertyName( PROP_STANDARD );
+ }
+}
+
+uno::Reference< beans::XPropertySet > SectionPropertyMap::GetPageStyle( DomainMapper_Impl& rDM_Impl,
+ bool bFirst )
+{
+ const uno::Reference< container::XNameContainer >& xPageStyles = rDM_Impl.GetPageStyles();
+ const uno::Reference < lang::XMultiServiceFactory >& xTextFactory = rDM_Impl.GetTextFactory();
+ uno::Reference< beans::XPropertySet > xRet;
+ try
+ {
+ if ( bFirst )
+ {
+ if ( m_sFirstPageStyleName.isEmpty() && xPageStyles.is() )
+ {
+ assert( !rDM_Impl.IsInFootOrEndnote() && "Don't create useless page styles" );
+ m_sFirstPageStyleName = rDM_Impl.GetUnusedPageStyleName();
+ m_aFirstPageStyle.set( xTextFactory->createInstance( "com.sun.star.style.PageStyle" ),
+ uno::UNO_QUERY );
+
+ // Call insertByName() before GetPageStyle(), otherwise the
+ // first and the follow page style will have the same name, and
+ // insertByName() will fail.
+ if ( xPageStyles.is() )
+ xPageStyles->insertByName( m_sFirstPageStyleName, uno::Any( m_aFirstPageStyle ) );
+
+ // Ensure that m_aFollowPageStyle has been created
+ GetPageStyle( rDM_Impl, false );
+ // Chain m_aFollowPageStyle to be after m_aFirstPageStyle
+ m_aFirstPageStyle->setPropertyValue( "FollowStyle",
+ uno::Any( m_sFollowPageStyleName ) );
+ }
+ else if ( !m_aFirstPageStyle.is() && xPageStyles.is() )
+ {
+ xPageStyles->getByName( m_sFirstPageStyleName ) >>= m_aFirstPageStyle;
+ }
+ xRet = m_aFirstPageStyle;
+ }
+ else
+ {
+ if ( m_sFollowPageStyleName.isEmpty() && xPageStyles.is() )
+ {
+ assert( !rDM_Impl.IsInFootOrEndnote() && "Don't create useless page styles" );
+ m_sFollowPageStyleName = rDM_Impl.GetUnusedPageStyleName();
+ m_aFollowPageStyle.set( xTextFactory->createInstance( "com.sun.star.style.PageStyle" ),
+ uno::UNO_QUERY );
+ xPageStyles->insertByName( m_sFollowPageStyleName, uno::Any( m_aFollowPageStyle ) );
+ }
+ else if ( !m_aFollowPageStyle.is() && xPageStyles.is() )
+ {
+ xPageStyles->getByName( m_sFollowPageStyleName ) >>= m_aFollowPageStyle;
+ }
+ xRet = m_aFollowPageStyle;
+ }
+
+ }
+ catch ( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION( "writerfilter" );
+ }
+
+ return xRet;
+}
+
+void SectionPropertyMap::SetBorder( BorderPosition ePos, sal_Int32 nLineDistance, const table::BorderLine2& rBorderLine, bool bShadow )
+{
+ m_oBorderLines[ePos] = rBorderLine;
+ m_nBorderDistances[ePos] = nLineDistance;
+ m_bBorderShadows[ePos] = bShadow;
+}
+
+void SectionPropertyMap::ApplyBorderToPageStyles( DomainMapper_Impl& rDM_Impl,
+ BorderApply eBorderApply, BorderOffsetFrom eOffsetFrom )
+{
+ /*
+ page border applies to:
+ nIntValue & 0x07 ->
+ 0 all pages in this section
+ 1 first page in this section
+ 2 all pages in this section but first
+ 3 whole document (all sections)
+ nIntValue & 0x18 -> page border depth 0 - in front 1- in back
+ nIntValue & 0xe0 ->
+ page border offset from:
+ 0 offset from text
+ 1 offset from edge of page
+ */
+ uno::Reference< beans::XPropertySet > xFirst;
+ uno::Reference< beans::XPropertySet > xSecond;
+ // todo: negative spacing (from ww8par6.cxx)
+ switch ( eBorderApply )
+ {
+ case BorderApply::ToAllInSection: // all styles
+ if ( !m_sFollowPageStyleName.isEmpty() )
+ xFirst = GetPageStyle( rDM_Impl, false );
+ if ( !m_sFirstPageStyleName.isEmpty() )
+ xSecond = GetPageStyle( rDM_Impl, true );
+ break;
+ case BorderApply::ToFirstPageInSection: // first page
+ if ( !m_sFirstPageStyleName.isEmpty() )
+ xFirst = GetPageStyle( rDM_Impl, true );
+ break;
+ case BorderApply::ToAllButFirstInSection: // left and right
+ if ( !m_sFollowPageStyleName.isEmpty() )
+ xFirst = GetPageStyle( rDM_Impl, false );
+ break;
+ default:
+ return;
+ }
+
+ // has to be sorted like enum BorderPosition: l-r-t-b
+ const PropertyIds aBorderIds[4] =
+ {
+ PROP_LEFT_BORDER,
+ PROP_RIGHT_BORDER,
+ PROP_TOP_BORDER,
+ PROP_BOTTOM_BORDER
+ };
+
+ const PropertyIds aBorderDistanceIds[4] =
+ {
+ PROP_LEFT_BORDER_DISTANCE,
+ PROP_RIGHT_BORDER_DISTANCE,
+ PROP_TOP_BORDER_DISTANCE,
+ PROP_BOTTOM_BORDER_DISTANCE
+ };
+
+ const PropertyIds aMarginIds[4] =
+ {
+ PROP_LEFT_MARGIN,
+ PROP_RIGHT_MARGIN,
+ PROP_TOP_MARGIN,
+ PROP_BOTTOM_MARGIN
+ };
+
+ for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
+ {
+ if ( m_oBorderLines[nBorder] )
+ {
+ const OUString sBorderName = getPropertyName( aBorderIds[nBorder] );
+ if ( xFirst.is() )
+ xFirst->setPropertyValue( sBorderName, uno::Any( *m_oBorderLines[nBorder] ) );
+ if ( xSecond.is() )
+ xSecond->setPropertyValue( sBorderName, uno::Any( *m_oBorderLines[nBorder] ) );
+ }
+ if ( m_nBorderDistances[nBorder] >= 0 )
+ {
+ sal_uInt32 nLineWidth = 0;
+ if ( m_oBorderLines[nBorder] )
+ nLineWidth = m_oBorderLines[nBorder]->LineWidth;
+ if ( xFirst.is() )
+ SetBorderDistance( xFirst, aMarginIds[nBorder], aBorderDistanceIds[nBorder],
+ m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth, rDM_Impl );
+ if ( xSecond.is() )
+ SetBorderDistance( xSecond, aMarginIds[nBorder], aBorderDistanceIds[nBorder],
+ m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth, rDM_Impl );
+ }
+ }
+
+ if ( m_bBorderShadows[BORDER_RIGHT] )
+ {
+ table::ShadowFormat aFormat = getShadowFromBorder( *m_oBorderLines[BORDER_RIGHT] );
+ if ( xFirst.is() )
+ xFirst->setPropertyValue( getPropertyName( PROP_SHADOW_FORMAT ), uno::Any( aFormat ) );
+ if ( xSecond.is() )
+ xSecond->setPropertyValue( getPropertyName( PROP_SHADOW_FORMAT ), uno::Any( aFormat ) );
+ }
+}
+
+table::ShadowFormat PropertyMap::getShadowFromBorder( const table::BorderLine2& rBorder )
+{
+ // In Word UI, shadow is a boolean property, in OOXML, it's a boolean
+ // property of each 4 border type, finally in Writer the border is a
+ // property of the page style, with shadow location, distance and
+ // color. See SwWW8ImplReader::SetShadow().
+ table::ShadowFormat aFormat;
+ aFormat.Color = sal_Int32(COL_BLACK);
+ aFormat.Location = table::ShadowLocation_BOTTOM_RIGHT;
+ aFormat.ShadowWidth = rBorder.LineWidth;
+ return aFormat;
+}
+
+void SectionPropertyMap::SetBorderDistance( const uno::Reference< beans::XPropertySet >& xStyle,
+ PropertyIds eMarginId,
+ PropertyIds eDistId,
+ sal_Int32 nDistance,
+ BorderOffsetFrom eOffsetFrom,
+ sal_uInt32 nLineWidth,
+ DomainMapper_Impl& rDM_Impl )
+{
+ if (!xStyle.is())
+ return;
+ const OUString sMarginName = getPropertyName( eMarginId );
+ const OUString sBorderDistanceName = getPropertyName( eDistId );
+ uno::Any aMargin = xStyle->getPropertyValue( sMarginName );
+ sal_Int32 nMargin = 0;
+ aMargin >>= nMargin;
+ editeng::BorderDistanceFromWord(eOffsetFrom == BorderOffsetFrom::Edge, nMargin, nDistance,
+ nLineWidth);
+
+ if (eOffsetFrom == BorderOffsetFrom::Edge)
+ {
+ uno::Any aGutterMargin = xStyle->getPropertyValue( "GutterMargin" );
+ sal_Int32 nGutterMargin = 0;
+ aGutterMargin >>= nGutterMargin;
+
+ if (eMarginId == PROP_LEFT_MARGIN && !rDM_Impl.GetSettingsTable()->GetGutterAtTop())
+ {
+ nMargin -= nGutterMargin;
+ nDistance += nGutterMargin;
+ }
+
+ if (eMarginId == PROP_TOP_MARGIN && rDM_Impl.GetSettingsTable()->GetGutterAtTop())
+ {
+ nMargin -= nGutterMargin;
+ nDistance += nGutterMargin;
+ }
+ }
+
+ // Change the margins with the border distance
+ uno::Reference< beans::XMultiPropertySet > xMultiSet( xStyle, uno::UNO_QUERY_THROW );
+ uno::Sequence<OUString> aProperties { sMarginName, sBorderDistanceName };
+ uno::Sequence<uno::Any> aValues { uno::Any( nMargin ), uno::Any( nDistance ) };
+ xMultiSet->setPropertyValues( aProperties, aValues );
+}
+
+void SectionPropertyMap::DontBalanceTextColumns()
+{
+ try
+ {
+ if ( m_xColumnContainer.is() )
+ m_xColumnContainer->setPropertyValue( "DontBalanceTextColumns", uno::Any( true ) );
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::DontBalanceTextColumns" );
+ }
+}
+
+void SectionPropertyMap::ApplySectionProperties( const uno::Reference< beans::XPropertySet >& xSection, DomainMapper_Impl& /*rDM_Impl*/ )
+{
+ try
+ {
+ if ( xSection.is() )
+ {
+ std::optional< PropertyMap::Property > pProp = getProperty( PROP_WRITING_MODE );
+ if ( pProp )
+ xSection->setPropertyValue( "WritingMode", pProp->second );
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "Exception in SectionPropertyMap::ApplySectionProperties");
+ }
+}
+
+void SectionPropertyMap::ApplyProtectionProperties( uno::Reference< beans::XPropertySet >& xSection, DomainMapper_Impl& rDM_Impl )
+{
+ try
+ {
+ // Word implements section protection differently than LO.
+ // PROP_IS_PROTECTED only applies if global setting GetProtectForm is enabled.
+ bool bIsProtected = rDM_Impl.GetSettingsTable()->GetProtectForm();
+ if ( bIsProtected )
+ {
+ // If form protection is enabled then section protection is enabled, unless explicitly disabled
+ if ( isSet(PROP_IS_PROTECTED) )
+ getProperty(PROP_IS_PROTECTED)->second >>= bIsProtected;
+ if ( !xSection.is() )
+ xSection = rDM_Impl.appendTextSectionAfter( m_xStartingRange );
+ if ( xSection.is() )
+ xSection->setPropertyValue( getPropertyName(PROP_IS_PROTECTED), uno::Any(bIsProtected) );
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "ApplyProtectionProperties failed setting PROP_IS_PROTECTED");
+ }
+}
+
+uno::Reference< text::XTextColumns > SectionPropertyMap::ApplyColumnProperties( const uno::Reference< beans::XPropertySet >& xColumnContainer,
+ DomainMapper_Impl& rDM_Impl )
+{
+ uno::Reference< text::XTextColumns > xColumns;
+ assert( m_nColumnCount > 1 && "ApplyColumnProperties called without any columns" );
+ try
+ {
+ const OUString sTextColumns = getPropertyName( PROP_TEXT_COLUMNS );
+ if ( xColumnContainer.is() )
+ xColumnContainer->getPropertyValue( sTextColumns ) >>= xColumns;
+ uno::Reference< beans::XPropertySet > xColumnPropSet( xColumns, uno::UNO_QUERY_THROW );
+ if ( !m_bEvenlySpaced &&
+ ( sal_Int32(m_aColWidth.size()) == m_nColumnCount ) &&
+ ( (sal_Int32(m_aColDistance.size()) == m_nColumnCount - 1) || (sal_Int32(m_aColDistance.size()) == m_nColumnCount) ) )
+ {
+ // the column width in word is an absolute value, in OOo it's relative
+ // the distances are both absolute
+ sal_Int32 nColSum = 0;
+ for ( sal_Int32 nCol = 0; nCol < m_nColumnCount; ++nCol )
+ {
+ nColSum += m_aColWidth[nCol];
+ if ( nCol )
+ nColSum += m_aColDistance[nCol - 1];
+ }
+
+ sal_Int32 nRefValue = xColumns->getReferenceValue();
+ double fRel = nColSum ? double( nRefValue ) / double( nColSum ) : 0.0;
+ uno::Sequence< text::TextColumn > aColumns( m_nColumnCount );
+ text::TextColumn* pColumn = aColumns.getArray();
+
+ nColSum = 0;
+ for ( sal_Int32 nCol = 0; nCol < m_nColumnCount; ++nCol )
+ {
+ const double fLeft = nCol ? m_aColDistance[nCol - 1] / 2 : 0;
+ pColumn[nCol].LeftMargin = fLeft;
+ const double fRight = (nCol == m_nColumnCount - 1) ? 0 : m_aColDistance[nCol] / 2;
+ pColumn[nCol].RightMargin = fRight;
+ const double fWidth = m_aColWidth[nCol];
+ pColumn[nCol].Width = (fWidth + fLeft + fRight) * fRel;
+ nColSum += pColumn[nCol].Width;
+ }
+ if ( nColSum != nRefValue )
+ pColumn[m_nColumnCount - 1].Width += (nRefValue - nColSum);
+ assert( pColumn[m_nColumnCount - 1].Width >= 0 );
+
+ xColumns->setColumns( aColumns );
+ }
+ else
+ {
+ xColumns->setColumnCount( m_nColumnCount );
+ xColumnPropSet->setPropertyValue( getPropertyName( PROP_AUTOMATIC_DISTANCE ), uno::Any( m_nColumnDistance ) );
+ }
+
+ if ( m_bSeparatorLineIsOn )
+ {
+ xColumnPropSet->setPropertyValue( "SeparatorLineIsOn", uno::Any( true ) );
+ xColumnPropSet->setPropertyValue( "SeparatorLineVerticalAlignment", uno::Any( style::VerticalAlignment_TOP ) );
+ xColumnPropSet->setPropertyValue( "SeparatorLineRelativeHeight", uno::Any( static_cast<sal_Int8>(100) ) );
+ xColumnPropSet->setPropertyValue( "SeparatorLineColor", uno::Any( static_cast<sal_Int32>(COL_BLACK) ) );
+ // 1 twip -> 2 mm100.
+ xColumnPropSet->setPropertyValue( "SeparatorLineWidth", uno::Any( static_cast<sal_Int32>(2) ) );
+ }
+ xColumnContainer->setPropertyValue( sTextColumns, uno::Any( xColumns ) );
+ // Set the columns to be unbalanced if that compatibility option is set or this is the last section.
+ m_xColumnContainer = xColumnContainer;
+ if ( rDM_Impl.GetSettingsTable()->GetNoColumnBalance() || rDM_Impl.GetIsLastSectionGroup() )
+ DontBalanceTextColumns();
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyColumnProperties" );
+ }
+ return xColumns;
+}
+
+bool SectionPropertyMap::HasHeader( bool bFirstPage ) const
+{
+ bool bRet = false;
+ if ( (bFirstPage && m_aFirstPageStyle.is()) || (!bFirstPage && m_aFollowPageStyle.is()) )
+ {
+ if ( bFirstPage )
+ m_aFirstPageStyle->getPropertyValue(
+ getPropertyName( PROP_HEADER_IS_ON ) ) >>= bRet;
+ else
+ m_aFollowPageStyle->getPropertyValue(
+ getPropertyName( PROP_HEADER_IS_ON ) ) >>= bRet;
+ }
+ return bRet;
+}
+
+bool SectionPropertyMap::HasFooter( bool bFirstPage ) const
+{
+ bool bRet = false;
+ if ( (bFirstPage && m_aFirstPageStyle.is()) || (!bFirstPage && m_aFollowPageStyle.is()) )
+ {
+ if ( bFirstPage )
+ m_aFirstPageStyle->getPropertyValue( getPropertyName( PROP_FOOTER_IS_ON ) ) >>= bRet;
+ else
+ m_aFollowPageStyle->getPropertyValue( getPropertyName( PROP_FOOTER_IS_ON ) ) >>= bRet;
+ }
+ return bRet;
+}
+
+#define MIN_HEAD_FOOT_HEIGHT 100 // minimum header/footer height
+
+void SectionPropertyMap::CopyHeaderFooterTextProperty( const uno::Reference< beans::XPropertySet >& xPrevStyle,
+ const uno::Reference< beans::XPropertySet >& xStyle,
+ PropertyIds ePropId )
+{
+ try {
+ OUString sName = getPropertyName( ePropId );
+
+ SAL_INFO( "writerfilter", "Copying " << sName );
+ uno::Reference< text::XTextCopy > xTxt;
+ if ( xStyle.is() )
+ xTxt.set( xStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
+
+ uno::Reference< text::XTextCopy > xPrevTxt;
+ if ( xPrevStyle.is() )
+ xPrevTxt.set( xPrevStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
+
+ xTxt->copyText( xPrevTxt );
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION( "writerfilter", "An exception occurred in SectionPropertyMap::CopyHeaderFooterTextProperty( )" );
+ }
+}
+
+// Copy headers and footers from the previous page style.
+void SectionPropertyMap::CopyHeaderFooter( const DomainMapper_Impl& rDM_Impl,
+ const uno::Reference< beans::XPropertySet >& xPrevStyle,
+ const uno::Reference< beans::XPropertySet >& xStyle,
+ bool bOmitRightHeader,
+ bool bOmitLeftHeader,
+ bool bOmitRightFooter,
+ bool bOmitLeftFooter )
+{
+ if (!rDM_Impl.IsNewDoc())
+ { // see also DomainMapper_Impl::PushPageHeaderFooter()
+ return; // tdf#139737 SwUndoInserts cannot deal with new header/footer
+ }
+ bool bHasPrevHeader = false;
+ bool bHeaderIsShared = true;
+ OUString sHeaderIsOn = getPropertyName( PROP_HEADER_IS_ON );
+ OUString sHeaderIsShared = getPropertyName( PROP_HEADER_IS_SHARED );
+ if ( xPrevStyle.is() )
+ {
+ xPrevStyle->getPropertyValue( sHeaderIsOn ) >>= bHasPrevHeader;
+ xPrevStyle->getPropertyValue( sHeaderIsShared ) >>= bHeaderIsShared;
+ }
+
+ if ( bHasPrevHeader )
+ {
+ uno::Reference< beans::XMultiPropertySet > xMultiSet( xStyle, uno::UNO_QUERY_THROW );
+ uno::Sequence<OUString> aProperties { sHeaderIsOn, sHeaderIsShared };
+ uno::Sequence<uno::Any> aValues { uno::Any( true ), uno::Any( bHeaderIsShared ) };
+ xMultiSet->setPropertyValues( aProperties, aValues );
+ if ( !bOmitRightHeader )
+ {
+ CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
+ PROP_HEADER_TEXT );
+ }
+ if ( !bHeaderIsShared && !bOmitLeftHeader )
+ {
+ CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
+ PROP_HEADER_TEXT_LEFT );
+ }
+ }
+
+ bool bHasPrevFooter = false;
+ bool bFooterIsShared = true;
+ OUString sFooterIsOn = getPropertyName( PROP_FOOTER_IS_ON );
+ OUString sFooterIsShared = getPropertyName( PROP_FOOTER_IS_SHARED );
+ if ( xPrevStyle.is() )
+ {
+ xPrevStyle->getPropertyValue( sFooterIsOn ) >>= bHasPrevFooter;
+ xPrevStyle->getPropertyValue( sFooterIsShared ) >>= bFooterIsShared;
+ }
+
+ if ( !bHasPrevFooter )
+ return;
+
+ uno::Reference< beans::XMultiPropertySet > xMultiSet( xStyle, uno::UNO_QUERY_THROW );
+ uno::Sequence<OUString> aProperties { sFooterIsOn, sFooterIsShared };
+ uno::Sequence<uno::Any> aValues { uno::Any( true ), uno::Any( bFooterIsShared ) };
+ xMultiSet->setPropertyValues( aProperties, aValues );
+ if ( !bOmitRightFooter )
+ {
+ CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
+ PROP_FOOTER_TEXT );
+ }
+ if ( !bFooterIsShared && !bOmitLeftFooter )
+ {
+ CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
+ PROP_FOOTER_TEXT_LEFT );
+ }
+}
+
+// Copy header and footer content from the previous docx section as needed.
+//
+// Any headers and footers which were not defined in this docx section
+// should be "linked" with the corresponding header or footer from the
+// previous section. LO does not support linking of header/footer content
+// across page styles so we just copy the content from the previous section.
+void SectionPropertyMap::CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl )
+{
+ SAL_INFO( "writerfilter", "START>>> SectionPropertyMap::CopyLastHeaderFooter()" );
+ SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
+ if ( pLastContext )
+ {
+ const bool bUseEvenPages = rDM_Impl.GetSettingsTable()->GetEvenAndOddHeaders();
+ uno::Reference< beans::XPropertySet > xPrevStyle = pLastContext->GetPageStyle( rDM_Impl,
+ bFirstPage );
+ uno::Reference< beans::XPropertySet > xStyle = GetPageStyle( rDM_Impl,
+ bFirstPage );
+
+ if ( bFirstPage )
+ {
+ CopyHeaderFooter(rDM_Impl, xPrevStyle, xStyle,
+ !m_bFirstPageHeaderLinkToPrevious, true,
+ !m_bFirstPageFooterLinkToPrevious, true );
+ }
+ else
+ {
+ CopyHeaderFooter(rDM_Impl, xPrevStyle, xStyle,
+ !m_bDefaultHeaderLinkToPrevious,
+ !(m_bEvenPageHeaderLinkToPrevious && bUseEvenPages),
+ !m_bDefaultFooterLinkToPrevious,
+ !(m_bEvenPageFooterLinkToPrevious && bUseEvenPages));
+ }
+ }
+ SAL_INFO( "writerfilter", "END>>> SectionPropertyMap::CopyLastHeaderFooter()" );
+}
+
+void SectionPropertyMap::PrepareHeaderFooterProperties( bool bFirstPage )
+{
+ bool bCopyFirstToFollow = bFirstPage && m_bTitlePage && m_aFollowPageStyle.is();
+
+ sal_Int32 nTopMargin = m_nTopMargin;
+ sal_Int32 nHeaderHeight = m_nHeaderTop;
+ if ( HasHeader( bFirstPage ) )
+ {
+ nTopMargin = m_nHeaderTop;
+ nHeaderHeight = m_nTopMargin - m_nHeaderTop;
+
+ // minimum header height 1mm
+ if ( nHeaderHeight < MIN_HEAD_FOOT_HEIGHT )
+ nHeaderHeight = MIN_HEAD_FOOT_HEIGHT;
+ }
+
+ Insert(PROP_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(m_bDynamicHeightTop));
+ Insert(PROP_HEADER_DYNAMIC_SPACING, uno::Any(m_bDynamicHeightTop));
+ Insert(PROP_HEADER_BODY_DISTANCE, uno::Any(nHeaderHeight - MIN_HEAD_FOOT_HEIGHT));
+ Insert(PROP_HEADER_HEIGHT, uno::Any(nHeaderHeight));
+ // looks like PROP_HEADER_HEIGHT = height of the header + space between the header, and the body
+
+ if ( m_bDynamicHeightTop ) //fixed height header -> see WW8Par6.hxx
+ {
+ if (bCopyFirstToFollow && HasHeader(/*bFirstPage=*/true))
+ {
+ m_aFollowPageStyle->setPropertyValue("HeaderDynamicSpacing",
+ getProperty(PROP_HEADER_DYNAMIC_SPACING)->second);
+ m_aFollowPageStyle->setPropertyValue("HeaderHeight",
+ getProperty(PROP_HEADER_HEIGHT)->second);
+ }
+ }
+
+ sal_Int32 nBottomMargin = m_nBottomMargin;
+ sal_Int32 nFooterHeight = m_nHeaderBottom;
+ if ( HasFooter( bFirstPage ) )
+ {
+ nBottomMargin = m_nHeaderBottom;
+ nFooterHeight = m_nBottomMargin - m_nHeaderBottom;
+
+ // minimum footer height 1mm
+ if ( nFooterHeight < MIN_HEAD_FOOT_HEIGHT )
+ nFooterHeight = MIN_HEAD_FOOT_HEIGHT;
+ }
+
+ Insert(PROP_FOOTER_IS_DYNAMIC_HEIGHT, uno::Any(m_bDynamicHeightBottom));
+ Insert(PROP_FOOTER_DYNAMIC_SPACING, uno::Any(m_bDynamicHeightBottom));
+ Insert(PROP_FOOTER_BODY_DISTANCE, uno::Any(nFooterHeight - MIN_HEAD_FOOT_HEIGHT));
+ Insert(PROP_FOOTER_HEIGHT, uno::Any(nFooterHeight));
+ if (m_bDynamicHeightBottom) //fixed height footer -> see WW8Par6.hxx
+ {
+ if (bCopyFirstToFollow && HasFooter(/*bFirstPage=*/true))
+ {
+ m_aFollowPageStyle->setPropertyValue("FooterDynamicSpacing",
+ getProperty(PROP_FOOTER_DYNAMIC_SPACING)->second);
+ m_aFollowPageStyle->setPropertyValue("FooterHeight",
+ getProperty(PROP_FOOTER_HEIGHT)->second);
+ }
+ }
+
+ //now set the top/bottom margin for the follow page style
+ Insert( PROP_TOP_MARGIN, uno::Any( std::max<sal_Int32>(nTopMargin, 0) ) );
+ Insert( PROP_BOTTOM_MARGIN, uno::Any( std::max<sal_Int32>(nBottomMargin, 0) ) );
+}
+
+static uno::Reference< beans::XPropertySet > lcl_GetRangeProperties( bool bIsFirstSection,
+ DomainMapper_Impl& rDM_Impl,
+ const uno::Reference< text::XTextRange >& xStartingRange )
+{
+ uno::Reference< beans::XPropertySet > xRangeProperties;
+ if ( bIsFirstSection && rDM_Impl.GetBodyText().is() )
+ {
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( rDM_Impl.GetBodyText(), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
+ xRangeProperties.set( xEnum->nextElement(), uno::UNO_QUERY_THROW );
+ if ( rDM_Impl.GetIsDummyParaAddedForTableInSection() && xEnum->hasMoreElements() )
+ xRangeProperties.set( xEnum->nextElement(), uno::UNO_QUERY_THROW );
+ }
+ else if ( xStartingRange.is() )
+ xRangeProperties.set( xStartingRange, uno::UNO_QUERY_THROW );
+ return xRangeProperties;
+}
+
+void SectionPropertyMap::HandleMarginsHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl )
+{
+ Insert( PROP_LEFT_MARGIN, uno::Any( m_nLeftMargin ) );
+ Insert( PROP_RIGHT_MARGIN, uno::Any( m_nRightMargin ) );
+ Insert(PROP_GUTTER_MARGIN, uno::Any(m_nGutterMargin));
+
+ if ( rDM_Impl.m_oBackgroundColor )
+ Insert( PROP_BACK_COLOR, uno::Any( *rDM_Impl.m_oBackgroundColor ) );
+
+ // Check for missing footnote separator only in case there is at least
+ // one footnote.
+ if (rDM_Impl.m_bHasFtn && !rDM_Impl.m_bHasFtnSep)
+ {
+ // Set footnote line width to zero, document has no footnote separator.
+ Insert(PROP_FOOTNOTE_LINE_RELATIVE_WIDTH, uno::Any(sal_Int32(0)));
+ }
+ if ( rDM_Impl.m_bHasFtnSep )
+ {
+ //If default paragraph style is RTL, footnote separator should be right aligned
+ //and for RTL locales, LTR default paragraph style should present a left aligned footnote separator
+ try
+ {
+ uno::Reference<style::XStyleFamiliesSupplier> xStylesSupplier(rDM_Impl.GetTextDocument(), uno::UNO_QUERY);
+ if ( xStylesSupplier.is() )
+ {
+ uno::Reference<container::XNameAccess> xStyleFamilies = xStylesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xParagraphStyles;
+ if ( xStyleFamilies.is() )
+ xStyleFamilies->getByName("ParagraphStyles") >>= xParagraphStyles;
+ uno::Reference<beans::XPropertySet> xStandard;
+ if ( xParagraphStyles.is() )
+ xParagraphStyles->getByName("Standard") >>= xStandard;
+ if ( xStandard.is() )
+ {
+ sal_Int16 aWritingMode(0);
+ xStandard->getPropertyValue( getPropertyName(PROP_WRITING_MODE) ) >>= aWritingMode;
+ if( aWritingMode == text::WritingMode2::RL_TB )
+ Insert( PROP_FOOTNOTE_LINE_ADJUST, uno::Any( sal_Int16(text::HorizontalAdjust_RIGHT) ), false );
+ else
+ Insert( PROP_FOOTNOTE_LINE_ADJUST, uno::Any( sal_Int16(text::HorizontalAdjust_LEFT) ), false );
+ }
+ }
+ }
+ catch ( const uno::Exception& ) {}
+ }
+
+ /*** if headers/footers are available then the top/bottom margins of the
+ header/footer are copied to the top/bottom margin of the page
+ */
+ CopyLastHeaderFooter( bFirstPage, rDM_Impl );
+ PrepareHeaderFooterProperties( bFirstPage );
+
+ // tdf#119952: If top/bottom margin was negative during docx import,
+ // then the header/footer and the body could be on top of each other
+ // writer is unable to display both of them in the same position, but can be simulated
+ // by moving the header/footer text into a flyframe anchored to the header/footer,
+ // leaving an empty dummy header/footer.
+ rDM_Impl.ConvertHeaderFooterToTextFrame(m_bDynamicHeightTop, m_bDynamicHeightBottom);
+}
+
+bool SectionPropertyMap::FloatingTableConversion( const DomainMapper_Impl& rDM_Impl, FloatingTableInfo& rInfo )
+{
+ // always convert non-floating tables to floating ones in footnotes and endnotes
+ if ( rInfo.m_bConvertToFloatingInFootnote )
+ return true;
+ // This is OOXML version of the code deciding if the table needs to be
+ // in a floating frame.
+ // For ww8 code, see SwWW8ImplReader::FloatingTableConversion in
+ // sw/source/filter/ww8/ww8par.cxx
+ // The two should do the same, so if you make changes here, please check
+ // that the other is in sync.
+
+ // Note that this is just a list of heuristics till sw core can have a
+ // table that is floating and can span over multiple pages at the same
+ // time.
+
+ // If there is an explicit section break right after a table, then there
+ // will be no wrapping anyway.
+ if (rDM_Impl.m_bConvertedTable && !rDM_Impl.GetIsLastSectionGroup() && rInfo.m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextPage)
+ return false;
+
+ sal_Int32 nVertOrientPosition = rInfo.getPropertyValue(u"VertOrientPosition").get<sal_Int32>();
+ sal_Int16 nHoriOrientRelation = rInfo.getPropertyValue( u"HoriOrientRelation" ).get<sal_Int16>();
+ if (nVertOrientPosition < 0 && nHoriOrientRelation != text::RelOrientation::PAGE_FRAME)
+ {
+ // Negative vertical position: then need a floating table, as normal tables can't have
+ // negative top margins.
+ return true;
+ }
+
+ sal_Int32 nPageWidth = GetPageWidth();
+ sal_Int32 nTextAreaWidth = nPageWidth - GetLeftMargin() - GetRightMargin();
+ // Count the layout width of the table.
+ sal_Int32 nTableWidth = rInfo.m_nTableWidth;
+ if (rInfo.m_nTableWidthType == text::SizeType::VARIABLE)
+ {
+ nTableWidth *= nTextAreaWidth / 100.0;
+ }
+ sal_Int32 nLeftMargin = 0;
+ if ( rInfo.getPropertyValue( u"LeftMargin" ) >>= nLeftMargin )
+ nTableWidth += nLeftMargin;
+ sal_Int32 nRightMargin = 0;
+ if ( rInfo.getPropertyValue( u"RightMargin" ) >>= nRightMargin )
+ nTableWidth += nRightMargin;
+
+ sal_Int16 nVertOrientRelation = rInfo.getPropertyValue( u"VertOrientRelation" ).get<sal_Int16>();
+ if ( nHoriOrientRelation == text::RelOrientation::PAGE_FRAME && nVertOrientRelation == text::RelOrientation::PAGE_FRAME )
+ {
+ sal_Int16 nHoriOrient = rInfo.getPropertyValue( u"HoriOrient" ).get<sal_Int16>();
+ sal_Int16 nVertOrient = rInfo.getPropertyValue( u"VertOrient" ).get<sal_Int16>();
+ if ( nHoriOrient == text::HoriOrientation::NONE && nVertOrient == text::VertOrientation::NONE )
+ {
+ // Anchor position is relative to the page horizontally and vertically as well and is an absolute position.
+ // The more close we are to the left edge, the less likely there will be any wrapping.
+ // The more close we are to the bottom, the more likely the table will span over to the next page
+ // So if we're in the bottom left quarter, don't do any conversion.
+ sal_Int32 nHoriOrientPosition = rInfo.getPropertyValue( u"HoriOrientPosition" ).get<sal_Int32>();
+ sal_Int32 nPageHeight = getProperty( PROP_HEIGHT )->second.get<sal_Int32>();
+ if ( nHoriOrientPosition < (nPageWidth / 2) && nVertOrientPosition >( nPageHeight / 2 ) )
+ return false;
+ }
+ }
+
+ // It seems Word has a limit here, so that in case the table width is quite
+ // close to the text area width, then it won't perform a wrapping, even in
+ // case the content (e.g. an empty paragraph) would fit. The magic constant
+ // here represents this limit.
+ const sal_Int32 nMagicNumber = 469;
+
+ // If the table's width is smaller than the text area width, text might
+ // be next to the table and so it should behave as a floating table.
+ if ( (nTableWidth + nMagicNumber) < nTextAreaWidth )
+ return true;
+
+ // If the position is relative to the edge of the page, then we need to check the whole
+ // page width to see whether text can fit next to the table.
+ if ( nHoriOrientRelation == text::RelOrientation::PAGE_FRAME )
+ {
+ // If the table is wide enough so that no text fits next to it, then don't create a fly
+ // for the table: no wrapping will be performed anyway, but multi-page
+ // tables will be broken.
+ if ((nTableWidth + nMagicNumber) < (nPageWidth - std::min(GetLeftMargin(), GetRightMargin())))
+ return true;
+ }
+
+ // If there are columns, always create the fly, otherwise the columns would
+ // restrict geometry of the table.
+ if ( ColumnCount() > 1 )
+ return true;
+
+ return false;
+}
+
+void SectionPropertyMap::InheritOrFinalizePageStyles( DomainMapper_Impl& rDM_Impl )
+{
+ // if no new styles have been created for this section, inherit from the previous section,
+ // otherwise apply this section's settings to the new style.
+ // Ensure that FollowPage is inherited first - otherwise GetPageStyle may auto-create a follow when checking FirstPage.
+ SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
+ //tdf124637 TODO: identify and skip special sections (like footnotes/endnotes)
+ if ( pLastContext && m_sFollowPageStyleName.isEmpty() )
+ m_sFollowPageStyleName = pLastContext->GetPageStyleName();
+ else
+ {
+ HandleMarginsHeaderFooter( /*bFirst=*/false, rDM_Impl );
+ GetPageStyle( rDM_Impl, /*bFirst=*/false );
+ if ( rDM_Impl.IsNewDoc() && m_aFollowPageStyle.is() )
+ ApplyProperties_( m_aFollowPageStyle );
+ }
+
+ // FirstPageStyle may only be inherited if it will not be used or re-linked to a different follow
+ if ( !m_bTitlePage && pLastContext && m_sFirstPageStyleName.isEmpty() )
+ m_sFirstPageStyleName = pLastContext->GetPageStyleName( /*bFirst=*/true );
+ else
+ {
+ HandleMarginsHeaderFooter( /*bFirst=*/true, rDM_Impl );
+ GetPageStyle( rDM_Impl, /*bFirst=*/true );
+ if ( rDM_Impl.IsNewDoc() && m_aFirstPageStyle.is() )
+ ApplyProperties_( m_aFirstPageStyle );
+
+ // Chain m_aFollowPageStyle to be after m_aFirstPageStyle
+ m_aFirstPageStyle->setPropertyValue( "FollowStyle", uno::Any( m_sFollowPageStyleName ) );
+ }
+}
+
+void SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing(DomainMapper_Impl& rDM_Impl)
+{
+ // Ignore Word 2010 and older.
+ if (rDM_Impl.GetSettingsTable()->GetWordCompatibilityMode() < 15)
+ return;
+
+ sal_Int32 nPageWidth = GetPageWidth();
+ sal_Int32 nTextAreaWidth = nPageWidth - GetLeftMargin() - GetRightMargin();
+
+ std::vector<AnchoredObjectsInfo>& rAnchoredObjectAnchors = rDM_Impl.m_aAnchoredObjectAnchors;
+ for (const auto& rAnchor : rAnchoredObjectAnchors)
+ {
+ // Ignore this paragraph when there are not enough shapes to trigger the Word bug we
+ // emulate.
+ if (rAnchor.m_aAnchoredObjects.size() < 4)
+ continue;
+
+ // Ignore this paragraph if none of the objects are wrapped in the background.
+ sal_Int32 nOpaqueCount = 0;
+ for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
+ {
+ // Ignore inline objects stored only for redlining.
+ if (rAnchored.m_xRedlineForInline)
+ continue;
+
+ uno::Reference<beans::XPropertySet> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
+ if (!xShape.is())
+ {
+ continue;
+ }
+
+ bool bOpaque = true;
+ xShape->getPropertyValue("Opaque") >>= bOpaque;
+ if (!bOpaque)
+ {
+ ++nOpaqueCount;
+ }
+ }
+ if (nOpaqueCount < 1)
+ {
+ continue;
+ }
+
+ // Analyze the anchored objects of this paragraph, now that we know the
+ // page width.
+ sal_Int32 nShapesWidth = 0;
+ for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
+ {
+ uno::Reference<drawing::XShape> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
+ if (!xShape.is())
+ continue;
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ continue;
+
+ // Ignore objects with no wrapping.
+ text::WrapTextMode eWrap = text::WrapTextMode_THROUGH;
+ xPropertySet->getPropertyValue("Surround") >>= eWrap;
+ if (eWrap == text::WrapTextMode_THROUGH)
+ continue;
+
+ // Use the original left margin, in case GraphicImport::lcl_sprm() reduced the doc model
+ // one to 0.
+ sal_Int32 nLeftMargin = rAnchored.m_nLeftMargin;
+ sal_Int32 nRightMargin = 0;
+ xPropertySet->getPropertyValue("RightMargin") >>= nRightMargin;
+ nShapesWidth += xShape->getSize().Width + nLeftMargin + nRightMargin;
+ }
+
+ // Ignore cases when we have enough horizontal space for the shapes.
+ if (nTextAreaWidth > nShapesWidth)
+ continue;
+
+ sal_Int32 nHeight = 0;
+ for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
+ {
+ uno::Reference<drawing::XShape> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
+ if (!xShape.is())
+ continue;
+
+ nHeight += xShape->getSize().Height;
+ }
+
+ uno::Reference<beans::XPropertySet> xParagraph(rAnchor.m_xParagraph, uno::UNO_QUERY);
+ if (xParagraph.is())
+ {
+ sal_Int32 nTopMargin = 0;
+ xParagraph->getPropertyValue("ParaTopMargin") >>= nTopMargin;
+ // Increase top spacing of the paragraph to match Word layout
+ // behavior.
+ nTopMargin = std::max(nTopMargin, nHeight);
+ xParagraph->setPropertyValue("ParaTopMargin", uno::Any(nTopMargin));
+ }
+ }
+ rAnchoredObjectAnchors.clear();
+}
+
+void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
+{
+ SectionPropertyMap* pPrevSection = rDM_Impl.GetLastSectionContext();
+
+ // The default section type is nextPage.
+ if ( m_nBreakType == -1 )
+ m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
+ else if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextColumn )
+ {
+ // Word 2013+ seems to treat a section column break as a page break all the time.
+ // It always acts like a page break if there are no columns, or a different number of columns.
+ // Also, if this is the first section, the break type is basically irrelevant - works best as nextPage.
+ if ( rDM_Impl.GetSettingsTable()->GetWordCompatibilityMode() > 14
+ || !pPrevSection
+ || m_nColumnCount < 2
+ || m_nColumnCount != pPrevSection->ColumnCount()
+ )
+ {
+ m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
+ }
+ }
+ else if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_continuous )
+ {
+ // if page orientation differs from previous section, it can't be treated as continuous
+ if ( pPrevSection )
+ {
+ bool bIsLandscape = false;
+ std::optional< PropertyMap::Property > pProp = getProperty( PROP_IS_LANDSCAPE );
+ if ( pProp )
+ pProp->second >>= bIsLandscape;
+
+ bool bPrevIsLandscape = false;
+ pProp = pPrevSection->getProperty( PROP_IS_LANDSCAPE );
+ if ( pProp )
+ pProp->second >>= bPrevIsLandscape;
+
+ if ( bIsLandscape != bPrevIsLandscape )
+ m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
+ }
+ }
+
+ // Text area width is known at the end of a section: decide if tables should be converted or not.
+ std::vector<FloatingTableInfo>& rPendingFloatingTables = rDM_Impl.m_aPendingFloatingTables;
+ for ( FloatingTableInfo & rInfo : rPendingFloatingTables )
+ {
+ rInfo.m_nBreakType = m_nBreakType;
+ if ( FloatingTableConversion( rDM_Impl, rInfo ) )
+ {
+ uno::Reference<text::XTextAppendAndConvert> xBodyText(
+ rInfo.m_bConvertToFloatingInFootnote
+ ? rInfo.m_xStart->getText()
+ : rDM_Impl.GetBodyText(), uno::UNO_QUERY );
+ std::deque<css::uno::Any> aFramedRedlines = rDM_Impl.m_aStoredRedlines[StoredRedlines::FRAME];
+ try
+ {
+ // convert redline ranges to cursor movement and character length
+ std::vector<sal_Int32> redPos, redLen;
+ std::vector<OUString> redCell;
+ std::vector<OUString> redTable;
+ for( size_t i = 0; i < aFramedRedlines.size(); i+=3)
+ {
+ uno::Reference<text::XText> xCell;
+ uno::Reference< text::XTextRange > xRange;
+ aFramedRedlines[i] >>= xRange;
+ uno::Reference< beans::XPropertySet > xRangeProperties;
+ if ( xRange.is() )
+ {
+ OUString sTableName;
+ OUString sCellName;
+ xRangeProperties.set( xRange, uno::UNO_QUERY_THROW );
+ if (xRangeProperties->getPropertySetInfo()->hasPropertyByName("TextTable"))
+ {
+ uno::Any aTable = xRangeProperties->getPropertyValue("TextTable");
+ if ( aTable != uno::Any() )
+ {
+ uno::Reference<text::XTextTable> xTable;
+ aTable >>= xTable;
+ uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
+ xTableProperties->getPropertyValue("TableName") >>= sTableName;
+ }
+ if (xRangeProperties->getPropertySetInfo()->hasPropertyByName("Cell"))
+ {
+ uno::Any aCell = xRangeProperties->getPropertyValue("Cell");
+ if ( aCell != uno::Any() )
+ {
+ aCell >>= xCell;
+ uno::Reference<beans::XPropertySet> xCellProperties(xCell, uno::UNO_QUERY);
+ xCellProperties->getPropertyValue("CellName") >>= sCellName;
+ }
+ }
+ }
+ redTable.push_back(sTableName);
+ redCell.push_back(sCellName);
+ bool bOk = false;
+ if (!sTableName.isEmpty() && !sCellName.isEmpty())
+ {
+ uno::Reference<text::XTextCursor> xRangeCursor = xCell->createTextCursorByRange( xRange );
+ if ( xRangeCursor.is() )
+ {
+ bOk = true;
+ sal_Int32 nLen = xRange->getString().getLength();
+ redLen.push_back(nLen);
+ xRangeCursor->gotoStart(true);
+ redPos.push_back(xRangeCursor->getString().getLength() - nLen);
+ }
+ }
+ if (!bOk)
+ {
+ // missing cell or failed createTextCursorByRange()
+ redLen.push_back(-1);
+ redPos.push_back(-1);
+ }
+ }
+ }
+
+ const uno::Reference< text::XTextContent >& xTextContent =
+ xBodyText->convertToTextFrame(rInfo.m_xStart, rInfo.m_xEnd,
+ rInfo.m_aFrameProperties);
+
+ // paragraph of the anchoring point of the floating table needs zero top and bottom
+ // margins, if the table was a not floating table in the footnote, otherwise
+ // docDefault margins could result bigger vertical spaces around the table
+ if ( rInfo.m_bConvertToFloatingInFootnote && xTextContent.is() )
+ {
+ uno::Reference<beans::XPropertySet> xParagraph(
+ xTextContent->getAnchor(), uno::UNO_QUERY);
+ if ( xParagraph.is() )
+ {
+ xParagraph->setPropertyValue("ParaTopMargin",
+ uno::Any(static_cast<sal_Int32>(0)));
+ xParagraph->setPropertyValue("ParaBottomMargin",
+ uno::Any(static_cast<sal_Int32>(0)));
+ }
+ }
+
+ uno::Reference<text::XTextTablesSupplier> xTextDocument(rDM_Impl.GetTextDocument(), uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xTables = xTextDocument->getTextTables();
+ for( size_t i = 0; i < aFramedRedlines.size(); i+=3)
+ {
+ OUString sType;
+ beans::PropertyValues aRedlineProperties( 3 );
+ // skip failed createTextCursorByRange()
+ if (redPos[i/3] == -1)
+ continue;
+ aFramedRedlines[i+1] >>= sType;
+ aFramedRedlines[i+2] >>= aRedlineProperties;
+ uno::Reference<text::XTextTable> xTable(xTables->getByName(redTable[i/3]), uno::UNO_QUERY);
+ uno::Reference<text::XText> xCell(xTable->getCellByName(redCell[i/3]), uno::UNO_QUERY);
+ uno::Reference<text::XTextCursor> xCrsr = xCell->createTextCursor();
+ xCrsr->goRight(redPos[i/3], false);
+ xCrsr->goRight(redLen[i/3], true);
+ uno::Reference < text::XRedline > xRedline( xCrsr, uno::UNO_QUERY_THROW );
+ try
+ {
+ xRedline->makeRedline( sType, aRedlineProperties );
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "makeRedline() failed");
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "convertToTextFrame() failed");
+ }
+
+ aFramedRedlines.clear();
+ }
+ }
+ rPendingFloatingTables.clear();
+
+ try
+ {
+ HandleIncreasedAnchoredObjectSpacing(rDM_Impl);
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "HandleIncreasedAnchoredObjectSpacing() failed");
+ }
+
+ if ( m_nLnnMod )
+ {
+ bool bFirst = rDM_Impl.IsLineNumberingSet();
+ rDM_Impl.SetLineNumbering( m_nLnnMod, m_nLnc, m_ndxaLnn );
+ if ( m_nLnnMin > 0 || (bFirst && m_nLnc == NS_ooxml::LN_Value_ST_LineNumberRestart_newSection) )
+ {
+ //set the starting value at the beginning of the section
+ try
+ {
+ uno::Reference< beans::XPropertySet > xRangeProperties;
+ if ( m_xStartingRange.is() )
+ {
+ xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY_THROW );
+ }
+ else
+ {
+ //set the start value at the beginning of the document
+ xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY_THROW );
+ }
+ // Writer is 1-based, Word is 0-based.
+ xRangeProperties->setPropertyValue(
+ getPropertyName(PROP_PARA_LINE_NUMBER_START_VALUE),
+ uno::Any(m_nLnnMin + 1));
+ }
+ catch ( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper", "Exception in SectionPropertyMap::CloseSectionGroup");
+ }
+ }
+ }
+
+ if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_continuous
+ && !rDM_Impl.IsInComments())
+ {
+ //todo: insert a section or access the already inserted section
+ uno::Reference< beans::XPropertySet > xSection =
+ rDM_Impl.appendTextSectionAfter( m_xStartingRange );
+ if ( xSection.is() )
+ {
+ if ( m_nColumnCount > 1 )
+ ApplyColumnProperties( xSection, rDM_Impl );
+
+ ApplyProtectionProperties( xSection, rDM_Impl );
+ }
+
+ try
+ {
+ InheritOrFinalizePageStyles( rDM_Impl );
+ ApplySectionProperties( xSection, rDM_Impl ); //depends on InheritOrFinalizePageStyles
+ OUString aName = m_bTitlePage ? m_sFirstPageStyleName : m_sFollowPageStyleName;
+ uno::Reference< beans::XPropertySet > xRangeProperties( lcl_GetRangeProperties( m_bIsFirstSection, rDM_Impl, m_xStartingRange ) );
+ if ( m_bIsFirstSection && !aName.isEmpty() && xRangeProperties.is() )
+ {
+ xRangeProperties->setPropertyValue( getPropertyName( PROP_PAGE_DESC_NAME ), uno::Any( aName ) );
+ }
+ else if ((!m_bFirstPageHeaderLinkToPrevious ||
+ !m_bFirstPageFooterLinkToPrevious ||
+ !m_bDefaultHeaderLinkToPrevious ||
+ !m_bDefaultFooterLinkToPrevious ||
+ !m_bEvenPageHeaderLinkToPrevious ||
+ !m_bEvenPageFooterLinkToPrevious)
+ && rDM_Impl.GetCurrentXText())
+ { // find a node in the section that has a page break and change
+ // it to apply the page style; see "nightmare scenario" in
+ // wwSectionManager::InsertSegments()
+ auto xTextAppend = rDM_Impl.GetCurrentXText();
+ uno::Reference<container::XEnumerationAccess> const xCursor(
+ xTextAppend->createTextCursorByRange(
+ uno::Reference<text::XTextContent>(xSection, uno::UNO_QUERY_THROW)->getAnchor()),
+ uno::UNO_QUERY_THROW);
+ uno::Reference<container::XEnumeration> const xEnum(
+ xCursor->createEnumeration());
+ bool isFound = false;
+ while (xEnum->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xElem;
+ xEnum->nextElement() >>= xElem;
+ if (xElem->getPropertySetInfo()->hasPropertyByName("BreakType"))
+ {
+ style::BreakType bt;
+ if ((xElem->getPropertyValue("BreakType") >>= bt)
+ && bt == style::BreakType_PAGE_BEFORE)
+ {
+ // tdf#112201: do *not* use m_sFirstPageStyleName here!
+ xElem->setPropertyValue(getPropertyName(PROP_PAGE_DESC_NAME),
+ uno::Any(m_sFollowPageStyleName));
+ isFound = true;
+ break;
+ }
+ }
+ }
+ uno::Reference<text::XParagraphCursor> const xPCursor(xCursor,
+ uno::UNO_QUERY_THROW);
+ float fCharHeight = 0;
+ if (!isFound)
+ { // HACK: try the last paragraph of the previous section
+ xPCursor->gotoPreviousParagraph(false);
+ uno::Reference<beans::XPropertySet> const xPSCursor(xCursor, uno::UNO_QUERY_THROW);
+ style::BreakType bt;
+ if ((xPSCursor->getPropertyValue("BreakType") >>= bt)
+ && bt == style::BreakType_PAGE_BEFORE)
+ {
+ xPSCursor->setPropertyValue(getPropertyName(PROP_PAGE_DESC_NAME),
+ uno::Any(m_sFollowPageStyleName));
+ isFound = true;
+ }
+ else
+ {
+ xPSCursor->getPropertyValue("CharHeight") >>= fCharHeight;
+ }
+ }
+ if (!isFound && fCharHeight <= 1.0)
+ {
+ // If still not found, see if the last paragraph is ~invisible, and work with
+ // the last-in-practice paragraph.
+ xPCursor->gotoPreviousParagraph(false);
+ uno::Reference<beans::XPropertySet> xPropertySet(xCursor, uno::UNO_QUERY_THROW);
+ OUString aPageDescName;
+ if ((xPropertySet->getPropertyValue("PageDescName") >>= aPageDescName)
+ && !aPageDescName.isEmpty())
+ {
+ uno::Reference<beans::XPropertySet> xPageStyle(
+ rDM_Impl.GetPageStyles()->getByName(aPageDescName), uno::UNO_QUERY);
+ xPageStyle->setPropertyValue("FollowStyle",
+ uno::Any(m_sFollowPageStyleName));
+ }
+ }
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "writerfilter", "failed to set PageDescName!" );
+ }
+ }
+ // If the section is of type "New column" (0x01), then simply insert a column break.
+ // But only if there actually are columns on the page, otherwise a column break
+ // seems to be handled like a page break by MSO.
+ else if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextColumn
+ && m_nColumnCount > 1 && !rDM_Impl.IsInComments())
+ {
+ try
+ {
+ InheritOrFinalizePageStyles( rDM_Impl );
+ /*TODO tdf#135343: Just inserting a column break sounds like the right idea, but the implementation is wrong.
+ * Somehow, the previous column section needs to be extended to cover this new text.
+ * Currently, it is completely broken, producing a no-column section that starts on a new page.
+ */
+ uno::Reference< beans::XPropertySet > xRangeProperties;
+ if ( m_xStartingRange.is() )
+ {
+ xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY_THROW );
+ }
+ else
+ {
+ //set the start value at the beginning of the document
+ xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY_THROW );
+ }
+ xRangeProperties->setPropertyValue( getPropertyName( PROP_BREAK_TYPE ), uno::Any( style::BreakType_COLUMN_BEFORE ) );
+ }
+ catch ( const uno::Exception& ) {}
+ }
+ else if (!rDM_Impl.IsInComments())
+ {
+ uno::Reference< beans::XPropertySet > xSection;
+ ApplyProtectionProperties( xSection, rDM_Impl );
+
+ //get the properties and create appropriate page styles
+ uno::Reference< beans::XPropertySet > xFollowPageStyle;
+ //This part certainly is not needed for footnotes, so don't create unused page styles.
+ if ( !rDM_Impl.IsInFootOrEndnote() )
+ {
+ xFollowPageStyle.set( GetPageStyle( rDM_Impl, false ) );
+
+ HandleMarginsHeaderFooter(/*bFirstPage=*/false, rDM_Impl );
+ }
+
+ if ( rDM_Impl.GetSettingsTable()->GetMirrorMarginSettings() )
+ {
+ Insert( PROP_PAGE_STYLE_LAYOUT, uno::Any( style::PageStyleLayout_MIRRORED ) );
+ }
+ uno::Reference< text::XTextColumns > xColumns;
+ if ( m_nColumnCount > 1 )
+ {
+ // prefer setting column properties into a section, not a page style if at all possible.
+ if ( !xSection.is() )
+ xSection = rDM_Impl.appendTextSectionAfter( m_xStartingRange );
+ if ( xSection.is() )
+ ApplyColumnProperties( xSection, rDM_Impl );
+ else if ( xFollowPageStyle.is() )
+ xColumns = ApplyColumnProperties( xFollowPageStyle, rDM_Impl );
+ }
+
+ // these BreakTypes are effectively page-breaks: don't evenly distribute text in columns before a page break;
+ if ( pPrevSection && pPrevSection->ColumnCount() )
+ pPrevSection->DontBalanceTextColumns();
+
+ //prepare text grid properties
+ sal_Int32 nHeight = 1;
+ std::optional< PropertyMap::Property > pProp = getProperty( PROP_HEIGHT );
+ if ( pProp )
+ pProp->second >>= nHeight;
+
+ sal_Int32 nWidth = 1;
+ pProp = getProperty( PROP_WIDTH );
+ if ( pProp )
+ pProp->second >>= nWidth;
+
+ sal_Int16 nWritingMode = text::WritingMode2::LR_TB;
+ pProp = getProperty( PROP_WRITING_MODE );
+ if ( pProp )
+ pProp->second >>= nWritingMode;
+
+ sal_Int32 nTextAreaHeight = nWritingMode == text::WritingMode2::LR_TB ?
+ nHeight - m_nTopMargin - m_nBottomMargin :
+ nWidth - m_nLeftMargin - m_nRightMargin;
+
+ sal_Int32 nGridLinePitch = m_nGridLinePitch;
+ //sep.dyaLinePitch
+ if ( nGridLinePitch < 1 || nGridLinePitch > 31680 )
+ {
+ SAL_WARN( "writerfilter", "sep.dyaLinePitch outside legal range: " << nGridLinePitch );
+ nGridLinePitch = 1;
+ }
+
+ const sal_Int32 nGridLines = nTextAreaHeight / nGridLinePitch;
+ sal_Int16 nGridType = m_nGridType;
+ if ( nGridLines >= 0 && nGridLines <= SAL_MAX_INT16 )
+ Insert( PROP_GRID_LINES, uno::Any( sal_Int16(nGridLines) ) );
+ else
+ nGridType = text::TextGridMode::NONE;
+
+ // PROP_GRID_MODE
+ if ( nGridType == text::TextGridMode::LINES_AND_CHARS )
+ {
+ if (!m_nDxtCharSpace)
+ nGridType = text::TextGridMode::LINES;
+ else
+ Insert( PROP_GRID_SNAP_TO_CHARS, uno::Any( m_bGridSnapToChars ) );
+ }
+
+ Insert( PROP_GRID_MODE, uno::Any( nGridType ) );
+
+ sal_Int32 nCharWidth = 423; //240 twip/ 12 pt
+ const StyleSheetEntryPtr pEntry = rDM_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( u"Standard" );
+ if ( pEntry )
+ {
+ std::optional< PropertyMap::Property > pPropHeight = pEntry->pProperties->getProperty( PROP_CHAR_HEIGHT_ASIAN );
+ if ( pPropHeight )
+ {
+ double fHeight = 0;
+ if ( pPropHeight->second >>= fHeight )
+ nCharWidth = ConversionHelper::convertTwipToMM100( static_cast<tools::Long>(fHeight * 20.0 + 0.5) );
+ }
+ }
+
+ //dxtCharSpace
+ if ( m_nDxtCharSpace )
+ {
+ sal_Int32 nCharSpace = m_nDxtCharSpace;
+ //main lives in top 20 bits, and is signed.
+ sal_Int32 nMain = (nCharSpace & 0xFFFFF000);
+ nMain /= 0x1000;
+ nCharWidth += ConversionHelper::convertTwipToMM100( nMain * 20 );
+
+ sal_Int32 nFraction = (nCharSpace & 0x00000FFF);
+ nFraction = (nFraction * 20) / 0xFFF;
+ nCharWidth += ConversionHelper::convertTwipToMM100( nFraction );
+ }
+
+ if ( m_nPageNumberType >= 0 )
+ Insert( PROP_NUMBERING_TYPE, uno::Any( m_nPageNumberType ) );
+
+ // #i119558#, force to set document as standard page mode,
+ // refer to ww8 import process function "SwWW8ImplReader::SetDocumentGrid"
+ try
+ {
+ uno::Reference< beans::XPropertySet > xDocProperties;
+ xDocProperties.set( rDM_Impl.GetTextDocument(), uno::UNO_QUERY_THROW );
+ Insert(PROP_GRID_STANDARD_MODE, uno::Any(true));
+ xDocProperties->setPropertyValue("DefaultPageMode", uno::Any(false));
+ }
+ catch ( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper", "Exception in SectionPropertyMap::CloseSectionGroup");
+ }
+
+ Insert( PROP_GRID_BASE_HEIGHT, uno::Any( nGridLinePitch ) );
+ Insert( PROP_GRID_BASE_WIDTH, uno::Any( nCharWidth ) );
+ Insert( PROP_GRID_RUBY_HEIGHT, uno::Any( sal_Int32( 0 ) ) );
+
+ if ( rDM_Impl.IsNewDoc() && xFollowPageStyle.is() )
+ ApplyProperties_( xFollowPageStyle );
+
+ //todo: creating a "First Page" style depends on HasTitlePage and _fFacingPage_
+ if ( m_bTitlePage )
+ {
+ CopyLastHeaderFooter( true, rDM_Impl );
+ PrepareHeaderFooterProperties( true );
+ uno::Reference< beans::XPropertySet > xFirstPageStyle = GetPageStyle(
+ rDM_Impl, true );
+ if ( rDM_Impl.IsNewDoc() )
+ ApplyProperties_( xFirstPageStyle );
+
+ if ( xColumns.is() )
+ xFirstPageStyle->setPropertyValue(
+ getPropertyName( PROP_TEXT_COLUMNS ), uno::Any( xColumns ) );
+ }
+
+ ApplyBorderToPageStyles( rDM_Impl, m_eBorderApply, m_eBorderOffsetFrom );
+
+ try
+ {
+ //now apply this break at the first paragraph of this section
+ uno::Reference< beans::XPropertySet > xRangeProperties( lcl_GetRangeProperties( m_bIsFirstSection, rDM_Impl, m_xStartingRange ) );
+
+ // Handle page breaks with odd/even page numbering. We need to use an extra page style for setting the page style
+ // to left/right, because if we set it to the normal style, we'd set it to "First Page"/"Default Style", which would
+ // break them (all default pages would be only left or right).
+ if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_evenPage || m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_oddPage )
+ {
+ OUString* pageStyle = m_bTitlePage ? &m_sFirstPageStyleName : &m_sFollowPageStyleName;
+ OUString evenOddStyleName = rDM_Impl.GetUnusedPageStyleName();
+ uno::Reference< beans::XPropertySet > evenOddStyle(
+ rDM_Impl.GetTextFactory()->createInstance( "com.sun.star.style.PageStyle" ),
+ uno::UNO_QUERY );
+ // Unfortunately using setParent() does not work for page styles, so make a deep copy of the page style.
+ uno::Reference< beans::XPropertySet > pageProperties( m_bTitlePage ? m_aFirstPageStyle : m_aFollowPageStyle );
+ uno::Reference< beans::XPropertySetInfo > pagePropertiesInfo( pageProperties->getPropertySetInfo() );
+ const uno::Sequence< beans::Property > propertyList( pagePropertiesInfo->getProperties() );
+ // Ignore write-only properties.
+ static const o3tl::sorted_vector<OUString> aDenylist
+ = { "FooterBackGraphicURL", "BackGraphicURL", "HeaderBackGraphicURL" };
+ for ( const auto& rProperty : propertyList )
+ {
+ if ( (rProperty.Attributes & beans::PropertyAttribute::READONLY) == 0 )
+ {
+ if (aDenylist.find(rProperty.Name) == aDenylist.end())
+ evenOddStyle->setPropertyValue(
+ rProperty.Name,
+ pageProperties->getPropertyValue(rProperty.Name));
+ }
+ }
+ evenOddStyle->setPropertyValue( "FollowStyle", uno::Any( *pageStyle ) );
+ rDM_Impl.GetPageStyles()->insertByName( evenOddStyleName, uno::Any( evenOddStyle ) );
+ evenOddStyle->setPropertyValue( "HeaderIsOn", uno::Any( false ) );
+ evenOddStyle->setPropertyValue( "FooterIsOn", uno::Any( false ) );
+ CopyHeaderFooter(rDM_Impl, pageProperties, evenOddStyle);
+ *pageStyle = evenOddStyleName; // And use it instead of the original one (which is set as follow of this one).
+ if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_evenPage )
+ evenOddStyle->setPropertyValue( getPropertyName( PROP_PAGE_STYLE_LAYOUT ), uno::Any( style::PageStyleLayout_LEFT ) );
+ else if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_oddPage )
+ evenOddStyle->setPropertyValue( getPropertyName( PROP_PAGE_STYLE_LAYOUT ), uno::Any( style::PageStyleLayout_RIGHT ) );
+ }
+
+ if (rDM_Impl.m_xAltChunkStartingRange.is())
+ {
+ xRangeProperties.set(rDM_Impl.m_xAltChunkStartingRange, uno::UNO_QUERY);
+ }
+ if (xRangeProperties.is() && (rDM_Impl.IsNewDoc() || rDM_Impl.IsAltChunk()))
+ {
+ // Avoid setting page style in case of autotext: so inserting the autotext at the
+ // end of the document does not introduce an unwanted page break.
+ // Also avoid setting the page style at the very beginning if it still is the default page style.
+ const OUString sPageStyle = m_bTitlePage ? m_sFirstPageStyleName : m_sFollowPageStyleName;
+ if (!rDM_Impl.IsReadGlossaries()
+ && !rDM_Impl.IsInFootOrEndnote()
+ && !(m_bIsFirstSection && sPageStyle == getPropertyName( PROP_STANDARD ) && m_nPageNumber < 0)
+ )
+ {
+ xRangeProperties->setPropertyValue(
+ getPropertyName( PROP_PAGE_DESC_NAME ),
+ uno::Any(sPageStyle) );
+ }
+
+ if (0 <= m_nPageNumber)
+ {
+ sal_Int16 nPageNumber = static_cast< sal_Int16 >(m_nPageNumber);
+ xRangeProperties->setPropertyValue(getPropertyName(PROP_PAGE_NUMBER_OFFSET),
+ uno::Any(nPageNumber));
+ }
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::CloseSectionGroup" );
+ }
+ }
+
+ // Now that the margins are known, resize relative width shapes because some shapes in LO do not support percentage-sizes
+ sal_Int32 nParagraphWidth = GetPageWidth() - m_nLeftMargin - m_nRightMargin;
+ if ( m_nColumnCount > 1 )
+ {
+ // skip custom-width columns since we don't know what column the shape is in.
+ if ( !m_aColWidth.empty() )
+ nParagraphWidth = 0;
+ else
+ nParagraphWidth = (nParagraphWidth - (m_nColumnDistance * (m_nColumnCount - 1))) / m_nColumnCount;
+ }
+ if ( nParagraphWidth > 0 )
+ {
+ const OUString sPropRelativeWidth = getPropertyName(PROP_RELATIVE_WIDTH);
+ for ( const auto& xShape : m_xRelativeWidthShapes )
+ {
+ const uno::Reference<beans::XPropertySet> xShapePropertySet( xShape, uno::UNO_QUERY );
+ if ( xShapePropertySet->getPropertySetInfo()->hasPropertyByName(sPropRelativeWidth) )
+ {
+ sal_uInt16 nPercent = 0;
+ try
+ {
+ xShapePropertySet->getPropertyValue(sPropRelativeWidth) >>= nPercent;
+ }
+ catch (const css::uno::RuntimeException& e)
+ {
+ // May happen e.g. when text frame has no frame format
+ // See sw/qa/extras/ooxmlimport/data/n779627.docx
+ SAL_WARN("writerfilter", "Getting relative width failed. " << e.Message);
+ }
+ if ( nPercent )
+ {
+ const sal_Int32 nWidth = nParagraphWidth * nPercent / 100;
+ xShape->setSize( awt::Size( nWidth, xShape->getSize().Height ) );
+ }
+ }
+ }
+ }
+ m_xRelativeWidthShapes.clear();
+
+ rDM_Impl.SetIsLastSectionGroup( false );
+ rDM_Impl.SetIsFirstParagraphInSection( true );
+
+ if ( !rDM_Impl.IsInFootOrEndnote() && !rDM_Impl.IsInComments() )
+ {
+ rDM_Impl.m_bHasFtn = false;
+ rDM_Impl.m_bHasFtnSep = false;
+ }
+}
+
+// Clear the flag that says we should take the header/footer content from
+// the previous section. This should be called when we encounter a header
+// or footer definition for this section.
+void SectionPropertyMap::ClearHeaderFooterLinkToPrevious( bool bHeader, PageType eType )
+{
+ if ( bHeader )
+ {
+ switch ( eType )
+ {
+ case PAGE_FIRST: m_bFirstPageHeaderLinkToPrevious = false; break;
+ case PAGE_LEFT: m_bEvenPageHeaderLinkToPrevious = false; break;
+ case PAGE_RIGHT: m_bDefaultHeaderLinkToPrevious = false; break;
+ // no default case as all enumeration values have been covered
+ }
+ }
+ else
+ {
+ switch ( eType )
+ {
+ case PAGE_FIRST: m_bFirstPageFooterLinkToPrevious = false; break;
+ case PAGE_LEFT: m_bEvenPageFooterLinkToPrevious = false; break;
+ case PAGE_RIGHT: m_bDefaultFooterLinkToPrevious = false; break;
+ }
+ }
+}
+
+namespace {
+
+class NamedPropertyValue
+{
+private:
+ OUString m_aName;
+
+public:
+ explicit NamedPropertyValue( const OUString& i_aStr )
+ : m_aName( i_aStr )
+ {
+ }
+
+ bool operator() ( beans::PropertyValue const & aVal )
+ {
+ return aVal.Name == m_aName;
+ }
+};
+
+}
+
+void SectionPropertyMap::ApplyProperties_( const uno::Reference< beans::XPropertySet >& xStyle )
+{
+ uno::Reference< beans::XMultiPropertySet > const xMultiSet( xStyle, uno::UNO_QUERY );
+
+ std::vector< OUString > vNames;
+ std::vector< uno::Any > vValues;
+ {
+ // Convert GetPropertyValues() value into something useful
+ const uno::Sequence< beans::PropertyValue > vPropVals = GetPropertyValues();
+
+ //Temporarily store the items that are in grab bags
+ uno::Sequence< beans::PropertyValue > vCharVals;
+ uno::Sequence< beans::PropertyValue > vParaVals;
+ const beans::PropertyValue* pCharGrabBag = std::find_if( vPropVals.begin(), vPropVals.end(), NamedPropertyValue( "CharInteropGrabBag" ) );
+ if ( pCharGrabBag != vPropVals.end() )
+ (pCharGrabBag->Value) >>= vCharVals;
+ const beans::PropertyValue* pParaGrabBag = std::find_if( vPropVals.begin(), vPropVals.end(), NamedPropertyValue( "ParaInteropGrabBag" ) );
+ if ( pParaGrabBag != vPropVals.end() )
+ (pParaGrabBag->Value) >>= vParaVals;
+
+ for ( const beans::PropertyValue* pIter = vPropVals.begin(); pIter != vPropVals.end(); ++pIter )
+ {
+ if ( pIter != pCharGrabBag && pIter != pParaGrabBag
+ && pIter->Name != "IsProtected" //section-only property
+ )
+ {
+ vNames.push_back( pIter->Name );
+ vValues.push_back( pIter->Value );
+ }
+ }
+ for ( const beans::PropertyValue & v : std::as_const(vCharVals) )
+ {
+ vNames.push_back( v.Name );
+ vValues.push_back( v.Value );
+ }
+ for ( const beans::PropertyValue & v : std::as_const(vParaVals) )
+ {
+ vNames.push_back( v.Name );
+ vValues.push_back( v.Value );
+ }
+ }
+ if ( xMultiSet.is() )
+ {
+ try
+ {
+ xMultiSet->setPropertyValues( comphelper::containerToSequence( vNames ), comphelper::containerToSequence( vValues ) );
+ return;
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyProperties_" );
+ }
+ }
+ for ( size_t i = 0; i < vNames.size(); ++i )
+ {
+ try
+ {
+ if ( xStyle.is() )
+ xStyle->setPropertyValue( vNames[i], vValues[i] );
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyProperties_" );
+ }
+ }
+}
+
+sal_Int32 SectionPropertyMap::GetPageWidth() const
+{
+ return getProperty( PROP_WIDTH )->second.get<sal_Int32>();
+}
+
+StyleSheetPropertyMap::StyleSheetPropertyMap()
+ : mnListLevel( -1 )
+ , mnOutlineLevel( -1 )
+{
+}
+
+ParagraphProperties::ParagraphProperties()
+ : m_bFrameMode( false )
+ , m_nDropCap( NS_ooxml::LN_Value_doc_ST_DropCap_none )
+ , m_nLines( 0 )
+ , m_w( -1 )
+ , m_h( -1 )
+ , m_nWrap( text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE )
+ , m_hAnchor( -1 )
+ , m_vAnchor( -1 )
+ , m_x( -1 )
+ , m_bxValid( false )
+ , m_y( -1 )
+ , m_byValid( false )
+ , m_hSpace( -1 )
+ , m_vSpace( -1 )
+ , m_hRule( -1 )
+ , m_xAlign( -1 )
+ , m_yAlign( -1 )
+ , m_nDropCapLength( 0 )
+{
+}
+
+bool ParagraphProperties::operator==( const ParagraphProperties& rCompare )
+{
+ return ( m_bFrameMode == rCompare.m_bFrameMode &&
+ m_nDropCap == rCompare.m_nDropCap &&
+ m_nLines == rCompare.m_nLines &&
+ m_w == rCompare.m_w &&
+ m_h == rCompare.m_h &&
+ m_nWrap == rCompare.m_nWrap &&
+ m_hAnchor == rCompare.m_hAnchor &&
+ m_vAnchor == rCompare.m_vAnchor &&
+ m_x == rCompare.m_x &&
+ m_bxValid == rCompare.m_bxValid &&
+ m_y == rCompare.m_y &&
+ m_byValid == rCompare.m_byValid &&
+ m_hSpace == rCompare.m_hSpace &&
+ m_vSpace == rCompare.m_vSpace &&
+ m_hRule == rCompare.m_hRule &&
+ m_xAlign == rCompare.m_xAlign &&
+ m_yAlign == rCompare.m_yAlign );
+}
+
+void ParagraphProperties::ResetFrameProperties()
+{
+ m_bFrameMode = false;
+ m_nDropCap = NS_ooxml::LN_Value_doc_ST_DropCap_none;
+ m_nLines = 0;
+ m_w = -1;
+ m_h = -1;
+ m_nWrap = text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE;
+ m_hAnchor = -1;
+ m_vAnchor = -1;
+ m_x = -1;
+ m_bxValid = false;
+ m_y = -1;
+ m_byValid = false;
+ m_hSpace = -1;
+ m_vSpace = -1;
+ m_hRule = -1;
+ m_xAlign = -1;
+ m_yAlign = -1;
+ m_nDropCapLength = 0;
+}
+
+bool TablePropertyMap::getValue( TablePropertyMapTarget eWhich, sal_Int32& nFill )
+{
+ if ( eWhich < TablePropertyMapTarget_MAX )
+ {
+ if ( m_aValidValues[eWhich].bValid )
+ nFill = m_aValidValues[eWhich].nValue;
+ return m_aValidValues[eWhich].bValid;
+ }
+ else
+ {
+ OSL_FAIL( "invalid TablePropertyMapTarget" );
+ return false;
+ }
+}
+
+void TablePropertyMap::setValue( TablePropertyMapTarget eWhich, sal_Int32 nSet )
+{
+ if ( eWhich < TablePropertyMapTarget_MAX )
+ {
+ m_aValidValues[eWhich].bValid = true;
+ m_aValidValues[eWhich].nValue = nSet;
+ }
+ else
+ OSL_FAIL( "invalid TablePropertyMapTarget" );
+}
+
+void TablePropertyMap::insertTableProperties( const PropertyMap* pMap, const bool bOverwrite )
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement( "TablePropertyMap.insertTableProperties" );
+ pMap->dumpXml();
+#endif
+
+ const TablePropertyMap* pSource = dynamic_cast< const TablePropertyMap* >(pMap);
+ if ( pSource )
+ {
+ for ( sal_Int32 eTarget = TablePropertyMapTarget_START;
+ eTarget < TablePropertyMapTarget_MAX; ++eTarget )
+ {
+ if ( pSource->m_aValidValues[eTarget].bValid && (bOverwrite || !m_aValidValues[eTarget].bValid) )
+ {
+ m_aValidValues[eTarget].bValid = true;
+ m_aValidValues[eTarget].nValue = pSource->m_aValidValues[eTarget].nValue;
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ dumpXml();
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+} // namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/PropertyMap.hxx b/writerfilter/source/dmapper/PropertyMap.hxx
new file mode 100644
index 000000000..988c99b02
--- /dev/null
+++ b/writerfilter/source/dmapper/PropertyMap.hxx
@@ -0,0 +1,614 @@
+/* -*- 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 <rtl/ustring.hxx>
+#include <tools/ref.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/drawing/XShape.hpp>
+#include "PropertyIds.hxx"
+#include <memory>
+#include <optional>
+#include <map>
+#include <vector>
+#include <set>
+
+namespace com::sun::star {
+ namespace beans {
+ struct PropertyValue;
+ }
+ namespace container {
+ class XNameContainer;
+ }
+ namespace lang {
+ class XMultiServiceFactory;
+ }
+ namespace text {
+ class XTextRange;
+ class XTextColumns;
+ class XFootnote;
+ }
+ namespace table {
+ struct BorderLine2;
+ struct ShadowFormat;
+ }
+}
+
+namespace writerfilter::dmapper {
+
+class DomainMapper_Impl;
+struct FloatingTableInfo;
+struct AnchoredObjectInfo;
+
+enum BorderPosition
+{
+ BORDER_LEFT,
+ BORDER_RIGHT,
+ BORDER_TOP,
+ BORDER_BOTTOM
+};
+
+enum GrabBagType
+{
+ NO_GRAB_BAG,
+ ROW_GRAB_BAG,
+ CELL_GRAB_BAG,
+ PARA_GRAB_BAG,
+ CHAR_GRAB_BAG
+};
+
+struct RedlineParams : public virtual SvRefBase
+{
+ OUString m_sAuthor;
+ OUString m_sDate;
+ sal_Int32 m_nToken;
+
+ // This can hold properties of runs that had formatted 'track changes' properties
+ css::uno::Sequence< css::beans::PropertyValue > m_aRevertProperties;
+};
+
+typedef tools::SvRef< RedlineParams > RedlineParamsPtr;
+
+class PropValue
+{
+private:
+ css::uno::Any m_aValue;
+ GrabBagType m_GrabBagType;
+ bool m_bIsDocDefault;
+
+public:
+ PropValue( const css::uno::Any& rValue, GrabBagType i_GrabBagType, bool bDocDefault )
+ : m_aValue( rValue )
+ , m_GrabBagType( i_GrabBagType )
+ , m_bIsDocDefault( bDocDefault )
+ {
+ }
+
+ PropValue( const css::uno::Any& rValue, GrabBagType i_GrabBagType )
+ : m_aValue( rValue )
+ , m_GrabBagType( i_GrabBagType )
+ , m_bIsDocDefault( false )
+ {
+ }
+
+ PropValue()
+ : m_aValue()
+ , m_GrabBagType( NO_GRAB_BAG )
+ , m_bIsDocDefault( false )
+ {
+ }
+
+ const css::uno::Any& getValue() const { return m_aValue; }
+
+ GrabBagType getGrabBagType() const { return m_GrabBagType; }
+
+ bool getIsDocDefault() const { return m_bIsDocDefault; }
+};
+
+class PropertyMap;
+typedef tools::SvRef< PropertyMap > PropertyMapPtr;
+
+class PropertyMap : public virtual SvRefBase
+{
+private:
+ // Cache the property values for the GetPropertyValues() call(s).
+ std::vector< css::beans::PropertyValue > m_aValues;
+
+ // marks context as footnote context - ::text( ) events contain either the footnote character or can be ignored
+ // depending on sprmCSymbol
+ css::uno::Reference< css::text::XFootnote > m_xFootnote;
+ OUString m_sFootnoteCharStyleName;
+ std::map< PropertyIds, PropValue > m_vMap;
+ std::vector< RedlineParamsPtr > m_aRedlines;
+
+public:
+ typedef std::pair< PropertyIds, css::uno::Any > Property;
+
+ PropertyMap() {}
+
+ // Sequence: Grab Bags: The CHAR_GRAB_BAG has Name "CharInteropGrabBag" and the PARA_GRAB_BAG has Name "ParaInteropGrabBag"
+ // the contained properties are their Value.
+ css::uno::Sequence< css::beans::PropertyValue > GetPropertyValues( bool bCharGrabBag = true );
+
+ std::vector< PropertyIds > GetPropertyIds();
+
+ // Add property, optionally overwriting existing attributes
+ void Insert( PropertyIds eId, const css::uno::Any& rAny, bool bOverwrite = true, GrabBagType i_GrabBagType = NO_GRAB_BAG, bool bDocDefault = false );
+
+ // Remove a named property from *this, does nothing if the property id has not been set
+ void Erase( PropertyIds eId);
+
+ // Imports properties from pMap (bOverwrite==false means m_bIsDocDefault=true setting)
+ void InsertProps( const PropertyMapPtr& rMap, const bool bOverwrite = true );
+
+ // Returns a copy of the property if it exists, .first is its PropertyIds and .second is its Value (type css::uno::Any)
+ std::optional< Property > getProperty( PropertyIds eId ) const;
+
+ // Has the property named been set (via Insert)?
+ bool isSet( PropertyIds eId ) const;
+ bool isDocDefault( PropertyIds eId ) const;
+
+ const css::uno::Reference< css::text::XFootnote >& GetFootnote() const { return m_xFootnote; }
+ const OUString& GetFootnoteStyle() const { return m_sFootnoteCharStyleName; }
+
+ void SetFootnote(const css::uno::Reference< css::text::XFootnote >& xFootnote, const OUString& sStyleName)
+ {
+ m_xFootnote = xFootnote;
+ m_sFootnoteCharStyleName = sStyleName;
+ }
+
+ virtual void insertTableProperties( const PropertyMap*, const bool bOverwrite = true );
+
+ const std::vector< RedlineParamsPtr >& Redlines() const { return m_aRedlines; }
+
+ std::vector< RedlineParamsPtr >& Redlines() { return m_aRedlines; }
+
+ void printProperties();
+
+#ifdef DBG_UTIL
+ void dumpXml() const;
+#endif
+
+ static css::table::ShadowFormat getShadowFromBorder( const css::table::BorderLine2& rBorder );
+
+protected:
+ void Invalidate()
+ {
+ if ( m_aValues.size() )
+ m_aValues.clear();
+ }
+};
+
+class SectionPropertyMap : public PropertyMap
+{
+public:
+ enum class BorderApply
+ {
+ ToAllInSection = 0,
+ ToFirstPageInSection = 1,
+ ToAllButFirstInSection = 2
+ };
+ enum class BorderOffsetFrom
+ {
+ Text = 0,
+ Edge = 1,
+ };
+private:
+#ifdef DBG_UTIL
+ sal_Int32 m_nDebugSectionNumber;
+#endif
+
+ // 'temporarily' the section page settings are imported as page styles
+ // empty strings mark page settings as not yet imported
+
+ bool m_bIsFirstSection;
+ css::uno::Reference< css::text::XTextRange > m_xStartingRange;
+
+ OUString m_sFirstPageStyleName;
+ OUString m_sFollowPageStyleName;
+ css::uno::Reference< css::beans::XPropertySet > m_aFirstPageStyle;
+ css::uno::Reference< css::beans::XPropertySet > m_aFollowPageStyle;
+
+ std::optional< css::table::BorderLine2 > m_oBorderLines[4];
+ sal_Int32 m_nBorderDistances[4];
+ BorderApply m_eBorderApply;
+ BorderOffsetFrom m_eBorderOffsetFrom;
+ bool m_bBorderShadows[4];
+
+ bool m_bTitlePage;
+ sal_Int16 m_nColumnCount;
+ sal_Int32 m_nColumnDistance;
+ css::uno::Reference< css::beans::XPropertySet > m_xColumnContainer;
+ std::vector< sal_Int32 > m_aColWidth;
+ std::vector< sal_Int32 > m_aColDistance;
+
+ bool m_bSeparatorLineIsOn;
+ bool m_bEvenlySpaced;
+
+ sal_Int32 m_nPageNumber;
+ // Page number type is a value from css::style::NumberingType.
+ sal_Int16 m_nPageNumberType;
+ sal_Int32 m_nBreakType;
+
+ sal_Int32 m_nLeftMargin;
+ sal_Int32 m_nRightMargin;
+ sal_Int32 m_nGutterMargin;
+ sal_Int32 m_nTopMargin;
+ sal_Int32 m_nBottomMargin;
+ sal_Int32 m_nHeaderTop;
+ sal_Int32 m_nHeaderBottom;
+
+ sal_Int32 m_nGridType;
+ sal_Int32 m_nGridLinePitch;
+ sal_Int32 m_nDxtCharSpace;
+ bool m_bGridSnapToChars;
+
+ // line numbering
+ sal_Int32 m_nLnnMod;
+ sal_uInt32 m_nLnc;
+ sal_Int32 m_ndxaLnn;
+ sal_Int32 m_nLnnMin;
+
+ bool m_bDynamicHeightTop;
+ bool m_bDynamicHeightBottom;
+
+ std::vector<css::uno::Reference<css::drawing::XShape>> m_xRelativeWidthShapes;
+
+ // The "Link To Previous" flag indicates whether the header/footer
+ // content should be taken from the previous section
+ bool m_bDefaultHeaderLinkToPrevious;
+ bool m_bEvenPageHeaderLinkToPrevious;
+ bool m_bFirstPageHeaderLinkToPrevious;
+ bool m_bDefaultFooterLinkToPrevious;
+ bool m_bEvenPageFooterLinkToPrevious;
+ bool m_bFirstPageFooterLinkToPrevious;
+
+ void ApplyProperties_( const css::uno::Reference< css::beans::XPropertySet >& xStyle );
+
+ void DontBalanceTextColumns();
+
+ /// Apply section-specific properties: only valid to use after PageStyle has been determined by InheritOrFinalizePageStyles
+ void ApplySectionProperties( const css::uno::Reference< css::beans::XPropertySet >& xSection, DomainMapper_Impl& rDM_Impl );
+
+ /// Check if document is protected. If so, ensure a section exists, and apply its protected value.
+ void ApplyProtectionProperties( css::uno::Reference< css::beans::XPropertySet >& xSection, DomainMapper_Impl& rDM_Impl );
+
+ css::uno::Reference< css::text::XTextColumns > ApplyColumnProperties( const css::uno::Reference< css::beans::XPropertySet >& xFollowPageStyle,
+ DomainMapper_Impl& rDM_Impl);
+
+ void CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl );
+
+ static void CopyHeaderFooter( const DomainMapper_Impl& rDM_Impl,
+ const css::uno::Reference< css::beans::XPropertySet >& xPrevStyle,
+ const css::uno::Reference< css::beans::XPropertySet >& xStyle,
+ bool bOmitRightHeader = false, bool bOmitLeftHeader = false,
+ bool bOmitRightFooter = false, bool bOmitLeftFooter = false );
+
+ static void CopyHeaderFooterTextProperty( const css::uno::Reference< css::beans::XPropertySet >& xPrevStyle,
+ const css::uno::Reference< css::beans::XPropertySet >& xStyle,
+ PropertyIds ePropId );
+
+ void PrepareHeaderFooterProperties( bool bFirstPage );
+
+ bool HasHeader( bool bFirstPage ) const;
+ bool HasFooter( bool bFirstPage ) const;
+
+ static void SetBorderDistance( const css::uno::Reference< css::beans::XPropertySet >& xStyle,
+ PropertyIds eMarginId,
+ PropertyIds eDistId,
+ sal_Int32 nDistance,
+ BorderOffsetFrom eOffsetFrom,
+ sal_uInt32 nLineWidth,
+ DomainMapper_Impl& rDM_Impl );
+
+ // Determines if conversion of a given floating table is wanted or not.
+ bool FloatingTableConversion( const DomainMapper_Impl& rDM_Impl, FloatingTableInfo& rInfo );
+
+ /// Increases paragraph spacing according to Word 2013+ needs if necessary.
+ void HandleIncreasedAnchoredObjectSpacing(DomainMapper_Impl& rDM_Impl);
+
+public:
+ enum PageType
+ {
+ PAGE_FIRST,
+ PAGE_LEFT,
+ PAGE_RIGHT
+ };
+
+ explicit SectionPropertyMap( bool bIsFirstSection );
+
+ bool IsFirstSection() const { return m_bIsFirstSection; }
+
+ void SetStart( const css::uno::Reference< css::text::XTextRange >& xRange ) { m_xStartingRange = xRange; }
+
+ const css::uno::Reference< css::text::XTextRange >& GetStartingRange() const { return m_xStartingRange; }
+
+ css::uno::Reference< css::beans::XPropertySet > GetPageStyle( DomainMapper_Impl& rDM_Impl, bool bFirst );
+
+ const OUString& GetPageStyleName( bool bFirstPage = false )
+ {
+ return bFirstPage ? m_sFirstPageStyleName : m_sFollowPageStyleName;
+ }
+
+ // @throws css::beans::UnknownPropertyException
+ // @throws css::beans::PropertyVetoException
+ // @throws css::lang::IllegalArgumentException
+ // @throws css::lang::WrappedTargetException
+ // @throws css::uno::RuntimeException
+ void InheritOrFinalizePageStyles( DomainMapper_Impl& rDM_Impl );
+
+ void SetBorder( BorderPosition ePos, sal_Int32 nLineDistance, const css::table::BorderLine2& rBorderLine, bool bShadow );
+ void SetBorderApply( BorderApply nSet ) { m_eBorderApply = nSet; }
+ void SetBorderOffsetFrom( BorderOffsetFrom nSet ) { m_eBorderOffsetFrom = nSet; }
+
+ void SetColumnCount( sal_Int16 nCount ) { m_nColumnCount = nCount; }
+ sal_Int16 ColumnCount() const { return m_nColumnCount; }
+
+ void SetColumnDistance( sal_Int32 nDist ) { m_nColumnDistance = nDist; }
+ void AppendColumnWidth( sal_Int32 nWidth ) { m_aColWidth.push_back( nWidth ); }
+ void AppendColumnSpacing( sal_Int32 nDist ) { m_aColDistance.push_back( nDist ); }
+
+ void SetTitlePage( bool bSet ) { m_bTitlePage = bSet; }
+ void SetSeparatorLine( bool bSet ) { m_bSeparatorLineIsOn = bSet; }
+ void SetEvenlySpaced( bool bSet ) { m_bEvenlySpaced = bSet; }
+ void SetPageNumber( sal_Int32 nSet ) { m_nPageNumber = nSet; }
+ void SetPageNumberType( sal_Int32 nSet ) { m_nPageNumberType = nSet; }
+ void SetBreakType( sal_Int32 nSet ) { m_nBreakType = nSet; }
+ // GetBreakType returns -1 if the breakType has not yet been identified for the section
+ sal_Int32 GetBreakType() const { return m_nBreakType; }
+
+ void SetLeftMargin( sal_Int32 nSet ) { m_nLeftMargin = nSet; }
+ sal_Int32 GetLeftMargin() const { return m_nLeftMargin; }
+ void SetRightMargin( sal_Int32 nSet ) { m_nRightMargin = nSet; }
+ sal_Int32 GetRightMargin() const { return m_nRightMargin; }
+ void SetTopMargin(sal_Int32 nSet) { m_bDynamicHeightTop = nSet >= 0; m_nTopMargin = std::abs(nSet); }
+ void SetBottomMargin( sal_Int32 nSet ) { m_bDynamicHeightBottom = nSet >= 0; m_nBottomMargin = std::abs(nSet); }
+ void SetHeaderTop( sal_Int32 nSet ) { m_nHeaderTop = nSet; }
+ void SetHeaderBottom( sal_Int32 nSet ) { m_nHeaderBottom = nSet; }
+ void SetGutterMargin( sal_Int32 nGutterMargin ) { m_nGutterMargin = nGutterMargin; }
+ sal_Int32 GetPageWidth() const;
+
+ void SetGridType( sal_Int32 nSet ) { m_nGridType = nSet; }
+ void SetGridLinePitch( sal_Int32 nSet ) { m_nGridLinePitch = nSet; }
+ void SetGridSnapToChars( bool bSet ) { m_bGridSnapToChars = bSet; }
+ void SetDxtCharSpace( sal_Int32 nSet ) { m_nDxtCharSpace = nSet; }
+
+ void SetLnnMod( sal_Int32 nValue ) { m_nLnnMod = nValue; }
+ void SetLnc( sal_Int32 nValue ) { m_nLnc = nValue; }
+ void SetdxaLnn( sal_Int32 nValue ) { m_ndxaLnn = nValue; }
+ void SetLnnMin( sal_Int32 nValue ) { m_nLnnMin = nValue; }
+
+ void addRelativeWidthShape( css::uno::Reference<css::drawing::XShape> xShape ) { m_xRelativeWidthShapes.push_back( xShape ); }
+
+ // determine which style gets the borders
+ void ApplyBorderToPageStyles( DomainMapper_Impl &rDM_Impl,
+ BorderApply eBorderApply, BorderOffsetFrom eOffsetFrom );
+
+ void CloseSectionGroup( DomainMapper_Impl& rDM_Impl );
+ // Handling of margins, header and footer for any kind of sections breaks.
+ void HandleMarginsHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl );
+ void ClearHeaderFooterLinkToPrevious( bool bHeader, PageType eType );
+};
+
+class ParagraphProperties : public virtual SvRefBase
+{
+private:
+ bool m_bFrameMode;
+ sal_Int32 m_nDropCap; // drop, margin ST_DropCap
+ sal_Int32 m_nLines; // number of lines of the drop cap
+ sal_Int32 m_w; // width
+ sal_Int32 m_h; // height
+ css::text::WrapTextMode m_nWrap; // from ST_Wrap around, auto, none, notBeside, through, tight
+ sal_Int32 m_hAnchor; // page, from ST_HAnchor margin, page, text
+ sal_Int32 m_vAnchor; // around from ST_VAnchor margin, page, text
+ sal_Int32 m_x; // x-position
+ bool m_bxValid;
+ sal_Int32 m_y; // y-position
+ bool m_byValid;
+ sal_Int32 m_hSpace; // frame padding h
+ sal_Int32 m_vSpace; // frame padding v
+ sal_Int32 m_hRule; // from ST_HeightRule exact, atLeast, auto
+ sal_Int32 m_xAlign; // from ST_XAlign center, inside, left, outside, right
+ sal_Int32 m_yAlign; // from ST_YAlign bottom, center, inline, inside, outside, top
+ sal_Int8 m_nDropCapLength; // number of characters
+ OUString m_sParaStyleName;
+ OUString m_sParaId; // [MS-DOCX] sect. 2.2.4 "p and tr Extensions"
+
+ css::uno::Reference< css::text::XTextRange > m_xStartingRange; // start of a frame
+ css::uno::Reference< css::text::XTextRange > m_xEndingRange; // end of the frame
+ sal_Int32 m_nListId = -1;
+
+public:
+ ParagraphProperties();
+
+ ParagraphProperties(ParagraphProperties const &) = default;
+ ParagraphProperties(ParagraphProperties &&) = default;
+ ParagraphProperties & operator =(ParagraphProperties const &) = default;
+ ParagraphProperties & operator =(ParagraphProperties &&) = default;
+
+ // Does not compare the starting/ending range, m_sParaStyleName and m_nDropCapLength
+ bool operator==( const ParagraphProperties& );
+
+ sal_Int32 GetListId() const { return m_nListId; }
+ void SetListId( sal_Int32 nId ) { m_nListId = nId; }
+
+ bool IsFrameMode() const { return m_bFrameMode; }
+ void SetFrameMode( bool set = true ) { m_bFrameMode = set; }
+
+ sal_Int32 GetDropCap() const { return m_nDropCap; }
+ void SetDropCap( sal_Int32 nSet ) { m_nDropCap = nSet; }
+
+ sal_Int32 GetLines() const { return m_nLines; }
+ void SetLines( sal_Int32 nSet ) { m_nLines = nSet; }
+
+ sal_Int32 Getw() const { return m_w; }
+ void Setw( sal_Int32 nSet ) { m_w = nSet; }
+
+ sal_Int32 Geth() const { return m_h; }
+ void Seth( sal_Int32 nSet ) { m_h = nSet; }
+
+ css::text::WrapTextMode GetWrap() const { return m_nWrap; }
+ void SetWrap( css::text::WrapTextMode nSet ) { m_nWrap = nSet; }
+
+ sal_Int32 GethAnchor() const { return m_hAnchor; }
+ void SethAnchor( sal_Int32 nSet ) { m_hAnchor = nSet; }
+
+ sal_Int32 GetvAnchor() const { return m_vAnchor; }
+ void SetvAnchor( sal_Int32 nSet ) { m_vAnchor = nSet; }
+
+ sal_Int32 Getx() const { return m_x; }
+ void Setx( sal_Int32 nSet ) { m_x = nSet; m_bxValid = true; }
+ bool IsxValid() const { return m_bxValid; }
+
+ sal_Int32 Gety() const { return m_y; }
+ void Sety( sal_Int32 nSet ) { m_y = nSet; m_byValid = true; }
+ bool IsyValid() const { return m_byValid; }
+
+ void SethSpace( sal_Int32 nSet ) { m_hSpace = nSet; }
+ sal_Int32 GethSpace() const { return m_hSpace; }
+
+ sal_Int32 GetvSpace() const { return m_vSpace; }
+ void SetvSpace( sal_Int32 nSet ) { m_vSpace = nSet; }
+
+ sal_Int32 GethRule() const { return m_hRule; }
+ void SethRule( sal_Int32 nSet ) { m_hRule = nSet; }
+
+ sal_Int32 GetxAlign() const { return m_xAlign; }
+ void SetxAlign( sal_Int32 nSet ) { m_xAlign = nSet; }
+
+ sal_Int32 GetyAlign() const { return m_yAlign; }
+ void SetyAlign( sal_Int32 nSet ) { m_yAlign = nSet; }
+
+ sal_Int8 GetDropCapLength() const { return m_nDropCapLength; }
+ void SetDropCapLength( sal_Int8 nSet ) { m_nDropCapLength = nSet; }
+
+ const css::uno::Reference< css::text::XTextRange >& GetStartingRange() const { return m_xStartingRange; }
+ void SetStartingRange( const css::uno::Reference< css::text::XTextRange >& xSet ) { m_xStartingRange = xSet; }
+
+ const css::uno::Reference< css::text::XTextRange >& GetEndingRange() const { return m_xEndingRange; }
+ void SetEndingRange( const css::uno::Reference< css::text::XTextRange >& xSet ) { m_xEndingRange = xSet; }
+
+ const OUString& GetParaStyleName() const { return m_sParaStyleName; }
+ void SetParaStyleName( const OUString& rSet ) { m_sParaStyleName = rSet; }
+
+ const OUString& GetParaId() const { return m_sParaId; }
+ void SetParaId(const OUString& rSet) { m_sParaId = rSet; }
+
+ void ResetFrameProperties();
+};
+
+typedef tools::SvRef< ParagraphProperties > ParagraphPropertiesPtr;
+
+/*-------------------------------------------------------------------------
+ property map of a stylesheet
+ -----------------------------------------------------------------------*/
+
+#define WW_OUTLINE_MAX sal_Int16( 9 )
+#define WW_OUTLINE_MIN sal_Int16( 0 )
+
+class StyleSheetPropertyMap
+ : public PropertyMap
+ , public ParagraphProperties
+{
+private:
+ sal_Int16 mnListLevel;
+ sal_Int16 mnOutlineLevel;
+
+public:
+ explicit StyleSheetPropertyMap();
+
+ sal_Int16 GetListLevel() const { return mnListLevel; }
+ void SetListLevel( sal_Int16 nLevel ) { mnListLevel = nLevel; }
+
+ sal_Int16 GetOutlineLevel() const { return mnOutlineLevel; }
+ void SetOutlineLevel(sal_Int16 nLevel) { if (nLevel <= WW_OUTLINE_MAX) mnOutlineLevel = nLevel; }
+};
+
+class ParagraphPropertyMap
+ : public PropertyMap
+ , public ParagraphProperties
+{
+public:
+ explicit ParagraphPropertyMap() {}
+};
+
+class TablePropertyMap
+ : public PropertyMap
+{
+public:
+ enum TablePropertyMapTarget
+ {
+ TablePropertyMapTarget_START,
+ CELL_MAR_LEFT = TablePropertyMapTarget_START,
+ CELL_MAR_RIGHT,
+ CELL_MAR_TOP,
+ CELL_MAR_BOTTOM,
+ TABLE_WIDTH,
+ TABLE_WIDTH_TYPE,
+ GAP_HALF,
+ LEFT_MARGIN,
+ HORI_ORIENT,
+ TablePropertyMapTarget_MAX
+ };
+
+private:
+ struct ValidValue
+ {
+ sal_Int32 nValue;
+ bool bValid;
+
+ ValidValue()
+ : nValue( 0 )
+ , bValid( false )
+ {
+ }
+ };
+
+ ValidValue m_aValidValues[TablePropertyMapTarget_MAX];
+
+public:
+ explicit TablePropertyMap() {}
+
+ bool getValue( TablePropertyMapTarget eWhich, sal_Int32& nFill );
+ void setValue( TablePropertyMapTarget eWhich, sal_Int32 nSet );
+
+ virtual void insertTableProperties( const PropertyMap*, const bool bOverwrite = true ) override;
+};
+
+typedef tools::SvRef< TablePropertyMap > TablePropertyMapPtr;
+
+/// Information about a paragraph to be finished after a table end.
+struct TableParagraph
+{
+ css::uno::Reference<css::text::XTextRange> m_rStartParagraph;
+ css::uno::Reference<css::text::XTextRange> m_rEndParagraph;
+ PropertyMapPtr m_pPropertyMap;
+ css::uno::Reference<css::beans::XPropertySet> m_rPropertySet;
+};
+
+typedef std::shared_ptr< std::vector<TableParagraph> > TableParagraphVectorPtr;
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/PropertyMapHelper.cxx b/writerfilter/source/dmapper/PropertyMapHelper.cxx
new file mode 100644
index 000000000..a944dc147
--- /dev/null
+++ b/writerfilter/source/dmapper/PropertyMapHelper.cxx
@@ -0,0 +1,98 @@
+/* -*- 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/TableColumnSeparator.hpp>
+#include "TagLogger.hxx"
+#include "PropertyMapHelper.hxx"
+
+namespace writerfilter::dmapper
+{
+using namespace ::com::sun::star;
+
+void lcl_DumpTableColumnSeparators(const uno::Any& rTableColumnSeparators)
+{
+#ifdef DBG_UTIL
+ uno::Sequence<text::TableColumnSeparator> aSeq;
+ rTableColumnSeparators >>= aSeq;
+
+ TagLogger::getInstance().startElement("property.TableColumnSeparators");
+
+ sal_uInt32 nLength = aSeq.getLength();
+ for (sal_uInt32 n = 0; n < nLength; ++n)
+ {
+ TagLogger::getInstance().startElement("separator");
+
+ TagLogger::getInstance().attribute("position", aSeq[n].Position);
+ TagLogger::getInstance().attribute("visible", sal_uInt32(aSeq[n].IsVisible));
+
+ TagLogger::getInstance().endElement();
+ }
+
+ TagLogger::getInstance().endElement();
+#else
+ (void)rTableColumnSeparators;
+#endif // DBG_UTIL
+}
+
+#ifdef DBG_UTIL
+void lcl_DumpPropertyValues(beans::PropertyValues const& rValues)
+{
+ TagLogger::getInstance().startElement("propertyValues");
+
+ for (beans::PropertyValue const& propVal : rValues)
+ {
+ TagLogger::getInstance().startElement("propertyValue");
+
+ TagLogger::getInstance().attribute("name", propVal.Name);
+
+ try
+ {
+ sal_Int32 aInt = 0;
+ propVal.Value >>= aInt;
+ TagLogger::getInstance().attribute("value", aInt);
+ }
+ catch (...)
+ {
+ }
+
+ if (propVal.Name == "TableColumnSeparators")
+ {
+ lcl_DumpTableColumnSeparators(propVal.Value);
+ }
+
+ TagLogger::getInstance().endElement();
+ }
+ TagLogger::getInstance().endElement();
+}
+
+void lcl_DumpPropertyValueSeq(css::uno::Sequence<css::beans::PropertyValues> const& rPropValSeq)
+{
+ TagLogger::getInstance().startElement("PropertyValueSeq");
+
+ for (auto const& propVal : rPropValSeq)
+ {
+ lcl_DumpPropertyValues(propVal);
+ }
+
+ TagLogger::getInstance().endElement();
+}
+#endif // DBG_UTIL
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/PropertyMapHelper.hxx b/writerfilter/source/dmapper/PropertyMapHelper.hxx
new file mode 100644
index 000000000..472e1bcfa
--- /dev/null
+++ b/writerfilter/source/dmapper/PropertyMapHelper.hxx
@@ -0,0 +1,34 @@
+/* -*- 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 <com/sun/star/beans/PropertyValues.hpp>
+
+namespace writerfilter::dmapper
+{
+void lcl_DumpTableColumnSeparators(const css::uno::Any& rTableColumnSeparators);
+#ifdef DBG_UTIL
+void lcl_DumpPropertyValues(css::beans::PropertyValues const& rValues);
+
+void lcl_DumpPropertyValueSeq(css::uno::Sequence<css::beans::PropertyValues> const& rPropValSeq);
+#endif // DBG_UTIL
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx
new file mode 100644
index 000000000..31c83d5a3
--- /dev/null
+++ b/writerfilter/source/dmapper/SdtHelper.cxx
@@ -0,0 +1,513 @@
+/* -*- 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 "SdtHelper.hxx"
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <editeng/unoprnms.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/outdev.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/sequence.hxx>
+#include <xmloff/odffields.hxx>
+#include <com/sun/star/text/XTextField.hpp>
+#include "DomainMapper_Impl.hxx"
+#include "StyleSheetTable.hxx"
+#include <officecfg/Office/Writer.hxx>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <com/sun/star/text/XTextFieldsSupplier.hpp>
+#include <com/sun/star/document/XOOXMLDocumentPropertiesImporter.hpp>
+#include <ooxml/OOXMLDocument.hxx>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+
+namespace writerfilter::dmapper
+{
+using namespace ::com::sun::star;
+using namespace ::css::xml::xpath;
+using namespace ::comphelper;
+
+/// w:sdt's w:dropDownList doesn't have width, so guess the size based on the longest string.
+static awt::Size lcl_getOptimalWidth(const StyleSheetTablePtr& pStyleSheet,
+ OUString const& rDefault, std::vector<OUString>& rItems)
+{
+ OUString aLongest = rDefault;
+ sal_Int32 nHeight = 0;
+ for (const OUString& rItem : rItems)
+ if (rItem.getLength() > aLongest.getLength())
+ aLongest = rItem;
+
+ MapMode aMap(MapUnit::Map100thMM);
+ OutputDevice* pOut = Application::GetDefaultDevice();
+ pOut->Push(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE);
+
+ PropertyMapPtr pDefaultCharProps = pStyleSheet->GetDefaultCharProps();
+ vcl::Font aFont(pOut->GetFont());
+ std::optional<PropertyMap::Property> aFontName
+ = pDefaultCharProps->getProperty(PROP_CHAR_FONT_NAME);
+ if (aFontName)
+ aFont.SetFamilyName(aFontName->second.get<OUString>());
+ std::optional<PropertyMap::Property> aHeight = pDefaultCharProps->getProperty(PROP_CHAR_HEIGHT);
+ if (aHeight)
+ {
+ nHeight = aHeight->second.get<double>() * 35; // points -> mm100
+ aFont.SetFontSize(Size(0, nHeight));
+ }
+ pOut->SetFont(aFont);
+ pOut->SetMapMode(aMap);
+ sal_Int32 nWidth = pOut->GetTextWidth(aLongest);
+
+ pOut->Pop();
+
+ // Border: see PDFWriterImpl::drawFieldBorder(), border size is font height / 4,
+ // so additional width / height needed is height / 2.
+ sal_Int32 nBorder = nHeight / 2;
+
+ // Width: space for the text + the square having the dropdown arrow.
+ return { nWidth + nBorder + nHeight, nHeight + nBorder };
+}
+
+SdtHelper::SdtHelper(DomainMapper_Impl& rDM_Impl,
+ css::uno::Reference<css::uno::XComponentContext> const& xContext)
+ : m_rDM_Impl(rDM_Impl)
+ , m_xComponentContext(xContext)
+ , m_aControlType(SdtControlType::unknown)
+ , m_bHasElements(false)
+ , m_bOutsideAParagraph(false)
+ , m_bPropertiesXMLsLoaded(false)
+{
+}
+
+SdtHelper::~SdtHelper() = default;
+
+void SdtHelper::loadPropertiesXMLs()
+{
+ // Initialize properties xml storage (m_xPropertiesXMLs)
+ uno::Reference<uno::XInterface> xTemp
+ = m_xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.document.OOXMLDocumentPropertiesImporter", m_xComponentContext);
+ uno::Reference<document::XOOXMLDocumentPropertiesImporter> xImporter(xTemp, uno::UNO_QUERY);
+ if (!xImporter.is())
+ return;
+
+ uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder(
+ xml::dom::DocumentBuilder::create(m_xComponentContext));
+ if (!xDomBuilder.is())
+ return;
+
+ // Load core properties
+ try
+ {
+ auto xCorePropsStream = xImporter->getCorePropertiesStream(m_rDM_Impl.m_xDocumentStorage);
+ m_xPropertiesXMLs.insert(
+ { OUString("{6C3C8BC8-F283-45AE-878A-BAB7291924A1}"), // hardcoded id for core props
+ xDomBuilder->parse(xCorePropsStream) });
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN("writerfilter",
+ "SdtHelper::loadPropertiesXMLs: failed loading core properties XML");
+ }
+
+ // Load extended properties
+ try
+ {
+ auto xExtPropsStream
+ = xImporter->getExtendedPropertiesStream(m_rDM_Impl.m_xDocumentStorage);
+ m_xPropertiesXMLs.insert(
+ { OUString("{6668398D-A668-4E3E-A5EB-62B293D839F1}"), // hardcoded id for extended props
+ xDomBuilder->parse(xExtPropsStream) });
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN("writerfilter",
+ "SdtHelper::loadPropertiesXMLs: failed loading extended properties XML");
+ }
+
+ // TODO: some other property items?
+
+ // Add custom XMLs
+ uno::Sequence<uno::Reference<xml::dom::XDocument>> aCustomXmls
+ = m_rDM_Impl.getDocumentReference()->getCustomXmlDomList();
+ uno::Sequence<uno::Reference<xml::dom::XDocument>> aCustomXmlProps
+ = m_rDM_Impl.getDocumentReference()->getCustomXmlDomPropsList();
+ if (aCustomXmls.getLength())
+ {
+ uno::Reference<XXPathAPI> xXpathAPI = XPathAPI::create(m_xComponentContext);
+ xXpathAPI->registerNS("ds",
+ "http://schemas.openxmlformats.org/officeDocument/2006/customXml");
+ sal_Int32 nItem = 0;
+ // Hereby we assume that items from getCustomXmlDomList() and getCustomXmlDomPropsList()
+ // are matching each other:
+ // item1.xml -> itemProps1.xml, item2.xml -> itemProps2.xml
+ // This does works practically, but is it true in general?
+ for (const auto& xDoc : aCustomXmls)
+ {
+ // Retrieve storeid from properties xml
+ OUString aStoreId;
+ uno::Reference<XXPathObject> xResult
+ = xXpathAPI->eval(aCustomXmlProps[nItem], "string(/ds:datastoreItem/@ds:itemID)");
+
+ if (xResult.is() && xResult->getString().getLength())
+ {
+ aStoreId = xResult->getString();
+ }
+ else
+ {
+ SAL_WARN("writerfilter",
+ "SdtHelper::loadPropertiesXMLs: can't fetch storeid for custom doc!");
+ }
+
+ m_xPropertiesXMLs.insert({ aStoreId, xDoc });
+ nItem++;
+ }
+ }
+
+ m_bPropertiesXMLsLoaded = true;
+}
+
+static void lcl_registerNamespaces(std::u16string_view sNamespaceString,
+ const uno::Reference<XXPathAPI>& xXPathAPI)
+{
+ // Split namespaces and register it in XPathAPI
+ auto aNamespaces = string::split(sNamespaceString, ' ');
+ for (const auto& sNamespace : aNamespaces)
+ {
+ // Here we have just one namespace in format "xmlns:ns0='http://someurl'"
+ auto aNamespace = string::split(sNamespace, '=');
+ if (aNamespace.size() < 2)
+ {
+ SAL_WARN("writerfilter",
+ "SdtHelper::getValueFromDataBinding: invalid namespace: " << sNamespace);
+ continue;
+ }
+
+ auto aNamespaceId = string::split(aNamespace[0], ':');
+ if (aNamespaceId.size() < 2)
+ {
+ SAL_WARN("writerfilter",
+ "SdtHelper::getValueFromDataBinding: invalid namespace: " << aNamespace[0]);
+ continue;
+ }
+
+ OUString sNamespaceURL = aNamespace[1];
+ sNamespaceURL = string::strip(sNamespaceURL, ' ');
+ sNamespaceURL = string::strip(sNamespaceURL, '\'');
+
+ xXPathAPI->registerNS(aNamespaceId[1], sNamespaceURL);
+ }
+}
+
+std::optional<OUString> SdtHelper::getValueFromDataBinding()
+{
+ // No xpath - nothing to do
+ if (m_sDataBindingXPath.isEmpty())
+ return {};
+
+ // Load properties XMLs
+ if (!m_bPropertiesXMLsLoaded)
+ loadPropertiesXMLs();
+
+ uno::Reference<XXPathAPI> xXpathAPI = XPathAPI::create(m_xComponentContext);
+
+ lcl_registerNamespaces(m_sDataBindingPrefixMapping, xXpathAPI);
+
+ // Find storage by store id and eval xpath there
+ const auto& aSourceIt = m_xPropertiesXMLs.find(m_sDataBindingStoreItemID);
+ if (aSourceIt != m_xPropertiesXMLs.end())
+ {
+ uno::Reference<XXPathObject> xResult
+ = xXpathAPI->eval(aSourceIt->second, m_sDataBindingXPath);
+
+ if (xResult.is() && xResult->getNodeList() && xResult->getNodeList()->getLength()
+ && xResult->getString().getLength())
+ {
+ return xResult->getString();
+ }
+ }
+
+ // Nothing found? Try to iterate storages and eval xpath
+ for (const auto& aSource : m_xPropertiesXMLs)
+ {
+ uno::Reference<XXPathObject> xResult = xXpathAPI->eval(aSource.second, m_sDataBindingXPath);
+
+ if (xResult.is() && xResult->getNodeList() && xResult->getNodeList()->getLength()
+ && xResult->getString().getLength())
+ {
+ return xResult->getString();
+ }
+ }
+
+ // No data
+ return {};
+}
+
+void SdtHelper::createDropDownControl()
+{
+ assert(getControlType() == SdtControlType::dropDown);
+
+ const bool bDropDown
+ = officecfg::Office::Writer::Filter::Import::DOCX::ImportComboBoxAsDropDown::get();
+ const OUString aDefaultText = m_aSdtTexts.makeStringAndClear();
+
+ if (bDropDown)
+ {
+ // create field
+ uno::Reference<css::text::XTextField> xControlModel(
+ m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.text.TextField.DropDown"),
+ uno::UNO_QUERY);
+
+ const auto it = std::find_if(
+ m_aDropDownItems.begin(), m_aDropDownItems.end(),
+ [aDefaultText](const OUString& item) -> bool { return !item.compareTo(aDefaultText); });
+
+ if (m_aDropDownItems.end() == it)
+ {
+ m_aDropDownItems.push_back(aDefaultText);
+ }
+
+ // set properties
+ uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("SelectedItem", uno::Any(aDefaultText));
+ xPropertySet->setPropertyValue("Items",
+ uno::Any(comphelper::containerToSequence(m_aDropDownItems)));
+
+ // add it into document
+ m_rDM_Impl.appendTextContent(xControlModel, uno::Sequence<beans::PropertyValue>());
+
+ m_bHasElements = true;
+ }
+ else
+ {
+ // create control
+ uno::Reference<awt::XControlModel> xControlModel(
+ m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.form.component.ComboBox"),
+ uno::UNO_QUERY);
+
+ // set properties
+ uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("DefaultText", uno::Any(aDefaultText));
+ xPropertySet->setPropertyValue("Dropdown", uno::Any(true));
+ xPropertySet->setPropertyValue("StringItemList",
+ uno::Any(comphelper::containerToSequence(m_aDropDownItems)));
+
+ // add it into document
+ createControlShape(
+ lcl_getOptimalWidth(m_rDM_Impl.GetStyleSheetTable(), aDefaultText, m_aDropDownItems),
+ xControlModel, uno::Sequence<beans::PropertyValue>());
+ }
+
+ // clean up
+ clear();
+}
+
+void SdtHelper::createPlainTextControl()
+{
+ assert(getControlType() == SdtControlType::plainText);
+
+ OUString aDefaultText = m_aSdtTexts.makeStringAndClear();
+
+ // create field
+ uno::Reference<css::text::XTextField> xControlModel(
+ m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.text.TextField.Input"),
+ uno::UNO_QUERY);
+
+ // set properties
+ uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
+
+ std::optional<OUString> oData = getValueFromDataBinding();
+ if (oData.has_value())
+ aDefaultText = *oData;
+
+ xPropertySet->setPropertyValue("Content", uno::Any(aDefaultText));
+
+ // add it into document
+ m_rDM_Impl.appendTextContent(xControlModel, uno::Sequence<beans::PropertyValue>());
+
+ // Store all unused sdt parameters from grabbag
+ xPropertySet->setPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG,
+ uno::Any(getInteropGrabBagAndClear()));
+
+ // clean up
+ clear();
+}
+
+void SdtHelper::createDateContentControl()
+{
+ if (!m_xDateFieldStartRange.is())
+ return;
+
+ uno::Reference<text::XTextCursor> xCrsr;
+ if (m_rDM_Impl.HasTopText())
+ {
+ uno::Reference<text::XTextAppend> xTextAppend = m_rDM_Impl.GetTopTextAppend();
+ if (xTextAppend.is())
+ {
+ xCrsr = xTextAppend->createTextCursorByRange(xTextAppend);
+ }
+ }
+ if (!xCrsr.is())
+ return;
+
+ try
+ {
+ xCrsr->gotoRange(m_xDateFieldStartRange, false);
+ bool bIsInTable = (m_rDM_Impl.hasTableManager() && m_rDM_Impl.getTableManager().isInTable())
+ || (m_rDM_Impl.m_nTableDepth > 0);
+ if (bIsInTable)
+ xCrsr->goRight(1, false);
+ xCrsr->gotoEnd(true);
+ }
+ catch (uno::Exception&)
+ {
+ OSL_ENSURE(false, "Cannot get the right text range for date field");
+ return;
+ }
+
+ uno::Reference<uno::XInterface> xFieldInterface
+ = m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.text.Fieldmark");
+ uno::Reference<text::XFormField> xFormField(xFieldInterface, uno::UNO_QUERY);
+ uno::Reference<text::XTextContent> xToInsert(xFormField, uno::UNO_QUERY);
+ if (!(xFormField.is() && xToInsert.is()))
+ return;
+
+ xToInsert->attach(uno::Reference<text::XTextRange>(xCrsr, uno::UNO_QUERY_THROW));
+ xFormField->setFieldType(ODF_FORMDATE);
+ uno::Reference<container::XNameContainer> xNameCont = xFormField->getParameters();
+ if (xNameCont.is())
+ {
+ OUString sDateFormat = m_sDateFormat.makeStringAndClear();
+
+ // Replace quotation mark used for marking static strings in date format
+ sDateFormat = sDateFormat.replaceAll("'", "\"");
+ xNameCont->insertByName(ODF_FORMDATE_DATEFORMAT, uno::Any(sDateFormat));
+ xNameCont->insertByName(ODF_FORMDATE_DATEFORMAT_LANGUAGE,
+ uno::Any(m_sLocale.makeStringAndClear()));
+ }
+ OUString sFullDate = m_sDate.makeStringAndClear();
+
+ std::optional<OUString> oData = getValueFromDataBinding();
+ if (oData.has_value())
+ sFullDate = *oData;
+
+ if (!sFullDate.isEmpty())
+ {
+ sal_Int32 nTimeSep = sFullDate.indexOf("T");
+ if (nTimeSep != -1)
+ sFullDate = sFullDate.copy(0, nTimeSep);
+ xNameCont->insertByName(ODF_FORMDATE_CURRENTDATE, uno::Any(sFullDate));
+ }
+
+ uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(m_rDM_Impl.GetTextDocument(),
+ uno::UNO_QUERY);
+ uno::Reference<util::XRefreshable> xRefreshable(xTextFieldsSupplier->getTextFields(),
+ uno::UNO_QUERY);
+ xRefreshable->refresh();
+
+ // Store all unused sdt parameters from grabbag
+ xNameCont->insertByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG,
+ uno::Any(getInteropGrabBagAndClear()));
+
+ clear();
+}
+
+void SdtHelper::createControlShape(awt::Size aSize,
+ uno::Reference<awt::XControlModel> const& xControlModel,
+ const uno::Sequence<beans::PropertyValue>& rGrabBag)
+{
+ uno::Reference<drawing::XControlShape> xControlShape(
+ m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.drawing.ControlShape"),
+ uno::UNO_QUERY);
+ xControlShape->setSize(aSize);
+ xControlShape->setControl(xControlModel);
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xControlShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("VertOrient", uno::Any(text::VertOrientation::CENTER));
+
+ if (rGrabBag.hasElements())
+ xPropertySet->setPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG, uno::Any(rGrabBag));
+
+ uno::Reference<text::XTextContent> xTextContent(xControlShape, uno::UNO_QUERY);
+ m_rDM_Impl.appendTextContent(xTextContent, uno::Sequence<beans::PropertyValue>());
+ m_bHasElements = true;
+}
+
+void SdtHelper::appendToInteropGrabBag(const beans::PropertyValue& rValue)
+{
+ m_aGrabBag.push_back(rValue);
+}
+
+uno::Sequence<beans::PropertyValue> SdtHelper::getInteropGrabBagAndClear()
+{
+ uno::Sequence<beans::PropertyValue> aRet = comphelper::containerToSequence(m_aGrabBag);
+ m_aGrabBag.clear();
+ return aRet;
+}
+
+bool SdtHelper::isInteropGrabBagEmpty() const { return m_aGrabBag.empty(); }
+
+sal_Int32 SdtHelper::getInteropGrabBagSize() const { return m_aGrabBag.size(); }
+
+bool SdtHelper::containedInInteropGrabBag(const OUString& rValueName)
+{
+ return std::any_of(
+ m_aGrabBag.begin(), m_aGrabBag.end(),
+ [&rValueName](const beans::PropertyValue& i) { return i.Name == rValueName; });
+}
+
+void SdtHelper::SetShowingPlcHdr() { m_bShowingPlcHdr = true; }
+
+bool SdtHelper::GetShowingPlcHdr() const { return m_bShowingPlcHdr; }
+
+void SdtHelper::SetChecked() { m_bChecked = true; }
+
+bool SdtHelper::GetChecked() const { return m_bChecked; }
+
+void SdtHelper::SetCheckedState(const OUString& rCheckedState) { m_aCheckedState = rCheckedState; }
+
+OUString SdtHelper::GetCheckedState() const { return m_aCheckedState; }
+
+void SdtHelper::SetUncheckedState(const OUString& rUncheckedState)
+{
+ m_aUncheckedState = rUncheckedState;
+}
+
+OUString SdtHelper::GetUncheckedState() const { return m_aUncheckedState; }
+
+void SdtHelper::clear()
+{
+ m_aDropDownItems.clear();
+ m_aDropDownDisplayTexts.clear();
+ setControlType(SdtControlType::unknown);
+ m_sDataBindingPrefixMapping.clear();
+ m_sDataBindingXPath.clear();
+ m_sDataBindingStoreItemID.clear();
+ m_aGrabBag.clear();
+ m_bShowingPlcHdr = false;
+ m_bChecked = false;
+ m_aCheckedState.clear();
+ m_aUncheckedState.clear();
+}
+
+void SdtHelper::SetPlaceholderDocPart(const OUString& rPlaceholderDocPart)
+{
+ m_aPlaceholderDocPart = rPlaceholderDocPart;
+}
+
+OUString SdtHelper::GetPlaceholderDocPart() const { return m_aPlaceholderDocPart; }
+
+void SdtHelper::SetColor(const OUString& rColor) { m_aColor = rColor; }
+
+OUString SdtHelper::GetColor() const { return m_aColor; }
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx
new file mode 100644
index 000000000..11bdfdcd1
--- /dev/null
+++ b/writerfilter/source/dmapper/SdtHelper.hxx
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vector>
+#include <optional>
+#include <unordered_map>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <tools/ref.hxx>
+
+namespace com::sun::star
+{
+namespace uno
+{
+class XComponentContext;
+}
+namespace awt
+{
+struct Size;
+class XControlModel;
+}
+}
+
+namespace writerfilter::dmapper
+{
+class DomainMapper_Impl;
+
+enum class SdtControlType
+{
+ datePicker,
+ dropDown,
+ plainText,
+ richText,
+ checkBox,
+ picture,
+ unsupported, // Sdt block is defined, but we still do not support such type of field
+ unknown
+};
+
+/**
+ * Helper to create form controls from w:sdt tokens.
+ *
+ * w:sdt tokens can't be imported as form fields, as w:sdt supports
+ * e.g. date picking as well.
+ */
+class SdtHelper final : public virtual SvRefBase
+{
+ DomainMapper_Impl& m_rDM_Impl;
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+
+ /// Items of the drop-down control: <w:listItem w:value="...">.
+ std::vector<OUString> m_aDropDownItems;
+ /// Display texts of a drop-down control: <w:listItem w:displayText="...">.
+ std::vector<OUString> m_aDropDownDisplayTexts;
+ /// Type of sdt control
+ SdtControlType m_aControlType;
+ /// Pieces of the default text -- currently used only by the dropdown control.
+ OUStringBuffer m_aSdtTexts;
+ /// Date ISO string contained in the w:date element, used by the date control.
+ OUStringBuffer m_sDate;
+ /// Date format string as it comes from the ooxml document.
+ OUStringBuffer m_sDateFormat;
+
+ /// <w:dataBinding w:prefixMappings="">
+ OUString m_sDataBindingPrefixMapping;
+ /// <w:dataBinding w:xpath="">
+ OUString m_sDataBindingXPath;
+ /// <w:dataBinding w:storeItemID="">
+ OUString m_sDataBindingStoreItemID;
+
+ /// Start range of the date field
+ css::uno::Reference<css::text::XTextRange> m_xDateFieldStartRange;
+ /// Locale string as it comes from the ooxml document.
+ OUStringBuffer m_sLocale;
+ /// Grab bag to store unsupported SDTs, aiming to save them back on export.
+ std::vector<css::beans::PropertyValue> m_aGrabBag;
+
+ bool m_bHasElements;
+ /// The last stored SDT element is outside paragraphs.
+ bool m_bOutsideAParagraph;
+
+ /// Storage for all properties documents as xml::dom::XDocument for later querying xpath for data
+ std::unordered_map<OUString, css::uno::Reference<css::xml::dom::XDocument>> m_xPropertiesXMLs;
+
+ /// Check if m_xPropertiesXMLs is initialized and loaded (need extra flag to distinguish
+ /// empty sequence from not yet initialized)
+ bool m_bPropertiesXMLsLoaded;
+
+ /// Current contents are placeholder text.
+ bool m_bShowingPlcHdr = false;
+
+ /// If this is a checkbox, is the checkbox checked?
+ bool m_bChecked = false;
+
+ /// If this is a checkbox, the value of a checked checkbox.
+ OUString m_aCheckedState;
+
+ /// If this is a checkbox, the value of an unchecked checkbox.
+ OUString m_aUncheckedState;
+
+ /// Create and append the drawing::XControlShape, containing the various models.
+ void createControlShape(css::awt::Size aSize,
+ css::uno::Reference<css::awt::XControlModel> const& xControlModel,
+ const css::uno::Sequence<css::beans::PropertyValue>& rGrabBag);
+
+ std::optional<OUString> getValueFromDataBinding();
+
+ void loadPropertiesXMLs();
+
+ /// <w:placeholder>'s <w:docPart w:val="...">.
+ OUString m_aPlaceholderDocPart;
+
+ /// <w:sdtPr>'s <w15:color w:val="...">.
+ OUString m_aColor;
+
+public:
+ explicit SdtHelper(DomainMapper_Impl& rDM_Impl,
+ css::uno::Reference<css::uno::XComponentContext> const& xContext);
+ ~SdtHelper() override;
+
+ std::vector<OUString>& getDropDownItems() { return m_aDropDownItems; }
+ std::vector<OUString>& getDropDownDisplayTexts() { return m_aDropDownDisplayTexts; }
+ OUStringBuffer& getSdtTexts() { return m_aSdtTexts; }
+
+ OUStringBuffer& getDate() { return m_sDate; }
+
+ OUStringBuffer& getDateFormat() { return m_sDateFormat; }
+
+ void setDataBindingPrefixMapping(const OUString& sValue)
+ {
+ m_sDataBindingPrefixMapping = sValue;
+ }
+ OUString GetDataBindingPrefixMapping() const { return m_sDataBindingPrefixMapping; }
+
+ void setDataBindingXPath(const OUString& sValue) { m_sDataBindingXPath = sValue; }
+ OUString GetDataBindingXPath() const { return m_sDataBindingXPath; }
+
+ void setDataBindingStoreItemID(const OUString& sValue) { m_sDataBindingStoreItemID = sValue; }
+ OUString GetDataBindingStoreItemID() const { return m_sDataBindingStoreItemID; }
+
+ void setDateFieldStartRange(const css::uno::Reference<css::text::XTextRange>& xStartRange)
+ {
+ m_xDateFieldStartRange = xStartRange;
+ }
+
+ OUStringBuffer& getLocale() { return m_sLocale; }
+ /// If createControlShape() was ever called.
+ bool hasElements() const { return m_bHasElements; }
+
+ void setOutsideAParagraph(bool bOutsideAParagraph)
+ {
+ m_bOutsideAParagraph = bOutsideAParagraph;
+ }
+
+ bool isOutsideAParagraph() const { return m_bOutsideAParagraph; }
+
+ SdtControlType getControlType() { return m_aControlType; }
+ void setControlType(SdtControlType aType) { m_aControlType = aType; }
+
+ /// Create drop-down control from w:sdt's w:dropDownList.
+ void createDropDownControl();
+ /// Create date control from w:sdt's w:date.
+ void createDateContentControl();
+
+ void createPlainTextControl();
+
+ void appendToInteropGrabBag(const css::beans::PropertyValue& rValue);
+ css::uno::Sequence<css::beans::PropertyValue> getInteropGrabBagAndClear();
+ bool isInteropGrabBagEmpty() const;
+ bool containedInInteropGrabBag(const OUString& rValueName);
+ sal_Int32 getInteropGrabBagSize() const;
+
+ void SetShowingPlcHdr();
+ bool GetShowingPlcHdr() const;
+
+ void SetChecked();
+ bool GetChecked() const;
+ void SetCheckedState(const OUString& rCheckedState);
+ OUString GetCheckedState() const;
+ void SetUncheckedState(const OUString& rUncheckedState);
+ OUString GetUncheckedState() const;
+
+ /// Clear all collected attributes for further reuse
+ void clear();
+
+ void SetPlaceholderDocPart(const OUString& rPlaceholderDocPart);
+ OUString GetPlaceholderDocPart() const;
+
+ void SetColor(const OUString& rColor);
+ OUString GetColor() const;
+};
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/SectionColumnHandler.cxx b/writerfilter/source/dmapper/SectionColumnHandler.cxx
new file mode 100644
index 000000000..9fed9c34a
--- /dev/null
+++ b/writerfilter/source/dmapper/SectionColumnHandler.cxx
@@ -0,0 +1,90 @@
+/* -*- 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 "SectionColumnHandler.hxx"
+#include "ConversionHelper.hxx"
+#include <ooxml/resourceids.hxx>
+#include <osl/diagnose.h>
+
+namespace writerfilter::dmapper
+{
+using namespace ::com::sun::star;
+
+SectionColumnHandler::SectionColumnHandler()
+ : LoggedProperties("SectionColumnHandler")
+ , m_bEqualWidth(false)
+ , m_nSpace(1270) // 720 twips
+ , m_nNum(0)
+ , m_bSep(false)
+{
+ m_aTempColumn.nWidth = m_aTempColumn.nSpace = 0;
+}
+
+SectionColumnHandler::~SectionColumnHandler() {}
+
+void SectionColumnHandler::lcl_attribute(Id rName, Value& rVal)
+{
+ sal_Int32 nIntValue = rVal.getInt();
+ switch (rName)
+ {
+ case NS_ooxml::LN_CT_Columns_equalWidth:
+ m_bEqualWidth = (nIntValue != 0);
+ break;
+ case NS_ooxml::LN_CT_Columns_space:
+ m_nSpace = ConversionHelper::convertTwipToMM100(nIntValue);
+ break;
+ case NS_ooxml::LN_CT_Columns_num:
+ m_nNum = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Columns_sep:
+ m_bSep = (nIntValue != 0);
+ break;
+
+ case NS_ooxml::LN_CT_Column_w:
+ m_aTempColumn.nWidth = ConversionHelper::convertTwipToMM100(nIntValue);
+ break;
+ case NS_ooxml::LN_CT_Column_space:
+ m_aTempColumn.nSpace = ConversionHelper::convertTwipToMM100(nIntValue);
+ break;
+ default:
+ OSL_FAIL("SectionColumnHandler: unknown attribute");
+ }
+}
+
+void SectionColumnHandler::lcl_sprm(Sprm& rSprm)
+{
+ switch (rSprm.getId())
+ {
+ case NS_ooxml::LN_CT_Columns_col:
+ {
+ m_aTempColumn.nWidth = m_aTempColumn.nSpace = 0;
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+ m_aCols.push_back(m_aTempColumn);
+ }
+ }
+ break;
+ default:
+ OSL_FAIL("SectionColumnHandler: unknown sprm");
+ }
+}
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/SectionColumnHandler.hxx b/writerfilter/source/dmapper/SectionColumnHandler.hxx
new file mode 100644
index 000000000..cbef67460
--- /dev/null
+++ b/writerfilter/source/dmapper/SectionColumnHandler.hxx
@@ -0,0 +1,62 @@
+/* -*- 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 "LoggedResources.hxx"
+#include <vector>
+
+namespace writerfilter::dmapper
+{
+struct Column_
+{
+ sal_Int32 nWidth;
+ sal_Int32 nSpace;
+};
+
+
+class SectionColumnHandler : public LoggedProperties
+{
+ bool m_bEqualWidth;
+ sal_Int32 m_nSpace;
+ sal_Int32 m_nNum;
+ bool m_bSep;
+ std::vector<Column_> m_aCols;
+
+ Column_ m_aTempColumn;
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+public:
+ SectionColumnHandler();
+ virtual ~SectionColumnHandler() override;
+
+ bool IsEqualWidth() const { return m_bEqualWidth; }
+ sal_Int32 GetSpace() const { return m_nSpace; }
+ sal_Int32 GetNum() const { return m_nNum; }
+ bool IsSeparator() const { return m_bSep; }
+
+ const std::vector<Column_>& GetColumns() const { return m_aCols;}
+
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/SettingsTable.cxx b/writerfilter/source/dmapper/SettingsTable.cxx
new file mode 100644
index 000000000..45dc67b9f
--- /dev/null
+++ b/writerfilter/source/dmapper/SettingsTable.cxx
@@ -0,0 +1,692 @@
+/* -*- 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 "SettingsTable.hxx"
+#include "DocumentProtection.hxx"
+#include "TagLogger.hxx"
+#include "WriteProtection.hxx"
+
+#include <vector>
+
+#include <rtl/ustring.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include "ConversionHelper.hxx"
+#include "DomainMapper.hxx"
+#include "util.hxx"
+
+using namespace com::sun::star;
+
+namespace writerfilter {
+namespace
+{
+/// Maps OOXML <w:zoom w:val="..."> to SvxZoomType.
+sal_Int16 lcl_GetZoomType(Id nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_Value_doc_ST_Zoom_fullPage:
+ return sal_Int16(SvxZoomType::WHOLEPAGE);
+ case NS_ooxml::LN_Value_doc_ST_Zoom_bestFit:
+ return sal_Int16(SvxZoomType::PAGEWIDTH);
+ case NS_ooxml::LN_Value_doc_ST_Zoom_textFit:
+ return sal_Int16(SvxZoomType::OPTIMAL);
+ }
+
+ return sal_Int16(SvxZoomType::PERCENT);
+}
+}
+
+namespace dmapper
+{
+
+struct SettingsTable_Impl
+{
+ int m_nDefaultTabStop;
+
+ bool m_bRecordChanges;
+ bool m_bShowInsDelChanges;
+ bool m_bShowFormattingChanges;
+ bool m_bShowMarkupChanges;
+ bool m_bLinkStyles;
+ sal_Int16 m_nZoomFactor;
+ sal_Int16 m_nZoomType = 0;
+ sal_Int32 m_nWordCompatibilityMode;
+ Id m_nView;
+ bool m_bEvenAndOddHeaders;
+ bool m_bUsePrinterMetrics;
+ bool embedTrueTypeFonts;
+ bool embedSystemFonts;
+ bool m_bDoNotUseHTMLParagraphAutoSpacing;
+ bool m_bNoColumnBalance;
+ bool m_bAutoHyphenation;
+ bool m_bNoHyphenateCaps;
+ sal_Int16 m_nHyphenationZone;
+ bool m_bWidowControl;
+ bool m_bLongerSpaceSequence;
+ bool m_bSplitPgBreakAndParaMark;
+ bool m_bMirrorMargin;
+ bool m_bDoNotExpandShiftReturn;
+ bool m_bDisplayBackgroundShape;
+ bool m_bNoLeading = false;
+ OUString m_sDecimalSymbol;
+ OUString m_sListSeparator;
+
+ uno::Sequence<beans::PropertyValue> m_pThemeFontLangProps;
+
+ std::vector<beans::PropertyValue> m_aCompatSettings;
+ uno::Sequence<beans::PropertyValue> m_pCurrentCompatSetting;
+ OUString m_sCurrentDatabaseDataSource;
+
+ std::shared_ptr<DocumentProtection> m_pDocumentProtection;
+ std::shared_ptr<WriteProtection> m_pWriteProtection;
+ bool m_bGutterAtTop = false;
+
+ SettingsTable_Impl() :
+ m_nDefaultTabStop( 720 ) //default is 1/2 in
+ , m_bRecordChanges(false)
+ , m_bShowInsDelChanges(true)
+ , m_bShowFormattingChanges(false)
+ , m_bShowMarkupChanges(true)
+ , m_bLinkStyles(false)
+ , m_nZoomFactor(0)
+ , m_nWordCompatibilityMode(-1)
+ , m_nView(0)
+ , m_bEvenAndOddHeaders(false)
+ , m_bUsePrinterMetrics(false)
+ , embedTrueTypeFonts(false)
+ , embedSystemFonts(false)
+ , m_bDoNotUseHTMLParagraphAutoSpacing(false)
+ , m_bNoColumnBalance(false)
+ , m_bAutoHyphenation(false)
+ , m_bNoHyphenateCaps(false)
+ , m_nHyphenationZone(0)
+ , m_bWidowControl(false)
+ , m_bLongerSpaceSequence(false)
+ , m_bSplitPgBreakAndParaMark(false)
+ , m_bMirrorMargin(false)
+ , m_bDoNotExpandShiftReturn(false)
+ , m_bDisplayBackgroundShape(false)
+ , m_sDecimalSymbol(".")
+ , m_sListSeparator(",")
+ , m_pThemeFontLangProps(3)
+ , m_pCurrentCompatSetting(3)
+ {}
+
+};
+
+SettingsTable::SettingsTable(const DomainMapper& rDomainMapper)
+: LoggedProperties("SettingsTable")
+, LoggedTable("SettingsTable")
+, m_pImpl( new SettingsTable_Impl )
+{
+ if (rDomainMapper.IsRTFImport())
+ {
+ // HTML paragraph auto-spacing is opt-in for RTF, opt-out for OOXML.
+ m_pImpl->m_bDoNotUseHTMLParagraphAutoSpacing = true;
+ // Longer space sequence is opt-in for RTF, and not in OOXML.
+ m_pImpl->m_bLongerSpaceSequence = true;
+ }
+ m_pImpl->m_pDocumentProtection = std::make_shared<DocumentProtection>();
+ m_pImpl->m_pWriteProtection = std::make_shared<WriteProtection>();
+}
+
+SettingsTable::~SettingsTable()
+{
+}
+
+void SettingsTable::lcl_attribute(Id nName, Value & val)
+{
+ int nIntValue = val.getInt();
+ OUString sStringValue = val.getString();
+
+ switch(nName)
+ {
+ case NS_ooxml::LN_CT_Zoom_percent:
+ m_pImpl->m_nZoomFactor = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Zoom_val:
+ m_pImpl->m_nZoomType = lcl_GetZoomType(nIntValue);
+ break;
+ case NS_ooxml::LN_CT_Language_val:
+ m_pImpl->m_pThemeFontLangProps.getArray()[0]
+ = comphelper::makePropertyValue("val", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_Language_eastAsia:
+ m_pImpl->m_pThemeFontLangProps.getArray()[1]
+ = comphelper::makePropertyValue("eastAsia", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_Language_bidi:
+ m_pImpl->m_pThemeFontLangProps.getArray()[2]
+ = comphelper::makePropertyValue("bidi", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_View_val:
+ m_pImpl->m_nView = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_CompatSetting_name:
+ m_pImpl->m_pCurrentCompatSetting.getArray()[0]
+ = comphelper::makePropertyValue("name", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_CompatSetting_uri:
+ m_pImpl->m_pCurrentCompatSetting.getArray()[1]
+ = comphelper::makePropertyValue("uri", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_CompatSetting_val:
+ m_pImpl->m_pCurrentCompatSetting.getArray()[2]
+ = comphelper::makePropertyValue("val", sStringValue);
+ break;
+ case NS_ooxml::LN_CT_TrackChangesView_insDel:
+ m_pImpl->m_bShowInsDelChanges = (nIntValue != 0);
+ break;
+ case NS_ooxml::LN_CT_TrackChangesView_formatting:
+ m_pImpl->m_bShowFormattingChanges = (nIntValue != 0);
+ break;
+ case NS_ooxml::LN_CT_TrackChangesView_markup:
+ m_pImpl->m_bShowMarkupChanges = (nIntValue != 0);
+ break;
+ default:
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ }
+}
+
+void SettingsTable::lcl_sprm(Sprm& rSprm)
+{
+ sal_uInt32 nSprmId = rSprm.getId();
+
+ Value::Pointer_t pValue = rSprm.getValue();
+ sal_Int32 nIntValue = pValue->getInt();
+
+ switch(nSprmId)
+ {
+ case NS_ooxml::LN_CT_Settings_zoom: // 92469;
+ case NS_ooxml::LN_CT_Settings_proofState: // 92489;
+ case NS_ooxml::LN_CT_Settings_attachedTemplate: // 92491;
+ case NS_ooxml::LN_CT_Settings_hdrShapeDefaults: // 92544;
+ case NS_ooxml::LN_CT_Settings_footnotePr: // 92545;
+ case NS_ooxml::LN_CT_Settings_endnotePr: // 92546;
+ case NS_ooxml::LN_CT_Settings_compat: // 92547;
+ case NS_ooxml::LN_CT_Settings_themeFontLang: // 92552;
+ case NS_ooxml::LN_CT_Settings_shapeDefaults: // 92560;
+ case NS_ooxml::LN_CT_Settings_view:
+ //PropertySetValues - need to be resolved
+ {
+ resolveSprmProps(*this, rSprm);
+ }
+ break;
+ case NS_ooxml::LN_CT_Settings_stylePaneFormatFilter: // 92493;
+ break;
+ case NS_ooxml::LN_CT_Settings_defaultTabStop: // 92505;
+ m_pImpl->m_nDefaultTabStop = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_linkStyles: // 92663;
+ m_pImpl->m_bLinkStyles = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_evenAndOddHeaders:
+ m_pImpl->m_bEvenAndOddHeaders = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_noPunctuationKerning: // 92526;
+ break;
+ case NS_ooxml::LN_CT_Settings_characterSpacingControl: // 92527;
+ // doNotCompress, compressPunctuation, compressPunctuationAndJapaneseKana
+ break;
+ case NS_ooxml::LN_CT_Settings_doNotIncludeSubdocsInStats: // 92554; // Do Not Include Content in Text Boxes, Footnotes, and Endnotes in Document Statistics)
+ break;
+ case NS_ooxml::LN_CT_Settings_decimalSymbol: // 92562;
+ m_pImpl->m_sDecimalSymbol = pValue->getString();
+ break;
+ case NS_ooxml::LN_CT_Settings_listSeparator: // 92563;
+ m_pImpl->m_sListSeparator = pValue->getString();
+ break;
+ case NS_ooxml::LN_CT_Settings_rsids: // 92549; revision save Ids - probably not necessary
+ break;
+ case NS_ooxml::LN_CT_Settings_hyphenationZone: // 92508;
+ m_pImpl->m_nHyphenationZone = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Compat_useFELayout: // 92422;
+ // useFELayout (Do Not Bypass East Asian/Complex Script Layout Code - support of old versions of Word - ignored)
+ break;
+ case NS_ooxml::LN_CT_Settings_trackRevisions:
+ {
+ m_pImpl->m_bRecordChanges = bool(rSprm.getValue( )->getInt( ) );
+ }
+ break;
+ case NS_ooxml::LN_CT_Settings_revisionView:
+ resolveSprmProps(*this, rSprm);
+ break;
+ case NS_ooxml::LN_CT_Settings_documentProtection:
+ resolveSprmProps(*(m_pImpl->m_pDocumentProtection), rSprm);
+ break;
+ case NS_ooxml::LN_CT_Settings_writeProtection:
+ resolveSprmProps(*(m_pImpl->m_pWriteProtection), rSprm);
+ break;
+ case NS_ooxml::LN_CT_Compat_usePrinterMetrics:
+ m_pImpl->m_bUsePrinterMetrics = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_embedTrueTypeFonts:
+ m_pImpl->embedTrueTypeFonts = nIntValue != 0;
+ break;
+ case NS_ooxml::LN_CT_Settings_embedSystemFonts:
+ m_pImpl->embedSystemFonts = nIntValue != 0;
+ break;
+ case NS_ooxml::LN_CT_Compat_doNotUseHTMLParagraphAutoSpacing:
+ m_pImpl->m_bDoNotUseHTMLParagraphAutoSpacing = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Compat_splitPgBreakAndParaMark:
+ m_pImpl->m_bSplitPgBreakAndParaMark = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_mirrorMargins:
+ m_pImpl->m_bMirrorMargin = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_mailMerge:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_MailMerge_query:
+ {
+ // try to get the "database.table" name from the query saved previously
+ OUString sVal = pValue->getString();
+ if ( sVal.endsWith("$") && sVal.indexOf(".dbo.") > 0 )
+ {
+ sal_Int32 nSpace = sVal.lastIndexOf(' ');
+ sal_Int32 nDbo = sVal.lastIndexOf(".dbo.");
+ if ( nSpace > 0 && nSpace < nDbo - 1 )
+ {
+ m_pImpl->m_sCurrentDatabaseDataSource = OUString::Concat(sVal.subView(nSpace + 1, nDbo - nSpace - 1)) +
+ sVal.subView(nDbo + 4, sVal.getLength() - nDbo - 5);
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Compat_compatSetting:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+
+ beans::PropertyValue aValue;
+ aValue.Name = "compatSetting";
+ aValue.Value <<= m_pImpl->m_pCurrentCompatSetting;
+ m_pImpl->m_aCompatSettings.push_back(aValue);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Compat_noColumnBalance:
+ m_pImpl->m_bNoColumnBalance = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_autoHyphenation:
+ m_pImpl->m_bAutoHyphenation = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_doNotHyphenateCaps:
+ m_pImpl->m_bNoHyphenateCaps = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_widowControl:
+ m_pImpl->m_bWidowControl = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Settings_longerSpaceSequence:
+ m_pImpl->m_bLongerSpaceSequence = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Compat_doNotExpandShiftReturn:
+ m_pImpl->m_bDoNotExpandShiftReturn = true;
+ break;
+ case NS_ooxml::LN_CT_Settings_displayBackgroundShape:
+ m_pImpl->m_bDisplayBackgroundShape = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Compat_noLeading:
+ m_pImpl->m_bNoLeading = nIntValue != 0;
+ break;
+ case NS_ooxml::LN_CT_Settings_gutterAtTop:
+ m_pImpl->m_bGutterAtTop = nIntValue != 0;
+ break;
+ default:
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ }
+}
+
+void SettingsTable::lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref)
+{
+ ref->resolve(*this);
+}
+
+//returns default TabStop in 1/100th mm
+int SettingsTable::GetDefaultTabStop() const
+{
+ return ConversionHelper::convertTwipToMM100( m_pImpl->m_nDefaultTabStop );
+}
+
+bool SettingsTable::GetLinkStyles() const
+{
+ return m_pImpl->m_bLinkStyles;
+}
+
+sal_Int16 SettingsTable::GetZoomFactor() const
+{
+ return m_pImpl->m_nZoomFactor;
+}
+
+sal_Int16 SettingsTable::GetZoomType() const { return m_pImpl->m_nZoomType; }
+
+Id SettingsTable::GetView() const
+{
+ return m_pImpl->m_nView;
+}
+
+bool SettingsTable::GetUsePrinterMetrics() const
+{
+ return m_pImpl->m_bUsePrinterMetrics;
+}
+
+bool SettingsTable::GetEvenAndOddHeaders() const
+{
+ return m_pImpl->m_bEvenAndOddHeaders;
+}
+
+bool SettingsTable::GetEmbedTrueTypeFonts() const
+{
+ return m_pImpl->embedTrueTypeFonts;
+}
+
+bool SettingsTable::GetEmbedSystemFonts() const
+{
+ return m_pImpl->embedSystemFonts;
+}
+
+bool SettingsTable::GetDoNotUseHTMLParagraphAutoSpacing() const
+{
+ return m_pImpl->m_bDoNotUseHTMLParagraphAutoSpacing;
+}
+
+bool SettingsTable::GetNoColumnBalance() const
+{
+ return m_pImpl->m_bNoColumnBalance;
+}
+
+bool SettingsTable::GetSplitPgBreakAndParaMark() const
+{
+ return m_pImpl->m_bSplitPgBreakAndParaMark;
+}
+
+bool SettingsTable::GetMirrorMarginSettings() const
+{
+ return m_pImpl->m_bMirrorMargin;
+}
+
+bool SettingsTable::GetDisplayBackgroundShape() const
+{
+ return m_pImpl->m_bDisplayBackgroundShape;
+}
+
+bool SettingsTable::GetDoNotExpandShiftReturn() const
+{
+ return m_pImpl->m_bDoNotExpandShiftReturn;
+}
+
+bool SettingsTable::GetProtectForm() const
+{
+ return m_pImpl->m_pDocumentProtection->getProtectForm()
+ && m_pImpl->m_pDocumentProtection->getEnforcement();
+}
+
+bool SettingsTable::GetReadOnly() const
+{
+ return m_pImpl->m_pWriteProtection->getRecommended()
+ || (m_pImpl->m_pDocumentProtection->getReadOnly()
+ && m_pImpl->m_pDocumentProtection->getEnforcement());
+}
+
+bool SettingsTable::GetNoHyphenateCaps() const
+{
+ return m_pImpl->m_bNoHyphenateCaps;
+}
+
+sal_Int16 SettingsTable::GetHyphenationZone() const
+{
+ return m_pImpl->m_nHyphenationZone;
+}
+
+const OUString & SettingsTable::GetDecimalSymbol() const
+{
+ return m_pImpl->m_sDecimalSymbol;
+}
+
+const OUString & SettingsTable::GetListSeparator() const
+{
+ return m_pImpl->m_sListSeparator;
+}
+
+
+uno::Sequence<beans::PropertyValue> const & SettingsTable::GetThemeFontLangProperties() const
+{
+ return m_pImpl->m_pThemeFontLangProps;
+}
+
+uno::Sequence<beans::PropertyValue> SettingsTable::GetCompatSettings() const
+{
+ if ( GetWordCompatibilityMode() == -1 )
+ {
+ // the default value for an undefined compatibilityMode is 12 (Word 2007)
+ uno::Sequence<beans::PropertyValue> aCompatSetting( comphelper::InitPropertySequence({
+ { "name", uno::Any(OUString("compatibilityMode")) },
+ { "uri", uno::Any(OUString("http://schemas.microsoft.com/office/word")) },
+ { "val", uno::Any(OUString("12")) } //12: Use word processing features specified in ECMA-376. This is the default.
+ }));
+
+ beans::PropertyValue aValue;
+ aValue.Name = "compatSetting";
+ aValue.Value <<= aCompatSetting;
+
+ m_pImpl->m_aCompatSettings.push_back(aValue);
+ }
+
+ return comphelper::containerToSequence(m_pImpl->m_aCompatSettings);
+}
+
+uno::Sequence<beans::PropertyValue> SettingsTable::GetDocumentProtectionSettings() const
+{
+ return m_pImpl->m_pDocumentProtection->toSequence();
+}
+
+uno::Sequence<beans::PropertyValue> SettingsTable::GetWriteProtectionSettings() const
+{
+ return m_pImpl->m_pWriteProtection->toSequence();
+}
+
+const OUString & SettingsTable::GetCurrentDatabaseDataSource() const
+{
+ return m_pImpl->m_sCurrentDatabaseDataSource;
+}
+
+static bool lcl_isDefault(const uno::Reference<beans::XPropertyState>& xPropertyState, const OUString& rPropertyName)
+{
+ return xPropertyState->getPropertyState(rPropertyName) == beans::PropertyState_DEFAULT_VALUE;
+}
+
+void SettingsTable::ApplyProperties(uno::Reference<text::XTextDocument> const& xDoc)
+{
+ uno::Reference< beans::XPropertySet> xDocProps( xDoc, uno::UNO_QUERY );
+ uno::Reference<lang::XMultiServiceFactory> xTextFactory(xDoc, uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xDocumentSettings(xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY_THROW);
+
+ xDocumentSettings->setPropertyValue("TableRowKeep", uno::Any(true));
+
+ if (GetWordCompatibilityMode() <= 14)
+ {
+ xDocumentSettings->setPropertyValue("MsWordCompMinLineHeightByFly", uno::Any(true));
+ xDocumentSettings->setPropertyValue("TabOverMargin", uno::Any(true));
+ }
+
+ // Show changes value
+ if (xDocProps.is())
+ {
+ bool bHideChanges = !m_pImpl->m_bShowInsDelChanges || !m_pImpl->m_bShowMarkupChanges;
+ xDocProps->setPropertyValue("ShowChanges", uno::Any( !bHideChanges || m_pImpl->m_bShowFormattingChanges ) );
+ }
+
+ // Record changes value
+ if (xDocProps.is())
+ {
+ xDocProps->setPropertyValue("RecordChanges", uno::Any( m_pImpl->m_bRecordChanges ) );
+ // Password protected Record changes
+ if (m_pImpl->m_bRecordChanges && m_pImpl->m_pDocumentProtection->getRedlineProtection()
+ && m_pImpl->m_pDocumentProtection->getEnforcement())
+ {
+ // use dummy protection key to forbid disabling of Record changes without a notice
+ // (extending the recent GrabBag support) TODO support password verification...
+ css::uno::Sequence<sal_Int8> aDummyKey { 1 };
+ xDocProps->setPropertyValue("RedlineProtectionKey", uno::Any( aDummyKey ));
+ }
+ }
+
+ // Auto hyphenation: turns on hyphenation by default, <w:suppressAutoHyphens/> may still disable it at a paragraph level.
+ // Situation is similar for RTF_WIDOWCTRL, which turns on widow / orphan control by default.
+ if (!(m_pImpl->m_bAutoHyphenation || m_pImpl->m_bNoHyphenateCaps || m_pImpl->m_bWidowControl))
+ return;
+
+ uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xDoc, uno::UNO_QUERY);
+ if (!xStyleFamiliesSupplier.is())
+ return;
+
+ uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameContainer> xParagraphStyles = xStyleFamilies->getByName("ParagraphStyles").get< uno::Reference<container::XNameContainer> >();
+ uno::Reference<style::XStyle> xDefault = xParagraphStyles->getByName("Standard").get< uno::Reference<style::XStyle> >();
+ uno::Reference<beans::XPropertyState> xPropertyState(xDefault, uno::UNO_QUERY);
+ if (m_pImpl->m_bAutoHyphenation && lcl_isDefault(xPropertyState, "ParaIsHyphenation"))
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xDefault, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("ParaIsHyphenation", uno::Any(true));
+ }
+ if (m_pImpl->m_bNoHyphenateCaps)
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xDefault, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("ParaHyphenationNoCaps", uno::Any(true));
+ }
+ if (m_pImpl->m_nHyphenationZone)
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xDefault, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("ParaHyphenationZone", uno::Any(GetHyphenationZone()));
+ }
+ if (m_pImpl->m_bWidowControl && lcl_isDefault(xPropertyState, "ParaWidows") && lcl_isDefault(xPropertyState, "ParaOrphans"))
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xDefault, uno::UNO_QUERY);
+ uno::Any aAny(static_cast<sal_Int8>(2));
+ xPropertySet->setPropertyValue("ParaWidows", aAny);
+ xPropertySet->setPropertyValue("ParaOrphans", aAny);
+ }
+}
+
+bool SettingsTable::GetCompatSettingValue( std::u16string_view sCompatName ) const
+{
+ bool bRet = false;
+ for (const auto& rProp : m_pImpl->m_aCompatSettings)
+ {
+ if (rProp.Name == "compatSetting") //always true
+ {
+ css::uno::Sequence<css::beans::PropertyValue> aCurrentCompatSettings;
+ rProp.Value >>= aCurrentCompatSettings;
+
+ OUString sName;
+ aCurrentCompatSettings[0].Value >>= sName;
+ if ( sName != sCompatName )
+ continue;
+
+ OUString sUri;
+ aCurrentCompatSettings[1].Value >>= sUri;
+ if ( sUri != "http://schemas.microsoft.com/office/word" )
+ continue;
+
+ OUString sVal;
+ aCurrentCompatSettings[2].Value >>= sVal;
+ // if repeated, what happens? Last one wins
+ bRet = sVal.toBoolean();
+ }
+ }
+
+ return bRet;
+}
+
+//Keep this function in-sync with the one in sw/.../docxattributeoutput.cxx
+sal_Int32 SettingsTable::GetWordCompatibilityMode() const
+{
+ if ( m_pImpl->m_nWordCompatibilityMode != -1 )
+ return m_pImpl->m_nWordCompatibilityMode;
+
+ for (const auto& rProp : m_pImpl->m_aCompatSettings)
+ {
+ if (rProp.Name == "compatSetting") //always true
+ {
+ css::uno::Sequence<css::beans::PropertyValue> aCurrentCompatSettings;
+ rProp.Value >>= aCurrentCompatSettings;
+
+ OUString sName;
+ aCurrentCompatSettings[0].Value >>= sName;
+ if ( sName != "compatibilityMode" )
+ continue;
+
+ OUString sUri;
+ aCurrentCompatSettings[1].Value >>= sUri;
+ if ( sUri != "http://schemas.microsoft.com/office/word" )
+ continue;
+
+ OUString sVal;
+ aCurrentCompatSettings[2].Value >>= sVal;
+ const sal_Int32 nValidMode = sVal.toInt32();
+ // if repeated, highest mode wins in MS Word. 11 is the first valid mode.
+ if ( nValidMode > 10 && nValidMode > m_pImpl->m_nWordCompatibilityMode )
+ m_pImpl->m_nWordCompatibilityMode = nValidMode;
+ }
+ }
+
+ return m_pImpl->m_nWordCompatibilityMode;
+}
+
+bool SettingsTable::GetLongerSpaceSequence() const
+{
+ return m_pImpl->m_bLongerSpaceSequence;
+}
+
+bool SettingsTable::GetNoLeading() const
+{
+ return m_pImpl->m_bNoLeading;
+}
+
+bool SettingsTable::GetGutterAtTop() const { return m_pImpl->m_bGutterAtTop; }
+
+}//namespace dmapper
+} //namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/SettingsTable.hxx b/writerfilter/source/dmapper/SettingsTable.hxx
new file mode 100644
index 000000000..a0af31bed
--- /dev/null
+++ b/writerfilter/source/dmapper/SettingsTable.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "LoggedResources.hxx"
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <memory>
+
+namespace com::sun::star::lang
+{
+class XMultiServiceFactory;
+struct Locale;
+}
+
+namespace writerfilter::dmapper
+{
+class DomainMapper;
+
+struct SettingsTable_Impl;
+
+class SettingsTable : public LoggedProperties, public LoggedTable
+{
+ std::unique_ptr<SettingsTable_Impl> m_pImpl;
+
+public:
+ SettingsTable(const DomainMapper& rDomainMapper);
+ virtual ~SettingsTable() override;
+
+ //returns default TabStop in 1/100th mm
+ int GetDefaultTabStop() const;
+
+ /// Automatically update styles from document template?
+ bool GetLinkStyles() const;
+
+ /// What's the zoom factor set in percents?
+ sal_Int16 GetZoomFactor() const;
+
+ /// Gets the type of the zoom.
+ sal_Int16 GetZoomType() const;
+
+ /// What's the requested view? E.g. "web".
+ Id GetView() const;
+
+ bool GetEvenAndOddHeaders() const;
+
+ bool GetUsePrinterMetrics() const;
+
+ bool GetEmbedTrueTypeFonts() const;
+ bool GetEmbedSystemFonts() const;
+
+ bool GetDoNotUseHTMLParagraphAutoSpacing() const;
+ bool GetSplitPgBreakAndParaMark() const;
+ bool GetMirrorMarginSettings() const;
+ bool GetDisplayBackgroundShape() const;
+ bool GetDoNotExpandShiftReturn() const;
+ bool GetNoColumnBalance() const;
+ bool GetProtectForm() const;
+ bool GetReadOnly() const;
+ bool GetLongerSpaceSequence() const;
+ bool GetNoLeading() const;
+ bool GetNoHyphenateCaps() const;
+ sal_Int16 GetHyphenationZone() const;
+
+ const OUString& GetDecimalSymbol() const;
+ const OUString& GetListSeparator() const;
+
+ css::uno::Sequence<css::beans::PropertyValue> const& GetThemeFontLangProperties() const;
+
+ css::uno::Sequence<css::beans::PropertyValue> GetCompatSettings() const;
+
+ css::uno::Sequence<css::beans::PropertyValue> GetDocumentProtectionSettings() const;
+
+ css::uno::Sequence<css::beans::PropertyValue> GetWriteProtectionSettings() const;
+
+ void ApplyProperties(css::uno::Reference<css::text::XTextDocument> const& xDoc);
+
+ bool GetCompatSettingValue(std::u16string_view sCompatName) const;
+ sal_Int32 GetWordCompatibilityMode() const;
+
+ const OUString& GetCurrentDatabaseDataSource() const;
+ bool GetGutterAtTop() const;
+
+private:
+ // Properties
+ virtual void lcl_attribute(Id Name, Value& val) override;
+ virtual void lcl_sprm(Sprm& sprm) override;
+
+ // Table
+ virtual void lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref) override;
+};
+typedef tools::SvRef<SettingsTable> SettingsTablePtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/SmartTagHandler.cxx b/writerfilter/source/dmapper/SmartTagHandler.cxx
new file mode 100644
index 000000000..c92fa7c44
--- /dev/null
+++ b/writerfilter/source/dmapper/SmartTagHandler.cxx
@@ -0,0 +1,127 @@
+/* -*- 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 "SmartTagHandler.hxx"
+
+#include <com/sun/star/rdf/Literal.hpp>
+#include <com/sun/star/rdf/URI.hpp>
+#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+
+#include <ooxml/resourceids.hxx>
+
+#include <sal/log.hxx>
+
+namespace
+{
+OUString lcl_getTypePath(OUString& rType)
+{
+ OUString aRet;
+ if (rType.startsWith("urn:bails"))
+ {
+ rType = "urn:bails";
+ aRet = "tscp/bails.rdf";
+ }
+ return aRet;
+}
+}
+
+namespace writerfilter::dmapper
+{
+using namespace ::com::sun::star;
+
+SmartTagHandler::SmartTagHandler(uno::Reference<uno::XComponentContext> xComponentContext,
+ const uno::Reference<text::XTextDocument>& xTextDocument)
+ : LoggedProperties("SmartTagHandler")
+ , m_xComponentContext(std::move(xComponentContext))
+ , m_xDocumentMetadataAccess(xTextDocument, uno::UNO_QUERY)
+{
+}
+
+SmartTagHandler::~SmartTagHandler() = default;
+
+void SmartTagHandler::lcl_attribute(Id nId, Value& rValue)
+{
+ switch (nId)
+ {
+ case NS_ooxml::LN_CT_Attr_name:
+ m_aAttributes.emplace_back(rValue.getString(), OUString());
+ break;
+ case NS_ooxml::LN_CT_Attr_val:
+ if (!m_aAttributes.empty())
+ m_aAttributes.back().second = rValue.getString();
+ break;
+ default:
+ SAL_WARN("writerfilter", "SmartTagHandler::lcl_attribute: unhandled attribute "
+ << nId << " (string value: '" << rValue.getString()
+ << "')");
+ break;
+ }
+}
+
+void SmartTagHandler::lcl_sprm(Sprm& rSprm)
+{
+ switch (rSprm.getId())
+ {
+ case NS_ooxml::LN_CT_SmartTagPr_attr:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(*this);
+ break;
+ }
+ }
+}
+
+void SmartTagHandler::setURI(const OUString& rURI) { m_aURI = rURI; }
+
+void SmartTagHandler::setElement(const OUString& rElement) { m_aElement = rElement; }
+
+void SmartTagHandler::handle(const uno::Reference<text::XTextRange>& xParagraph)
+{
+ if (m_aURI.isEmpty() || m_aElement.isEmpty() || m_aAttributes.empty())
+ return;
+
+ uno::Reference<rdf::XResource> xSubject(xParagraph, uno::UNO_QUERY);
+
+ for (const std::pair<OUString, OUString>& rAttribute : m_aAttributes)
+ {
+ OUString aTypeNS = rAttribute.first;
+ OUString aMetadataFilePath = lcl_getTypePath(aTypeNS);
+ if (aMetadataFilePath.isEmpty())
+ continue;
+
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(m_xComponentContext, aTypeNS);
+ uno::Sequence<uno::Reference<rdf::XURI>> aGraphNames
+ = m_xDocumentMetadataAccess->getMetadataGraphsWithType(xType);
+ uno::Reference<rdf::XURI> xGraphName;
+ if (aGraphNames.hasElements())
+ xGraphName = aGraphNames[0];
+ else
+ {
+ uno::Sequence<uno::Reference<rdf::XURI>> xTypes = { xType };
+ xGraphName = m_xDocumentMetadataAccess->addMetadataFile(aMetadataFilePath, xTypes);
+ }
+ uno::Reference<rdf::XNamedGraph> xGraph
+ = m_xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(m_xComponentContext, rAttribute.first);
+ uno::Reference<rdf::XLiteral> xValue
+ = rdf::Literal::create(m_xComponentContext, rAttribute.second);
+ xGraph->addStatement(xSubject, xKey, xValue);
+ }
+
+ m_aURI.clear();
+ m_aElement.clear();
+ m_aAttributes.clear();
+}
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/SmartTagHandler.hxx b/writerfilter/source/dmapper/SmartTagHandler.hxx
new file mode 100644
index 000000000..7999b9dcc
--- /dev/null
+++ b/writerfilter/source/dmapper/SmartTagHandler.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <vector>
+
+#include "LoggedResources.hxx"
+
+namespace com::sun::star
+{
+namespace rdf
+{
+class XDocumentMetadataAccess;
+}
+namespace text
+{
+class XTextDocument;
+class XTextRange;
+}
+namespace uno
+{
+class XComponentContext;
+}
+}
+
+namespace writerfilter::dmapper
+{
+/// Handler for smart tags, i.e. <w:smartTag> and below.
+class SmartTagHandler : public LoggedProperties
+{
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+ css::uno::Reference<css::rdf::XDocumentMetadataAccess> m_xDocumentMetadataAccess;
+ OUString m_aURI;
+ OUString m_aElement;
+ std::vector<std::pair<OUString, OUString>> m_aAttributes;
+
+public:
+ SmartTagHandler(css::uno::Reference<css::uno::XComponentContext> xComponentContext,
+ const css::uno::Reference<css::text::XTextDocument>& xTextDocument);
+ ~SmartTagHandler() override;
+
+ void lcl_attribute(Id nId, Value& rValue) override;
+ void lcl_sprm(Sprm& rSprm) override;
+
+ void setURI(const OUString& rURI);
+ void setElement(const OUString& rElement);
+
+ /// Set m_aAttributes as RDF statements on xParagraph.
+ void handle(const css::uno::Reference<css::text::XTextRange>& xParagraph);
+};
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/StyleSheetTable.cxx b/writerfilter/source/dmapper/StyleSheetTable.cxx
new file mode 100644
index 000000000..3a6d540b7
--- /dev/null
+++ b/writerfilter/source/dmapper/StyleSheetTable.cxx
@@ -0,0 +1,1707 @@
+/* -*- 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 "StyleSheetTable.hxx"
+#include "util.hxx"
+#include "ConversionHelper.hxx"
+#include "TblStylePrHandler.hxx"
+#include "TagLogger.hxx"
+#include "BorderHandler.hxx"
+#include "LatentStyleHandler.hxx"
+#include <ooxml/resourceids.hxx>
+#include <vector>
+#include <iterator>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <map>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+#include <o3tl/sorted_vector.hxx>
+
+using namespace ::com::sun::star;
+
+namespace writerfilter::dmapper
+{
+
+StyleSheetEntry::StyleSheetEntry() :
+ bIsDefaultStyle(false)
+ ,bAssignedAsChapterNumbering(false)
+ ,bInvalidHeight(false)
+ ,bHasUPE(false)
+ ,nStyleTypeCode(STYLE_TYPE_UNKNOWN)
+ ,pProperties(new StyleSheetPropertyMap)
+ ,bAutoRedefine(false)
+{
+}
+
+StyleSheetEntry::~StyleSheetEntry()
+{
+}
+
+TableStyleSheetEntry::TableStyleSheetEntry( StyleSheetEntry const & rEntry )
+{
+ bIsDefaultStyle = rEntry.bIsDefaultStyle;
+ bInvalidHeight = rEntry.bInvalidHeight;
+ bHasUPE = rEntry.bHasUPE;
+ nStyleTypeCode = STYLE_TYPE_TABLE;
+ sBaseStyleIdentifier = rEntry.sBaseStyleIdentifier;
+ sNextStyleIdentifier = rEntry.sNextStyleIdentifier;
+ sLinkStyleIdentifier = rEntry.sLinkStyleIdentifier;
+ sStyleName = rEntry.sStyleName;
+ sStyleIdentifierD = rEntry.sStyleIdentifierD;
+}
+
+TableStyleSheetEntry::~TableStyleSheetEntry( )
+{
+}
+
+void TableStyleSheetEntry::AddTblStylePr( TblStyleType nType, const PropertyMapPtr& pProps )
+{
+ static const int nTypesProps = 4;
+ static const TblStyleType pTypesToFix[nTypesProps] =
+ {
+ TBL_STYLE_FIRSTROW,
+ TBL_STYLE_LASTROW,
+ TBL_STYLE_FIRSTCOL,
+ TBL_STYLE_LASTCOL
+ };
+
+ static const PropertyIds pPropsToCheck[nTypesProps] =
+ {
+ PROP_BOTTOM_BORDER,
+ PROP_TOP_BORDER,
+ PROP_RIGHT_BORDER,
+ PROP_LEFT_BORDER
+ };
+
+ for (int i=0; i < nTypesProps; ++i )
+ {
+ if ( nType == pTypesToFix[i] )
+ {
+ PropertyIds nChecked = pPropsToCheck[i];
+ std::optional<PropertyMap::Property> pChecked = pProps->getProperty(nChecked);
+
+ PropertyIds nInsideProp = ( i < 2 ) ? META_PROP_HORIZONTAL_BORDER : META_PROP_VERTICAL_BORDER;
+ std::optional<PropertyMap::Property> pInside = pProps->getProperty(nInsideProp);
+
+ if ( pChecked && pInside )
+ {
+ // In this case, remove the inside border
+ pProps->Erase( nInsideProp );
+ }
+
+ break;
+ }
+ }
+
+ // Append the tblStylePr
+ m_aStyles[nType] = pProps;
+}
+
+PropertyMapPtr TableStyleSheetEntry::GetProperties( sal_Int32 nMask )
+{
+ PropertyMapPtr pProps( new PropertyMap );
+
+ // And finally get the mask ones
+ pProps->InsertProps(GetLocalPropertiesFromMask(nMask));
+
+ return pProps;
+}
+
+beans::PropertyValues StyleSheetEntry::GetInteropGrabBagSeq() const
+{
+ return comphelper::containerToSequence(m_aInteropGrabBag);
+}
+
+beans::PropertyValue StyleSheetEntry::GetInteropGrabBag()
+{
+ beans::PropertyValue aRet;
+ aRet.Name = sStyleIdentifierD;
+
+ beans::PropertyValues aSeq = GetInteropGrabBagSeq();
+ aRet.Value <<= aSeq;
+ return aRet;
+}
+
+void StyleSheetEntry::AppendInteropGrabBag(const beans::PropertyValue& rValue)
+{
+ m_aInteropGrabBag.push_back(rValue);
+}
+
+PropertyMapPtr StyleSheetEntry::GetMergedInheritedProperties(const StyleSheetTablePtr& pStyleSheetTable)
+{
+ PropertyMapPtr pRet;
+ if ( pStyleSheetTable && !sBaseStyleIdentifier.isEmpty() && sBaseStyleIdentifier != sStyleIdentifierD )
+ {
+ const StyleSheetEntryPtr pParentStyleSheet = pStyleSheetTable->FindStyleSheetByISTD(sBaseStyleIdentifier);
+ if ( pParentStyleSheet )
+ pRet = pParentStyleSheet->GetMergedInheritedProperties(pStyleSheetTable);
+ }
+
+ if ( !pRet )
+ pRet = new PropertyMap;
+
+ pRet->InsertProps(pProperties.get());
+
+ return pRet;
+}
+
+static void lcl_mergeProps( const PropertyMapPtr& pToFill, const PropertyMapPtr& pToAdd, TblStyleType nStyleId )
+{
+ static const PropertyIds pPropsToCheck[] =
+ {
+ PROP_BOTTOM_BORDER,
+ PROP_TOP_BORDER,
+ PROP_RIGHT_BORDER,
+ PROP_LEFT_BORDER,
+ };
+
+ bool pRemoveInside[] =
+ {
+ ( nStyleId == TBL_STYLE_FIRSTROW ),
+ ( nStyleId == TBL_STYLE_LASTROW ),
+ ( nStyleId == TBL_STYLE_LASTCOL ),
+ ( nStyleId == TBL_STYLE_FIRSTCOL )
+ };
+
+ for ( unsigned i = 0 ; i != SAL_N_ELEMENTS(pPropsToCheck); i++ )
+ {
+ PropertyIds nId = pPropsToCheck[i];
+ std::optional<PropertyMap::Property> pProp = pToAdd->getProperty(nId);
+
+ if ( pProp )
+ {
+ if ( pRemoveInside[i] )
+ {
+ // Remove the insideH and insideV depending on the cell pos
+ PropertyIds nInsideProp = ( i < 2 ) ? META_PROP_HORIZONTAL_BORDER : META_PROP_VERTICAL_BORDER;
+ pToFill->Erase(nInsideProp);
+ }
+ }
+ }
+
+ pToFill->InsertProps(pToAdd);
+}
+
+PropertyMapPtr TableStyleSheetEntry::GetLocalPropertiesFromMask( sal_Int32 nMask )
+{
+ // Order from right to left
+ struct TblStyleTypeAndMask {
+ sal_Int32 mask;
+ TblStyleType type;
+ };
+
+ static const TblStyleTypeAndMask aOrderedStyleTable[] =
+ {
+ { 0x010, TBL_STYLE_BAND2HORZ },
+ { 0x020, TBL_STYLE_BAND1HORZ },
+ { 0x040, TBL_STYLE_BAND2VERT },
+ { 0x080, TBL_STYLE_BAND1VERT },
+ { 0x100, TBL_STYLE_LASTCOL },
+ { 0x200, TBL_STYLE_FIRSTCOL },
+ { 0x400, TBL_STYLE_LASTROW },
+ { 0x800, TBL_STYLE_FIRSTROW },
+ { 0x001, TBL_STYLE_SWCELL },
+ { 0x002, TBL_STYLE_SECELL },
+ { 0x004, TBL_STYLE_NWCELL },
+ { 0x008, TBL_STYLE_NECELL }
+ };
+
+ // Get the properties applying according to the mask
+ PropertyMapPtr pProps( new PropertyMap( ) );
+ for (const TblStyleTypeAndMask & i : aOrderedStyleTable)
+ {
+ TblStylePrs::iterator pIt = m_aStyles.find( i.type );
+ if ( ( nMask & i.mask ) && ( pIt != m_aStyles.end( ) ) )
+ lcl_mergeProps( pProps, pIt->second, i.type );
+ }
+ return pProps;
+}
+
+namespace {
+
+struct ListCharStylePropertyMap_t
+{
+ OUString sCharStyleName;
+ PropertyValueVector_t aPropertyValues;
+
+ ListCharStylePropertyMap_t(const OUString& rCharStyleName, PropertyValueVector_t&& rPropertyValues):
+ sCharStyleName( rCharStyleName ),
+ aPropertyValues( std::move(rPropertyValues) )
+ {}
+};
+
+}
+
+struct StyleSheetTable_Impl
+{
+ DomainMapper& m_rDMapper;
+ uno::Reference< text::XTextDocument> m_xTextDocument;
+ uno::Reference< beans::XPropertySet> m_xTextDefaults;
+ std::vector< StyleSheetEntryPtr > m_aStyleSheetEntries;
+ std::map< OUString, StyleSheetEntryPtr > m_aStyleSheetEntriesMap;
+ StyleSheetEntryPtr m_pCurrentEntry;
+ PropertyMapPtr m_pDefaultParaProps, m_pDefaultCharProps;
+ OUString m_sDefaultParaStyleName; //WW8 name
+ std::vector< ListCharStylePropertyMap_t > m_aListCharStylePropertyVector;
+ bool m_bHasImportedDefaultParaProps;
+ bool m_bIsNewDoc;
+
+ StyleSheetTable_Impl(DomainMapper& rDMapper, uno::Reference< text::XTextDocument> const& xTextDocument, bool bIsNewDoc);
+
+ OUString HasListCharStyle( const PropertyValueVector_t& rCharProperties );
+
+ /// Appends the given key-value pair to the list of latent style properties of the current entry.
+ void AppendLatentStyleProperty(const OUString& aName, Value const & rValue);
+ /// Sets all properties of xStyle back to default.
+ static void SetPropertiesToDefault(const uno::Reference<style::XStyle>& xStyle);
+};
+
+
+StyleSheetTable_Impl::StyleSheetTable_Impl(DomainMapper& rDMapper,
+ uno::Reference< text::XTextDocument> const& xTextDocument,
+ bool const bIsNewDoc)
+ : m_rDMapper( rDMapper ),
+ m_xTextDocument( xTextDocument ),
+ m_pDefaultParaProps(new PropertyMap),
+ m_pDefaultCharProps(new PropertyMap),
+ m_sDefaultParaStyleName("Normal"),
+ m_bHasImportedDefaultParaProps(false),
+ m_bIsNewDoc(bIsNewDoc)
+{
+ //set font height default to 10pt
+ uno::Any aVal( 10.0 );
+ m_pDefaultCharProps->Insert( PROP_CHAR_HEIGHT, aVal );
+ m_pDefaultCharProps->Insert( PROP_CHAR_HEIGHT_ASIAN, aVal );
+ m_pDefaultCharProps->Insert( PROP_CHAR_HEIGHT_COMPLEX, aVal );
+
+ // See SwDoc::RemoveAllFormatLanguageDependencies(), internal filters
+ // disable kerning by default, do the same here.
+ m_pDefaultCharProps->Insert(PROP_CHAR_AUTO_KERNING, uno::Any(false));
+}
+
+
+OUString StyleSheetTable_Impl::HasListCharStyle( const PropertyValueVector_t& rPropValues )
+{
+ for( const auto& rListVector : m_aListCharStylePropertyVector )
+ {
+ const auto& rPropertyValues = rListVector.aPropertyValues;
+ //if size is identical
+ if( rPropertyValues.size() == rPropValues.size() )
+ {
+ bool bBreak = false;
+ //then search for all contained properties
+ for( const auto& rPropVal1 : rPropValues)
+ {
+ //find the property
+ auto aListIter = std::find_if(rPropertyValues.begin(), rPropertyValues.end(),
+ [&rPropVal1](const css::beans::PropertyValue& rPropVal2) { return rPropVal2.Name == rPropVal1.Name; });
+ //set break flag if property hasn't been found
+ bBreak = (aListIter == rPropertyValues.end()) || (aListIter->Value != rPropVal1.Value);
+ if( bBreak )
+ break;
+ }
+ if( !bBreak )
+ return rListVector.sCharStyleName;
+ }
+ }
+ return OUString();
+}
+
+void StyleSheetTable_Impl::AppendLatentStyleProperty(const OUString& aName, Value const & rValue)
+{
+ beans::PropertyValue aValue;
+ aValue.Name = aName;
+ aValue.Value <<= rValue.getString();
+ m_pCurrentEntry->aLatentStyles.push_back(aValue);
+}
+
+void StyleSheetTable_Impl::SetPropertiesToDefault(const uno::Reference<style::XStyle>& xStyle)
+{
+ // See if the existing style has any non-default properties. If so, reset them back to default.
+ uno::Reference<beans::XPropertySet> xPropertySet(xStyle, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
+ const uno::Sequence<beans::Property> aProperties = xPropertySetInfo->getProperties();
+ std::vector<OUString> aPropertyNames;
+ aPropertyNames.reserve(aProperties.getLength());
+ std::transform(aProperties.begin(), aProperties.end(), std::back_inserter(aPropertyNames),
+ [](const beans::Property& rProp) { return rProp.Name; });
+
+ uno::Reference<beans::XPropertyState> xPropertyState(xStyle, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyState> aStates = xPropertyState->getPropertyStates(comphelper::containerToSequence(aPropertyNames));
+ for (sal_Int32 i = 0; i < aStates.getLength(); ++i)
+ {
+ if (aStates[i] == beans::PropertyState_DIRECT_VALUE)
+ {
+ try
+ {
+ xPropertyState->setPropertyToDefault(aPropertyNames[i]);
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_INFO_EXCEPTION("writerfilter", "setPropertyToDefault(" << aPropertyNames[i] << ") failed");
+ }
+ }
+ }
+}
+
+StyleSheetTable::StyleSheetTable(DomainMapper& rDMapper,
+ uno::Reference< text::XTextDocument> const& xTextDocument,
+ bool const bIsNewDoc)
+: LoggedProperties("StyleSheetTable")
+, LoggedTable("StyleSheetTable")
+, m_pImpl( new StyleSheetTable_Impl(rDMapper, xTextDocument, bIsNewDoc) )
+{
+}
+
+
+StyleSheetTable::~StyleSheetTable()
+{
+}
+
+void StyleSheetTable::SetDefaultParaProps(PropertyIds eId, const css::uno::Any& rAny)
+{
+ m_pImpl->m_pDefaultParaProps->Insert(eId, rAny, /*bOverwrite=*/false, NO_GRAB_BAG, /*bDocDefault=*/true);
+}
+
+PropertyMapPtr const & StyleSheetTable::GetDefaultParaProps() const
+{
+ return m_pImpl->m_pDefaultParaProps;
+}
+
+PropertyMapPtr const & StyleSheetTable::GetDefaultCharProps() const
+{
+ return m_pImpl->m_pDefaultCharProps;
+}
+
+void StyleSheetTable::lcl_attribute(Id Name, Value & val)
+{
+ OSL_ENSURE( m_pImpl->m_pCurrentEntry, "current entry has to be set here");
+ if(!m_pImpl->m_pCurrentEntry)
+ return ;
+ int nIntValue = val.getInt();
+ OUString sValue = val.getString();
+
+ // The default type is paragraph, and it needs to be processed first,
+ // because the NS_ooxml::LN_CT_Style_type handling may set m_pImpl->m_pCurrentEntry
+ // to point to a different object.
+ if( m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_UNKNOWN )
+ {
+ if( Name != NS_ooxml::LN_CT_Style_type )
+ m_pImpl->m_pCurrentEntry->nStyleTypeCode = STYLE_TYPE_PARA;
+ }
+ switch(Name)
+ {
+ case NS_ooxml::LN_CT_Style_type:
+ {
+ SAL_WARN_IF( m_pImpl->m_pCurrentEntry->nStyleTypeCode != STYLE_TYPE_UNKNOWN,
+ "writerfilter", "Style type needs to be processed first" );
+ StyleType nType(STYLE_TYPE_UNKNOWN);
+ switch (nIntValue)
+ {
+ case NS_ooxml::LN_Value_ST_StyleType_paragraph:
+ nType = STYLE_TYPE_PARA;
+ break;
+ case NS_ooxml::LN_Value_ST_StyleType_character:
+ nType = STYLE_TYPE_CHAR;
+ break;
+ case NS_ooxml::LN_Value_ST_StyleType_table:
+ nType = STYLE_TYPE_TABLE;
+ break;
+ case NS_ooxml::LN_Value_ST_StyleType_numbering:
+ nType = STYLE_TYPE_LIST;
+ break;
+ default:
+ SAL_WARN("writerfilter", "unknown LN_CT_Style_type " << nType);
+ [[fallthrough]];
+ case 0: // explicit unknown set by tokenizer
+ break;
+
+ }
+ if ( nType == STYLE_TYPE_TABLE )
+ {
+ StyleSheetEntryPtr pEntry = m_pImpl->m_pCurrentEntry;
+ tools::SvRef<TableStyleSheetEntry> pTableEntry( new TableStyleSheetEntry( *pEntry ) );
+ m_pImpl->m_pCurrentEntry = pTableEntry.get();
+ }
+ else
+ m_pImpl->m_pCurrentEntry->nStyleTypeCode = nType;
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_default:
+ m_pImpl->m_pCurrentEntry->bIsDefaultStyle = (nIntValue != 0);
+
+ if (m_pImpl->m_pCurrentEntry->nStyleTypeCode != STYLE_TYPE_UNKNOWN)
+ {
+ // "If this attribute is specified by multiple styles, then the last instance shall be used."
+ if (m_pImpl->m_pCurrentEntry->bIsDefaultStyle
+ && m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_PARA
+ && !m_pImpl->m_pCurrentEntry->sStyleIdentifierD.isEmpty())
+ {
+ m_pImpl->m_sDefaultParaStyleName = m_pImpl->m_pCurrentEntry->sStyleIdentifierD;
+ }
+
+ beans::PropertyValue aValue;
+ aValue.Name = "default";
+ aValue.Value <<= m_pImpl->m_pCurrentEntry->bIsDefaultStyle;
+ m_pImpl->m_pCurrentEntry->AppendInteropGrabBag(aValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_customStyle:
+ if (m_pImpl->m_pCurrentEntry->nStyleTypeCode != STYLE_TYPE_UNKNOWN)
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "customStyle";
+ aValue.Value <<= (nIntValue != 0);
+ m_pImpl->m_pCurrentEntry->AppendInteropGrabBag(aValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_styleId:
+ m_pImpl->m_pCurrentEntry->sStyleIdentifierD = sValue;
+ if(m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_TABLE)
+ {
+ TableStyleSheetEntry* pTableEntry = static_cast<TableStyleSheetEntry *>(m_pImpl->m_pCurrentEntry.get());
+ beans::PropertyValue aValue;
+ aValue.Name = "styleId";
+ aValue.Value <<= sValue;
+ pTableEntry->AppendInteropGrabBag(aValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_TblWidth_w:
+ break;
+ case NS_ooxml::LN_CT_TblWidth_type:
+ break;
+ case NS_ooxml::LN_CT_LatentStyles_defQFormat:
+ m_pImpl->AppendLatentStyleProperty("defQFormat", val);
+ break;
+ case NS_ooxml::LN_CT_LatentStyles_defUnhideWhenUsed:
+ m_pImpl->AppendLatentStyleProperty("defUnhideWhenUsed", val);
+ break;
+ case NS_ooxml::LN_CT_LatentStyles_defSemiHidden:
+ m_pImpl->AppendLatentStyleProperty("defSemiHidden", val);
+ break;
+ case NS_ooxml::LN_CT_LatentStyles_count:
+ m_pImpl->AppendLatentStyleProperty("count", val);
+ break;
+ case NS_ooxml::LN_CT_LatentStyles_defUIPriority:
+ m_pImpl->AppendLatentStyleProperty("defUIPriority", val);
+ break;
+ case NS_ooxml::LN_CT_LatentStyles_defLockedState:
+ m_pImpl->AppendLatentStyleProperty("defLockedState", val);
+ break;
+ default:
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ break;
+ }
+}
+
+
+void StyleSheetTable::lcl_sprm(Sprm & rSprm)
+{
+ sal_uInt32 nSprmId = rSprm.getId();
+ Value::Pointer_t pValue = rSprm.getValue();
+ sal_Int32 nIntValue = pValue ? pValue->getInt() : 0;
+ OUString sStringValue = pValue ? pValue->getString() : OUString();
+
+ switch(nSprmId)
+ {
+ case NS_ooxml::LN_CT_Style_name:
+ //this is only a UI name!
+ m_pImpl->m_pCurrentEntry->sStyleName = sStringValue;
+ if(m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_TABLE)
+ {
+ TableStyleSheetEntry* pTableEntry = static_cast<TableStyleSheetEntry *>(m_pImpl->m_pCurrentEntry.get());
+ beans::PropertyValue aValue;
+ aValue.Name = "name";
+ aValue.Value <<= sStringValue;
+ pTableEntry->AppendInteropGrabBag(aValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_basedOn:
+ m_pImpl->m_pCurrentEntry->sBaseStyleIdentifier = sStringValue;
+ if(m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_TABLE)
+ {
+ TableStyleSheetEntry* pTableEntry = static_cast<TableStyleSheetEntry *>(m_pImpl->m_pCurrentEntry.get());
+ beans::PropertyValue aValue;
+ aValue.Name = "basedOn";
+ aValue.Value <<= sStringValue;
+ pTableEntry->AppendInteropGrabBag(aValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_link:
+ m_pImpl->m_pCurrentEntry->sLinkStyleIdentifier = sStringValue;
+ break;
+ case NS_ooxml::LN_CT_Style_next:
+ m_pImpl->m_pCurrentEntry->sNextStyleIdentifier = sStringValue;
+ break;
+ case NS_ooxml::LN_CT_Style_aliases:
+ case NS_ooxml::LN_CT_Style_hidden:
+ case NS_ooxml::LN_CT_Style_personal:
+ case NS_ooxml::LN_CT_Style_personalCompose:
+ case NS_ooxml::LN_CT_Style_personalReply:
+ break;
+ case NS_ooxml::LN_CT_Style_autoRedefine:
+ m_pImpl->m_pCurrentEntry->bAutoRedefine = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Style_tcPr:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties && m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_TABLE)
+ {
+ auto pTblStylePrHandler = std::make_shared<TblStylePrHandler>(m_pImpl->m_rDMapper);
+ pProperties->resolve(*pTblStylePrHandler);
+ StyleSheetEntry* pEntry = m_pImpl->m_pCurrentEntry.get();
+ TableStyleSheetEntry& rTableEntry = dynamic_cast<TableStyleSheetEntry&>(*pEntry);
+ rTableEntry.AppendInteropGrabBag(pTblStylePrHandler->getInteropGrabBag("tcPr"));
+
+ // This is a <w:tcPr> directly under <w:style>, so it affects the whole table.
+ rTableEntry.pProperties->InsertProps(pTblStylePrHandler->getProperties());
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_trPr:
+ break;
+ case NS_ooxml::LN_CT_Style_rsid:
+ case NS_ooxml::LN_CT_Style_qFormat:
+ case NS_ooxml::LN_CT_Style_semiHidden:
+ case NS_ooxml::LN_CT_Style_unhideWhenUsed:
+ case NS_ooxml::LN_CT_Style_uiPriority:
+ case NS_ooxml::LN_CT_Style_locked:
+ if (m_pImpl->m_pCurrentEntry->nStyleTypeCode != STYLE_TYPE_UNKNOWN)
+ {
+ StyleSheetEntryPtr pEntry = m_pImpl->m_pCurrentEntry;
+ beans::PropertyValue aValue;
+ switch (nSprmId)
+ {
+ case NS_ooxml::LN_CT_Style_rsid:
+ {
+ // We want the rsid as a hex string, but always with the length of 8.
+ OUStringBuffer aBuf = OUString::number(nIntValue, 16);
+ OUStringBuffer aStr;
+ comphelper::string::padToLength(aStr, 8 - aBuf.getLength(), '0');
+ aStr.append(aBuf.getStr());
+
+ aValue.Name = "rsid";
+ aValue.Value <<= aStr.makeStringAndClear();
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_qFormat:
+ aValue.Name = "qFormat";
+ break;
+ case NS_ooxml::LN_CT_Style_semiHidden:
+ aValue.Name = "semiHidden";
+ break;
+ case NS_ooxml::LN_CT_Style_unhideWhenUsed:
+ aValue.Name = "unhideWhenUsed";
+ break;
+ case NS_ooxml::LN_CT_Style_uiPriority:
+ {
+ aValue.Name = "uiPriority";
+ aValue.Value <<= OUString::number(nIntValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_locked:
+ aValue.Name = "locked";
+ break;
+ }
+ pEntry->AppendInteropGrabBag(aValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_tblPr: //contains table properties
+ case NS_ooxml::LN_CT_Style_tblStylePr: //contains to table properties
+ case NS_ooxml::LN_CT_TblPrBase_tblInd: //table properties - at least width value and type
+ case NS_ooxml::LN_EG_RPrBase_rFonts: //table fonts
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pTblStylePrHandler = std::make_shared<TblStylePrHandler>( m_pImpl->m_rDMapper );
+ pProperties->resolve( *pTblStylePrHandler );
+
+ // Add the properties to the table style
+ TblStyleType nType = pTblStylePrHandler->getType( );
+ PropertyMapPtr pProps = pTblStylePrHandler->getProperties( );
+ StyleSheetEntry * pEntry = m_pImpl->m_pCurrentEntry.get();
+
+ TableStyleSheetEntry * pTableEntry = dynamic_cast<TableStyleSheetEntry*>( pEntry );
+ if (nType == TBL_STYLE_UNKNOWN)
+ {
+ pEntry->pProperties->InsertProps(pProps);
+ }
+ else
+ {
+ if (pTableEntry != nullptr)
+ pTableEntry->AddTblStylePr( nType, pProps );
+ }
+
+ if (nSprmId == NS_ooxml::LN_CT_Style_tblPr)
+ {
+ if (pTableEntry != nullptr)
+ pTableEntry->AppendInteropGrabBag(pTblStylePrHandler->getInteropGrabBag("tblPr"));
+ }
+ else if (nSprmId == NS_ooxml::LN_CT_Style_tblStylePr)
+ {
+ pTblStylePrHandler->appendInteropGrabBag("type", pTblStylePrHandler->getTypeString());
+ if (pTableEntry != nullptr)
+ pTableEntry->AppendInteropGrabBag(pTblStylePrHandler->getInteropGrabBag("tblStylePr"));
+ }
+ }
+ break;
+ }
+ case NS_ooxml::LN_CT_PPrDefault_pPr:
+ case NS_ooxml::LN_CT_DocDefaults_pPrDefault:
+ if (nSprmId == NS_ooxml::LN_CT_DocDefaults_pPrDefault)
+ m_pImpl->m_rDMapper.SetDocDefaultsImport(true);
+
+ m_pImpl->m_rDMapper.PushStyleSheetProperties( m_pImpl->m_pDefaultParaProps );
+ resolveSprmProps( m_pImpl->m_rDMapper, rSprm );
+ if ( nSprmId == NS_ooxml::LN_CT_DocDefaults_pPrDefault && m_pImpl->m_pDefaultParaProps &&
+ !m_pImpl->m_pDefaultParaProps->isSet( PROP_PARA_TOP_MARGIN ) )
+ {
+ SetDefaultParaProps( PROP_PARA_TOP_MARGIN, uno::Any( sal_Int32(0) ) );
+ }
+ m_pImpl->m_rDMapper.PopStyleSheetProperties();
+ applyDefaults( true );
+ m_pImpl->m_bHasImportedDefaultParaProps = true;
+ if (nSprmId == NS_ooxml::LN_CT_DocDefaults_pPrDefault)
+ m_pImpl->m_rDMapper.SetDocDefaultsImport(false);
+ break;
+ case NS_ooxml::LN_CT_RPrDefault_rPr:
+ case NS_ooxml::LN_CT_DocDefaults_rPrDefault:
+ if (nSprmId == NS_ooxml::LN_CT_DocDefaults_rPrDefault)
+ m_pImpl->m_rDMapper.SetDocDefaultsImport(true);
+
+ m_pImpl->m_rDMapper.PushStyleSheetProperties( m_pImpl->m_pDefaultCharProps );
+ resolveSprmProps( m_pImpl->m_rDMapper, rSprm );
+ m_pImpl->m_rDMapper.PopStyleSheetProperties();
+ applyDefaults( false );
+ if (nSprmId == NS_ooxml::LN_CT_DocDefaults_rPrDefault)
+ m_pImpl->m_rDMapper.SetDocDefaultsImport(false);
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_jc: //table alignment - row properties!
+ m_pImpl->m_pCurrentEntry->pProperties->Insert( PROP_HORI_ORIENT,
+ uno::Any( ConversionHelper::convertTableJustification( nIntValue )));
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_jc: //table alignment - row properties!
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblBorders: //table borders, might be defined in table style
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pBorderHandler = std::make_shared<BorderHandler>(m_pImpl->m_rDMapper.IsOOXMLImport());
+ pProperties->resolve(*pBorderHandler);
+ m_pImpl->m_pCurrentEntry->pProperties->InsertProps(
+ pBorderHandler->getProperties());
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblStyleRowBandSize:
+ case NS_ooxml::LN_CT_TblPrBase_tblStyleColBandSize:
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblCellMar:
+ //no cell margins in styles
+ break;
+ case NS_ooxml::LN_CT_LatentStyles_lsdException:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ tools::SvRef<LatentStyleHandler> pLatentStyleHandler(new LatentStyleHandler());
+ pProperties->resolve(*pLatentStyleHandler);
+ beans::PropertyValue aValue;
+ aValue.Name = "lsdException";
+ aValue.Value <<= comphelper::containerToSequence(pLatentStyleHandler->getAttributes());
+ m_pImpl->m_pCurrentEntry->aLsdExceptions.push_back(aValue);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_Style_pPr:
+ // no break
+ case NS_ooxml::LN_CT_Style_rPr:
+ // no break
+ default:
+ {
+ if (!m_pImpl->m_pCurrentEntry)
+ break;
+
+ tools::SvRef<TablePropertiesHandler> pTblHandler(new TablePropertiesHandler());
+ pTblHandler->SetProperties( m_pImpl->m_pCurrentEntry->pProperties.get() );
+ if ( !pTblHandler->sprm( rSprm ) )
+ {
+ m_pImpl->m_rDMapper.PushStyleSheetProperties( m_pImpl->m_pCurrentEntry->pProperties.get() );
+
+ PropertyMapPtr pProps(new PropertyMap());
+ if (m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_TABLE)
+ {
+ if (nSprmId == NS_ooxml::LN_CT_Style_pPr)
+ m_pImpl->m_rDMapper.enableInteropGrabBag("pPr");
+ else if (nSprmId == NS_ooxml::LN_CT_Style_rPr)
+ m_pImpl->m_rDMapper.enableInteropGrabBag("rPr");
+ }
+ m_pImpl->m_rDMapper.sprmWithProps( rSprm, pProps );
+ if (m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_TABLE)
+ {
+ if (nSprmId == NS_ooxml::LN_CT_Style_pPr || nSprmId == NS_ooxml::LN_CT_Style_rPr)
+ {
+ TableStyleSheetEntry* pTableEntry = static_cast<TableStyleSheetEntry *>(m_pImpl->m_pCurrentEntry.get());
+ pTableEntry->AppendInteropGrabBag(m_pImpl->m_rDMapper.getInteropGrabBag());
+ }
+ }
+
+ m_pImpl->m_pCurrentEntry->pProperties->InsertProps(pProps);
+
+ m_pImpl->m_rDMapper.PopStyleSheetProperties( );
+ }
+ }
+ break;
+}
+}
+
+
+void StyleSheetTable::lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref)
+{
+ //create a new style entry
+ OSL_ENSURE( !m_pImpl->m_pCurrentEntry, "current entry has to be NULL here");
+ StyleSheetEntryPtr pNewEntry( new StyleSheetEntry );
+ m_pImpl->m_pCurrentEntry = pNewEntry;
+ m_pImpl->m_rDMapper.PushStyleSheetProperties( m_pImpl->m_pCurrentEntry->pProperties.get() );
+ ref->resolve(*this);
+ //append it to the table
+ m_pImpl->m_rDMapper.PopStyleSheetProperties();
+ if( !m_pImpl->m_rDMapper.IsOOXMLImport() || !m_pImpl->m_pCurrentEntry->sStyleName.isEmpty())
+ {
+ m_pImpl->m_pCurrentEntry->sConvertedStyleName = ConvertStyleName( m_pImpl->m_pCurrentEntry->sStyleName );
+ m_pImpl->m_aStyleSheetEntries.push_back( m_pImpl->m_pCurrentEntry );
+ m_pImpl->m_aStyleSheetEntriesMap.emplace( m_pImpl->m_pCurrentEntry->sStyleIdentifierD, m_pImpl->m_pCurrentEntry );
+ }
+ else
+ {
+ //TODO: this entry contains the default settings - they have to be added to the settings
+ }
+
+ if (!m_pImpl->m_pCurrentEntry->aLatentStyles.empty())
+ {
+ // We have latent styles for this entry, then process them.
+ std::vector<beans::PropertyValue>& rLatentStyles = m_pImpl->m_pCurrentEntry->aLatentStyles;
+
+ if (!m_pImpl->m_pCurrentEntry->aLsdExceptions.empty())
+ {
+ std::vector<beans::PropertyValue>& rLsdExceptions = m_pImpl->m_pCurrentEntry->aLsdExceptions;
+ beans::PropertyValue aValue;
+ aValue.Name = "lsdExceptions";
+ aValue.Value <<= comphelper::containerToSequence(rLsdExceptions);
+ rLatentStyles.push_back(aValue);
+ }
+
+ uno::Sequence<beans::PropertyValue> aLatentStyles( comphelper::containerToSequence(rLatentStyles) );
+
+ // We can put all latent style info directly to the document interop
+ // grab bag, as we can be sure that only a single style entry has
+ // latent style info.
+ uno::Reference<beans::XPropertySet> xPropertySet(m_pImpl->m_xTextDocument, uno::UNO_QUERY);
+ auto aGrabBag = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(xPropertySet->getPropertyValue("InteropGrabBag").get< uno::Sequence<beans::PropertyValue> >());
+ beans::PropertyValue aValue;
+ aValue.Name = "latentStyles";
+ aValue.Value <<= aLatentStyles;
+ aGrabBag.push_back(aValue);
+ xPropertySet->setPropertyValue("InteropGrabBag", uno::Any(comphelper::containerToSequence(aGrabBag)));
+ }
+
+ StyleSheetEntryPtr pEmptyEntry;
+ m_pImpl->m_pCurrentEntry = pEmptyEntry;
+}
+/*-------------------------------------------------------------------------
+ sorting helper
+ -----------------------------------------------------------------------*/
+namespace {
+
+class PropValVector
+{
+ std::vector<beans::PropertyValue> m_aValues;
+public:
+ PropValVector(){}
+
+ void Insert(const beans::PropertyValue& rVal);
+ uno::Sequence< uno::Any > getValues();
+ uno::Sequence< OUString > getNames();
+ const std::vector<beans::PropertyValue>& getProperties() const { return m_aValues; };
+};
+
+}
+
+void PropValVector::Insert(const beans::PropertyValue& rVal)
+{
+ auto aIt = std::find_if(m_aValues.begin(), m_aValues.end(),
+ [&rVal](beans::PropertyValue& rPropVal) { return rPropVal.Name > rVal.Name; });
+ if (aIt != m_aValues.end())
+ {
+ m_aValues.insert( aIt, rVal );
+ return;
+ }
+ m_aValues.push_back(rVal);
+}
+
+uno::Sequence< uno::Any > PropValVector::getValues()
+{
+ std::vector<uno::Any> aRet;
+ std::transform(m_aValues.begin(), m_aValues.end(), std::back_inserter(aRet), [](const beans::PropertyValue& rValue) { return rValue.Value; });
+ return comphelper::containerToSequence(aRet);
+}
+
+uno::Sequence< OUString > PropValVector::getNames()
+{
+ std::vector<OUString> aRet;
+ std::transform(m_aValues.begin(), m_aValues.end(), std::back_inserter(aRet), [](const beans::PropertyValue& rValue) { return rValue.Name; });
+ return comphelper::containerToSequence(aRet);
+}
+
+void StyleSheetTable::ApplyNumberingStyleNameToParaStyles()
+{
+ try
+ {
+ uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier( m_pImpl->m_xTextDocument, uno::UNO_QUERY_THROW );
+ uno::Reference< lang::XMultiServiceFactory > xDocFactory( m_pImpl->m_xTextDocument, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameContainer> xParaStyles;
+ xStyleFamilies->getByName(getPropertyName( PROP_PARAGRAPH_STYLES )) >>= xParaStyles;
+
+ if ( !xParaStyles.is() )
+ return;
+
+ for ( const auto& pEntry : m_pImpl->m_aStyleSheetEntries )
+ {
+ StyleSheetPropertyMap* pStyleSheetProperties = nullptr;
+ if ( pEntry->nStyleTypeCode == STYLE_TYPE_PARA && (pStyleSheetProperties = pEntry->pProperties.get()) )
+ {
+ // ListId 0 means turn off numbering - to cancel inheritance - so make sure that can be set.
+ if (pStyleSheetProperties->GetListId() > -1)
+ {
+ uno::Reference< style::XStyle > xStyle;
+ xParaStyles->getByName( ConvertStyleName(pEntry->sStyleName) ) >>= xStyle;
+
+ if ( !xStyle.is() )
+ break;
+
+ uno::Reference<beans::XPropertySet> xPropertySet( xStyle, uno::UNO_QUERY_THROW );
+ const OUString sNumberingStyleName = m_pImpl->m_rDMapper.GetListStyleName( pStyleSheetProperties->GetListId() );
+ if ( !sNumberingStyleName.isEmpty() || !pStyleSheetProperties->GetListId() )
+ xPropertySet->setPropertyValue( getPropertyName(PROP_NUMBERING_STYLE_NAME), uno::Any(sNumberingStyleName) );
+
+ // Word 2010+ (not Word 2003, and Word 2007 is completely broken)
+ // does something rather strange. It does not allow two paragraph styles
+ // to share the same listLevel on a numbering rule.
+ // Consider this style to just be body level if already used previously.
+ m_pImpl->m_rDMapper.ValidateListLevel(pEntry->sStyleIdentifierD);
+ }
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "Failed applying numbering style name to Paragraph styles");
+ }
+}
+
+/* Counteract the destructive tendencies of LibreOffice's Chapter Numbering
+ *
+ * Any assignment to Chapter Numbering will erase the numbering-like properties of inherited styles.
+ * So go through the list of styles and any that inherit from a Chapter Numbering style
+ * should have the Outline Level reapplied.
+ */
+void StyleSheetTable::ReApplyInheritedOutlineLevelFromChapterNumbering()
+{
+ try
+ {
+ uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(m_pImpl->m_xTextDocument, uno::UNO_QUERY_THROW);
+ uno::Reference< lang::XMultiServiceFactory > xDocFactory(m_pImpl->m_xTextDocument, uno::UNO_QUERY_THROW);
+ uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameContainer> xParaStyles;
+ xStyleFamilies->getByName(getPropertyName(PROP_PARAGRAPH_STYLES)) >>= xParaStyles;
+
+ if (!xParaStyles.is())
+ return;
+
+ for (const auto& pEntry : m_pImpl->m_aStyleSheetEntries)
+ {
+ if (pEntry->nStyleTypeCode != STYLE_TYPE_PARA || pEntry->sBaseStyleIdentifier.isEmpty())
+ continue;
+
+ sal_Int16 nOutlineLevel = pEntry->pProperties->GetOutlineLevel();
+ if (nOutlineLevel != -1)
+ continue;
+
+ StyleSheetEntryPtr pParent = FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier);
+ if (!pParent || !pParent->bAssignedAsChapterNumbering)
+ continue;
+
+ nOutlineLevel = pParent->pProperties->GetOutlineLevel();
+ assert(nOutlineLevel >= WW_OUTLINE_MIN && nOutlineLevel < WW_OUTLINE_MAX);
+
+ // convert MS level to LO equivalent outline level
+ ++nOutlineLevel;
+
+ uno::Reference< style::XStyle > xStyle;
+ xParaStyles->getByName(pEntry->sConvertedStyleName) >>= xStyle;
+ if ( !xStyle.is() )
+ break;
+
+ uno::Reference<beans::XPropertySet> xPropertySet( xStyle, uno::UNO_QUERY_THROW );
+ xPropertySet->setPropertyValue(getPropertyName(PROP_OUTLINE_LEVEL), uno::Any(nOutlineLevel));
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "Failed applying outlineLevel to Paragraph styles");
+ }
+}
+
+void StyleSheetTable::ApplyStyleSheets( const FontTablePtr& rFontTable )
+{
+ try
+ {
+ uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier( m_pImpl->m_xTextDocument, uno::UNO_QUERY_THROW );
+ uno::Reference< lang::XMultiServiceFactory > xDocFactory( m_pImpl->m_xTextDocument, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameContainer> xCharStyles;
+ uno::Reference<container::XNameContainer> xParaStyles;
+ uno::Reference<container::XNameContainer> xNumberingStyles;
+
+ xStyleFamilies->getByName(getPropertyName( PROP_CHARACTER_STYLES )) >>= xCharStyles;
+ xStyleFamilies->getByName(getPropertyName( PROP_PARAGRAPH_STYLES )) >>= xParaStyles;
+ xStyleFamilies->getByName("NumberingStyles") >>= xNumberingStyles;
+ if(xCharStyles.is() && xParaStyles.is())
+ {
+ std::vector< ::std::pair<OUString, uno::Reference<style::XStyle>> > aMissingParent;
+ std::vector< ::std::pair<OUString, uno::Reference<style::XStyle>> > aMissingFollow;
+ std::vector<std::pair<OUString, uno::Reference<style::XStyle>>> aMissingLink;
+ std::vector<beans::PropertyValue> aTableStylesVec;
+ for( auto& pEntry : m_pImpl->m_aStyleSheetEntries )
+ {
+ if( pEntry->nStyleTypeCode == STYLE_TYPE_UNKNOWN && !pEntry->sStyleName.isEmpty() )
+ pEntry->nStyleTypeCode = STYLE_TYPE_PARA; // unspecified style types are considered paragraph styles
+
+ if( pEntry->nStyleTypeCode == STYLE_TYPE_CHAR || pEntry->nStyleTypeCode == STYLE_TYPE_PARA || pEntry->nStyleTypeCode == STYLE_TYPE_LIST )
+ {
+ bool bParaStyle = pEntry->nStyleTypeCode == STYLE_TYPE_PARA;
+ bool bCharStyle = pEntry->nStyleTypeCode == STYLE_TYPE_CHAR;
+ bool bListStyle = pEntry->nStyleTypeCode == STYLE_TYPE_LIST;
+ bool bInsert = false;
+ uno::Reference< container::XNameContainer > xStyles = bParaStyle ? xParaStyles : (bListStyle ? xNumberingStyles : xCharStyles);
+ uno::Reference< style::XStyle > xStyle;
+ const OUString sConvertedStyleName = ConvertStyleName( pEntry->sStyleName );
+
+ if(xStyles->hasByName( sConvertedStyleName ))
+ {
+ // When pasting, don't update existing styles.
+ if (!m_pImpl->m_bIsNewDoc)
+ {
+ continue;
+ }
+ xStyles->getByName( sConvertedStyleName ) >>= xStyle;
+
+ {
+ StyleSheetTable_Impl::SetPropertiesToDefault(xStyle);
+
+ // resolve import conflicts with built-in styles (only if defaults have been defined)
+ if ( m_pImpl->m_bHasImportedDefaultParaProps
+ && pEntry->sBaseStyleIdentifier.isEmpty() //imported style has no inheritance
+ && !xStyle->getParentStyle().isEmpty() ) //built-in style has a default inheritance
+ {
+ xStyle->setParentStyle( "" );
+ }
+ }
+ }
+ else
+ {
+ bInsert = true;
+ xStyle.set(xDocFactory->createInstance(
+ bParaStyle ?
+ getPropertyName( PROP_SERVICE_PARA_STYLE ) :
+ (bListStyle ? OUString("com.sun.star.style.NumberingStyle") : getPropertyName( PROP_SERVICE_CHAR_STYLE ))),
+ uno::UNO_QUERY_THROW);
+
+ // Numbering styles have to be inserted early, as e.g. the NumberingRules property is only available after insertion.
+ if (bListStyle)
+ {
+ xStyles->insertByName( sConvertedStyleName, uno::Any( xStyle ) );
+ xStyle.set(xStyles->getByName(sConvertedStyleName), uno::UNO_QUERY_THROW);
+
+ StyleSheetPropertyMap* pPropertyMap = pEntry->pProperties.get();
+ if (pPropertyMap && pPropertyMap->GetListId() == -1)
+ {
+ // No properties? Word default is 'none', Writer one is 'arabic', handle this.
+ uno::Reference<beans::XPropertySet> xPropertySet(xStyle, uno::UNO_QUERY_THROW);
+ uno::Reference<container::XIndexReplace> xNumberingRules;
+ xPropertySet->getPropertyValue("NumberingRules") >>= xNumberingRules;
+ uno::Reference<container::XIndexAccess> xIndexAccess(xNumberingRules, uno::UNO_QUERY_THROW);
+ for (sal_Int32 i = 0; i < xIndexAccess->getCount(); ++i)
+ {
+ uno::Sequence< beans::PropertyValue > aLvlProps{
+ comphelper::makePropertyValue(
+ "NumberingType", style::NumberingType::NUMBER_NONE)
+ };
+ xNumberingRules->replaceByIndex(i, uno::Any(aLvlProps));
+ xPropertySet->setPropertyValue("NumberingRules", uno::Any(xNumberingRules));
+ }
+ }
+ }
+ }
+ if( !pEntry->sBaseStyleIdentifier.isEmpty() )
+ {
+ try
+ {
+ //TODO: Handle cases where a paragraph <> character style relation is needed
+ StyleSheetEntryPtr pParent = FindStyleSheetByISTD( pEntry->sBaseStyleIdentifier );
+ // Writer core doesn't support numbering styles having a parent style, it seems
+ if (pParent && !bListStyle)
+ {
+ const OUString sParentStyleName = ConvertStyleName( pParent->sStyleName );
+ if ( !sParentStyleName.isEmpty() && !xStyles->hasByName( sParentStyleName ) )
+ aMissingParent.emplace_back( sParentStyleName, xStyle );
+ else
+ xStyle->setParentStyle( sParentStyleName );
+ }
+ }
+ catch( const uno::RuntimeException& )
+ {
+ OSL_FAIL( "Styles parent could not be set");
+ }
+ }
+ else if( bParaStyle )
+ {
+ // Paragraph styles that don't inherit from some parent need to apply the DocDefaults
+ pEntry->pProperties->InsertProps( m_pImpl->m_pDefaultParaProps, /*bOverwrite=*/false );
+
+ //now it's time to set the default parameters - for paragraph styles
+ //Fonts: Western first entry in font table
+ //CJK: second entry
+ //CTL: third entry, if it exists
+
+ sal_uInt32 nFontCount = rFontTable->size();
+ if( !m_pImpl->m_rDMapper.IsOOXMLImport() && nFontCount > 2 )
+ {
+ uno::Any aTwoHundredFortyTwip(12.);
+
+ // font size to 240 twip (12 pts) for all if not set
+ pEntry->pProperties->Insert(PROP_CHAR_HEIGHT, aTwoHundredFortyTwip, false);
+
+ // western font not already set -> apply first font
+ const FontEntry::Pointer_t pWesternFontEntry(rFontTable->getFontEntry( 0 ));
+ OUString sWesternFontName = pWesternFontEntry->sFontName;
+ pEntry->pProperties->Insert(PROP_CHAR_FONT_NAME, uno::Any( sWesternFontName ), false);
+
+ // CJK ... apply second font
+ const FontEntry::Pointer_t pCJKFontEntry(rFontTable->getFontEntry( 2 ));
+ pEntry->pProperties->Insert(PROP_CHAR_FONT_NAME_ASIAN, uno::Any( pCJKFontEntry->sFontName ), false);
+ pEntry->pProperties->Insert(PROP_CHAR_HEIGHT_ASIAN, aTwoHundredFortyTwip, false);
+
+ // CTL ... apply third font, if available
+ if( nFontCount > 3 )
+ {
+ const FontEntry::Pointer_t pCTLFontEntry(rFontTable->getFontEntry( 3 ));
+ pEntry->pProperties->Insert(PROP_CHAR_FONT_NAME_COMPLEX, uno::Any( pCTLFontEntry->sFontName ), false);
+ pEntry->pProperties->Insert(PROP_CHAR_HEIGHT_COMPLEX, aTwoHundredFortyTwip, false);
+ }
+ }
+ }
+
+ auto aPropValues = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(pEntry->pProperties->GetPropertyValues());
+
+ if (bParaStyle || bCharStyle)
+ {
+ // delay adding LinkStyle property: all styles need to be created first
+ if (!pEntry->sLinkStyleIdentifier.isEmpty())
+ {
+ StyleSheetEntryPtr pLinkStyle
+ = FindStyleSheetByISTD(pEntry->sLinkStyleIdentifier);
+ if (pLinkStyle && !pLinkStyle->sStyleName.isEmpty())
+ aMissingLink.emplace_back(ConvertStyleName(pLinkStyle->sStyleName),
+ xStyle);
+ }
+ }
+
+ if( bParaStyle )
+ {
+ // delay adding FollowStyle property: all styles need to be created first
+ if ( !pEntry->sNextStyleIdentifier.isEmpty() )
+ {
+ StyleSheetEntryPtr pFollowStyle = FindStyleSheetByISTD( pEntry->sNextStyleIdentifier );
+ if ( pFollowStyle && !pFollowStyle->sStyleName.isEmpty() )
+ aMissingFollow.emplace_back( ConvertStyleName( pFollowStyle->sStyleName ), xStyle );
+ }
+
+ // Set the outline levels
+ StyleSheetPropertyMap* pStyleSheetProperties = pEntry ? pEntry->pProperties.get() : nullptr;
+
+ if ( pStyleSheetProperties )
+ {
+ sal_Int16 nLvl = pStyleSheetProperties->GetOutlineLevel();
+ // convert MS body Level (9) to LO body level (0) and equivalent outline levels
+ if (nLvl != -1)
+ {
+ if (nLvl == WW_OUTLINE_MAX)
+ nLvl = 0;
+ else
+ ++nLvl;
+
+ beans::PropertyValue aLvlVal(getPropertyName(PROP_OUTLINE_LEVEL), 0,
+ uno::Any(nLvl),
+ beans::PropertyState_DIRECT_VALUE);
+ aPropValues.push_back(aLvlVal);
+ }
+ }
+
+ uno::Reference< beans::XPropertyState >xState( xStyle, uno::UNO_QUERY_THROW );
+ if( sConvertedStyleName == "Contents Heading" ||
+ sConvertedStyleName == "User Index Heading" ||
+ sConvertedStyleName == "Index Heading" )
+ {
+ // remove Left/RightMargin values from TOX heading styles
+ //left margin is set to NULL by default
+ xState->setPropertyToDefault(getPropertyName( PROP_PARA_LEFT_MARGIN ));
+ }
+ else if ( sConvertedStyleName == "Text body" )
+ xState->setPropertyToDefault(getPropertyName( PROP_PARA_BOTTOM_MARGIN ));
+ else if ( sConvertedStyleName == "Heading 1" ||
+ sConvertedStyleName == "Heading 2" ||
+ sConvertedStyleName == "Heading 3" ||
+ sConvertedStyleName == "Heading 4" ||
+ sConvertedStyleName == "Heading 5" ||
+ sConvertedStyleName == "Heading 6" ||
+ sConvertedStyleName == "Heading 7" ||
+ sConvertedStyleName == "Heading 8" ||
+ sConvertedStyleName == "Heading 9" )
+ {
+ xState->setPropertyToDefault(getPropertyName( PROP_CHAR_WEIGHT ));
+ xState->setPropertyToDefault(getPropertyName( PROP_CHAR_WEIGHT_ASIAN ));
+ xState->setPropertyToDefault(getPropertyName( PROP_CHAR_WEIGHT_COMPLEX ));
+ xState->setPropertyToDefault(getPropertyName( PROP_CHAR_POSTURE ));
+ xState->setPropertyToDefault(getPropertyName( PROP_CHAR_POSTURE_ASIAN ));
+ xState->setPropertyToDefault(getPropertyName( PROP_CHAR_POSTURE_COMPLEX ));
+ xState->setPropertyToDefault(getPropertyName( PROP_CHAR_PROP_HEIGHT ));
+ xState->setPropertyToDefault(getPropertyName( PROP_CHAR_PROP_HEIGHT_ASIAN ));
+ xState->setPropertyToDefault(getPropertyName( PROP_CHAR_PROP_HEIGHT_COMPLEX));
+
+ }
+ }
+
+ if ( !aPropValues.empty() )
+ {
+ PropValVector aSortedPropVals;
+ for (const beans::PropertyValue& rValue : aPropValues)
+ {
+ // Don't add the style name properties
+ bool bIsParaStyleName = rValue.Name == "ParaStyleName";
+ bool bIsCharStyleName = rValue.Name == "CharStyleName";
+ if ( !bIsParaStyleName && !bIsCharStyleName )
+ {
+ aSortedPropVals.Insert(rValue);
+ }
+ }
+
+ try
+ {
+ uno::Reference< beans::XMultiPropertySet > xMultiPropertySet( xStyle, uno::UNO_QUERY_THROW);
+ try
+ {
+ xMultiPropertySet->setPropertyValues( aSortedPropVals.getNames(), aSortedPropVals.getValues() );
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xStyle, uno::UNO_QUERY_THROW);
+ for ( const beans::PropertyValue& rValue : aSortedPropVals.getProperties() )
+ {
+ try
+ {
+ xPropertySet->setPropertyValue( rValue.Name, rValue.Value );
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "writerfilter", "StyleSheetTable::ApplyStyleSheets could not set property " << rValue.Name );
+ }
+ }
+ }
+ // Duplicate MSWord's single footnote reference into Footnote Characters and Footnote anchor
+ if( pEntry->sStyleName.equalsIgnoreAsciiCase("footnote reference")
+ || pEntry->sStyleName.equalsIgnoreAsciiCase("endnote reference") )
+ {
+ uno::Reference< style::XStyle > xCopyStyle;
+ if( pEntry->sStyleName.equalsIgnoreAsciiCase("footnote reference") )
+ xStyles->getByName( "Footnote anchor" ) >>= xCopyStyle;
+ else
+ xStyles->getByName( "Endnote anchor" ) >>= xCopyStyle;
+
+ xMultiPropertySet.set( xCopyStyle, uno::UNO_QUERY_THROW);
+ xMultiPropertySet->setPropertyValues( aSortedPropVals.getNames(), aSortedPropVals.getValues() );
+ }
+ }
+ catch( const lang::WrappedTargetException& rWrapped)
+ {
+#ifdef DBG_UTIL
+ OUString aMessage("StyleSheetTable::ApplyStyleSheets: Some style properties could not be set");
+ beans::UnknownPropertyException aUnknownPropertyException;
+
+ if (rWrapped.TargetException >>= aUnknownPropertyException)
+ aMessage += ": " + aUnknownPropertyException.Message;
+
+ SAL_WARN("writerfilter", aMessage);
+#else
+ (void) rWrapped;
+#endif
+ }
+ catch( const uno::Exception& )
+ {
+ OSL_FAIL( "Some style properties could not be set");
+ }
+ }
+ // Numbering style got inserted earlier.
+ if(bInsert && !bListStyle)
+ {
+ const OUString sParentStyle = xStyle->getParentStyle();
+ if( !sParentStyle.isEmpty() && !xStyles->hasByName( sParentStyle ) )
+ aMissingParent.emplace_back( sParentStyle, xStyle );
+
+ xStyles->insertByName( sConvertedStyleName, uno::Any( xStyle) );
+ }
+
+ beans::PropertyValues aGrabBag = pEntry->GetInteropGrabBagSeq();
+ uno::Reference<beans::XPropertySet> xPropertySet(xStyle, uno::UNO_QUERY);
+ if (aGrabBag.hasElements())
+ {
+ xPropertySet->setPropertyValue("StyleInteropGrabBag", uno::Any(aGrabBag));
+ }
+
+ // Only paragraph styles support automatic updates.
+ if (pEntry->bAutoRedefine && bParaStyle)
+ xPropertySet->setPropertyValue("IsAutoUpdate", uno::Any(true));
+ }
+ else if(pEntry->nStyleTypeCode == STYLE_TYPE_TABLE)
+ {
+ // If this is a table style, save its contents as-is for roundtrip purposes.
+ TableStyleSheetEntry* pTableEntry = static_cast<TableStyleSheetEntry *>(pEntry.get());
+ aTableStylesVec.push_back(pTableEntry->GetInteropGrabBag());
+
+ // if DocDefaults exist, MS Word includes these in the table style definition.
+ pEntry->pProperties->InsertProps( m_pImpl->m_pDefaultCharProps, /*bOverwrite=*/false );
+ pEntry->pProperties->InsertProps( m_pImpl->m_pDefaultParaProps, /*bOverwrite=*/false );
+ }
+ }
+
+ // Update the styles that were created before their parents or next-styles
+ for( auto const & iter : aMissingParent )
+ {
+ iter.second->setParentStyle( iter.first );
+ }
+
+ for( auto const & iter : aMissingFollow )
+ {
+ try
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(iter.second, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue( "FollowStyle", uno::Any(iter.first) );
+ }
+ catch( uno::Exception & ) {}
+ }
+
+ // Update the styles that were created before their linked styles.
+ for (auto const& rLinked : aMissingLink)
+ {
+ try
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(rLinked.second,
+ uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("LinkStyle", uno::Any(rLinked.first));
+ }
+ catch (uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "writerfilter",
+ "StyleSheetTable::ApplyStyleSheets: failed to set LinkStyle");
+ }
+ }
+
+ if (!aTableStylesVec.empty())
+ {
+ // If we had any table styles, add a new document-level InteropGrabBag entry for them.
+ uno::Reference<beans::XPropertySet> xPropertySet(m_pImpl->m_xTextDocument, uno::UNO_QUERY);
+ uno::Any aAny = xPropertySet->getPropertyValue("InteropGrabBag");
+ auto aGrabBag = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aAny.get< uno::Sequence<beans::PropertyValue> >());
+ beans::PropertyValue aValue;
+ aValue.Name = "tableStyles";
+ aValue.Value <<= comphelper::containerToSequence(aTableStylesVec);
+ aGrabBag.push_back(aValue);
+ xPropertySet->setPropertyValue("InteropGrabBag", uno::Any(comphelper::containerToSequence(aGrabBag)));
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("writerfilter", "Styles could not be imported completely");
+ }
+}
+
+
+StyleSheetEntryPtr StyleSheetTable::FindStyleSheetByISTD(const OUString& sIndex)
+{
+ auto findIt = m_pImpl->m_aStyleSheetEntriesMap.find(sIndex);
+ if (findIt != m_pImpl->m_aStyleSheetEntriesMap.end())
+ return findIt->second;
+ return StyleSheetEntryPtr();
+}
+
+
+StyleSheetEntryPtr StyleSheetTable::FindStyleSheetByConvertedStyleName(std::u16string_view sIndex)
+{
+ StyleSheetEntryPtr pRet;
+ for(const StyleSheetEntryPtr & rpEntry : m_pImpl->m_aStyleSheetEntries)
+ {
+ if( rpEntry->sConvertedStyleName == sIndex)
+ {
+ pRet = rpEntry;
+ break;
+ }
+ }
+ return pRet;
+}
+
+
+StyleSheetEntryPtr StyleSheetTable::FindDefaultParaStyle()
+{
+ return FindStyleSheetByISTD( m_pImpl->m_sDefaultParaStyleName );
+}
+
+const StyleSheetEntryPtr & StyleSheetTable::GetCurrentEntry() const
+{
+ return m_pImpl->m_pCurrentEntry;
+}
+
+OUString StyleSheetTable::ConvertStyleName( const OUString& rWWName, bool bExtendedSearch)
+{
+ OUString sRet( rWWName );
+ if( bExtendedSearch )
+ {
+ //search for the rWWName in the IdentifierD of the existing styles and convert the sStyleName member
+ auto findIt = m_pImpl->m_aStyleSheetEntriesMap.find(rWWName);
+ if (findIt != m_pImpl->m_aStyleSheetEntriesMap.end())
+ sRet = findIt->second->sStyleName;
+ }
+
+ // create a map only once
+ static const std::map< OUString, OUString> StyleNameMap {
+ { "Normal", "Standard" },
+ { "heading 1", "Heading 1" },
+ { "heading 2", "Heading 2" },
+ { "heading 3", "Heading 3" },
+ { "heading 4", "Heading 4" },
+ { "heading 5", "Heading 5" },
+ { "heading 6", "Heading 6" },
+ { "heading 7", "Heading 7" },
+ { "heading 8", "Heading 8" },
+ { "heading 9", "Heading 9" },
+ { "Heading 1", "Heading 1" },
+ { "Heading 2", "Heading 2" },
+ { "Heading 3", "Heading 3" },
+ { "Heading 4", "Heading 4" },
+ { "Heading 5", "Heading 5" },
+ { "Heading 6", "Heading 6" },
+ { "Heading 7", "Heading 7" },
+ { "Heading 8", "Heading 8" },
+ { "Heading 9", "Heading 9" },
+ { "Index 1", "Index 1" },
+ { "Index 2", "Index 2" },
+ { "Index 3", "Index 3" },
+// { "Index 4", "" },
+// { "Index 5", "" },
+// { "Index 6", "" },
+// { "Index 7", "" },
+// { "Index 8", "" },
+// { "Index 9", "" },
+ { "TOC 1", "Contents 1" },
+ { "TOC 2", "Contents 2" },
+ { "TOC 3", "Contents 3" },
+ { "TOC 4", "Contents 4" },
+ { "TOC 5", "Contents 5" },
+ { "TOC 6", "Contents 6" },
+ { "TOC 7", "Contents 7" },
+ { "TOC 8", "Contents 8" },
+ { "TOC 9", "Contents 9" },
+ { "TOC Heading", "Contents Heading" },
+ { "TOCHeading", "Contents Heading" },
+ { "toc 1", "Contents 1" },
+ { "toc 2", "Contents 2" },
+ { "toc 3", "Contents 3" },
+ { "toc 4", "Contents 4" },
+ { "toc 5", "Contents 5" },
+ { "toc 6", "Contents 6" },
+ { "toc 7", "Contents 7" },
+ { "toc 8", "Contents 8" },
+ { "toc 9", "Contents 9" },
+ { "TOC1", "Contents 1" },
+ { "TOC2", "Contents 2" },
+ { "TOC3", "Contents 3" },
+ { "TOC4", "Contents 4" },
+ { "TOC5", "Contents 5" },
+ { "TOC6", "Contents 6" },
+ { "TOC7", "Contents 7" },
+ { "TOC8", "Contents 8" },
+ { "TOC9", "Contents 9" },
+// { "Normal Indent", "" },
+ { "footnote text", "Footnote" },
+ { "Footnote Text", "Footnote" },
+// { "Annotation Text", "" },
+ { "Header", "Header" },
+ { "header", "Header" },
+ { "Footer", "Footer" },
+ { "footer", "Footer" },
+ { "Index Heading", "Index Heading" },
+// { "Caption", "" },
+// { "Table of Figures", "" },
+ { "Envelope Address", "Addressee" },
+ { "Envelope Return", "Sender" },
+ { "footnote reference", "Footnote Symbol" },
+ { "Footnote Reference", "Footnote Symbol" },
+// { "Annotation Reference", "" },
+ { "Line Number", "Line numbering" },
+ { "Page Number", "Page Number" },
+ { "endnote reference", "Endnote Symbol" },
+ { "Endnote Reference", "Endnote Symbol" },
+ { "endnote text", "Endnote" },
+ { "Endnote Text", "Endnote" },
+// { "Table of Authorities", "" },
+// { "Macro Text", "" },
+// { "TOA Heading", "" },
+ { "List", "List" },
+// { "List 2", "" },
+// { "List 3", "" },
+// { "List 4", "" },
+// { "List 5", "" },
+// { "List Bullet", "" },
+// { "List Bullet 2", "" },
+// { "List Bullet 3", "" },
+// { "List Bullet 4", "" },
+// { "List Bullet 5", "" },
+// { "List Number", "" },
+// { "List Number 2", "" },
+// { "List Number 3", "" },
+// { "List Number 4", "" },
+// { "List Number 5", "" },
+ { "Title", "Title" },
+// { "Closing", "" },
+ { "Signature", "Signature" },
+// { "Default Paragraph Font", "" },
+ { "DefaultParagraphFont", "Default Paragraph Font" },
+ { "Body Text", "Text body" },
+ { "BodyText", "Text body" },
+ { "BodyTextIndentItalic", "Text body indent italic" },
+ { "Body Text Indent", "Text body indent" },
+ { "BodyTextIndent", "Text body indent" },
+ { "BodyTextIndent2", "Text body indent2" },
+// { "List Continue", "" },
+// { "List Continue 2", "" },
+// { "List Continue 3", "" },
+// { "List Continue 4", "" },
+// { "List Continue 5", "" },
+// { "Message Header", "" },
+ { "Subtitle", "Subtitle" },
+// { "Salutation", "" },
+// { "Date", "" },
+ { "Body Text First Indent", "Body Text Indent" },
+// { "Body Text First Indent 2", "" },
+// { "Note Heading", "" },
+// { "Body Text 2", "" },
+// { "Body Text 3", "" },
+// { "Body Text Indent 2", "" },
+// { "Body Text Indent 3", "" },
+// { "Block Text", "" },
+ { "Hyperlink", "Internet link" },
+ { "FollowedHyperlink", "Visited Internet Link" },
+ { "Emphasis", "Emphasis" },
+// { "Document Map", "" },
+// { "Plain Text", "" },
+ { "NoList", "No List" },
+ { "AbstractHeading", "Abstract Heading" },
+ { "AbstractBody", "Abstract Body" },
+ { "PageNumber", "page number" },
+ { "TableNormal", "Normal Table" },
+ { "DocumentMap", "Document Map" },
+ };
+
+ // find style-name using map
+ if (const auto aIt = StyleNameMap.find(sRet); aIt != StyleNameMap.end())
+ {
+ sRet = aIt->second;
+ }
+ else
+ {
+ // Style names which should not be used without a " (user)" suffix
+ static const o3tl::sorted_vector<OUString> ReservedStyleNames = [] {
+ o3tl::sorted_vector<OUString> set;
+ for (const auto& pair : StyleNameMap)
+ set.insert(pair.second);
+ return set;
+ }();
+ // SwStyleNameMapper doc says: If the UI style name equals a
+ // programmatic name, then it must append " (user)" to the end.
+ if (ReservedStyleNames.find(sRet) != ReservedStyleNames.end())
+ sRet += " (user)";
+ }
+
+ return sRet;
+}
+
+void StyleSheetTable::applyDefaults(bool bParaProperties)
+{
+ try{
+
+ if (!m_pImpl->m_bIsNewDoc)
+ {
+ // tdf#72942: do not corrupts original styles in master document
+ // during inserting of text from second document
+ return;
+ }
+
+ if(!m_pImpl->m_xTextDefaults.is())
+ {
+ m_pImpl->m_xTextDefaults.set(
+ m_pImpl->m_rDMapper.GetTextFactory()->createInstance("com.sun.star.text.Defaults"),
+ uno::UNO_QUERY_THROW );
+ }
+
+ // WARNING: these defaults only take effect IF there is a DocDefaults style section. Normally there is, but not always.
+ if( bParaProperties && m_pImpl->m_pDefaultParaProps)
+ {
+ // tdf#87533 LO will have different defaults here, depending on the locale. Import with documented defaults
+ SetDefaultParaProps(PROP_WRITING_MODE, uno::Any(sal_Int16(text::WritingMode_LR_TB)));
+ SetDefaultParaProps(PROP_PARA_ADJUST, uno::Any(sal_Int16(style::ParagraphAdjust_LEFT)));
+
+ // Widow/Orphan -> set both to two if not already set
+ uno::Any aTwo(sal_Int8(2));
+ SetDefaultParaProps(PROP_PARA_WIDOWS, aTwo);
+ SetDefaultParaProps(PROP_PARA_ORPHANS, aTwo);
+
+ uno::Reference<style::XStyleFamiliesSupplier> xStylesSupplier(m_pImpl->m_xTextDocument, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xStyleFamilies = xStylesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xParagraphStyles;
+ xStyleFamilies->getByName("ParagraphStyles") >>= xParagraphStyles;
+ uno::Reference<beans::XPropertySet> xDefault;
+ // This is the built-in default style that every style inherits from
+ xParagraphStyles->getByName("Paragraph style") >>= xDefault;
+
+ const uno::Sequence< beans::PropertyValue > aPropValues = m_pImpl->m_pDefaultParaProps->GetPropertyValues();
+ for( const auto& rPropValue : aPropValues )
+ {
+ try
+ {
+ xDefault->setPropertyValue(rPropValue.Name, rPropValue.Value);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "setPropertyValue");
+ }
+ }
+ }
+ if( !bParaProperties && m_pImpl->m_pDefaultCharProps )
+ {
+ // tdf#108350: Earlier in DomainMapper for DOCX, Calibri/11pt was set to match MSWord 2007+,
+ // but that is valid only if DocDefaults_rPrDefault is omitted.
+ // Now that DocDefaults_rPrDefault is known, the defaults should be reset to Times New Roman/10pt.
+ if ( m_pImpl->m_rDMapper.IsOOXMLImport() )
+ m_pImpl->m_xTextDefaults->setPropertyValue( getPropertyName(PROP_CHAR_FONT_NAME), css::uno::Any(OUString("Times New Roman")) );
+
+ const uno::Sequence< beans::PropertyValue > aPropValues = m_pImpl->m_pDefaultCharProps->GetPropertyValues();
+ for( const auto& rPropValue : aPropValues )
+ {
+ try
+ {
+ m_pImpl->m_xTextDefaults->setPropertyValue( rPropValue.Name, rPropValue.Value );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "exception");
+ }
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+
+OUString StyleSheetTable::getOrCreateCharStyle( PropertyValueVector_t& rCharProperties, bool bAlwaysCreate )
+{
+ //find out if any of the styles already has the required properties then return its name
+ OUString sListLabel = m_pImpl->HasListCharStyle(rCharProperties);
+ // Don't try to reuse an existing character style if requested.
+ if( !sListLabel.isEmpty() && !bAlwaysCreate)
+ return sListLabel;
+
+ //create a new one otherwise
+ const uno::Reference< container::XNameContainer >& xCharStyles = m_pImpl->m_rDMapper.GetCharacterStyles();
+ sListLabel = m_pImpl->m_rDMapper.GetUnusedCharacterStyleName();
+ uno::Reference< lang::XMultiServiceFactory > xDocFactory( m_pImpl->m_xTextDocument, uno::UNO_QUERY_THROW );
+ try
+ {
+ uno::Reference< style::XStyle > xStyle( xDocFactory->createInstance(
+ getPropertyName( PROP_SERVICE_CHAR_STYLE )), uno::UNO_QUERY_THROW);
+ uno::Reference< beans::XPropertySet > xStyleProps(xStyle, uno::UNO_QUERY_THROW );
+ for( const auto& rCharProp : rCharProperties)
+ {
+ try
+ {
+ xStyleProps->setPropertyValue( rCharProp.Name, rCharProp.Value );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "StyleSheetTable::getOrCreateCharStyle - Style::setPropertyValue");
+ }
+ }
+ xCharStyles->insertByName( sListLabel, uno::Any( xStyle) );
+ m_pImpl->m_aListCharStylePropertyVector.emplace_back( sListLabel, std::vector(rCharProperties) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "writerfilter", "StyleSheetTable::getOrCreateCharStyle");
+ }
+
+ return sListLabel;
+}
+
+}//namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/StyleSheetTable.hxx b/writerfilter/source/dmapper/StyleSheetTable.hxx
new file mode 100644
index 000000000..5dcf84b78
--- /dev/null
+++ b/writerfilter/source/dmapper/StyleSheetTable.hxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <memory>
+#include "TblStylePrHandler.hxx"
+
+#include "DomainMapper.hxx"
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include "PropertyMap.hxx"
+#include "FontTable.hxx"
+#include "LoggedResources.hxx"
+
+namespace com::sun::star::text { class XTextDocument; }
+
+
+namespace writerfilter::dmapper
+{
+
+
+enum StyleType
+{
+ STYLE_TYPE_UNKNOWN,
+ STYLE_TYPE_PARA,
+ STYLE_TYPE_CHAR,
+ STYLE_TYPE_TABLE,
+ STYLE_TYPE_LIST
+};
+class StyleSheetTable;
+typedef tools::SvRef<StyleSheetTable> StyleSheetTablePtr;
+
+struct StyleSheetTable_Impl;
+class StyleSheetEntry : public virtual SvRefBase
+{
+ std::vector<css::beans::PropertyValue> m_aInteropGrabBag;
+public:
+ OUString sStyleIdentifierD; // WW8 name
+ bool bIsDefaultStyle;
+ bool bAssignedAsChapterNumbering;
+ bool bInvalidHeight;
+ bool bHasUPE; //universal property expansion
+ StyleType nStyleTypeCode; //sgc
+ OUString sBaseStyleIdentifier;
+ OUString sNextStyleIdentifier;
+ OUString sLinkStyleIdentifier;
+ OUString sStyleName;
+ const tools::SvRef<StyleSheetPropertyMap> pProperties;
+ OUString sConvertedStyleName;
+ std::vector<css::beans::PropertyValue> aLatentStyles; ///< Attributes of latentStyles
+ std::vector<css::beans::PropertyValue> aLsdExceptions; ///< List of lsdException attribute lists
+ bool bAutoRedefine; ///< Writer calls this auto-update.
+
+ void AppendInteropGrabBag(const css::beans::PropertyValue& rValue);
+ css::beans::PropertyValue GetInteropGrabBag(); ///< Used for table styles, has a name.
+ css::beans::PropertyValues GetInteropGrabBagSeq() const; ///< Used for existing styles, just a list of properties.
+
+ // Get all properties, merged with the all of the parent's properties
+ PropertyMapPtr GetMergedInheritedProperties(const StyleSheetTablePtr& pStyleSheetTable);
+
+ StyleSheetEntry();
+ virtual ~StyleSheetEntry() override;
+};
+
+typedef tools::SvRef<StyleSheetEntry> StyleSheetEntryPtr;
+
+class DomainMapper;
+class StyleSheetTable :
+ public LoggedProperties,
+ public LoggedTable
+{
+ std::unique_ptr<StyleSheetTable_Impl> m_pImpl;
+
+public:
+ StyleSheetTable(DomainMapper& rDMapper, css::uno::Reference<css::text::XTextDocument> const& xTextDocument, bool bIsNewDoc);
+ virtual ~StyleSheetTable() override;
+
+ void ReApplyInheritedOutlineLevelFromChapterNumbering();
+ void ApplyNumberingStyleNameToParaStyles();
+ void ApplyStyleSheets( const FontTablePtr& rFontTable );
+ StyleSheetEntryPtr FindStyleSheetByISTD(const OUString& sIndex);
+ StyleSheetEntryPtr FindStyleSheetByConvertedStyleName(std::u16string_view rIndex);
+ StyleSheetEntryPtr FindDefaultParaStyle();
+
+ OUString ConvertStyleName( const OUString& rWWName, bool bExtendedSearch = false );
+
+ OUString getOrCreateCharStyle( PropertyValueVector_t& rCharProperties, bool bAlwaysCreate );
+
+ void SetDefaultParaProps(PropertyIds eId, const css::uno::Any& rAny);
+ PropertyMapPtr const & GetDefaultParaProps() const;
+ /// Returns the default character properties.
+ PropertyMapPtr const & GetDefaultCharProps() const;
+
+ const StyleSheetEntryPtr & GetCurrentEntry() const;
+
+private:
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+ // Table
+ virtual void lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref) override;
+
+ void applyDefaults(bool bParaProperties);
+};
+
+
+class TableStyleSheetEntry :
+ public StyleSheetEntry
+{
+public:
+ // Adds a new tblStylePr to the table style entry. This method
+ // fixes some possible properties conflicts, like borders ones.
+ void AddTblStylePr( TblStyleType nType, const PropertyMapPtr& pProps );
+
+ // Gets all the properties
+ // + corresponding to the mask,
+ // + from the parent styles
+
+ // @param mask mask describing which properties to return
+ PropertyMapPtr GetProperties( sal_Int32 nMask);
+
+ TableStyleSheetEntry( StyleSheetEntry const & aEntry );
+ virtual ~TableStyleSheetEntry( ) override;
+
+private:
+ typedef std::map<TblStyleType, PropertyMapPtr> TblStylePrs;
+ TblStylePrs m_aStyles;
+ PropertyMapPtr GetLocalPropertiesFromMask( sal_Int32 nMask );
+};
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TDefTableHandler.cxx b/writerfilter/source/dmapper/TDefTableHandler.cxx
new file mode 100644
index 000000000..c47ac7db8
--- /dev/null
+++ b/writerfilter/source/dmapper/TDefTableHandler.cxx
@@ -0,0 +1,458 @@
+/* -*- 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 "TDefTableHandler.hxx"
+#include "PropertyMap.hxx"
+#include "ConversionHelper.hxx"
+#include <ooxml/resourceids.hxx>
+#include <filter/msfilter/util.hxx>
+#include <tools/color.hxx>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <comphelper/sequence.hxx>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+
+
+TDefTableHandler::TDefTableHandler() :
+LoggedProperties("TDefTableHandler"),
+m_nLineWidth(0),
+m_nLineType(0),
+m_nLineColor(0)
+{
+}
+
+
+TDefTableHandler::~TDefTableHandler()
+{
+}
+
+OUString TDefTableHandler::getBorderTypeString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_Value_ST_Border_nil: return "nil";
+ case NS_ooxml::LN_Value_ST_Border_none: return "none";
+ case NS_ooxml::LN_Value_ST_Border_single: return "single";
+ case NS_ooxml::LN_Value_ST_Border_thick: return "thick";
+ case NS_ooxml::LN_Value_ST_Border_double: return "double";
+ case NS_ooxml::LN_Value_ST_Border_dotted: return "dotted";
+ case NS_ooxml::LN_Value_ST_Border_dashed: return "dashed";
+ case NS_ooxml::LN_Value_ST_Border_dotDash: return "dotDash";
+ case NS_ooxml::LN_Value_ST_Border_dotDotDash: return "dotDotDash";
+ case NS_ooxml::LN_Value_ST_Border_triple: return "triple";
+ case NS_ooxml::LN_Value_ST_Border_thinThickSmallGap: return "thinThickSmallGap";
+ case NS_ooxml::LN_Value_ST_Border_thickThinSmallGap: return "thickThinSmallGap";
+ case NS_ooxml::LN_Value_ST_Border_thinThickThinSmallGap: return "thinThickThinSmallGap";
+ case NS_ooxml::LN_Value_ST_Border_thinThickMediumGap: return "thinThickMediumGap";
+ case NS_ooxml::LN_Value_ST_Border_thickThinMediumGap: return "thickThinMediumGap";
+ case NS_ooxml::LN_Value_ST_Border_thinThickThinMediumGap: return "thinThickThinMediumGap";
+ case NS_ooxml::LN_Value_ST_Border_thinThickLargeGap: return "thinThickLargeGap";
+ case NS_ooxml::LN_Value_ST_Border_thickThinLargeGap: return "thickThinLargeGap";
+ case NS_ooxml::LN_Value_ST_Border_thinThickThinLargeGap: return "thinThickThinLargeGap";
+ case NS_ooxml::LN_Value_ST_Border_wave: return "wave";
+ case NS_ooxml::LN_Value_ST_Border_doubleWave: return "doubleWave";
+ case NS_ooxml::LN_Value_ST_Border_dashSmallGap: return "dashSmallGap";
+ case NS_ooxml::LN_Value_ST_Border_dashDotStroked: return "dashDotStroked";
+ case NS_ooxml::LN_Value_ST_Border_threeDEmboss: return "threeDEmboss";
+ case NS_ooxml::LN_Value_ST_Border_threeDEngrave: return "threeDEngrave";
+ case NS_ooxml::LN_Value_ST_Border_outset: return "outset";
+ case NS_ooxml::LN_Value_ST_Border_inset: return "inset";
+ case NS_ooxml::LN_Value_ST_Border_apples: return "apples";
+ case NS_ooxml::LN_Value_ST_Border_archedScallops: return "archedScallops";
+ case NS_ooxml::LN_Value_ST_Border_babyPacifier: return "babyPacifier";
+ case NS_ooxml::LN_Value_ST_Border_babyRattle: return "babyRattle";
+ case NS_ooxml::LN_Value_ST_Border_balloons3Colors: return "balloons3Colors";
+ case NS_ooxml::LN_Value_ST_Border_balloonsHotAir: return "balloonsHotAir";
+ case NS_ooxml::LN_Value_ST_Border_basicBlackDashes: return "basicBlackDashes";
+ case NS_ooxml::LN_Value_ST_Border_basicBlackDots: return "basicBlackDots";
+ case NS_ooxml::LN_Value_ST_Border_basicBlackSquares: return "basicBlackSquares";
+ case NS_ooxml::LN_Value_ST_Border_basicThinLines: return "basicThinLines";
+ case NS_ooxml::LN_Value_ST_Border_basicWhiteDashes: return "basicWhiteDashes";
+ case NS_ooxml::LN_Value_ST_Border_basicWhiteDots: return "basicWhiteDots";
+ case NS_ooxml::LN_Value_ST_Border_basicWhiteSquares: return "basicWhiteSquares";
+ case NS_ooxml::LN_Value_ST_Border_basicWideInline: return "basicWideInline";
+ case NS_ooxml::LN_Value_ST_Border_basicWideMidline: return "basicWideMidline";
+ case NS_ooxml::LN_Value_ST_Border_basicWideOutline: return "basicWideOutline";
+ case NS_ooxml::LN_Value_ST_Border_bats: return "bats";
+ case NS_ooxml::LN_Value_ST_Border_birds: return "birds";
+ case NS_ooxml::LN_Value_ST_Border_birdsFlight: return "birdsFlight";
+ case NS_ooxml::LN_Value_ST_Border_cabins: return "cabins";
+ case NS_ooxml::LN_Value_ST_Border_cakeSlice: return "cakeSlice";
+ case NS_ooxml::LN_Value_ST_Border_candyCorn: return "candyCorn";
+ case NS_ooxml::LN_Value_ST_Border_celticKnotwork: return "celticKnotwork";
+ case NS_ooxml::LN_Value_ST_Border_certificateBanner: return "certificateBanner";
+ case NS_ooxml::LN_Value_ST_Border_chainLink: return "chainLink";
+ case NS_ooxml::LN_Value_ST_Border_champagneBottle: return "champagneBottle";
+ case NS_ooxml::LN_Value_ST_Border_checkedBarBlack: return "checkedBarBlack";
+ case NS_ooxml::LN_Value_ST_Border_checkedBarColor: return "checkedBarColor";
+ case NS_ooxml::LN_Value_ST_Border_checkered: return "checkered";
+ case NS_ooxml::LN_Value_ST_Border_christmasTree: return "christmasTree";
+ case NS_ooxml::LN_Value_ST_Border_circlesLines: return "circlesLines";
+ case NS_ooxml::LN_Value_ST_Border_circlesRectangles: return "circlesRectangles";
+ case NS_ooxml::LN_Value_ST_Border_classicalWave: return "classicalWave";
+ case NS_ooxml::LN_Value_ST_Border_clocks: return "clocks";
+ case NS_ooxml::LN_Value_ST_Border_compass: return "compass";
+ case NS_ooxml::LN_Value_ST_Border_confetti: return "confetti";
+ case NS_ooxml::LN_Value_ST_Border_confettiGrays: return "confettiGrays";
+ case NS_ooxml::LN_Value_ST_Border_confettiOutline: return "confettiOutline";
+ case NS_ooxml::LN_Value_ST_Border_confettiStreamers: return "confettiStreamers";
+ case NS_ooxml::LN_Value_ST_Border_confettiWhite: return "confettiWhite";
+ case NS_ooxml::LN_Value_ST_Border_cornerTriangles: return "cornerTriangles";
+ case NS_ooxml::LN_Value_ST_Border_couponCutoutDashes: return "couponCutoutDashes";
+ case NS_ooxml::LN_Value_ST_Border_couponCutoutDots: return "couponCutoutDots";
+ case NS_ooxml::LN_Value_ST_Border_crazyMaze: return "crazyMaze";
+ case NS_ooxml::LN_Value_ST_Border_creaturesButterfly: return "creaturesButterfly";
+ case NS_ooxml::LN_Value_ST_Border_creaturesFish: return "creaturesFish";
+ case NS_ooxml::LN_Value_ST_Border_creaturesInsects: return "creaturesInsects";
+ case NS_ooxml::LN_Value_ST_Border_creaturesLadyBug: return "creaturesLadyBug";
+ case NS_ooxml::LN_Value_ST_Border_crossStitch: return "crossStitch";
+ case NS_ooxml::LN_Value_ST_Border_cup: return "cup";
+ case NS_ooxml::LN_Value_ST_Border_decoArch: return "decoArch";
+ case NS_ooxml::LN_Value_ST_Border_decoArchColor: return "decoArchColor";
+ case NS_ooxml::LN_Value_ST_Border_decoBlocks: return "decoBlocks";
+ case NS_ooxml::LN_Value_ST_Border_diamondsGray: return "diamondsGray";
+ case NS_ooxml::LN_Value_ST_Border_doubleD: return "doubleD";
+ case NS_ooxml::LN_Value_ST_Border_doubleDiamonds: return "doubleDiamonds";
+ case NS_ooxml::LN_Value_ST_Border_earth1: return "earth1";
+ case NS_ooxml::LN_Value_ST_Border_earth2: return "earth2";
+ case NS_ooxml::LN_Value_ST_Border_eclipsingSquares1: return "eclipsingSquares1";
+ case NS_ooxml::LN_Value_ST_Border_eclipsingSquares2: return "eclipsingSquares2";
+ case NS_ooxml::LN_Value_ST_Border_eggsBlack: return "eggsBlack";
+ case NS_ooxml::LN_Value_ST_Border_fans: return "fans";
+ case NS_ooxml::LN_Value_ST_Border_film: return "film";
+ case NS_ooxml::LN_Value_ST_Border_firecrackers: return "firecrackers";
+ case NS_ooxml::LN_Value_ST_Border_flowersBlockPrint: return "flowersBlockPrint";
+ case NS_ooxml::LN_Value_ST_Border_flowersDaisies: return "flowersDaisies";
+ case NS_ooxml::LN_Value_ST_Border_flowersModern1: return "flowersModern1";
+ case NS_ooxml::LN_Value_ST_Border_flowersModern2: return "flowersModern2";
+ case NS_ooxml::LN_Value_ST_Border_flowersPansy: return "flowersPansy";
+ case NS_ooxml::LN_Value_ST_Border_flowersRedRose: return "flowersRedRose";
+ case NS_ooxml::LN_Value_ST_Border_flowersRoses: return "flowersRoses";
+ case NS_ooxml::LN_Value_ST_Border_flowersTeacup: return "flowersTeacup";
+ case NS_ooxml::LN_Value_ST_Border_flowersTiny: return "flowersTiny";
+ case NS_ooxml::LN_Value_ST_Border_gems: return "gems";
+ case NS_ooxml::LN_Value_ST_Border_gingerbreadMan: return "gingerbreadMan";
+ case NS_ooxml::LN_Value_ST_Border_gradient: return "gradient";
+ case NS_ooxml::LN_Value_ST_Border_handmade1: return "handmade1";
+ case NS_ooxml::LN_Value_ST_Border_handmade2: return "handmade2";
+ case NS_ooxml::LN_Value_ST_Border_heartBalloon: return "heartBalloon";
+ case NS_ooxml::LN_Value_ST_Border_heartGray: return "heartGray";
+ case NS_ooxml::LN_Value_ST_Border_hearts: return "hearts";
+ case NS_ooxml::LN_Value_ST_Border_heebieJeebies: return "heebieJeebies";
+ case NS_ooxml::LN_Value_ST_Border_holly: return "holly";
+ case NS_ooxml::LN_Value_ST_Border_houseFunky: return "houseFunky";
+ case NS_ooxml::LN_Value_ST_Border_hypnotic: return "hypnotic";
+ case NS_ooxml::LN_Value_ST_Border_iceCreamCones: return "iceCreamCones";
+ case NS_ooxml::LN_Value_ST_Border_lightBulb: return "lightBulb";
+ case NS_ooxml::LN_Value_ST_Border_lightning1: return "lightning1";
+ case NS_ooxml::LN_Value_ST_Border_lightning2: return "lightning2";
+ case NS_ooxml::LN_Value_ST_Border_mapPins: return "mapPins";
+ case NS_ooxml::LN_Value_ST_Border_mapleLeaf: return "mapleLeaf";
+ case NS_ooxml::LN_Value_ST_Border_mapleMuffins: return "mapleMuffins";
+ case NS_ooxml::LN_Value_ST_Border_marquee: return "marquee";
+ case NS_ooxml::LN_Value_ST_Border_marqueeToothed: return "marqueeToothed";
+ case NS_ooxml::LN_Value_ST_Border_moons: return "moons";
+ case NS_ooxml::LN_Value_ST_Border_mosaic: return "mosaic";
+ case NS_ooxml::LN_Value_ST_Border_musicNotes: return "musicNotes";
+ case NS_ooxml::LN_Value_ST_Border_northwest: return "northwest";
+ case NS_ooxml::LN_Value_ST_Border_ovals: return "ovals";
+ case NS_ooxml::LN_Value_ST_Border_packages: return "packages";
+ case NS_ooxml::LN_Value_ST_Border_palmsBlack: return "palmsBlack";
+ case NS_ooxml::LN_Value_ST_Border_palmsColor: return "palmsColor";
+ case NS_ooxml::LN_Value_ST_Border_paperClips: return "paperClips";
+ case NS_ooxml::LN_Value_ST_Border_papyrus: return "papyrus";
+ case NS_ooxml::LN_Value_ST_Border_partyFavor: return "partyFavor";
+ case NS_ooxml::LN_Value_ST_Border_partyGlass: return "partyGlass";
+ case NS_ooxml::LN_Value_ST_Border_pencils: return "pencils";
+ case NS_ooxml::LN_Value_ST_Border_people: return "people";
+ case NS_ooxml::LN_Value_ST_Border_peopleWaving: return "peopleWaving";
+ case NS_ooxml::LN_Value_ST_Border_peopleHats: return "peopleHats";
+ case NS_ooxml::LN_Value_ST_Border_poinsettias: return "poinsettias";
+ case NS_ooxml::LN_Value_ST_Border_postageStamp: return "postageStamp";
+ case NS_ooxml::LN_Value_ST_Border_pumpkin1: return "pumpkin1";
+ case NS_ooxml::LN_Value_ST_Border_pushPinNote2: return "pushPinNote2";
+ case NS_ooxml::LN_Value_ST_Border_pushPinNote1: return "pushPinNote1";
+ case NS_ooxml::LN_Value_ST_Border_pyramids: return "pyramids";
+ case NS_ooxml::LN_Value_ST_Border_pyramidsAbove: return "pyramidsAbove";
+ case NS_ooxml::LN_Value_ST_Border_quadrants: return "quadrants";
+ case NS_ooxml::LN_Value_ST_Border_rings: return "rings";
+ case NS_ooxml::LN_Value_ST_Border_safari: return "safari";
+ case NS_ooxml::LN_Value_ST_Border_sawtooth: return "sawtooth";
+ case NS_ooxml::LN_Value_ST_Border_sawtoothGray: return "sawtoothGray";
+ case NS_ooxml::LN_Value_ST_Border_scaredCat: return "scaredCat";
+ case NS_ooxml::LN_Value_ST_Border_seattle: return "seattle";
+ case NS_ooxml::LN_Value_ST_Border_shadowedSquares: return "shadowedSquares";
+ case NS_ooxml::LN_Value_ST_Border_sharksTeeth: return "sharksTeeth";
+ case NS_ooxml::LN_Value_ST_Border_shorebirdTracks: return "shorebirdTracks";
+ case NS_ooxml::LN_Value_ST_Border_skyrocket: return "skyrocket";
+ case NS_ooxml::LN_Value_ST_Border_snowflakeFancy: return "snowflakeFancy";
+ case NS_ooxml::LN_Value_ST_Border_snowflakes: return "snowflakes";
+ case NS_ooxml::LN_Value_ST_Border_sombrero: return "sombrero";
+ case NS_ooxml::LN_Value_ST_Border_southwest: return "southwest";
+ case NS_ooxml::LN_Value_ST_Border_stars: return "stars";
+ case NS_ooxml::LN_Value_ST_Border_starsTop: return "starsTop";
+ case NS_ooxml::LN_Value_ST_Border_stars3d: return "stars3d";
+ case NS_ooxml::LN_Value_ST_Border_starsBlack: return "starsBlack";
+ case NS_ooxml::LN_Value_ST_Border_starsShadowed: return "starsShadowed";
+ case NS_ooxml::LN_Value_ST_Border_sun: return "sun";
+ case NS_ooxml::LN_Value_ST_Border_swirligig: return "swirligig";
+ case NS_ooxml::LN_Value_ST_Border_tornPaper: return "tornPaper";
+ case NS_ooxml::LN_Value_ST_Border_tornPaperBlack: return "tornPaperBlack";
+ case NS_ooxml::LN_Value_ST_Border_trees: return "trees";
+ case NS_ooxml::LN_Value_ST_Border_triangleParty: return "triangleParty";
+ case NS_ooxml::LN_Value_ST_Border_triangles: return "triangles";
+ case NS_ooxml::LN_Value_ST_Border_tribal1: return "tribal1";
+ case NS_ooxml::LN_Value_ST_Border_tribal2: return "tribal2";
+ case NS_ooxml::LN_Value_ST_Border_tribal3: return "tribal3";
+ case NS_ooxml::LN_Value_ST_Border_tribal4: return "tribal4";
+ case NS_ooxml::LN_Value_ST_Border_tribal5: return "tribal5";
+ case NS_ooxml::LN_Value_ST_Border_tribal6: return "tribal6";
+ case NS_ooxml::LN_Value_ST_Border_twistedLines1: return "twistedLines1";
+ case NS_ooxml::LN_Value_ST_Border_twistedLines2: return "twistedLines2";
+ case NS_ooxml::LN_Value_ST_Border_vine: return "vine";
+ case NS_ooxml::LN_Value_ST_Border_waveline: return "waveline";
+ case NS_ooxml::LN_Value_ST_Border_weavingAngles: return "weavingAngles";
+ case NS_ooxml::LN_Value_ST_Border_weavingBraid: return "weavingBraid";
+ case NS_ooxml::LN_Value_ST_Border_weavingRibbon: return "weavingRibbon";
+ case NS_ooxml::LN_Value_ST_Border_weavingStrips: return "weavingStrips";
+ case NS_ooxml::LN_Value_ST_Border_whiteFlowers: return "whiteFlowers";
+ case NS_ooxml::LN_Value_ST_Border_woodwork: return "woodwork";
+ case NS_ooxml::LN_Value_ST_Border_xIllusions: return "xIllusions";
+ case NS_ooxml::LN_Value_ST_Border_zanyTriangles: return "zanyTriangles";
+ case NS_ooxml::LN_Value_ST_Border_zigZag: return "zigZag";
+ case NS_ooxml::LN_Value_ST_Border_zigZagStitch: return "zigZagStitch";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TDefTableHandler::getThemeColorTypeString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_Value_St_ThemeColor_dark1: return "dark1";
+ case NS_ooxml::LN_Value_St_ThemeColor_light1: return "light1";
+ case NS_ooxml::LN_Value_St_ThemeColor_dark2: return "dark2";
+ case NS_ooxml::LN_Value_St_ThemeColor_light2: return "light2";
+ case NS_ooxml::LN_Value_St_ThemeColor_accent1: return "accent1";
+ case NS_ooxml::LN_Value_St_ThemeColor_accent2: return "accent2";
+ case NS_ooxml::LN_Value_St_ThemeColor_accent3: return "accent3";
+ case NS_ooxml::LN_Value_St_ThemeColor_accent4: return "accent4";
+ case NS_ooxml::LN_Value_St_ThemeColor_accent5: return "accent5";
+ case NS_ooxml::LN_Value_St_ThemeColor_accent6: return "accent6";
+ case NS_ooxml::LN_Value_St_ThemeColor_hyperlink: return "hyperlink";
+ case NS_ooxml::LN_Value_St_ThemeColor_followedHyperlink: return "followedHyperlink";
+ case NS_ooxml::LN_Value_St_ThemeColor_none: return "none";
+ case NS_ooxml::LN_Value_St_ThemeColor_background1: return "background1";
+ case NS_ooxml::LN_Value_St_ThemeColor_text1: return "text1";
+ case NS_ooxml::LN_Value_St_ThemeColor_background2: return "background2";
+ case NS_ooxml::LN_Value_St_ThemeColor_text2: return "text2";
+ default: break;
+ }
+ return OUString();
+}
+
+void TDefTableHandler::lcl_attribute(Id rName, Value & rVal)
+{
+ sal_Int32 nIntValue = rVal.getInt();
+ switch( rName )
+ {
+ case NS_ooxml::LN_CT_Border_sz:
+ // width of a single line in 1/8 pt, max of 32 pt -> twip * 5 / 2.
+ m_nLineWidth = nIntValue * 5 / 2;
+ appendGrabBag("sz", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Border_val:
+ m_nLineType = nIntValue;
+ appendGrabBag("val", TDefTableHandler::getBorderTypeString(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Border_color:
+ appendGrabBag("color", msfilter::util::ConvertColorOU(Color(ColorTransparency,nIntValue)));
+ m_nLineColor = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Border_space:
+ appendGrabBag("space", OUString::number(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Border_shadow:
+ //if 1 then line has shadow - unsupported
+ case NS_ooxml::LN_CT_Border_frame:
+ // ignored
+ break;
+ case NS_ooxml::LN_CT_Border_themeColor:
+ appendGrabBag("themeColor", TDefTableHandler::getThemeColorTypeString(nIntValue));
+ break;
+ case NS_ooxml::LN_CT_Border_themeTint:
+ case NS_ooxml::LN_CT_Border_themeShade:
+ // ignored
+ break;
+ default:
+ OSL_FAIL("unknown attribute");
+ }
+}
+
+
+void TDefTableHandler::localResolve(Id rName, const writerfilter::Reference<Properties>::Pointer_t& pProperties)
+{
+ if( !pProperties )
+ return;
+
+ m_nLineWidth = m_nLineType = m_nLineColor = 0;
+ std::vector<beans::PropertyValue> aSavedGrabBag;
+ if (!m_aInteropGrabBagName.isEmpty())
+ {
+ aSavedGrabBag = m_aInteropGrabBag;
+ m_aInteropGrabBag.clear();
+ }
+ pProperties->resolve( *this );
+ table::BorderLine2 aBorderLine;
+ ConversionHelper::MakeBorderLine(m_nLineWidth, m_nLineType, m_nLineColor, aBorderLine, /*bIsOOXML=*/true);
+ const bool rtl = false; // TODO
+ switch( rName )
+ {
+ case NS_ooxml::LN_CT_TcBorders_top:
+ m_aTopBorderLines.push_back(aBorderLine);
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("top"));
+ break;
+ case NS_ooxml::LN_CT_TcBorders_start:
+ if( rtl )
+ m_aRightBorderLines.push_back(aBorderLine);
+ else
+ m_aLeftBorderLines.push_back(aBorderLine);
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("start"));
+ break;
+ case NS_ooxml::LN_CT_TcBorders_left:
+ m_aLeftBorderLines.push_back(aBorderLine);
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("left"));
+ break;
+ case NS_ooxml::LN_CT_TcBorders_bottom:
+ m_aBottomBorderLines.push_back(aBorderLine);
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("bottom"));
+ break;
+ case NS_ooxml::LN_CT_TcBorders_end:
+ if( rtl )
+ m_aLeftBorderLines.push_back(aBorderLine);
+ else
+ m_aRightBorderLines.push_back(aBorderLine);
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("end"));
+ break;
+ case NS_ooxml::LN_CT_TcBorders_right:
+ m_aRightBorderLines.push_back(aBorderLine);
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("right"));
+ break;
+ case NS_ooxml::LN_CT_TcBorders_insideH:
+ m_aInsideHBorderLines.push_back(aBorderLine);
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("insideH"));
+ break;
+ case NS_ooxml::LN_CT_TcBorders_insideV:
+ m_aInsideVBorderLines.push_back(aBorderLine);
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("insideV"));
+ break;
+ case NS_ooxml::LN_CT_TcBorders_tl2br:
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("tl2br"));
+ break;
+ case NS_ooxml::LN_CT_TcBorders_tr2bl:
+ if (!m_aInteropGrabBagName.isEmpty())
+ aSavedGrabBag.push_back(getInteropGrabBag("tr2bl"));
+ break;
+ default:;
+ }
+ if (!m_aInteropGrabBagName.isEmpty())
+ m_aInteropGrabBag = aSavedGrabBag;
+}
+
+
+void TDefTableHandler::lcl_sprm(Sprm & rSprm)
+{
+ switch( rSprm.getId() )
+ {
+ case NS_ooxml::LN_CT_TcBorders_top:
+ case NS_ooxml::LN_CT_TcBorders_left:
+ case NS_ooxml::LN_CT_TcBorders_start:
+ case NS_ooxml::LN_CT_TcBorders_bottom:
+ case NS_ooxml::LN_CT_TcBorders_right:
+ case NS_ooxml::LN_CT_TcBorders_end:
+ case NS_ooxml::LN_CT_TcBorders_insideH:
+ case NS_ooxml::LN_CT_TcBorders_insideV:
+ case NS_ooxml::LN_CT_TcBorders_tl2br:
+ case NS_ooxml::LN_CT_TcBorders_tr2bl:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ localResolve( rSprm.getId(), pProperties );
+ }
+ break;
+ default:;
+ }
+}
+
+void TDefTableHandler::fillCellProperties( const ::tools::SvRef< TablePropertyMap >& pCellProperties ) const
+{
+ if( !m_aTopBorderLines.empty() )
+ pCellProperties->Insert( PROP_TOP_BORDER, uno::Any( m_aTopBorderLines[0] ) );
+ if( !m_aLeftBorderLines.empty() )
+ pCellProperties->Insert( PROP_LEFT_BORDER, uno::Any( m_aLeftBorderLines[0] ) );
+ if( !m_aBottomBorderLines.empty() )
+ pCellProperties->Insert( PROP_BOTTOM_BORDER, uno::Any( m_aBottomBorderLines[0] ) );
+ if( !m_aRightBorderLines.empty() )
+ pCellProperties->Insert( PROP_RIGHT_BORDER, uno::Any( m_aRightBorderLines[0] ) );
+ if( !m_aInsideHBorderLines.empty() )
+ pCellProperties->Insert( META_PROP_HORIZONTAL_BORDER, uno::Any( m_aInsideHBorderLines[0] ) );
+ if( !m_aInsideVBorderLines.empty() )
+ pCellProperties->Insert( META_PROP_VERTICAL_BORDER, uno::Any( m_aInsideVBorderLines[0] ) );
+}
+
+
+void TDefTableHandler::enableInteropGrabBag(const OUString& aName)
+{
+ m_aInteropGrabBagName = aName;
+}
+
+beans::PropertyValue TDefTableHandler::getInteropGrabBag(const OUString& aName)
+{
+ beans::PropertyValue aRet;
+ if (aName.isEmpty())
+ aRet.Name = m_aInteropGrabBagName;
+ else
+ aRet.Name = aName;
+
+ aRet.Value <<= comphelper::containerToSequence(m_aInteropGrabBag);
+ m_aInteropGrabBag.clear();
+ return aRet;
+}
+
+void TDefTableHandler::appendGrabBag(const OUString& aKey, const OUString& aValue)
+{
+ beans::PropertyValue aProperty;
+ aProperty.Name = aKey;
+ aProperty.Value <<= aValue;
+ m_aInteropGrabBag.push_back(aProperty);
+}
+
+} //namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TDefTableHandler.hxx b/writerfilter/source/dmapper/TDefTableHandler.hxx
new file mode 100644
index 000000000..17e6f2ed4
--- /dev/null
+++ b/writerfilter/source/dmapper/TDefTableHandler.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "LoggedResources.hxx"
+#include <vector>
+namespace com::sun::star{
+ namespace table {
+ struct BorderLine2;
+ }
+ namespace beans {
+ struct PropertyValue;
+ }
+}
+
+namespace writerfilter::dmapper
+{
+class PropertyMap;
+class TablePropertyMap;
+class TDefTableHandler : public LoggedProperties
+{
+ std::vector<css::table::BorderLine2> m_aLeftBorderLines;
+ std::vector<css::table::BorderLine2> m_aRightBorderLines;
+ std::vector<css::table::BorderLine2> m_aTopBorderLines;
+ std::vector<css::table::BorderLine2> m_aBottomBorderLines;
+ std::vector<css::table::BorderLine2> m_aInsideHBorderLines;
+ std::vector<css::table::BorderLine2> m_aInsideVBorderLines;
+
+ //values of the current border
+ sal_Int32 m_nLineWidth;
+ sal_Int32 m_nLineType;
+ sal_Int32 m_nLineColor;
+
+ OUString m_aInteropGrabBagName;
+ std::vector<css::beans::PropertyValue> m_aInteropGrabBag;
+ void appendGrabBag(const OUString& aKey, const OUString& aValue);
+
+ void localResolve(Id Name, const writerfilter::Reference<Properties>::Pointer_t& pProperties);
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+public:
+ TDefTableHandler();
+ virtual ~TDefTableHandler() override;
+
+ void fillCellProperties( const ::tools::SvRef< TablePropertyMap >& pCellProperties) const;
+ void enableInteropGrabBag(const OUString& aName);
+ css::beans::PropertyValue getInteropGrabBag(const OUString& aName = OUString());
+ static OUString getBorderTypeString(sal_Int32 nType);
+ static OUString getThemeColorTypeString(sal_Int32 nType);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TableData.hxx b/writerfilter/source/dmapper/TableData.hxx
new file mode 100644
index 000000000..a863e9d9f
--- /dev/null
+++ b/writerfilter/source/dmapper/TableData.hxx
@@ -0,0 +1,390 @@
+/* -*- 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 <com/sun/star/text/XTextRange.hpp>
+
+#include "PropertyMap.hxx"
+
+#include <vector>
+
+namespace writerfilter::dmapper
+{
+
+/**
+ Class containing the data to describe a table cell.
+ */
+class CellData final : public virtual SvRefBase
+{
+ /**
+ Handle to start of cell.
+ */
+ css::uno::Reference<css::text::XTextRange> mStart;
+
+ /**
+ Handle to end of cell.
+ */
+ css::uno::Reference<css::text::XTextRange> mEnd;
+
+ /**
+ Pointer to properties of cell.
+ */
+ TablePropertyMapPtr mpProps;
+
+ bool mbOpen;
+
+ sal_uInt32 m_nGridSpan; ///< number of grid columns in the parent table's table grid which this cell defines
+
+public:
+ typedef tools::SvRef<CellData> Pointer_t;
+
+ CellData(css::uno::Reference<css::text::XTextRange> const & start, TablePropertyMapPtr pProps)
+ : mStart(start), mEnd(start), mpProps(pProps), mbOpen(true)
+ , m_nGridSpan(1)
+ {
+ }
+
+ /**
+ Set the end handle of a cell.
+
+ @param end the end handle of the cell
+ */
+ void setEnd(css::uno::Reference<css::text::XTextRange> const & end) { mEnd = end; mbOpen = false; }
+
+ /**
+ Adds properties to the cell.
+
+ @param pProps the properties to add
+ */
+ void insertProperties(TablePropertyMapPtr pProps)
+ {
+ if( mpProps )
+ mpProps->InsertProps(pProps.get());
+ else
+ mpProps = pProps;
+ }
+
+ /**
+ Return start handle of the cell.
+ */
+ const css::uno::Reference<css::text::XTextRange>& getStart() const { return mStart; }
+
+ /**
+ Return end handle of the cell.
+ */
+ const css::uno::Reference<css::text::XTextRange>& getEnd() const { return mEnd; }
+
+ /**
+ Return properties of the cell.
+ */
+ const TablePropertyMapPtr& getProperties() const { return mpProps; }
+
+ bool isOpen() const { return mbOpen; }
+
+ sal_uInt32 getGridSpan() const { return m_nGridSpan; }
+ void setGridSpan( sal_uInt32 nSpan ) { m_nGridSpan = nSpan; }
+};
+
+/**
+ Class to handle data of a table row.
+ */
+class RowData final : public virtual SvRefBase
+{
+ typedef ::std::vector<CellData::Pointer_t> Cells;
+
+ /**
+ the cell data of the row
+ */
+ Cells mCells;
+
+ /**
+ the properties of the row
+ */
+ mutable TablePropertyMapPtr mpProperties;
+
+ sal_uInt32 m_nGridBefore; ///< number of grid columns in the parent table's table grid which must be skipped before the contents of this table row are added to the parent table
+ sal_uInt32 m_nGridAfter; ///< number of grid columns in the parent table's table grid which shall be left after the last cell in the table row
+
+public:
+ typedef tools::SvRef<RowData> Pointer_t;
+
+ RowData()
+ : m_nGridBefore(0)
+ , m_nGridAfter(0)
+ {
+ }
+
+ RowData(const RowData& rRowData)
+ : SvRefBase(), mCells(rRowData.mCells), mpProperties(rRowData.mpProperties)
+ , m_nGridBefore(rRowData.m_nGridBefore)
+ , m_nGridAfter(rRowData.m_nGridAfter)
+ {
+ }
+
+ /**
+ Add a cell to the row.
+
+ @param start the start handle of the cell
+ @param end the end handle of the cell
+ @param pProps the properties of the cell
+ @param bAddBefore true: add an empty cell at beginning of the row for gridBefore
+ */
+ void addCell(const css::uno::Reference<css::text::XTextRange>& start, TablePropertyMapPtr pProps, bool bAddBefore = false)
+ {
+ CellData::Pointer_t pCellData(new CellData(start, pProps));
+ if (bAddBefore)
+ {
+ mCells.insert(mCells.begin(), pCellData);
+ mCells[0]->setEnd(start);
+ }
+ else
+ mCells.push_back(pCellData);
+ }
+
+ void endCell(const css::uno::Reference<css::text::XTextRange>& end)
+ {
+ if (mCells.size() > 0)
+ mCells.back()->setEnd(end);
+ }
+
+ bool isCellOpen() const
+ {
+ return mCells.size() > 0 && mCells.back()->isOpen();
+ }
+
+ /**
+ Add properties to the row.
+
+ @param pProperties the properties to set
+ */
+ void insertProperties(TablePropertyMapPtr pProperties)
+ {
+ if( pProperties )
+ {
+ if( !mpProperties )
+ mpProperties = pProperties;
+ else
+ mpProperties->InsertProps(pProperties.get());
+ }
+ }
+
+ /**
+ Add properties to the last cell of the row.
+ */
+ void insertCellProperties(TablePropertyMapPtr pProps)
+ {
+ if (!mCells.empty())
+ mCells.back()->insertProperties(pProps);
+ }
+
+ /**
+ Return number of cells in the row.
+ */
+ unsigned int getCellCount() const
+ {
+ return mCells.size();
+ }
+
+ /**
+ Return start handle of a cell in the row.
+
+ @param i index of the cell
+ */
+ const css::uno::Reference<css::text::XTextRange>& getCellStart(unsigned int i) const
+ {
+ return mCells[i]->getStart();
+ }
+
+ /**
+ Return end handle of a cell in the row.
+
+ @param i index of the cell
+ */
+ const css::uno::Reference<css::text::XTextRange>& getCellEnd(unsigned int i) const
+ {
+ return mCells[i]->getEnd();
+ }
+
+ /**
+ Return the properties of a cell in the row.
+
+ @param i index of the cell
+ */
+ TablePropertyMapPtr const & getCellProperties(unsigned int i) const
+ {
+ return mCells[i]->getProperties();
+ }
+
+ /**
+ Return properties of the row.
+ */
+ const TablePropertyMapPtr& getProperties() const
+ {
+ return mpProperties;
+ }
+
+ sal_uInt32 getGridBefore() const { return m_nGridBefore; }
+ void setGridBefore(sal_uInt32 nSkipGrids) { m_nGridBefore = nSkipGrids; }
+ sal_uInt32 getGridAfter() const { return m_nGridAfter; }
+ void setGridAfter(sal_uInt32 nSkipGrids) { m_nGridAfter = nSkipGrids; }
+ sal_uInt32 getGridSpan(sal_uInt32 i) { return mCells[i]->getGridSpan(); }
+ std::vector< sal_uInt32 > getGridSpans()
+ {
+ std::vector< sal_uInt32 > nRet;
+ for (auto const& aCell: mCells)
+ nRet.push_back(aCell->getGridSpan());
+ return nRet;
+ }
+ void setCurrentGridSpan(sal_uInt32 nSpan, bool bFirstCell = false)
+ {
+ if ( mCells.size() )
+ {
+ if ( bFirstCell )
+ mCells.front()->setGridSpan(nSpan);
+ else
+ mCells.back()->setGridSpan(nSpan);
+ }
+ }
+};
+
+/**
+ Class that holds the data of a table.
+ */
+class TableData : public virtual SvRefBase
+{
+ typedef RowData::Pointer_t RowPointer_t;
+ typedef ::std::vector<RowPointer_t> Rows;
+
+ /**
+ the data of the rows of the table
+ */
+ Rows mRows;
+
+ /**
+ pointer to the data of the current row (while building up the table data).
+ */
+ RowPointer_t mpRow;
+
+ /**
+ depth of the current table in a hierarchy of tables
+ */
+ unsigned int mnDepth;
+
+ /**
+ initialize mpRow
+ */
+ void newRow() { mpRow = RowPointer_t(new RowData()); }
+
+public:
+ typedef tools::SvRef<TableData> Pointer_t;
+
+ explicit TableData(unsigned int nDepth) : mnDepth(nDepth) { newRow(); }
+
+ /**
+ End the current row.
+
+ Sets properties of the current row and pushes the row to the
+ back of the rows currently contained in the table.
+
+ @param pProperties properties of the row to be ended
+ */
+ void endRow(TablePropertyMapPtr pProperties)
+ {
+ mpRow->insertProperties(pProperties);
+ mRows.push_back(mpRow);
+ newRow();
+ }
+
+ /**
+ Add a cell to the current row.
+
+ @param start start handle of the cell
+ @param end end handle of the cell
+ @param pProps properties of the cell
+ */
+ void addCell(const css::uno::Reference<css::text::XTextRange>& start, TablePropertyMapPtr pProps)
+ {
+ mpRow->addCell(start, pProps);
+ }
+
+ /**
+ End the current cell of the current row.
+
+ @parm end end handle of the cell
+ */
+ void endCell(const css::uno::Reference<css::text::XTextRange>& end)
+ {
+ mpRow->endCell(end);
+ }
+
+ /**
+ Return if the current cell of the current row is open.
+ */
+ bool isCellOpen() const
+ {
+ return mpRow->isCellOpen();
+ }
+
+ /**
+ Insert properties to the current cell of the current row.
+
+ @param pProps the properties to add
+ */
+ void insertCellProperties(TablePropertyMapPtr pProps)
+ {
+ mpRow->insertCellProperties(pProps);
+ }
+
+ /**
+ Return number of rows in the table.
+ */
+ unsigned int getRowCount() const
+ {
+ return mRows.size();
+ }
+
+ /**
+ Return depth of table in surrounding table hierarchy.
+ */
+ unsigned int getDepth() const
+ {
+ return mnDepth;
+ }
+
+ /**
+ Return row data of a certain row.
+
+ @param i index of the row
+ */
+ RowPointer_t const & getRow(unsigned int i) const
+ {
+ return mRows[i];
+ }
+
+ const RowPointer_t& getCurrentRow() const
+ {
+ return mpRow;
+ }
+};
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TableManager.cxx b/writerfilter/source/dmapper/TableManager.cxx
new file mode 100644
index 000000000..17fc3085d
--- /dev/null
+++ b/writerfilter/source/dmapper/TableManager.cxx
@@ -0,0 +1,609 @@
+/* -*- 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 "TableManager.hxx"
+#include <ooxml/resourceids.hxx>
+#include "TagLogger.hxx"
+#include "DomainMapperTableHandler.hxx"
+#include "DomainMapper_Impl.hxx"
+#include "util.hxx"
+
+#include <tools/diagnose_ex.h>
+
+namespace writerfilter::dmapper
+{
+void TableManager::clearData() {}
+
+void TableManager::openCell(const css::uno::Reference<css::text::XTextRange>& rHandle,
+ const TablePropertyMapPtr& pProps)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.openCell");
+ TagLogger::getInstance().chars(XTextRangeToString(rHandle));
+ TagLogger::getInstance().endElement();
+#endif
+
+ if (!mTableDataStack.empty())
+ {
+ TableData::Pointer_t pTableData = mTableDataStack.top();
+
+ pTableData->addCell(rHandle, pProps);
+ }
+}
+
+bool TableManager::isIgnore() const { return isRowEnd(); }
+
+sal_uInt32 TableManager::getGridBefore(sal_uInt32 nRow)
+{
+ assert(isInTable());
+ if (nRow >= mTableDataStack.top()->getRowCount())
+ return 0;
+ return mTableDataStack.top()->getRow(nRow)->getGridBefore();
+}
+
+sal_uInt32 TableManager::getCurrentGridBefore()
+{
+ return mTableDataStack.top()->getCurrentRow()->getGridBefore();
+}
+
+void TableManager::setCurrentGridBefore(sal_uInt32 nSkipGrids)
+{
+ mTableDataStack.top()->getCurrentRow()->setGridBefore(nSkipGrids);
+}
+
+sal_uInt32 TableManager::getGridAfter(sal_uInt32 nRow)
+{
+ assert(isInTable());
+ if (nRow >= mTableDataStack.top()->getRowCount())
+ return 0;
+ return mTableDataStack.top()->getRow(nRow)->getGridAfter();
+}
+
+void TableManager::setCurrentGridAfter(sal_uInt32 nSkipGrids)
+{
+ assert(isInTable());
+ mTableDataStack.top()->getCurrentRow()->setGridAfter(nSkipGrids);
+}
+
+std::vector<sal_uInt32> TableManager::getCurrentGridSpans()
+{
+ return mTableDataStack.top()->getCurrentRow()->getGridSpans();
+}
+
+void TableManager::setCurrentGridSpan(sal_uInt32 nGridSpan, bool bFirstCell)
+{
+ mTableDataStack.top()->getCurrentRow()->setCurrentGridSpan(nGridSpan, bFirstCell);
+}
+
+sal_uInt32 TableManager::findColumn(const sal_uInt32 nRow, const sal_uInt32 nCell)
+{
+ if (nRow >= mTableDataStack.top()->getRowCount())
+ return SAL_MAX_UINT32;
+
+ RowData::Pointer_t pRow = mTableDataStack.top()->getRow(nRow);
+ if (!pRow || nCell < pRow->getGridBefore()
+ || nCell >= pRow->getCellCount() - pRow->getGridAfter())
+ {
+ return SAL_MAX_UINT32;
+ }
+
+ // The gridSpans provide a one-based index, so add up all the spans of the PREVIOUS columns,
+ // and that result will provide the first possible zero-based number for the desired column.
+ sal_uInt32 nColumn = 0;
+ for (sal_uInt32 n = 0; n < nCell; ++n)
+ nColumn += pRow->getGridSpan(n);
+ return nColumn;
+}
+
+sal_uInt32 TableManager::findColumnCell(const sal_uInt32 nRow, const sal_uInt32 nCol)
+{
+ if (nRow >= mTableDataStack.top()->getRowCount())
+ return SAL_MAX_UINT32;
+
+ RowData::Pointer_t pRow = mTableDataStack.top()->getRow(nRow);
+ if (!pRow || nCol < pRow->getGridBefore())
+ return SAL_MAX_UINT32;
+
+ sal_uInt32 nCell = 0;
+ sal_uInt32 nGrids = 0;
+ // The gridSpans give us a one-based index, but requested column is zero-based - so keep that in mind.
+ const sal_uInt32 nMaxCell = pRow->getCellCount() - pRow->getGridAfter() - 1;
+ for (const auto& rSpan : pRow->getGridSpans())
+ {
+ nGrids += rSpan;
+ if (nCol < nGrids)
+ return nCell;
+
+ ++nCell;
+ if (nCell > nMaxCell)
+ break;
+ }
+ return SAL_MAX_UINT32; // must be in gridAfter or invalid column request
+}
+
+void TableManager::endOfRowAction() {}
+
+void TableManager::endOfCellAction() {}
+
+void TableManager::insertTableProps(const TablePropertyMapPtr& pProps)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.insertTableProps");
+#endif
+
+ if (getTableProps() && getTableProps() != pProps)
+ getTableProps()->InsertProps(pProps.get());
+ else
+ mState.setTableProps(pProps);
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void TableManager::insertRowProps(const TablePropertyMapPtr& pProps)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.insertRowProps");
+#endif
+
+ if (getRowProps())
+ getRowProps()->InsertProps(pProps.get());
+ else
+ mState.setRowProps(pProps);
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void TableManager::cellProps(const TablePropertyMapPtr& pProps)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.cellProps");
+#endif
+
+ if (getCellProps())
+ getCellProps()->InsertProps(pProps.get());
+ else
+ mState.setCellProps(pProps);
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void TableManager::tableExceptionProps(const TablePropertyMapPtr& pProps)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.tableExceptionProps");
+#endif
+
+ if (getTableExceptionProps())
+ getTableExceptionProps()->InsertProps(pProps.get());
+ else
+ mState.setTableExceptionProps(pProps);
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void TableManager::utext(const sal_uInt8* data, std::size_t len)
+{
+ // optimization: cell/row end characters are the last characters in a run
+
+ if (len > 0)
+ {
+ sal_Unicode nChar = data[(len - 1) * 2] + (data[(len - 1) * 2 + 1] << 8);
+ if (nChar == 0x7)
+ handle0x7();
+ }
+}
+
+void TableManager::text(const sal_uInt8* data, std::size_t len)
+{
+ // optimization: cell/row end characters are the last characters in a run
+ if (len > 0 && data[len - 1] == 0x7)
+ handle0x7();
+}
+
+void TableManager::handle0x7()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.handle0x7");
+#endif
+
+ if (mnTableDepthNew < 1)
+ mnTableDepthNew = 1;
+
+ if (isInCell())
+ endCell();
+ else
+ endRow();
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+bool TableManager::sprm(Sprm& rSprm)
+{
+ bool bRet = true;
+ switch (rSprm.getId())
+ {
+ case NS_ooxml::LN_tblDepth:
+ {
+ Value::Pointer_t pValue = rSprm.getValue();
+
+ cellDepth(pValue->getInt());
+ }
+ break;
+ case NS_ooxml::LN_inTbl:
+ inCell();
+ break;
+ case NS_ooxml::LN_tblCell:
+ endCell();
+ break;
+ case NS_ooxml::LN_tblRow:
+ endRow();
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+void TableManager::closeCell(const css::uno::Reference<css::text::XTextRange>& rHandle)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.closeCell");
+ TagLogger::getInstance().chars(XTextRangeToString(rHandle));
+ TagLogger::getInstance().endElement();
+#endif
+
+ if (!mTableDataStack.empty())
+ {
+ TableData::Pointer_t pTableData = mTableDataStack.top();
+
+ pTableData->endCell(rHandle);
+
+ if (mpTableDataHandler)
+ mpTableDataHandler->getDomainMapperImpl().ClearPreviousParagraph();
+ }
+}
+
+void TableManager::ensureOpenCell(const TablePropertyMapPtr& pProps)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.ensureOpenCell");
+#endif
+
+ if (!mTableDataStack.empty())
+ {
+ TableData::Pointer_t pTableData = mTableDataStack.top();
+
+ if (pTableData != nullptr)
+ {
+ if (!pTableData->isCellOpen())
+ openCell(getHandle(), pProps);
+ else
+ pTableData->insertCellProperties(pProps);
+ }
+ }
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void TableManager::endParagraphGroup()
+{
+ sal_Int32 nTableDepthDifference = mnTableDepthNew - mnTableDepth;
+
+ TablePropertyMapPtr pEmptyProps;
+
+ while (nTableDepthDifference > 0)
+ {
+ ensureOpenCell(pEmptyProps);
+ startLevel();
+
+ --nTableDepthDifference;
+ }
+ while (nTableDepthDifference < 0)
+ {
+ endLevel();
+
+ ++nTableDepthDifference;
+ }
+
+ mnTableDepth = mnTableDepthNew;
+
+ if (mnTableDepth <= 0)
+ return;
+
+ if (isRowEnd())
+ {
+ endOfRowAction();
+ mTableDataStack.top()->endRow(getRowProps());
+ mState.resetRowProps();
+ }
+
+ else if (isInCell())
+ {
+ ensureOpenCell(getCellProps());
+
+ if (mState.isCellEnd())
+ {
+ endOfCellAction();
+ closeCell(getHandle());
+ }
+ }
+ mState.resetCellProps();
+}
+
+void TableManager::startParagraphGroup()
+{
+ mState.resetCellSpecifics();
+ mnTableDepthNew = 0;
+}
+
+void TableManager::resolveCurrentTable()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.resolveCurrentTable");
+#endif
+
+ if (mpTableDataHandler != nullptr)
+ {
+ try
+ {
+ TableData::Pointer_t pTableData = mTableDataStack.top();
+
+ unsigned int nRows = pTableData->getRowCount();
+
+ mpTableDataHandler->startTable(getTableProps());
+
+ for (unsigned int nRow = 0; nRow < nRows; ++nRow)
+ {
+ RowData::Pointer_t pRowData = pTableData->getRow(nRow);
+
+ unsigned int nCells = pRowData->getCellCount();
+
+ mpTableDataHandler->startRow(pRowData->getProperties());
+
+ for (unsigned int nCell = 0; nCell < nCells; ++nCell)
+ {
+ mpTableDataHandler->startCell(pRowData->getCellStart(nCell),
+ pRowData->getCellProperties(nCell));
+
+ mpTableDataHandler->endCell(pRowData->getCellEnd(nCell));
+ }
+
+ mpTableDataHandler->endRow();
+ }
+
+ mpTableDataHandler->endTable(mTableDataStack.size() - 1, m_bTableStartsAtCellStart);
+ }
+ catch (css::uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter", "resolving of current table failed");
+ }
+ }
+ mState.resetTableProps();
+ clearData();
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void TableManager::endLevel()
+{
+ if (mpTableDataHandler != nullptr)
+ resolveCurrentTable();
+
+ // Store the unfinished row as it will be used for the next table
+ if (mbKeepUnfinishedRow)
+ mpUnfinishedRow = mTableDataStack.top()->getCurrentRow();
+ mState.endLevel();
+ mTableDataStack.pop();
+
+#ifdef DBG_UTIL
+ TableData::Pointer_t pTableData;
+
+ if (!mTableDataStack.empty())
+ pTableData = mTableDataStack.top();
+
+ TagLogger::getInstance().startElement("tablemanager.endLevel");
+ TagLogger::getInstance().attribute("level", mTableDataStack.size());
+
+ if (pTableData != nullptr)
+ TagLogger::getInstance().attribute("openCell", pTableData->isCellOpen() ? "yes" : "no");
+
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void TableManager::startLevel()
+{
+#ifdef DBG_UTIL
+ TableData::Pointer_t pTableData;
+
+ if (!mTableDataStack.empty())
+ pTableData = mTableDataStack.top();
+
+ TagLogger::getInstance().startElement("tablemanager.startLevel");
+ TagLogger::getInstance().attribute("level", mTableDataStack.size());
+
+ if (pTableData != nullptr)
+ TagLogger::getInstance().attribute("openCell", pTableData->isCellOpen() ? "yes" : "no");
+
+ TagLogger::getInstance().endElement();
+#endif
+
+ TableData::Pointer_t pTableData2(new TableData(mTableDataStack.size()));
+
+ // If we have an unfinished row stored here, then push it to the new TableData
+ if (mpUnfinishedRow)
+ {
+ for (unsigned int i = 0; i < mpUnfinishedRow->getCellCount(); ++i)
+ {
+ pTableData2->addCell(mpUnfinishedRow->getCellStart(i),
+ mpUnfinishedRow->getCellProperties(i));
+ pTableData2->endCell(mpUnfinishedRow->getCellEnd(i));
+ pTableData2->getCurrentRow()->setCurrentGridSpan(mpUnfinishedRow->getGridSpan(i));
+ }
+ pTableData2->getCurrentRow()->setGridBefore(mpUnfinishedRow->getGridBefore());
+ pTableData2->getCurrentRow()->setGridAfter(mpUnfinishedRow->getGridAfter());
+ mpUnfinishedRow.clear();
+ }
+
+ mTableDataStack.push(pTableData2);
+ mState.startLevel();
+}
+
+bool TableManager::isInTable()
+{
+ bool bInTable = false;
+ if (!mTableDataStack.empty())
+ bInTable = mTableDataStack.top()->getDepth() > 0;
+ return bInTable;
+}
+
+void TableManager::handle(const css::uno::Reference<css::text::XTextRange>& rHandle)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.handle");
+ TagLogger::getInstance().chars(XTextRangeToString(rHandle));
+ TagLogger::getInstance().endElement();
+#endif
+
+ setHandle(rHandle);
+}
+
+void TableManager::setHandler(const tools::SvRef<DomainMapperTableHandler>& pTableDataHandler)
+{
+ mpTableDataHandler = pTableDataHandler;
+}
+
+void TableManager::endRow()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("tablemanager.endRow");
+#endif
+ TableData::Pointer_t pTableData = mTableDataStack.top();
+
+ // Add borderless w:gridBefore cell(s) to the row
+ sal_uInt32 nGridBefore = getCurrentGridBefore();
+ if (pTableData && nGridBefore > 0 && pTableData->getCurrentRow()->getCellCount() > 0)
+ {
+ const css::uno::Reference<css::text::XTextRange>& xRowStart
+ = pTableData->getCurrentRow()->getCellStart(0);
+ if (xRowStart.is())
+ {
+ try
+ {
+ // valid TextRange for table creation (not a nested table)?
+ xRowStart->getText()->createTextCursorByRange(xRowStart);
+
+ for (unsigned int i = 0; i < nGridBefore; ++i)
+ {
+ css::table::BorderLine2 aBorderLine;
+ aBorderLine.Color = 0;
+ aBorderLine.InnerLineWidth = 0;
+ aBorderLine.OuterLineWidth = 0;
+ TablePropertyMapPtr pCellProperties(new TablePropertyMap);
+ pCellProperties->Insert(PROP_TOP_BORDER, css::uno::Any(aBorderLine));
+ pCellProperties->Insert(PROP_LEFT_BORDER, css::uno::Any(aBorderLine));
+ pCellProperties->Insert(PROP_BOTTOM_BORDER, css::uno::Any(aBorderLine));
+ pCellProperties->Insert(PROP_RIGHT_BORDER, css::uno::Any(aBorderLine));
+ pTableData->getCurrentRow()->addCell(xRowStart, pCellProperties,
+ /*bAddBefore=*/true);
+ }
+ }
+ catch (css::uno::Exception const&)
+ {
+ // don't add gridBefore cells in not valid TextRange
+ setCurrentGridBefore(0);
+ setCurrentGridSpan(getCurrentGridSpans().front() + nGridBefore,
+ /*bFirstCell=*/true);
+ }
+ }
+ }
+
+ setRowEnd(true);
+}
+
+void TableManager::endCell()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("tablemanager.endCell");
+#endif
+
+ setCellEnd(true);
+}
+
+void TableManager::inCell()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("tablemanager.inCell");
+#endif
+ setInCell(true);
+
+ if (mnTableDepthNew < 1)
+ mnTableDepthNew = 1;
+}
+
+void TableManager::cellDepth(sal_uInt32 nDepth)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.cellDepth");
+ TagLogger::getInstance().attribute("depth", nDepth);
+ TagLogger::getInstance().endElement();
+#endif
+
+ mnTableDepthNew = nDepth;
+}
+
+void TableManager::setTableStartsAtCellStart(bool bTableStartsAtCellStart)
+{
+ m_bTableStartsAtCellStart = bTableStartsAtCellStart;
+}
+
+void TableManager::setCellLastParaAfterAutospacing(bool bIsAfterAutospacing)
+{
+ m_bCellLastParaAfterAutospacing = bIsAfterAutospacing;
+}
+
+TableManager::TableManager()
+ : mnTableDepthNew(0)
+ , mnTableDepth(0)
+ , mbKeepUnfinishedRow(false)
+ , m_bTableStartsAtCellStart(false)
+{
+ setRowEnd(false);
+ setInCell(false);
+ setCellEnd(false);
+ m_bCellLastParaAfterAutospacing = false;
+}
+
+TableManager::~TableManager() = default;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TableManager.hxx b/writerfilter/source/dmapper/TableManager.hxx
new file mode 100644
index 000000000..e6600d357
--- /dev/null
+++ b/writerfilter/source/dmapper/TableManager.hxx
@@ -0,0 +1,530 @@
+/* -*- 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 <memory>
+#include <stack>
+
+#include "PropertyMap.hxx"
+#include "TableData.hxx"
+
+#include <dmapper/resourcemodel.hxx>
+
+namespace writerfilter::dmapper
+{
+
+class DomainMapperTableHandler;
+
+/**
+ The table manager.
+
+ This class gets forwarded events from the tokenizer. It gathers the
+ table data and after ending the table generates events for the
+ table structure. The events have to be handles by a TableDataHandler.
+
+ */
+class TableManager : public virtual SvRefBase
+{
+ class TableManagerState final
+ {
+ /**
+ properties of the current cell
+ */
+ TablePropertyMapPtr mpCellProps;
+
+ /**
+ properties of the current row
+ */
+ TablePropertyMapPtr mpRowProps;
+
+ /**
+ table exception properties of the current row
+ */
+ TablePropertyMapPtr mpTableExceptionProps;
+
+ /**
+ properties of the current table
+ */
+ std::stack<TablePropertyMapPtr> mTableProps;
+
+ /**
+ true if at the end of a row
+ */
+ bool mbRowEnd;
+
+ /**
+ true when in a cell
+ */
+ bool mbInCell;
+
+ /**
+ true when at the end of a cell
+ */
+ bool mbCellEnd;
+
+ public:
+ /**
+ Constructor
+ */
+ TableManagerState()
+ : mbRowEnd(false), mbInCell(false), mbCellEnd(false)
+ {
+ }
+
+ void startLevel()
+ {
+ TablePropertyMapPtr pProps;
+ mTableProps.push(pProps);
+ }
+
+ void endLevel()
+ {
+ mTableProps.pop();
+ }
+
+ /**
+ Reset to initial state at beginning of row.
+ */
+ void resetCellSpecifics()
+ {
+ mbRowEnd = false;
+ mbInCell = false;
+ mbCellEnd = false;
+ }
+
+ void resetCellProps()
+ {
+ // copy tblPrEx table exception properties, if they exist
+ if (getTableExceptionProps().is())
+ {
+ mpCellProps = new TablePropertyMap;
+ mpCellProps->InsertProps(getTableExceptionProps().get());
+ }
+ else
+ mpCellProps.clear();
+ }
+
+ void setCellProps(TablePropertyMapPtr pProps)
+ {
+ mpCellProps = pProps;
+ }
+
+ const TablePropertyMapPtr& getCellProps() const
+ {
+ return mpCellProps;
+ }
+
+ void resetRowProps()
+ {
+ // reset also table exception and
+ // its copy set by the previous resetCellProps()
+ mpTableExceptionProps.clear();
+ resetCellProps();
+ mpRowProps.clear();
+ }
+
+ void setRowProps(TablePropertyMapPtr pProps)
+ {
+ mpRowProps = pProps;
+ }
+
+ const TablePropertyMapPtr& getRowProps() const
+ {
+ return mpRowProps;
+ }
+
+ void setTableExceptionProps(TablePropertyMapPtr pProps)
+ {
+ mpTableExceptionProps = pProps;
+ // set table exception properties of the first cell
+ resetCellProps();
+ }
+
+ const TablePropertyMapPtr& getTableExceptionProps() const
+ {
+ return mpTableExceptionProps;
+ }
+
+ void resetTableProps()
+ {
+ if (mTableProps.size() > 0)
+ mTableProps.top().clear();
+ }
+
+ void setTableProps(TablePropertyMapPtr pProps)
+ {
+ if (mTableProps.size() > 0)
+ mTableProps.top() = pProps;
+ }
+
+ TablePropertyMapPtr getTableProps()
+ {
+ TablePropertyMapPtr pResult;
+
+ if (mTableProps.size() > 0)
+ pResult = mTableProps.top();
+
+ return pResult;
+ }
+
+ void setInCell(bool bInCell)
+ {
+ mbInCell = bInCell;
+ }
+
+ bool isInCell() const
+ {
+ return mbInCell;
+ }
+
+ void setCellEnd(bool bCellEnd)
+ {
+ mbCellEnd = bCellEnd;
+ }
+
+ bool isCellEnd() const
+ {
+ return mbCellEnd;
+ }
+
+ void setRowEnd(bool bRowEnd)
+ {
+ mbRowEnd = bRowEnd;
+ }
+
+ bool isRowEnd() const
+ {
+ return mbRowEnd;
+ }
+ };
+
+ /**
+ handle for the current position in document
+ */
+ css::uno::Reference<css::text::XTextRange> mCurHandle;
+
+ TableManagerState mState;
+
+protected:
+ TablePropertyMapPtr const & getCellProps() const
+ {
+ return mState.getCellProps();
+ }
+
+public:
+ TablePropertyMapPtr const & getRowProps() const
+ {
+ return mState.getRowProps();
+ }
+
+ TablePropertyMapPtr const & getTableExceptionProps() const
+ {
+ return mState.getTableExceptionProps();
+ }
+
+protected:
+ void setInCell(bool bInCell)
+ {
+ mState.setInCell(bInCell);
+ }
+
+ bool isInCell() const
+ {
+ return mState.isInCell();
+ }
+
+ void setCellEnd(bool bCellEnd)
+ {
+ mState.setCellEnd(bCellEnd);
+ }
+
+ void setRowEnd(bool bRowEnd)
+ {
+ mState.setRowEnd(bRowEnd);
+ }
+
+ bool isRowEnd() const
+ {
+ return mState.isRowEnd();
+ }
+
+ TablePropertyMapPtr getTableProps()
+ {
+ return mState.getTableProps();
+ }
+
+ const css::uno::Reference<css::text::XTextRange>& getHandle() const
+ {
+ return mCurHandle;
+ }
+
+ void setHandle(const css::uno::Reference<css::text::XTextRange>& rHandle)
+ {
+ mCurHandle = rHandle;
+ }
+
+private:
+ typedef tools::SvRef< css::uno::Reference<css::text::XTextRange> > T_p;
+
+ /**
+ depth of the current cell
+ */
+ sal_uInt32 mnTableDepthNew;
+
+ /**
+ depth of the previous cell
+ */
+ sal_uInt32 mnTableDepth;
+
+ /**
+ stack of table data
+
+ for each level of nested tables there is one frame in the stack
+ */
+ std::stack<TableData::Pointer_t> mTableDataStack;
+ RowData::Pointer_t mpUnfinishedRow;
+ bool mbKeepUnfinishedRow;
+ /// If this is a nested table, does it start at cell start?
+ bool m_bTableStartsAtCellStart;
+
+ bool m_bCellLastParaAfterAutospacing;
+
+ /**
+ handler for resolveCurrentTable
+ */
+ tools::SvRef<DomainMapperTableHandler> mpTableDataHandler;
+
+ /**
+ Set flag which indicates the current handle is in a cell.
+ */
+ void inCell();
+
+ /**
+ Set flag which indicate the current handle is at the end of a cell.
+ */
+ void endCell();
+
+ /**
+ Set the table depth of the current cell.
+
+ @param nDepth the cell depth
+ */
+ void cellDepth(sal_uInt32 nDepth);
+
+ /**
+ Set flag indication the current handle is at the end of a row.
+ */
+ void endRow();
+
+ /**
+ Resolve the current table to the TableDataHandler.
+ */
+ void resolveCurrentTable();
+
+ /**
+ Open a cell at current level.
+ */
+
+ void openCell(const css::uno::Reference<css::text::XTextRange>& rHandle, const TablePropertyMapPtr& pProps);
+
+ /**
+ Close a cell at current level.
+ */
+ void closeCell(const css::uno::Reference<css::text::XTextRange>& rHandle);
+
+ /**
+ Ensure a cell is open at the current level.
+ */
+ void ensureOpenCell(const TablePropertyMapPtr& pProps);
+
+protected:
+ /**
+ Return the current table difference, i.e. 1 if we are in the first cell of a new table, etc.
+ */
+ sal_Int32 getTableDepthDifference() const { return mnTableDepthNew - mnTableDepth; }
+
+ sal_uInt32 getTableDepth() const { return mnTableDepthNew; }
+
+ /**
+ Action to be carried out at the end of the last paragraph of a
+ cell.
+ */
+ virtual void endOfCellAction();
+
+ /**
+ Action to be carried out at the end of the "table row"
+ paragraph.
+ */
+ virtual void endOfRowAction();
+ /** let the derived class clear their table related data
+ */
+ virtual void clearData();
+
+ /** Should we keep the unfinished row in endLevel to initialize the table
+ data in the following startLevel.
+ */
+ void setKeepUnfinishedRow(bool bKeep)
+ {
+ mbKeepUnfinishedRow = bKeep;
+ }
+
+
+public:
+ TableManager();
+ ~TableManager();
+
+ /**
+ Set handler for resolveCurrentTable.
+
+ @param pTableDataHandler the handler
+ */
+ void setHandler(const tools::SvRef<DomainMapperTableHandler>& pTableDataHandler);
+
+ /**
+ Set the current handle.
+
+ @param rHandle the handle
+ */
+ void handle(const css::uno::Reference<css::text::XTextRange>& rHandle);
+
+ /**
+ Start a new table level.
+
+ A new context is pushed onto the table data stack,
+ */
+ virtual void startLevel();
+
+ /**
+ End a table level.
+
+ The current table is resolved and the context is popped from
+ the stack.
+ */
+ virtual void endLevel();
+
+ /**
+ * Signal that the next paragraph definitely won't be part of any table.
+ */
+ void endTable()
+ {
+ setRowEnd(false);
+ }
+
+ /**
+ Tells whether a table has been started or not
+ */
+ bool isInTable();
+
+ /**
+ Handle the start of a paragraph group.
+ */
+ void startParagraphGroup();
+
+ /**
+ Handle the end of a paragraph group.
+ */
+ void endParagraphGroup();
+
+ /**
+ Handle an SPRM at current handle.
+
+ @param rSprm the SPRM
+ */
+ virtual bool sprm(Sprm & rSprm);
+
+ /**
+ Handle occurrence of character 0x7.
+ */
+ void handle0x7();
+
+ /**
+ Handle 8 bit text at current handle.
+
+ @param data array of characters
+ @param len number of characters to handle
+ */
+ void text(const sal_uInt8 * data, size_t len);
+
+ /**
+ Handle 16 bit text at current handle.
+
+ @param data array of characters
+ @param len number of characters to handle
+ */
+ void utext(const sal_uInt8 * data, size_t len);
+
+ /**
+ Handle properties of the current cell.
+
+ @param pProps the properties
+ */
+ virtual void cellProps(const TablePropertyMapPtr& pProps);
+
+ /**
+ Handle properties of the current row.
+
+ @param pProps the properties
+ */
+ virtual void insertRowProps(const TablePropertyMapPtr& pProps);
+
+ /**
+ Handle table exception properties of the current row.
+
+ @param pProps the properties
+ */
+ virtual void tableExceptionProps(const TablePropertyMapPtr& pProps);
+
+ /**
+ Handle properties of the current table.
+
+ @param pProps the properties
+ */
+ virtual void insertTableProps(const TablePropertyMapPtr& pProps);
+
+ /**
+ Return if table manager has detected paragraph to ignore.
+
+ If this function returns true the current paragraph contains
+ only control information, e.g. end of row.
+ */
+ bool isIgnore() const;
+
+ sal_uInt32 getGridBefore(sal_uInt32 nRow);
+ sal_uInt32 getCurrentGridBefore();
+ void setCurrentGridBefore( sal_uInt32 nSkipGrids );
+ sal_uInt32 getGridAfter(sal_uInt32 nRow);
+ void setCurrentGridAfter( sal_uInt32 nSkipGrids );
+ std::vector<sal_uInt32> getCurrentGridSpans();
+ void setCurrentGridSpan( sal_uInt32 nGridSpan, bool bFirstCell = false );
+ /// Given a zero-based row/cell, return the zero-based grid it belongs to, or SAL_MAX_UINT16 for invalid.
+ sal_uInt32 findColumn( const sal_uInt32 nRow, const sal_uInt32 nCell );
+ /// Given a zero-based row/col, return the zero-based cell describing that grid, or SAL_MAX_UINT16 for invalid.
+ sal_uInt32 findColumnCell( const sal_uInt32 nRow, const sal_uInt32 nCol );
+
+ void setTableStartsAtCellStart(bool bTableStartsAtCellStart);
+ void setCellLastParaAfterAutospacing(bool bIsAfterAutospacing);
+ bool isCellLastParaAfterAutospacing() const {return m_bCellLastParaAfterAutospacing;}
+};
+
+}
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TablePositionHandler.cxx b/writerfilter/source/dmapper/TablePositionHandler.cxx
new file mode 100644
index 000000000..5eea4a000
--- /dev/null
+++ b/writerfilter/source/dmapper/TablePositionHandler.cxx
@@ -0,0 +1,154 @@
+/* -*- 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 "TablePositionHandler.hxx"
+#include "ConversionHelper.hxx"
+#include "TagLogger.hxx"
+#include <ooxml/resourceids.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <comphelper/sequenceashashmap.hxx>
+
+namespace writerfilter::dmapper
+{
+using namespace ::com::sun::star;
+
+TablePositionHandler::TablePositionHandler()
+ : LoggedProperties("TablePositionHandler")
+{
+}
+
+TablePositionHandler::~TablePositionHandler() = default;
+
+void TablePositionHandler::lcl_attribute(Id nId, Value& rVal)
+{
+ switch (nId)
+ {
+ case NS_ooxml::LN_CT_TblPPr_vertAnchor:
+ m_aVertAnchor = rVal.getString();
+ break;
+ case NS_ooxml::LN_CT_TblPPr_tblpYSpec:
+ m_aYSpec = rVal.getString();
+ break;
+ case NS_ooxml::LN_CT_TblPPr_horzAnchor:
+ m_aHorzAnchor = rVal.getString();
+ break;
+ case NS_ooxml::LN_CT_TblPPr_tblpXSpec:
+ m_aXSpec = rVal.getString();
+ break;
+ case NS_ooxml::LN_CT_TblPPr_tblpY:
+ m_nY = rVal.getInt();
+ break;
+ case NS_ooxml::LN_CT_TblPPr_tblpX:
+ m_nX = rVal.getInt();
+ break;
+ case NS_ooxml::LN_CT_TblPPr_leftFromText:
+ m_nLeftFromText = rVal.getInt();
+ break;
+ case NS_ooxml::LN_CT_TblPPr_rightFromText:
+ m_nRightFromText = rVal.getInt();
+ break;
+ case NS_ooxml::LN_CT_TblPPr_topFromText:
+ m_nTopFromText = rVal.getInt();
+ break;
+ case NS_ooxml::LN_CT_TblPPr_bottomFromText:
+ m_nBottomFromText = rVal.getInt();
+ break;
+ default:
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ break;
+ }
+}
+
+void TablePositionHandler::lcl_sprm(Sprm& /*rSprm*/) {}
+
+uno::Sequence<beans::PropertyValue> TablePositionHandler::getTablePosition() const
+{
+ comphelper::SequenceAsHashMap aFrameProperties;
+
+ aFrameProperties["LeftBorderDistance"] <<= sal_Int32(0);
+ aFrameProperties["RightBorderDistance"] <<= sal_Int32(0);
+ aFrameProperties["TopBorderDistance"] <<= sal_Int32(0);
+ aFrameProperties["BottomBorderDistance"] <<= sal_Int32(0);
+
+ aFrameProperties["LeftMargin"] <<= ConversionHelper::convertTwipToMM100(m_nLeftFromText);
+ aFrameProperties["RightMargin"] <<= ConversionHelper::convertTwipToMM100(m_nRightFromText);
+ aFrameProperties["TopMargin"] <<= ConversionHelper::convertTwipToMM100(m_nTopFromText);
+ aFrameProperties["BottomMargin"] <<= ConversionHelper::convertTwipToMM100(m_nBottomFromText);
+
+ table::BorderLine2 aEmptyBorder;
+ aFrameProperties["TopBorder"] <<= aEmptyBorder;
+ aFrameProperties["BottomBorder"] <<= aEmptyBorder;
+ aFrameProperties["LeftBorder"] <<= aEmptyBorder;
+ aFrameProperties["RightBorder"] <<= aEmptyBorder;
+
+ // Horizontal positioning
+ sal_Int16 nHoriOrient = text::HoriOrientation::NONE;
+ if (m_aXSpec == "center")
+ nHoriOrient = text::HoriOrientation::CENTER;
+ else if (m_aXSpec == "inside")
+ nHoriOrient = text::HoriOrientation::INSIDE;
+ else if (m_aXSpec == "left")
+ nHoriOrient = text::HoriOrientation::LEFT;
+ else if (m_aXSpec == "outside")
+ nHoriOrient = text::HoriOrientation::OUTSIDE;
+ else if (m_aXSpec == "right")
+ nHoriOrient = text::HoriOrientation::RIGHT;
+
+ sal_Int16 nHoriOrientRelation;
+ if (m_aHorzAnchor == "margin")
+ nHoriOrientRelation = text::RelOrientation::PAGE_PRINT_AREA;
+ else if (m_aHorzAnchor == "page")
+ nHoriOrientRelation = text::RelOrientation::PAGE_FRAME;
+ else if (m_aHorzAnchor == "text")
+ nHoriOrientRelation = text::RelOrientation::FRAME;
+
+ aFrameProperties["HoriOrient"] <<= nHoriOrient;
+ aFrameProperties["HoriOrientRelation"] <<= nHoriOrientRelation;
+ aFrameProperties["HoriOrientPosition"] <<= ConversionHelper::convertTwipToMM100(m_nX);
+
+ // Vertical positioning
+ sal_Int16 nVertOrient = text::VertOrientation::NONE;
+ if (m_aYSpec == "bottom")
+ nVertOrient = text::VertOrientation::BOTTOM;
+ else if (m_aYSpec == "center")
+ nVertOrient = text::VertOrientation::CENTER;
+ else if (m_aYSpec == "top")
+ nVertOrient = text::VertOrientation::TOP;
+ // TODO There are a few cases we can't map ATM.
+
+ sal_Int16 nVertOrientRelation;
+ if (m_aVertAnchor == "margin")
+ nVertOrientRelation = text::RelOrientation::PAGE_PRINT_AREA;
+ else if (m_aVertAnchor == "page")
+ nVertOrientRelation = text::RelOrientation::PAGE_FRAME;
+ else if (m_aVertAnchor == "text")
+ nVertOrientRelation = text::RelOrientation::FRAME;
+
+ aFrameProperties["VertOrient"] <<= nVertOrient;
+ aFrameProperties["VertOrientRelation"] <<= nVertOrientRelation;
+ aFrameProperties["VertOrientPosition"] <<= ConversionHelper::convertTwipToMM100(m_nY);
+ aFrameProperties["FillTransparence"] <<= sal_Int32(100);
+
+ return aFrameProperties.getAsConstPropertyValueList();
+}
+
+bool TablePositionHandler::operator==(const TablePositionHandler& rHandler) const
+{
+ return m_aVertAnchor == rHandler.m_aVertAnchor && m_aYSpec == rHandler.m_aYSpec
+ && m_aHorzAnchor == rHandler.m_aHorzAnchor && m_aXSpec == rHandler.m_aXSpec
+ && m_nY == rHandler.m_nY && m_nX == rHandler.m_nX;
+}
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TablePositionHandler.hxx b/writerfilter/source/dmapper/TablePositionHandler.hxx
new file mode 100644
index 000000000..23d042b69
--- /dev/null
+++ b/writerfilter/source/dmapper/TablePositionHandler.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include "LoggedResources.hxx"
+
+namespace com::sun::star::beans
+{
+struct PropertyValue;
+}
+
+namespace writerfilter::dmapper
+{
+/// Handler for floating table positioning
+class TablePositionHandler : public LoggedProperties
+{
+ OUString m_aVertAnchor{ "margin" };
+ OUString m_aYSpec;
+ OUString m_aHorzAnchor{ "text" };
+ OUString m_aXSpec;
+ sal_Int32 m_nY = 0;
+ sal_Int32 m_nX = 0;
+ sal_Int32 m_nLeftFromText = 0;
+ sal_Int32 m_nRightFromText = 0;
+ sal_Int32 m_nTopFromText = 0;
+ sal_Int32 m_nBottomFromText = 0;
+
+ // Properties
+ void lcl_attribute(Id nId, Value& rVal) override;
+ void lcl_sprm(Sprm& sprm) override;
+
+public:
+ sal_Int32 getY() const { return m_nY; }
+ sal_Int32 getX() const { return m_nX; }
+ sal_Int32 getLeftFromText() const { return m_nLeftFromText; }
+ sal_Int32 getRightFromText() const { return m_nRightFromText; }
+ sal_Int32 getTopFromText() const { return m_nTopFromText; }
+ sal_Int32 getBottomFromText() const { return m_nBottomFromText; }
+
+ const OUString& getVertAnchor() const { return m_aVertAnchor; }
+ const OUString& getYSpec() const { return m_aYSpec; }
+ const OUString& getHorzAnchor() const { return m_aHorzAnchor; }
+ const OUString& getXSpec() const { return m_aXSpec; }
+
+ TablePositionHandler();
+ ~TablePositionHandler() override;
+
+ /** Compute the UNO properties for the frame containing the table based
+ on the received tokens.
+
+ Note that the properties will need to be adjusted with the table
+ properties before actually using them.
+ */
+ css::uno::Sequence<css::beans::PropertyValue> getTablePosition() const;
+
+ bool operator==(const TablePositionHandler& rHandler) const;
+};
+
+using TablePositionHandlerPtr = tools::SvRef<TablePositionHandler>;
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TablePropertiesHandler.cxx b/writerfilter/source/dmapper/TablePropertiesHandler.cxx
new file mode 100644
index 000000000..8ea3eae5c
--- /dev/null
+++ b/writerfilter/source/dmapper/TablePropertiesHandler.cxx
@@ -0,0 +1,398 @@
+/* -*- 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 "BorderHandler.hxx"
+#include "CellColorHandler.hxx"
+#include "CellMarginHandler.hxx"
+#include "ConversionHelper.hxx"
+#include "MeasureHandler.hxx"
+#include "TrackChangesHandler.hxx"
+#include "TablePropertiesHandler.hxx"
+#include "TagLogger.hxx"
+#include "TDefTableHandler.hxx"
+#include "DomainMapperTableManager.hxx"
+
+#include <ooxml/resourceids.hxx>
+
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <oox/token/tokens.hxx>
+
+using namespace com::sun::star;
+using namespace oox;
+
+namespace writerfilter::dmapper {
+
+ TablePropertiesHandler::TablePropertiesHandler() :
+ m_pCurrentInteropGrabBag(nullptr),
+ m_pTableManager( nullptr )
+ {
+ }
+
+ bool TablePropertiesHandler::sprm(Sprm & rSprm)
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("TablePropertiesHandler.sprm");
+ TagLogger::getInstance().attribute("sprm", rSprm.toString());
+#endif
+
+ bool bRet = true;
+ sal_uInt32 nSprmId = rSprm.getId();
+ Value::Pointer_t pValue = rSprm.getValue();
+ sal_Int32 nIntValue = (pValue ? pValue->getInt() : 0);
+ switch( nSprmId )
+ {
+ case NS_ooxml::LN_CT_TrPrBase_jc:
+ case NS_ooxml::LN_CT_TblPrBase_jc:
+ {
+ sal_Int16 nOrient = ConversionHelper::convertTableJustification( nIntValue );
+ TablePropertyMapPtr pTableMap( new TablePropertyMap );
+ pTableMap->setValue( TablePropertyMap::HORI_ORIENT, nOrient );
+ insertTableProps( pTableMap );
+ }
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_trHeight:
+ {
+ //contains unit and value
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ { //contains attributes x2902 (LN_unit) and x17e2 (LN_trleft)
+ MeasureHandlerPtr pMeasureHandler( new MeasureHandler );
+ pProperties->resolve(*pMeasureHandler);
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+
+ pPropMap->Insert( PROP_SIZE_TYPE, uno::Any( pMeasureHandler->GetRowHeightSizeType() ), false);
+ pPropMap->Insert( PROP_HEIGHT, uno::Any(pMeasureHandler->getMeasureValue() ));
+
+ insertRowProps(pPropMap);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TrPr_ins:
+ case NS_ooxml::LN_CT_TrPr_del:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ sal_Int32 nToken = sal_Int32();
+ switch( nSprmId )
+ {
+ case NS_ooxml::LN_CT_TrPr_ins:
+ nToken = XML_tableRowInsert;
+ break;
+ case NS_ooxml::LN_CT_TrPr_del:
+ nToken = XML_tableRowDelete;
+ break;
+ }
+ auto pTrackChangesHandler = std::make_shared<TrackChangesHandler>( nToken );
+ pProperties->resolve(*pTrackChangesHandler);
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+
+ // Add the 'track changes' properties to the 'table row' via UNO.
+ // This way - in the SW core - when it receives this - it will create a new 'Table Redline' object for that row
+ uno::Sequence<beans::PropertyValue> aTableRedlineProperties = pTrackChangesHandler->getRedlineProperties();
+ pPropMap->Insert( PROP_TABLE_REDLINE_PARAMS , uno::Any( aTableRedlineProperties ));
+ insertRowProps(pPropMap);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_cellIns:
+ case NS_ooxml::LN_CT_TcPrBase_cellDel:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ sal_Int32 nToken;
+ switch( nSprmId )
+ {
+ case NS_ooxml::LN_CT_TcPrBase_cellIns:
+ nToken = XML_tableCellInsert;
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_cellDel:
+ nToken = XML_tableCellDelete;
+ break;
+ default:
+ throw lang::IllegalArgumentException("illegal redline token type", nullptr, 0);
+ break;
+ }
+ auto pTrackChangesHandler = std::make_shared<TrackChangesHandler>( nToken );
+ pProperties->resolve(*pTrackChangesHandler);
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+
+ // Add the 'track changes' properties to the 'table row' via UNO.
+ // This way - in the SW core - when it receives this - it will create a new 'Table Redline' object for that row
+ uno::Sequence<beans::PropertyValue> aTableRedlineProperties = pTrackChangesHandler->getRedlineProperties();
+ pPropMap->Insert( PROP_TABLE_REDLINE_PARAMS , uno::Any( aTableRedlineProperties ));
+ cellProps(pPropMap);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_cantSplit:
+ {
+ //row can't break across pages if nIntValue == 1
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( PROP_IS_SPLIT_ALLOWED, uno::Any( nIntValue != 1 ) );
+ insertRowProps(pPropMap);
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_vAlign:
+ {
+ sal_Int16 nVertOrient = text::VertOrientation::NONE;
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_ST_VerticalJc_center: nVertOrient = text::VertOrientation::CENTER; break;
+ case NS_ooxml::LN_Value_ST_VerticalJc_bottom: nVertOrient = text::VertOrientation::BOTTOM; break;
+ default:;
+ }
+ TablePropertyMapPtr pCellPropMap( new TablePropertyMap() );
+ pCellPropMap->Insert( PROP_VERT_ORIENT, uno::Any( nVertOrient ) );
+ //todo: in ooxml import the value of m_ncell is wrong
+ cellProps( pCellPropMap );
+ if (m_pCurrentInteropGrabBag)
+ {
+ OUString aVertOrient;
+ switch( nIntValue )
+ {
+ case NS_ooxml::LN_Value_ST_VerticalJc_top: aVertOrient = "top"; break;
+ case NS_ooxml::LN_Value_ST_VerticalJc_center: aVertOrient = "center"; break;
+ case NS_ooxml::LN_Value_ST_VerticalJc_both: aVertOrient = "both"; break;
+ case NS_ooxml::LN_Value_ST_VerticalJc_bottom: aVertOrient = "bottom"; break;
+ }
+ if (!aVertOrient.isEmpty())
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = "vAlign";
+ aValue.Value <<= aVertOrient;
+ m_pCurrentInteropGrabBag->push_back(aValue);
+ }
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblBorders: //table borders, might be defined in table style
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pBorderHandler = std::make_shared<BorderHandler>(true);
+ if (m_pCurrentInteropGrabBag)
+ pBorderHandler->enableInteropGrabBag("tblBorders");
+ pProperties->resolve(*pBorderHandler);
+ if (m_pCurrentInteropGrabBag)
+ m_pCurrentInteropGrabBag->push_back(pBorderHandler->getInteropGrabBag());
+ TablePropertyMapPtr pTablePropMap( new TablePropertyMap );
+ pTablePropMap->InsertProps(pBorderHandler->getProperties());
+
+#ifdef DBG_UTIL
+ pTablePropMap->dumpXml();
+#endif
+ insertTableProps( pTablePropMap );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblLayout:
+ {
+ DomainMapperTableManager* pManager = dynamic_cast<DomainMapperTableManager*>(m_pTableManager);
+ if (pManager)
+ pManager->SetLayoutType(static_cast<sal_uInt32>(nIntValue));
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrEx_tblBorders:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties)
+ {
+ auto pBorderHandler = std::make_shared<BorderHandler>(true);
+ pProperties->resolve(*pBorderHandler);
+ TablePropertyMapPtr pTablePropMap( new TablePropertyMap );
+ pTablePropMap->InsertProps(pBorderHandler->getProperties());
+
+#ifdef DBG_UTIL
+ pTablePropMap->dumpXml();
+#endif
+ tableExceptionProps( pTablePropMap );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_tcBorders ://cell borders
+ //contains CT_TcBorders_left, right, top, bottom
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ //in OOXML there's one set of borders at each cell (if there is any)
+ tools::SvRef< TDefTableHandler > pTDefTableHandler( new TDefTableHandler());
+ if (m_pCurrentInteropGrabBag)
+ pTDefTableHandler->enableInteropGrabBag("tcBorders");
+ pProperties->resolve( *pTDefTableHandler );
+ if (m_pCurrentInteropGrabBag)
+ m_pCurrentInteropGrabBag->push_back(pTDefTableHandler->getInteropGrabBag());
+ TablePropertyMapPtr pCellPropMap( new TablePropertyMap );
+ pTDefTableHandler->fillCellProperties( pCellPropMap );
+ cellProps( pCellPropMap );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_tcMar:
+
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ auto pCellMarginHandler = std::make_shared<CellMarginHandler>();
+ if (m_pCurrentInteropGrabBag)
+ pCellMarginHandler->enableInteropGrabBag("tcMar");
+ pProperties->resolve(*pCellMarginHandler);
+ if (m_pCurrentInteropGrabBag)
+ m_pCurrentInteropGrabBag->push_back(pCellMarginHandler->getInteropGrabBag());
+ TablePropertyMapPtr pCellProperties(new TablePropertyMap);
+ if (pCellMarginHandler->m_bTopMarginValid)
+ pCellProperties->Insert(PROP_TOP_BORDER_DISTANCE, uno::Any(pCellMarginHandler->m_nTopMargin));
+ if (pCellMarginHandler->m_bLeftMarginValid)
+ pCellProperties->Insert(PROP_LEFT_BORDER_DISTANCE, uno::Any(pCellMarginHandler->m_nLeftMargin));
+ if (pCellMarginHandler->m_bBottomMarginValid)
+ pCellProperties->Insert(PROP_BOTTOM_BORDER_DISTANCE, uno::Any(pCellMarginHandler->m_nBottomMargin));
+ if (pCellMarginHandler->m_bRightMarginValid)
+ pCellProperties->Insert(PROP_RIGHT_BORDER_DISTANCE, uno::Any(pCellMarginHandler->m_nRightMargin));
+ cellProps(pCellProperties);
+ }
+ }
+ break;
+/* // tdf#123189 skip to keep MSO interoperability
+ case NS_ooxml::LN_CT_TblPrBase_shd:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties.get())
+ {
+ std::shared_ptr<CellColorHandler> pCellColorHandler( new CellColorHandler);
+ pProperties->resolve( *pCellColorHandler );
+ TablePropertyMapPtr pTablePropMap( new TablePropertyMap );
+ insertTableProps( pCellColorHandler->getProperties() );
+ }
+ }
+*/
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_shd:
+ {
+ // each color sprm contains as much colors as cells are in a row
+ //LN_CT_TcPrBase_shd: cell shading contains: LN_CT_Shd_val, LN_CT_Shd_fill, LN_CT_Shd_color
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pCellColorHandler = std::make_shared<CellColorHandler>();
+ pCellColorHandler->enableInteropGrabBag("shd"); //enable to store shd unsupported props in grab bag
+ pProperties->resolve( *pCellColorHandler );
+ TablePropertyMapPtr pPropertyMap = pCellColorHandler->getProperties();
+ beans::PropertyValue aGrabBag = pCellColorHandler->getInteropGrabBag();
+ if (m_pCurrentInteropGrabBag)
+ m_pCurrentInteropGrabBag->push_back(aGrabBag);
+ pPropertyMap->Insert( PROP_CELL_INTEROP_GRAB_BAG, aGrabBag.Value );
+ cellProps( pPropertyMap );
+ }
+ }
+ break;
+//OOXML table properties
+ case NS_ooxml::LN_CT_TblPrBase_tblCellMar: //cell margins
+ {
+ //contains LN_CT_TblCellMar_top, LN_CT_TblCellMar_left, LN_CT_TblCellMar_bottom, LN_CT_TblCellMar_right
+ // LN_CT_TblCellMar_start, LN_CT_TblCellMar_end
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ auto pCellMarginHandler = std::make_shared<CellMarginHandler>();
+ if (m_pCurrentInteropGrabBag)
+ pCellMarginHandler->enableInteropGrabBag("tblCellMar");
+ pProperties->resolve( *pCellMarginHandler );
+ if (m_pCurrentInteropGrabBag)
+ m_pCurrentInteropGrabBag->push_back(pCellMarginHandler->getInteropGrabBag());
+ TablePropertyMapPtr pMarginProps( new TablePropertyMap );
+ if( pCellMarginHandler->m_bTopMarginValid )
+ pMarginProps->setValue( TablePropertyMap::CELL_MAR_TOP, pCellMarginHandler->m_nTopMargin );
+ if( pCellMarginHandler->m_bBottomMarginValid )
+ pMarginProps->setValue( TablePropertyMap::CELL_MAR_BOTTOM, pCellMarginHandler->m_nBottomMargin );
+ if( pCellMarginHandler->m_bLeftMarginValid )
+ pMarginProps->setValue( TablePropertyMap::CELL_MAR_LEFT, pCellMarginHandler->m_nLeftMargin );
+ if( pCellMarginHandler->m_bRightMarginValid )
+ pMarginProps->setValue( TablePropertyMap::CELL_MAR_RIGHT, pCellMarginHandler->m_nRightMargin );
+ insertTableProps(pMarginProps);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblInd:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ MeasureHandlerPtr pHandler(new MeasureHandler);
+ if (m_pCurrentInteropGrabBag)
+ pHandler->enableInteropGrabBag("tblInd");
+ pProperties->resolve( *pHandler );
+ if (m_pCurrentInteropGrabBag)
+ m_pCurrentInteropGrabBag->push_back(pHandler->getInteropGrabBag());
+ TablePropertyMapPtr pTblIndMap(new TablePropertyMap);
+ sal_uInt32 nTblInd = pHandler->getMeasureValue();
+ pTblIndMap->setValue( TablePropertyMap::LEFT_MARGIN, nTblInd);
+ insertTableProps(pTblIndMap);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_hideMark:
+ if (nIntValue)
+ {
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(PROP_CELL_HIDE_MARK, uno::Any(nIntValue));
+ cellProps(pPropMap);
+ }
+ break;
+ default:
+ // Not handled here, give the next handler a chance.
+ bRet = false;
+ // However, these logically belong here, so save the value if necessary.
+ switch (nSprmId)
+ {
+ case NS_ooxml::LN_CT_TblPrBase_tblStyleRowBandSize:
+ case NS_ooxml::LN_CT_TblPrBase_tblStyleColBandSize:
+ if (m_pCurrentInteropGrabBag)
+ {
+ beans::PropertyValue aValue;
+ aValue.Name = (nSprmId == NS_ooxml::LN_CT_TblPrBase_tblStyleRowBandSize ? std::u16string_view(u"tblStyleRowBandSize") : std::u16string_view(u"tblStyleColBandSize"));
+ aValue.Value <<= nIntValue;
+ m_pCurrentInteropGrabBag->push_back(aValue);
+ }
+ break;
+ }
+ break;
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+
+ return bRet;
+ }
+
+ void TablePropertiesHandler::SetInteropGrabBag(std::vector<beans::PropertyValue>& rValue)
+ {
+ m_pCurrentInteropGrabBag = &rValue;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TablePropertiesHandler.hxx b/writerfilter/source/dmapper/TablePropertiesHandler.hxx
new file mode 100644
index 000000000..bd0901356
--- /dev/null
+++ b/writerfilter/source/dmapper/TablePropertiesHandler.hxx
@@ -0,0 +1,94 @@
+/* -*- 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 "PropertyMap.hxx"
+
+#include "TableManager.hxx"
+#include <dmapper/resourcemodel.hxx>
+
+#include <vector>
+
+namespace writerfilter::dmapper {
+
+class DomainMapper;
+
+class TablePropertiesHandler final : public virtual SvRefBase
+{
+private:
+ PropertyMapPtr m_pCurrentProperties;
+ std::vector<css::beans::PropertyValue>* m_pCurrentInteropGrabBag;
+ TableManager* m_pTableManager;
+
+public:
+ TablePropertiesHandler();
+
+ bool sprm(Sprm & sprm);
+
+ void SetTableManager( TableManager* pTableManager )
+ {
+ m_pTableManager = pTableManager;
+ };
+
+ void SetProperties( PropertyMapPtr pProperties )
+ {
+ m_pCurrentProperties = pProperties;
+ };
+
+ void SetInteropGrabBag(std::vector<css::beans::PropertyValue>& rValue);
+
+private:
+
+ void cellProps( TablePropertyMapPtr pProps )
+ {
+ if ( m_pTableManager )
+ m_pTableManager->cellProps( pProps );
+ else
+ m_pCurrentProperties->InsertProps(pProps.get());
+ };
+
+ void insertRowProps( TablePropertyMapPtr pProps )
+ {
+ if ( m_pTableManager )
+ m_pTableManager->insertRowProps( pProps );
+ else
+ m_pCurrentProperties->InsertProps(pProps.get());
+ };
+
+ void tableExceptionProps( TablePropertyMapPtr pProps )
+ {
+ if ( m_pTableManager )
+ m_pTableManager->tableExceptionProps( pProps );
+ else
+ m_pCurrentProperties->InsertProps(pProps.get());
+ };
+
+ void insertTableProps( TablePropertyMapPtr pProps )
+ {
+ if ( m_pTableManager )
+ m_pTableManager->insertTableProps( pProps );
+ else
+ m_pCurrentProperties->InsertProps(pProps.get());
+ };
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TagLogger.cxx b/writerfilter/source/dmapper/TagLogger.cxx
new file mode 100644
index 000000000..607f01aaf
--- /dev/null
+++ b/writerfilter/source/dmapper/TagLogger.cxx
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+#include "TagLogger.hxx"
+#ifdef DBG_UTIL
+#include <unotools/pathoptions.hxx>
+#endif
+
+using namespace css;
+
+namespace writerfilter
+{
+ TagLogger::TagLogger()
+ : pWriter( nullptr ), pName( "DOMAINMAPPER" )
+ {
+ }
+
+ TagLogger::~TagLogger()
+ {
+ pWriter = nullptr;
+ pName = nullptr;
+ }
+
+#ifdef DBG_UTIL
+ void TagLogger::setFileName( const std::string & filename )
+ {
+ if ( pWriter )
+ endDocument();
+
+ std::string fileName;
+ char * temp = getenv("TAGLOGGERTMP");
+
+ if (temp != nullptr)
+ fileName += temp;
+ else
+ fileName += SvtPathOptions().GetTempPath().toUtf8().getStr();
+
+ std::string sPrefix = filename;
+ size_t nLastSlash = sPrefix.find_last_of('/');
+ size_t nLastBackslash = sPrefix.find_last_of('\\');
+ size_t nCutPos = nLastSlash;
+ if (nLastBackslash < nCutPos)
+ nCutPos = nLastBackslash;
+ if (nCutPos < sPrefix.size())
+ sPrefix = sPrefix.substr(nCutPos + 1);
+
+ fileName += "/";
+ fileName += sPrefix;
+ fileName += ".";
+ fileName += pName;
+ fileName += ".xml";
+
+ pWriter = xmlNewTextWriterFilename( fileName.c_str(), 0 );
+ xmlTextWriterSetIndent(pWriter,1);
+ (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
+ xmlTextWriterSetIndent( pWriter, 4 );
+ }
+
+ void TagLogger::startDocument()
+ {
+ if (!pWriter)
+ return;
+ (void)xmlTextWriterStartDocument( pWriter, nullptr, nullptr, nullptr );
+ (void)xmlTextWriterStartElement( pWriter, BAD_CAST( "root" ) );
+ }
+
+ void TagLogger::endDocument()
+ {
+ if (!pWriter)
+ return;
+ (void)xmlTextWriterEndDocument( pWriter );
+ xmlFreeTextWriter( pWriter );
+ pWriter = nullptr;
+ }
+
+#endif
+
+ TagLogger& TagLogger::getInstance()
+ {
+ static TagLogger theTagLogger;
+ return theTagLogger;
+ }
+
+#ifdef DBG_UTIL
+ void TagLogger::element(const std::string & name)
+ {
+ startElement(name);
+ endElement();
+ }
+
+ void TagLogger::unoPropertySet(const uno::Reference<beans::XPropertySet>& rPropSet)
+ {
+ uno::Reference<beans::XPropertySetInfo> xPropSetInfo(rPropSet->getPropertySetInfo());
+ const uno::Sequence<beans::Property> aProps(xPropSetInfo->getProperties());
+
+ startElement( "unoPropertySet" );
+
+ for (beans::Property const & prop : aProps)
+ {
+ startElement( "property" );
+ OUString sName(prop.Name);
+
+ attribute( "name", sName );
+ try
+ {
+ attribute( "value", rPropSet->getPropertyValue( sName ) );
+ }
+ catch (const uno::Exception &)
+ {
+ startElement( "exception" );
+
+ chars(std::string("getPropertyValue(\""));
+ chars(sName);
+ chars(std::string("\")"));
+
+ endElement( );
+ }
+ endElement( );
+ }
+ endElement( );
+ }
+
+ void TagLogger::startElement(const std::string & name)
+ {
+ if (!pWriter)
+ return;
+ xmlChar* xmlName = xmlCharStrdup( name.c_str() );
+ (void)xmlTextWriterStartElement( pWriter, xmlName );
+ xmlFree( xmlName );
+ }
+#endif
+
+ void TagLogger::attribute(const std::string & name, const std::string & value)
+ {
+ if (!pWriter)
+ return;
+ xmlChar* xmlName = xmlCharStrdup( name.c_str() );
+ xmlChar* xmlValue = xmlCharStrdup( value.c_str() );
+ (void)xmlTextWriterWriteAttribute( pWriter, xmlName, xmlValue );
+
+ xmlFree( xmlValue );
+ xmlFree( xmlName );
+ }
+
+#ifdef DBG_UTIL
+ void TagLogger::attribute(const std::string & name, std::u16string_view value)
+ {
+ attribute( name, OUStringToOString( value, RTL_TEXTENCODING_ASCII_US ).getStr() );
+ }
+
+ void TagLogger::attribute(const std::string & name, sal_uInt32 value)
+ {
+ if (!pWriter)
+ return;
+ xmlChar* xmlName = xmlCharStrdup( name.c_str() );
+ (void)xmlTextWriterWriteFormatAttribute( pWriter, xmlName,
+ "%" SAL_PRIuUINT32, value );
+ xmlFree( xmlName );
+ }
+
+ void TagLogger::attribute(const std::string & name, const uno::Any& aAny)
+ {
+ if (!pWriter)
+ return;
+
+ sal_Int32 nInt = 0;
+ float nFloat = 0.0;
+ OUString aStr;
+
+ xmlChar* xmlName = xmlCharStrdup( name.c_str() );
+ if ( aAny >>= nInt )
+ {
+ (void)xmlTextWriterWriteFormatAttribute( pWriter, xmlName,
+ "%" SAL_PRIdINT32, nInt );
+ }
+ else if ( aAny >>= nFloat )
+ {
+ (void)xmlTextWriterWriteFormatAttribute( pWriter, xmlName,
+ "%f", nFloat );
+ }
+ else if ( aAny >>= aStr )
+ {
+ attribute( name, aStr );
+ }
+ xmlFree( xmlName );
+ }
+
+ void TagLogger::chars(const std::string & rChars)
+ {
+ if (!pWriter)
+ return;
+ xmlChar* xmlChars = xmlCharStrdup( rChars.c_str() );
+ (void)xmlTextWriterWriteString( pWriter, xmlChars );
+ xmlFree( xmlChars );
+ }
+
+ void TagLogger::chars(std::u16string_view rChars)
+ {
+ chars(OUStringToOString(rChars, RTL_TEXTENCODING_ASCII_US).getStr());
+ }
+
+ void TagLogger::endElement()
+ {
+ if (!pWriter)
+ return;
+ (void)xmlTextWriterEndElement( pWriter );
+ }
+
+#endif
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TagLogger.hxx b/writerfilter/source/dmapper/TagLogger.hxx
new file mode 100644
index 000000000..33da346ee
--- /dev/null
+++ b/writerfilter/source/dmapper/TagLogger.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <tools/ref.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <string>
+#include <string_view>
+#include <libxml/xmlwriter.h>
+
+namespace writerfilter
+{
+
+ class TagLogger
+ {
+ private:
+ xmlTextWriterPtr pWriter;
+ const char* pName;
+
+ public:
+ explicit TagLogger();
+ ~TagLogger();
+
+ static TagLogger& getInstance();
+
+#ifdef DBG_UTIL
+ void setFileName(const std::string & filename);
+ void startDocument();
+ void endDocument();
+
+ void element(const std::string & name);
+ void unoPropertySet(const css::uno::Reference<css::beans::XPropertySet>& rPropSet);
+ void startElement(const std::string & name);
+#endif
+ void attribute(const std::string & name, const std::string & value);
+#ifdef DBG_UTIL
+ void attribute(const std::string & name, std::u16string_view value);
+ void attribute(const std::string & name, sal_uInt32 value);
+ void attribute(const std::string & name, const css::uno::Any& aAny);
+ void chars(const std::string & chars);
+ void chars(std::u16string_view chars);
+ void endElement();
+#endif
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TblStylePrHandler.cxx b/writerfilter/source/dmapper/TblStylePrHandler.cxx
new file mode 100644
index 000000000..13656e169
--- /dev/null
+++ b/writerfilter/source/dmapper/TblStylePrHandler.cxx
@@ -0,0 +1,259 @@
+/* -*- 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 "TblStylePrHandler.hxx"
+#include "TagLogger.hxx"
+#include "CellMarginHandler.hxx"
+#include "PropertyMap.hxx"
+#include "MeasureHandler.hxx"
+#include <ooxml/resourceids.hxx>
+#include <comphelper/sequence.hxx>
+
+
+using namespace css;
+
+namespace writerfilter::dmapper {
+
+TblStylePrHandler::TblStylePrHandler( DomainMapper & rDMapper ) :
+LoggedProperties("TblStylePrHandler"),
+m_rDMapper( rDMapper ),
+m_pTablePropsHandler(new TablePropertiesHandler()),
+m_nType( TBL_STYLE_UNKNOWN ),
+m_pProperties( new PropertyMap )
+{
+}
+
+TblStylePrHandler::~TblStylePrHandler( )
+{
+}
+
+OUString TblStylePrHandler::getTypeString() const
+{
+ switch (m_nType)
+ {
+ case TBL_STYLE_WHOLETABLE: return "wholeTable";
+ case TBL_STYLE_FIRSTROW: return "firstRow";
+ case TBL_STYLE_LASTROW: return "lastRow";
+ case TBL_STYLE_FIRSTCOL: return "firstCol";
+ case TBL_STYLE_LASTCOL: return "lastCol";
+ case TBL_STYLE_BAND1VERT: return "band1Vert";
+ case TBL_STYLE_BAND2VERT: return "band2Vert";
+ case TBL_STYLE_BAND1HORZ: return "band1Horz";
+ case TBL_STYLE_BAND2HORZ: return "band2Horz";
+ case TBL_STYLE_NECELL: return "neCell";
+ case TBL_STYLE_NWCELL: return "nwCell";
+ case TBL_STYLE_SECELL: return "seCell";
+ case TBL_STYLE_SWCELL: return "swCell";
+ default: break;
+ }
+ return OUString();
+}
+
+void TblStylePrHandler::lcl_attribute(Id rName, Value & rVal)
+{
+
+ switch ( rName )
+ {
+ case NS_ooxml::LN_CT_TblStyleOverrideType:
+ {
+ switch (rVal.getInt())
+ {
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_wholeTable:
+ m_nType = TBL_STYLE_WHOLETABLE;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_firstRow:
+ m_nType = TBL_STYLE_FIRSTROW;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_lastRow:
+ m_nType = TBL_STYLE_LASTROW;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_firstCol:
+ m_nType = TBL_STYLE_FIRSTCOL;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_lastCol:
+ m_nType = TBL_STYLE_LASTCOL;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_band1Vert:
+ m_nType = TBL_STYLE_BAND1VERT;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_band2Vert:
+ m_nType = TBL_STYLE_BAND2VERT;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_band1Horz:
+ m_nType = TBL_STYLE_BAND1HORZ;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_band2Horz:
+ m_nType = TBL_STYLE_BAND2HORZ;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_neCell:
+ m_nType = TBL_STYLE_NECELL;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_nwCell:
+ m_nType = TBL_STYLE_NWCELL;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_seCell:
+ m_nType = TBL_STYLE_SECELL;
+ break;
+ case NS_ooxml::LN_Value_ST_TblStyleOverrideType_swCell:
+ m_nType = TBL_STYLE_SWCELL;
+ break;
+ }
+ }
+ break;
+ }
+}
+
+void TblStylePrHandler::lcl_sprm(Sprm & rSprm)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("TblStylePrHandler.sprm");
+ TagLogger::getInstance().attribute("sprm", rSprm.toString());
+#endif
+
+ switch ( rSprm.getId( ) )
+ {
+ case NS_ooxml::LN_CT_PPrBase:
+ case NS_ooxml::LN_EG_RPrBase:
+ case NS_ooxml::LN_CT_TblPrBase:
+ case NS_ooxml::LN_CT_TrPrBase:
+ case NS_ooxml::LN_CT_TcPrBase:
+ {
+ std::vector<beans::PropertyValue> aSavedGrabBag;
+ bool bGrabBag = rSprm.getId() == NS_ooxml::LN_CT_PPrBase ||
+ rSprm.getId() == NS_ooxml::LN_EG_RPrBase ||
+ rSprm.getId() == NS_ooxml::LN_CT_TblPrBase ||
+ rSprm.getId() == NS_ooxml::LN_CT_TrPrBase ||
+ rSprm.getId() == NS_ooxml::LN_CT_TcPrBase;
+ if (bGrabBag)
+ {
+ std::swap(aSavedGrabBag, m_aInteropGrabBag);
+ }
+ resolveSprmProps( rSprm );
+ if (bGrabBag)
+ {
+ if (rSprm.getId() == NS_ooxml::LN_CT_PPrBase)
+ aSavedGrabBag.push_back(getInteropGrabBag("pPr"));
+ else if (rSprm.getId() == NS_ooxml::LN_EG_RPrBase)
+ aSavedGrabBag.push_back(getInteropGrabBag("rPr"));
+ else if (rSprm.getId() == NS_ooxml::LN_CT_TblPrBase)
+ aSavedGrabBag.push_back(getInteropGrabBag("tblPr"));
+ else if (rSprm.getId() == NS_ooxml::LN_CT_TrPrBase)
+ aSavedGrabBag.push_back(getInteropGrabBag("trPr"));
+ else if (rSprm.getId() == NS_ooxml::LN_CT_TcPrBase)
+ aSavedGrabBag.push_back(getInteropGrabBag("tcPr"));
+ std::swap(m_aInteropGrabBag, aSavedGrabBag);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_tblHeader:
+ {
+ m_pProperties->Insert( PROP_HEADER_ROW_COUNT, uno::Any(sal_Int32(1)));
+ beans::PropertyValue aValue;
+ aValue.Name = "tblHeader";
+ aValue.Value <<= true;
+ m_aInteropGrabBag.push_back(aValue);
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblInd:
+ {
+ //contains unit and value
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ MeasureHandlerPtr pMeasureHandler( new MeasureHandler );
+ pProperties->resolve(*pMeasureHandler);
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->setValue( TablePropertyMap::LEFT_MARGIN, pMeasureHandler->getMeasureValue() );
+ m_pProperties->Insert( PROP_LEFT_MARGIN, uno::Any(pMeasureHandler->getMeasureValue()) );
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblCellMar:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if ( pProperties )
+ {
+ auto pCellMarginHandler = std::make_shared<CellMarginHandler>();
+ pCellMarginHandler->enableInteropGrabBag("tblCellMar");
+ pProperties->resolve( *pCellMarginHandler );
+ m_aInteropGrabBag.push_back(pCellMarginHandler->getInteropGrabBag());
+
+ if( pCellMarginHandler->m_bTopMarginValid )
+ m_pProperties->Insert( META_PROP_CELL_MAR_TOP, uno::Any(pCellMarginHandler->m_nTopMargin) );
+ if( pCellMarginHandler->m_bBottomMarginValid )
+ m_pProperties->Insert( META_PROP_CELL_MAR_BOTTOM, uno::Any(pCellMarginHandler->m_nBottomMargin) );
+ if( pCellMarginHandler->m_bLeftMarginValid )
+ m_pProperties->Insert( META_PROP_CELL_MAR_LEFT, uno::Any(pCellMarginHandler->m_nLeftMargin) );
+ if( pCellMarginHandler->m_bRightMarginValid )
+ m_pProperties->Insert( META_PROP_CELL_MAR_RIGHT, uno::Any(pCellMarginHandler->m_nRightMargin) );
+ }
+ }
+ break;
+ default:
+ // Tables specific properties have to handled here
+ m_pTablePropsHandler->SetProperties( m_pProperties );
+ m_pTablePropsHandler->SetInteropGrabBag(m_aInteropGrabBag);
+ bool bRet = m_pTablePropsHandler->sprm( rSprm );
+
+ if ( !bRet )
+ {
+ // The DomainMapper can handle some of the properties
+ m_rDMapper.PushStyleSheetProperties( m_pProperties, true );
+ // Just pass a non-empty string, the array will have a single element anyway.
+ m_rDMapper.enableInteropGrabBag("TblStylePrHandler");
+ m_rDMapper.sprm( rSprm );
+ uno::Sequence<beans::PropertyValue> aGrabBag = m_rDMapper.getInteropGrabBag().Value.get< uno::Sequence<beans::PropertyValue> >();
+ if (aGrabBag.hasElements())
+ m_aInteropGrabBag.push_back(aGrabBag[0]);
+ m_rDMapper.PopStyleSheetProperties( true );
+ }
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void TblStylePrHandler::resolveSprmProps(Sprm & rSprm)
+{
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ pProperties->resolve(*this);
+}
+
+void TblStylePrHandler::appendInteropGrabBag(const OUString& aKey, const OUString& aValue)
+{
+ beans::PropertyValue aProperty;
+ aProperty.Name = aKey;
+ aProperty.Value <<= aValue;
+ m_aInteropGrabBag.push_back(aProperty);
+}
+
+beans::PropertyValue TblStylePrHandler::getInteropGrabBag(const OUString& aName)
+{
+ beans::PropertyValue aRet;
+ aRet.Name = aName;
+
+ aRet.Value <<= comphelper::containerToSequence(m_aInteropGrabBag);
+ return aRet;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TblStylePrHandler.hxx b/writerfilter/source/dmapper/TblStylePrHandler.hxx
new file mode 100644
index 000000000..4be7d379a
--- /dev/null
+++ b/writerfilter/source/dmapper/TblStylePrHandler.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 .
+ */
+
+#pragma once
+
+#include "TablePropertiesHandler.hxx"
+
+#include "DomainMapper.hxx"
+#include "LoggedResources.hxx"
+#include <memory>
+#include <vector>
+
+namespace writerfilter::dmapper {
+
+class DomainMapper;
+
+enum TblStyleType
+{
+ TBL_STYLE_UNKNOWN,
+ TBL_STYLE_WHOLETABLE,
+ TBL_STYLE_FIRSTROW,
+ TBL_STYLE_LASTROW,
+ TBL_STYLE_FIRSTCOL,
+ TBL_STYLE_LASTCOL,
+ TBL_STYLE_BAND1VERT,
+ TBL_STYLE_BAND2VERT,
+ TBL_STYLE_BAND1HORZ,
+ TBL_STYLE_BAND2HORZ,
+ TBL_STYLE_NECELL,
+ TBL_STYLE_NWCELL,
+ TBL_STYLE_SECELL,
+ TBL_STYLE_SWCELL
+};
+
+class TblStylePrHandler : public LoggedProperties
+{
+private:
+ DomainMapper & m_rDMapper;
+ std::unique_ptr<TablePropertiesHandler> m_pTablePropsHandler;
+
+ TblStyleType m_nType;
+ PropertyMapPtr m_pProperties;
+ std::vector<css::beans::PropertyValue> m_aInteropGrabBag;
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value & val) override;
+ virtual void lcl_sprm(Sprm & sprm) override;
+
+public:
+ explicit TblStylePrHandler( DomainMapper & rDMapper );
+ virtual ~TblStylePrHandler( ) override;
+
+ const PropertyMapPtr& getProperties() const { return m_pProperties; };
+ TblStyleType getType() const { return m_nType; };
+ OUString getTypeString() const;
+ void appendInteropGrabBag(const OUString& aKey, const OUString& aValue);
+ css::beans::PropertyValue getInteropGrabBag(const OUString& aName);
+
+private:
+
+ void resolveSprmProps(Sprm & rSprm);
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TextEffectsHandler.cxx b/writerfilter/source/dmapper/TextEffectsHandler.cxx
new file mode 100644
index 000000000..cce02393c
--- /dev/null
+++ b/writerfilter/source/dmapper/TextEffectsHandler.cxx
@@ -0,0 +1,804 @@
+/* -*- 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 <sal/config.h>
+
+#include <map>
+
+#include "TextEffectsHandler.hxx"
+
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/string.hxx>
+#include <ooxml/resourceids.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+
+namespace writerfilter::dmapper
+{
+
+using namespace com::sun::star;
+
+namespace
+{
+
+OUString lclGetNameForElementId(sal_uInt32 aId)
+{
+ static std::map<sal_uInt32, OUString> aIdMap;
+ if(aIdMap.empty())
+ {
+ aIdMap[NS_ooxml::LN_EG_ColorChoice_srgbClr] = "srgbClr";
+ aIdMap[NS_ooxml::LN_EG_ColorChoice_schemeClr] = "schemeClr";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_tint] = "tint";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_shade] = "shade";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_alpha] = "alpha";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_hueMod] = "hueMod";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_sat] = "sat";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_satOff] = "satOff";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_satMod] = "satMod";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_lum] = "lum";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_lumOff] = "lumOff";
+ aIdMap[NS_ooxml::LN_EG_ColorTransform_lumMod] = "lumMod";
+ aIdMap[NS_ooxml::LN_EG_FillProperties_noFill] = "noFill";
+ aIdMap[NS_ooxml::LN_EG_FillProperties_solidFill] = "solidFill";
+ aIdMap[NS_ooxml::LN_EG_FillProperties_gradFill] = "gradFill";
+ aIdMap[NS_ooxml::LN_CT_GradientFillProperties_gsLst] = "gsLst";
+ aIdMap[NS_ooxml::LN_CT_GradientStopList_gs] = "gs";
+ aIdMap[NS_ooxml::LN_CT_GradientStop_pos] = "pos";
+ aIdMap[NS_ooxml::LN_EG_ShadeProperties_lin] = "lin";
+ aIdMap[NS_ooxml::LN_EG_ShadeProperties_path] = "path";
+ aIdMap[NS_ooxml::LN_CT_PathShadeProperties_fillToRect] = "fillToRect";
+ aIdMap[NS_ooxml::LN_EG_LineDashProperties_prstDash] = "prstDash";
+ aIdMap[NS_ooxml::LN_EG_LineJoinProperties_round] = "round";
+ aIdMap[NS_ooxml::LN_EG_LineJoinProperties_bevel] = "bevel";
+ aIdMap[NS_ooxml::LN_EG_LineJoinProperties_miter] = "miter";
+ aIdMap[NS_ooxml::LN_CT_Scene3D_camera] = "camera";
+ aIdMap[NS_ooxml::LN_CT_Scene3D_lightRig] = "lightRig";
+ aIdMap[NS_ooxml::LN_CT_LightRig_rot] = "rot";
+ aIdMap[NS_ooxml::LN_CT_Props3D_bevelT] = "bevelT";
+ aIdMap[NS_ooxml::LN_CT_Props3D_bevelB] = "bevelB";
+ aIdMap[NS_ooxml::LN_CT_Props3D_extrusionClr] = "extrusionClr";
+ aIdMap[NS_ooxml::LN_CT_Props3D_contourClr] = "contourClr";
+ aIdMap[NS_ooxml::LN_CT_StylisticSets_styleSet] = "styleSet";
+ aIdMap[NS_ooxml::LN_cntxtAlts_cntxtAlts] = "cntxtAlts";
+ }
+ return aIdMap[aId];
+}
+
+constexpr OUStringLiteral constAttributesSequenceName = u"attributes";
+
+}
+
+OUString TextEffectsHandler::getSchemeColorValTypeString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_SchemeColorVal_bg1: return "bg1";
+ case NS_ooxml::LN_ST_SchemeColorVal_tx1: return "tx1";
+ case NS_ooxml::LN_ST_SchemeColorVal_bg2: return "bg2";
+ case NS_ooxml::LN_ST_SchemeColorVal_tx2: return "tx2";
+ case NS_ooxml::LN_ST_SchemeColorVal_accent1: return "accent1";
+ case NS_ooxml::LN_ST_SchemeColorVal_accent2: return "accent2";
+ case NS_ooxml::LN_ST_SchemeColorVal_accent3: return "accent3";
+ case NS_ooxml::LN_ST_SchemeColorVal_accent4: return "accent4";
+ case NS_ooxml::LN_ST_SchemeColorVal_accent5: return "accent5";
+ case NS_ooxml::LN_ST_SchemeColorVal_accent6: return "accent6";
+ case NS_ooxml::LN_ST_SchemeColorVal_hlink: return "hlink";
+ case NS_ooxml::LN_ST_SchemeColorVal_folHlink: return "folHlink";
+ case NS_ooxml::LN_ST_SchemeColorVal_dk1: return "dk1";
+ case NS_ooxml::LN_ST_SchemeColorVal_lt1: return "lt1";
+ case NS_ooxml::LN_ST_SchemeColorVal_dk2: return "dk2";
+ case NS_ooxml::LN_ST_SchemeColorVal_lt2: return "lt2";
+ case NS_ooxml::LN_ST_SchemeColorVal_phClr: return "phClr";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getRectAlignmentString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_RectAlignment_none: return "none";
+ case NS_ooxml::LN_ST_RectAlignment_tl: return "tl";
+ case NS_ooxml::LN_ST_RectAlignment_t: return "t";
+ case NS_ooxml::LN_ST_RectAlignment_tr: return "tr";
+ case NS_ooxml::LN_ST_RectAlignment_l: return "l";
+ case NS_ooxml::LN_ST_RectAlignment_ctr: return "ctr";
+ case NS_ooxml::LN_ST_RectAlignment_r: return "r";
+ case NS_ooxml::LN_ST_RectAlignment_bl: return "bl";
+ case NS_ooxml::LN_ST_RectAlignment_b: return "b";
+ case NS_ooxml::LN_ST_RectAlignment_br: return "br";
+
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getLineCapString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_LineCap_rnd: return "rnd";
+ case NS_ooxml::LN_ST_LineCap_sq: return "sq";
+ case NS_ooxml::LN_ST_LineCap_flat: return "flat";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getCompoundLineString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_CompoundLine_sng: return "sng";
+ case NS_ooxml::LN_ST_CompoundLine_dbl: return "dbl";
+ case NS_ooxml::LN_ST_CompoundLine_thickThin: return "thickThin";
+ case NS_ooxml::LN_ST_CompoundLine_thinThick: return "thinThick";
+ case NS_ooxml::LN_ST_CompoundLine_tri: return "tri";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getPenAlignmentString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_PenAlignment_ctr: return "ctr";
+ case NS_ooxml::LN_ST_PenAlignment_in: return "in";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getOnOffString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_OnOff_true: return "true";
+ case NS_ooxml::LN_ST_OnOff_false: return "false";
+ case NS_ooxml::LN_ST_OnOff_1: return "1";
+ case NS_ooxml::LN_ST_OnOff_0: return "0";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getPathShadeTypeString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_PathShadeType_shape: return "shape";
+ case NS_ooxml::LN_ST_PathShadeType_circle: return "circle";
+ case NS_ooxml::LN_ST_PathShadeType_rect: return "rect";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getPresetLineDashValString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_PresetLineDashVal_solid: return "solid";
+ case NS_ooxml::LN_ST_PresetLineDashVal_dot: return "dot";
+ case NS_ooxml::LN_ST_PresetLineDashVal_sysDot: return "sysDot";
+ case NS_ooxml::LN_ST_PresetLineDashVal_dash: return "dash";
+ case NS_ooxml::LN_ST_PresetLineDashVal_sysDash: return "sysDash";
+ case NS_ooxml::LN_ST_PresetLineDashVal_lgDash: return "lgDash";
+ case NS_ooxml::LN_ST_PresetLineDashVal_dashDot: return "dashDot";
+ case NS_ooxml::LN_ST_PresetLineDashVal_sysDashDot: return "sysDashDot";
+ case NS_ooxml::LN_ST_PresetLineDashVal_lgDashDot: return "lgDashDot";
+ case NS_ooxml::LN_ST_PresetLineDashVal_lgDashDotDot: return "lgDashDotDot";
+ case NS_ooxml::LN_ST_PresetLineDashVal_sysDashDotDot: return "sysDashDotDot";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getPresetCameraTypeString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_PresetCameraType_legacyObliqueTopLeft: return "legacyObliqueTopLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyObliqueTop: return "legacyObliqueTop";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyObliqueTopRight: return "legacyObliqueTopRight";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyObliqueLeft: return "legacyObliqueLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyObliqueFront: return "legacyObliqueFront";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyObliqueRight: return "legacyObliqueRight";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyObliqueBottomLeft: return "legacyObliqueBottomLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyObliqueBottom: return "legacyObliqueBottom";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyObliqueBottomRight: return "legacyObliqueBottomRight";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyPerspectiveTopLeft: return "legacyPerspectiveTopLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyPerspectiveTop: return "legacyPerspectiveTop";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyPerspectiveTopRight: return "legacyPerspectiveTopRight";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyPerspectiveLeft: return "legacyPerspectiveLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyPerspectiveFront: return "legacyPerspectiveFront";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyPerspectiveRight: return "legacyPerspectiveRight";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyPerspectiveBottomLeft: return "legacyPerspectiveBottomLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyPerspectiveBottom: return "legacyPerspectiveBottom";
+ case NS_ooxml::LN_ST_PresetCameraType_legacyPerspectiveBottomRight: return "legacyPerspectiveBottomRight";
+ case NS_ooxml::LN_ST_PresetCameraType_orthographicFront: return "orthographicFront";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricTopUp: return "isometricTopUp";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricTopDown: return "isometricTopDown";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricBottomUp: return "isometricBottomUp";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricBottomDown: return "isometricBottomDown";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricLeftUp: return "isometricLeftUp";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricLeftDown: return "isometricLeftDown";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricRightUp: return "isometricRightUp";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricRightDown: return "isometricRightDown";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis1Left: return "isometricOffAxis1Left";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis1Right: return "isometricOffAxis1Right";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis1Top: return "isometricOffAxis1Top";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis2Left: return "isometricOffAxis2Left";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis2Right: return "isometricOffAxis2Right";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis2Top: return "isometricOffAxis2Top";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis3Left: return "isometricOffAxis3Left";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis3Right: return "isometricOffAxis3Right";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis3Bottom: return "isometricOffAxis3Bottom";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis4Left: return "isometricOffAxis4Left";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis4Right: return "isometricOffAxis4Right";
+ case NS_ooxml::LN_ST_PresetCameraType_isometricOffAxis4Bottom: return "isometricOffAxis4Bottom";
+ case NS_ooxml::LN_ST_PresetCameraType_obliqueTopLeft: return "obliqueTopLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_obliqueTop: return "obliqueTop";
+ case NS_ooxml::LN_ST_PresetCameraType_obliqueTopRight: return "obliqueTopRight";
+ case NS_ooxml::LN_ST_PresetCameraType_obliqueLeft: return "obliqueLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_obliqueRight: return "obliqueRight";
+ case NS_ooxml::LN_ST_PresetCameraType_obliqueBottomLeft: return "obliqueBottomLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_obliqueBottom: return "obliqueBottom";
+ case NS_ooxml::LN_ST_PresetCameraType_obliqueBottomRight: return "obliqueBottomRight";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveFront: return "perspectiveFront";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveLeft: return "perspectiveLeft";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveRight: return "perspectiveRight";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveAbove: return "perspectiveAbove";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveBelow: return "perspectiveBelow";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveAboveLeftFacing: return "perspectiveAboveLeftFacing";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveAboveRightFacing: return "perspectiveAboveRightFacing";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveContrastingLeftFacing: return "perspectiveContrastingLeftFacing";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveContrastingRightFacing: return "perspectiveContrastingRightFacing";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveHeroicLeftFacing: return "perspectiveHeroicLeftFacing";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveHeroicRightFacing: return "perspectiveHeroicRightFacing";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveHeroicExtremeLeftFacing: return "perspectiveHeroicExtremeLeftFacing";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveHeroicExtremeRightFacing: return "perspectiveHeroicExtremeRightFacing";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveRelaxed: return "perspectiveRelaxed";
+ case NS_ooxml::LN_ST_PresetCameraType_perspectiveRelaxedModerately: return "perspectiveRelaxedModerately";
+ default: break;
+ }
+ return OUString();
+}
+
+
+OUString TextEffectsHandler::getLightRigTypeString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_LightRigType_legacyFlat1: return "legacyFlat1";
+ case NS_ooxml::LN_ST_LightRigType_legacyFlat2: return "legacyFlat2";
+ case NS_ooxml::LN_ST_LightRigType_legacyFlat3: return "legacyFlat3";
+ case NS_ooxml::LN_ST_LightRigType_legacyFlat4: return "legacyFlat4";
+ case NS_ooxml::LN_ST_LightRigType_legacyNormal1: return "legacyNormal1";
+ case NS_ooxml::LN_ST_LightRigType_legacyNormal2: return "legacyNormal2";
+ case NS_ooxml::LN_ST_LightRigType_legacyNormal3: return "legacyNormal3";
+ case NS_ooxml::LN_ST_LightRigType_legacyNormal4: return "legacyNormal4";
+ case NS_ooxml::LN_ST_LightRigType_legacyHarsh1: return "legacyHarsh1";
+ case NS_ooxml::LN_ST_LightRigType_legacyHarsh2: return "legacyHarsh2";
+ case NS_ooxml::LN_ST_LightRigType_legacyHarsh3: return "legacyHarsh3";
+ case NS_ooxml::LN_ST_LightRigType_legacyHarsh4: return "legacyHarsh4";
+ case NS_ooxml::LN_ST_LightRigType_threePt: return "threePt";
+ case NS_ooxml::LN_ST_LightRigType_balanced: return "balanced";
+ case NS_ooxml::LN_ST_LightRigType_soft: return "soft";
+ case NS_ooxml::LN_ST_LightRigType_harsh: return "harsh";
+ case NS_ooxml::LN_ST_LightRigType_flood: return "flood";
+ case NS_ooxml::LN_ST_LightRigType_contrasting: return "contrasting";
+ case NS_ooxml::LN_ST_LightRigType_morning: return "morning";
+ case NS_ooxml::LN_ST_LightRigType_sunrise: return "sunrise";
+ case NS_ooxml::LN_ST_LightRigType_sunset: return "sunset";
+ case NS_ooxml::LN_ST_LightRigType_chilly: return "chilly";
+ case NS_ooxml::LN_ST_LightRigType_freezing: return "freezing";
+ case NS_ooxml::LN_ST_LightRigType_flat: return "flat";
+ case NS_ooxml::LN_ST_LightRigType_twoPt: return "twoPt";
+ case NS_ooxml::LN_ST_LightRigType_glow: return "glow";
+ case NS_ooxml::LN_ST_LightRigType_brightRoom: return "brightRoom";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getLightRigDirectionString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_LightRigDirection_tl: return "tl";
+ case NS_ooxml::LN_ST_LightRigDirection_t: return "t";
+ case NS_ooxml::LN_ST_LightRigDirection_tr: return "tr";
+ case NS_ooxml::LN_ST_LightRigDirection_l: return "l";
+ case NS_ooxml::LN_ST_LightRigDirection_r: return "r";
+ case NS_ooxml::LN_ST_LightRigDirection_bl: return "bl";
+ case NS_ooxml::LN_ST_LightRigDirection_b: return "b";
+ case NS_ooxml::LN_ST_LightRigDirection_br: return "br";
+
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getBevelPresetTypeString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_BevelPresetType_relaxedInset: return "relaxedInset";
+ case NS_ooxml::LN_ST_BevelPresetType_circle: return "circle";
+ case NS_ooxml::LN_ST_BevelPresetType_slope: return "slope";
+ case NS_ooxml::LN_ST_BevelPresetType_cross: return "cross";
+ case NS_ooxml::LN_ST_BevelPresetType_angle: return "angle";
+ case NS_ooxml::LN_ST_BevelPresetType_softRound: return "softRound";
+ case NS_ooxml::LN_ST_BevelPresetType_convex: return "convex";
+ case NS_ooxml::LN_ST_BevelPresetType_coolSlant: return "coolSlant";
+ case NS_ooxml::LN_ST_BevelPresetType_divot: return "divot";
+ case NS_ooxml::LN_ST_BevelPresetType_riblet: return "riblet";
+ case NS_ooxml::LN_ST_BevelPresetType_hardEdge: return "hardEdge";
+ case NS_ooxml::LN_ST_BevelPresetType_artDeco: return "artDeco";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getPresetMaterialTypeString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_PresetMaterialType_legacyMatte: return "legacyMatte";
+ case NS_ooxml::LN_ST_PresetMaterialType_legacyPlastic: return "legacyPlastic";
+ case NS_ooxml::LN_ST_PresetMaterialType_legacyMetal: return "legacyMetal";
+ case NS_ooxml::LN_ST_PresetMaterialType_legacyWireframe: return "legacyWireframe";
+ case NS_ooxml::LN_ST_PresetMaterialType_matte: return "matte";
+ case NS_ooxml::LN_ST_PresetMaterialType_plastic: return "plastic";
+ case NS_ooxml::LN_ST_PresetMaterialType_metal: return "metal";
+ case NS_ooxml::LN_ST_PresetMaterialType_warmMatte: return "warmMatte";
+ case NS_ooxml::LN_ST_PresetMaterialType_translucentPowder: return "translucentPowder";
+ case NS_ooxml::LN_ST_PresetMaterialType_powder: return "powder";
+ case NS_ooxml::LN_ST_PresetMaterialType_dkEdge: return "dkEdge";
+ case NS_ooxml::LN_ST_PresetMaterialType_softEdge: return "softEdge";
+ case NS_ooxml::LN_ST_PresetMaterialType_clear: return "clear";
+ case NS_ooxml::LN_ST_PresetMaterialType_flat: return "flat";
+ case NS_ooxml::LN_ST_PresetMaterialType_softmetal: return "softmetal";
+ case NS_ooxml::LN_ST_PresetMaterialType_none: return "none";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getLigaturesString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_Ligatures_none: return "none";
+ case NS_ooxml::LN_ST_Ligatures_standard: return "standard";
+ case NS_ooxml::LN_ST_Ligatures_contextual: return "contextual";
+ case NS_ooxml::LN_ST_Ligatures_historical: return "historical";
+ case NS_ooxml::LN_ST_Ligatures_discretional: return "discretional";
+ case NS_ooxml::LN_ST_Ligatures_standardContextual: return "standardContextual";
+ case NS_ooxml::LN_ST_Ligatures_standardHistorical: return "standardHistorical";
+ case NS_ooxml::LN_ST_Ligatures_contextualHistorical: return "contextualHistorical";
+ case NS_ooxml::LN_ST_Ligatures_standardDiscretional: return "standardDiscretional";
+ case NS_ooxml::LN_ST_Ligatures_contextualDiscretional: return "contextualDiscretional";
+ case NS_ooxml::LN_ST_Ligatures_historicalDiscretional: return "historicalDiscretional";
+ case NS_ooxml::LN_ST_Ligatures_standardContextualHistorical: return "standardContextualHistorical";
+ case NS_ooxml::LN_ST_Ligatures_standardContextualDiscretional: return "standardContextualDiscretional";
+ case NS_ooxml::LN_ST_Ligatures_standardHistoricalDiscretional: return "standardHistoricalDiscretional";
+ case NS_ooxml::LN_ST_Ligatures_contextualHistoricalDiscretional: return "contextualHistoricalDiscretional";
+ case NS_ooxml::LN_ST_Ligatures_all: return "all";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getNumFormString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_NumForm_default: return "default";
+ case NS_ooxml::LN_ST_NumForm_lining: return "lining";
+ case NS_ooxml::LN_ST_NumForm_oldStyle: return "oldStyle";
+ default: break;
+ }
+ return OUString();
+}
+
+OUString TextEffectsHandler::getNumSpacingString(sal_Int32 nType)
+{
+ switch (nType)
+ {
+ case NS_ooxml::LN_ST_NumSpacing_default: return "default";
+ case NS_ooxml::LN_ST_NumSpacing_proportional: return "proportional";
+ case NS_ooxml::LN_ST_NumSpacing_tabular: return "tabular";
+ default: break;
+ }
+ return OUString();
+}
+
+void TextEffectsHandler::convertElementIdToPropertyId(sal_Int32 aElementId)
+{
+ switch(aElementId)
+ {
+ case NS_ooxml::LN_glow_glow:
+ maPropertyId = PROP_CHAR_GLOW_TEXT_EFFECT;
+ maElementName = "glow";
+ break;
+ case NS_ooxml::LN_shadow_shadow:
+ maPropertyId = PROP_CHAR_SHADOW_TEXT_EFFECT;
+ maElementName = "shadow";
+ break;
+ case NS_ooxml::LN_reflection_reflection:
+ maPropertyId = PROP_CHAR_REFLECTION_TEXT_EFFECT;
+ maElementName = "reflection";
+ break;
+ case NS_ooxml::LN_textOutline_textOutline:
+ maPropertyId = PROP_CHAR_TEXTOUTLINE_TEXT_EFFECT;
+ maElementName = "textOutline";
+ break;
+ case NS_ooxml::LN_textFill_textFill:
+ maPropertyId = PROP_CHAR_TEXTFILL_TEXT_EFFECT;
+ maElementName = "textFill";
+ break;
+ case NS_ooxml::LN_scene3d_scene3d:
+ maPropertyId = PROP_CHAR_SCENE3D_TEXT_EFFECT;
+ maElementName = "scene3d";
+ break;
+ case NS_ooxml::LN_props3d_props3d:
+ maPropertyId = PROP_CHAR_PROPS3D_TEXT_EFFECT;
+ maElementName = "props3d";
+ break;
+ case NS_ooxml::LN_ligatures_ligatures:
+ maPropertyId = PROP_CHAR_LIGATURES_TEXT_EFFECT;
+ maElementName = "ligatures";
+ break;
+ case NS_ooxml::LN_numForm_numForm:
+ maPropertyId = PROP_CHAR_NUMFORM_TEXT_EFFECT;
+ maElementName = "numForm";
+ break;
+ case NS_ooxml::LN_numSpacing_numSpacing:
+ maPropertyId = PROP_CHAR_NUMSPACING_TEXT_EFFECT;
+ maElementName = "numSpacing";
+ break;
+ case NS_ooxml::LN_stylisticSets_stylisticSets:
+ maPropertyId = PROP_CHAR_STYLISTICSETS_TEXT_EFFECT;
+ maElementName = "stylisticSets";
+ break;
+ case NS_ooxml::LN_cntxtAlts_cntxtAlts:
+ maPropertyId = PROP_CHAR_CNTXTALTS_TEXT_EFFECT;
+ maElementName = "cntxtAlts";
+ break;
+ default:
+ break;
+ }
+}
+
+TextEffectsHandler::TextEffectsHandler(sal_uInt32 aElementId) :
+ LoggedProperties("TextEffectsHandler")
+{
+ convertElementIdToPropertyId(aElementId);
+ mpGrabBagStack.reset(new oox::GrabBagStack(maElementName));
+}
+
+TextEffectsHandler::~TextEffectsHandler()
+{
+}
+
+
+void TextEffectsHandler::lcl_attribute(Id aName, Value& aValue)
+{
+ if (mpGrabBagStack->getCurrentName() != constAttributesSequenceName)
+ mpGrabBagStack->push(constAttributesSequenceName);
+
+ switch(aName)
+ {
+ case NS_ooxml::LN_CT_Percentage_val:
+ case NS_ooxml::LN_CT_PositiveFixedPercentage_val:
+ case NS_ooxml::LN_CT_PositivePercentage_val:
+ mpGrabBagStack->addInt32("val", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Glow_rad:
+ mpGrabBagStack->addInt32("rad", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_SchemeColor_val:
+ {
+ OUString aString = getSchemeColorValTypeString(sal_Int32(aValue.getInt()));
+ mpGrabBagStack->addString("val", aString);
+ }
+ break;
+ case NS_ooxml::LN_CT_SRgbColor_val:
+ {
+ OUString aBuffer = OUString::number(aValue.getInt(), 16);
+ OUStringBuffer aString;
+ comphelper::string::padToLength(aString, 6 - aBuffer.getLength(), '0');
+ aString.append(aBuffer.getStr());
+ mpGrabBagStack->addString("val", aString.makeStringAndClear().toAsciiUpperCase());
+ }
+ break;
+ case NS_ooxml::LN_CT_Shadow_blurRad:
+ case NS_ooxml::LN_CT_Reflection_blurRad:
+ mpGrabBagStack->addInt32("blurRad", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Reflection_stA:
+ mpGrabBagStack->addInt32("stA", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Reflection_stPos:
+ mpGrabBagStack->addInt32("stPos", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Reflection_endA:
+ mpGrabBagStack->addInt32("endA", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Reflection_endPos:
+ mpGrabBagStack->addInt32("endPos", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Shadow_dist:
+ case NS_ooxml::LN_CT_Reflection_dist:
+ mpGrabBagStack->addInt32("dist", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Shadow_dir:
+ case NS_ooxml::LN_CT_Reflection_dir:
+ mpGrabBagStack->addInt32("dir", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Reflection_fadeDir:
+ mpGrabBagStack->addInt32("fadeDir", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Shadow_sx:
+ case NS_ooxml::LN_CT_Reflection_sx:
+ mpGrabBagStack->addInt32("sx", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Shadow_sy:
+ case NS_ooxml::LN_CT_Reflection_sy:
+ mpGrabBagStack->addInt32("sy", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Shadow_kx:
+ case NS_ooxml::LN_CT_Reflection_kx:
+ mpGrabBagStack->addInt32("kx", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Shadow_ky:
+ case NS_ooxml::LN_CT_Reflection_ky:
+ mpGrabBagStack->addInt32("ky", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Shadow_algn:
+ case NS_ooxml::LN_CT_Reflection_algn:
+ {
+ uno::Any aAny(getRectAlignmentString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("algn", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_TextOutlineEffect_w:
+ mpGrabBagStack->addInt32("w", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_TextOutlineEffect_cap:
+ {
+ uno::Any aAny(getLineCapString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("cap", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_TextOutlineEffect_cmpd:
+ {
+ uno::Any aAny(getCompoundLineString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("cmpd", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_TextOutlineEffect_algn:
+ {
+ uno::Any aAny(getPenAlignmentString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("algn", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_GradientStop_pos:
+ mpGrabBagStack->addInt32("pos", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_LinearShadeProperties_ang:
+ mpGrabBagStack->addInt32("ang", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_LinearShadeProperties_scaled:
+ {
+ uno::Any aAny(getOnOffString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("scaled", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_PathShadeProperties_path:
+ {
+ uno::Any aAny(getPathShadeTypeString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("path", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_RelativeRect_l:
+ mpGrabBagStack->addInt32("l", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_RelativeRect_t:
+ mpGrabBagStack->addInt32("t", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_RelativeRect_r:
+ mpGrabBagStack->addInt32("r", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_RelativeRect_b:
+ mpGrabBagStack->addInt32("b", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_PresetLineDashProperties_val:
+ {
+ uno::Any aAny(getPresetLineDashValString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("val", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_LineJoinMiterProperties_lim:
+ mpGrabBagStack->addInt32("lim", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Camera_prst:
+ {
+ uno::Any aAny(getPresetCameraTypeString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("prst", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_LightRig_rig:
+ {
+ uno::Any aAny(getLightRigTypeString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("rig", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_LightRig_dir:
+ {
+ uno::Any aAny(getLightRigDirectionString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("dir", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_SphereCoords_lat:
+ mpGrabBagStack->addInt32("lat", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_SphereCoords_lon:
+ mpGrabBagStack->addInt32("lon", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_SphereCoords_rev:
+ mpGrabBagStack->addInt32("rev", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Props3D_extrusionH:
+ mpGrabBagStack->addInt32("extrusionH", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Props3D_contourW:
+ mpGrabBagStack->addInt32("contourW", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Props3D_prstMaterial:
+ {
+ uno::Any aAny(getPresetMaterialTypeString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("prstMaterial", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_Bevel_w:
+ mpGrabBagStack->addInt32("w", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Bevel_h:
+ mpGrabBagStack->addInt32("h", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_Bevel_prst:
+ {
+ uno::Any aAny(getBevelPresetTypeString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("prst", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_Ligatures_val:
+ {
+ uno::Any aAny(getLigaturesString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("val", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_NumForm_val:
+ {
+ uno::Any aAny(getNumFormString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("val", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_NumSpacing_val:
+ {
+ uno::Any aAny(getNumSpacingString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("val", aAny);
+ }
+ break;
+ case NS_ooxml::LN_CT_StyleSet_id:
+ mpGrabBagStack->addInt32("id", sal_Int32(aValue.getInt()));
+ break;
+ case NS_ooxml::LN_CT_StyleSet_val:
+ case NS_ooxml::LN_CT_OnOff_val:
+ {
+ uno::Any aAny(getOnOffString(sal_Int32(aValue.getInt())));
+ mpGrabBagStack->appendElement("val", aAny);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void TextEffectsHandler::lcl_sprm(Sprm& rSprm)
+{
+ if (mpGrabBagStack->getCurrentName() == constAttributesSequenceName)
+ mpGrabBagStack->pop();
+
+ sal_uInt32 nSprmId = rSprm.getId();
+ OUString aElementName = lclGetNameForElementId(nSprmId);
+ if(aElementName.isEmpty())
+ {
+ // Element is unknown -> leave.
+ return;
+ }
+
+ mpGrabBagStack->push(aElementName);
+
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( !pProperties )
+ return;
+
+ pProperties->resolve( *this );
+
+ if (mpGrabBagStack->getCurrentName() == constAttributesSequenceName)
+ mpGrabBagStack->pop();
+
+ mpGrabBagStack->pop();
+}
+
+beans::PropertyValue TextEffectsHandler::getInteropGrabBag()
+{
+ beans::PropertyValue aReturn = mpGrabBagStack->getRootProperty();
+ mpGrabBagStack.reset();
+ return aReturn;
+}
+
+sal_uInt8 TextEffectsHandler::GetTextFillSolidFillAlpha(const css::beans::PropertyValue& rValue)
+{
+ if (rValue.Name != "textFill")
+ {
+ return 0;
+ }
+
+ uno::Sequence<beans::PropertyValue> aPropertyValues;
+ rValue.Value >>= aPropertyValues;
+ comphelper::SequenceAsHashMap aMap(aPropertyValues);
+ auto it = aMap.find("solidFill");
+ if (it == aMap.end())
+ {
+ return 0;
+ }
+
+ comphelper::SequenceAsHashMap aSolidFillMap(it->second);
+ it = aSolidFillMap.find("srgbClr");
+ if (it == aSolidFillMap.end())
+ {
+ return 0;
+ }
+
+ comphelper::SequenceAsHashMap aSrgbClrMap(it->second);
+ it = aSrgbClrMap.find("alpha");
+ if (it == aSrgbClrMap.end())
+ {
+ return 0;
+ }
+
+ comphelper::SequenceAsHashMap aAlphaMap(it->second);
+ it = aAlphaMap.find("attributes");
+ if (it == aAlphaMap.end())
+ {
+ return 0;
+ }
+
+ comphelper::SequenceAsHashMap aAttributesMap(it->second);
+ it = aAttributesMap.find("val");
+ if (it == aAttributesMap.end())
+ {
+ return 0;
+ }
+ sal_Int32 nVal = 0;
+ it->second >>= nVal;
+ return nVal / oox::drawingml::PER_PERCENT;
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TextEffectsHandler.hxx b/writerfilter/source/dmapper/TextEffectsHandler.hxx
new file mode 100644
index 000000000..30a8435b2
--- /dev/null
+++ b/writerfilter/source/dmapper/TextEffectsHandler.hxx
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include "LoggedResources.hxx"
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include "PropertyIds.hxx"
+
+#include <oox/helper/grabbagstack.hxx>
+
+#include <memory>
+#include <optional>
+
+namespace writerfilter::dmapper
+{
+/// Class to process all text effects like glow, textOutline, ...
+class TextEffectsHandler : public LoggedProperties
+{
+private:
+ std::optional<PropertyIds> maPropertyId;
+ OUString maElementName;
+ std::unique_ptr<oox::GrabBagStack> mpGrabBagStack;
+
+ void convertElementIdToPropertyId(sal_Int32 aElementId);
+
+public:
+ explicit TextEffectsHandler(sal_uInt32 aElementId);
+ virtual ~TextEffectsHandler() override;
+
+ const std::optional<PropertyIds>& getGrabBagPropertyId() const { return maPropertyId; }
+
+ css::beans::PropertyValue getInteropGrabBag();
+
+ static OUString getSchemeColorValTypeString(sal_Int32 nType);
+ static OUString getRectAlignmentString(sal_Int32 nType);
+ static OUString getLineCapString(sal_Int32 nType);
+ static OUString getCompoundLineString(sal_Int32 nType);
+ static OUString getPenAlignmentString(sal_Int32 nType);
+ static OUString getOnOffString(sal_Int32 nType);
+ static OUString getPathShadeTypeString(sal_Int32 nType);
+ static OUString getPresetLineDashValString(sal_Int32 nType);
+ static OUString getPresetCameraTypeString(sal_Int32 nType);
+ static OUString getLightRigTypeString(sal_Int32 nType);
+ static OUString getLightRigDirectionString(sal_Int32 nType);
+ static OUString getBevelPresetTypeString(sal_Int32 nType);
+ static OUString getPresetMaterialTypeString(sal_Int32 nType);
+ static OUString getLigaturesString(sal_Int32 nType);
+ static OUString getNumFormString(sal_Int32 nType);
+ static OUString getNumSpacingString(sal_Int32 nType);
+
+ static sal_uInt8 GetTextFillSolidFillAlpha(const css::beans::PropertyValue& rValue);
+
+ // LoggedProperties
+ virtual void lcl_attribute(Id aName, Value& aValue) override;
+ virtual void lcl_sprm(Sprm& sprm) override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/ThemeTable.cxx b/writerfilter/source/dmapper/ThemeTable.cxx
new file mode 100644
index 000000000..4d6ed2b3b
--- /dev/null
+++ b/writerfilter/source/dmapper/ThemeTable.cxx
@@ -0,0 +1,563 @@
+/* -*- 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 "TagLogger.hxx"
+#include "ThemeTable.hxx"
+#include <i18nlangtag/languagetag.hxx>
+#include <ooxml/resourceids.hxx>
+
+#include <map>
+
+using namespace com::sun::star;
+
+namespace writerfilter::dmapper
+{
+
+struct ThemeTable_Impl
+{
+ ThemeTable_Impl() :
+ m_currentThemeFontId(0),
+ m_supplementalFontId(0)
+ {}
+ std::map<sal_uInt32, std::map<sal_uInt32, OUString> > m_themeFontMap;
+ sal_uInt32 m_currentThemeFontId;
+ std::map<sal_uInt32, OUString> m_currentFontThemeEntry;
+ OUString m_supplementalFontName;
+ sal_uInt32 m_supplementalFontId;
+ OUString m_themeFontLangEastAsia;
+ OUString m_themeFontLangBidi;
+};
+
+ThemeTable::ThemeTable()
+: LoggedProperties("ThemeTable")
+, LoggedTable("ThemeTable")
+, m_pImpl( new ThemeTable_Impl )
+{
+
+}
+
+ThemeTable::~ThemeTable()
+{
+}
+
+void ThemeTable::lcl_attribute(Id Name, Value & val)
+{
+ OUString sValue = val.getString();
+ switch(Name)
+ {
+ case NS_ooxml::LN_CT_TextFont_typeface:
+ if (!sValue.isEmpty())
+ m_pImpl->m_currentFontThemeEntry[m_pImpl->m_currentThemeFontId] = sValue;
+ break;
+ case NS_ooxml::LN_CT_SupplementalFont_script:
+ if (!sValue.isEmpty())
+ {
+ if (sValue == m_pImpl->m_themeFontLangBidi)
+ m_pImpl->m_supplementalFontId = NS_ooxml::LN_CT_FontCollection_cs;
+ else if (sValue == m_pImpl->m_themeFontLangEastAsia)
+ m_pImpl->m_supplementalFontId = NS_ooxml::LN_CT_FontCollection_ea;
+ }
+ break;
+ case NS_ooxml::LN_CT_SupplementalFont_typeface:
+ if (!sValue.isEmpty())
+ m_pImpl->m_supplementalFontName = sValue;
+ break;
+ default:
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ }
+ if(m_pImpl->m_supplementalFontId && m_pImpl->m_supplementalFontName.getLength() > 0)
+ {
+ m_pImpl->m_currentFontThemeEntry[m_pImpl->m_supplementalFontId] = m_pImpl->m_supplementalFontName;
+ m_pImpl->m_supplementalFontName.clear();
+ m_pImpl->m_supplementalFontId = 0;
+ }
+}
+
+void ThemeTable::lcl_sprm(Sprm& rSprm)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("ThemeTable.sprm");
+ TagLogger::getInstance().chars(rSprm.toString());
+#endif
+
+ m_pImpl->m_supplementalFontName.clear();
+ m_pImpl->m_supplementalFontId = 0;
+
+ sal_uInt32 nSprmId = rSprm.getId();
+ switch(nSprmId)
+ {
+ case NS_ooxml::LN_CT_BaseStyles_fontScheme:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_FontScheme_majorFont:
+ case NS_ooxml::LN_CT_FontScheme_minorFont:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ m_pImpl->m_currentFontThemeEntry = std::map<sal_uInt32, OUString>();
+ if( pProperties )
+ pProperties->resolve(*this);
+ m_pImpl->m_themeFontMap[nSprmId] = m_pImpl->m_currentFontThemeEntry;
+ }
+ break;
+ case NS_ooxml::LN_CT_FontCollection_latin:
+ case NS_ooxml::LN_CT_FontCollection_ea:
+ case NS_ooxml::LN_CT_FontCollection_cs:
+ {
+ m_pImpl->m_currentThemeFontId = nSprmId;
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ pProperties->resolve(*this);
+ }
+ break;
+ case NS_ooxml::LN_CT_FontCollection_font:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties )
+ pProperties->resolve(*this);
+ }
+ break;
+ default:
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ }
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void ThemeTable::lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("ThemeTable.entry");
+#endif
+
+ ref->resolve(*this);
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+OUString ThemeTable::getStringForTheme(const Id id)
+{
+ switch (id)
+ {
+ case NS_ooxml::LN_Value_ST_Theme_majorEastAsia:
+ return "majorEastAsia";
+ case NS_ooxml::LN_Value_ST_Theme_majorBidi:
+ return "majorBidi";
+ case NS_ooxml::LN_Value_ST_Theme_majorAscii:
+ return "majorAscii";
+ case NS_ooxml::LN_Value_ST_Theme_majorHAnsi:
+ return "majorHAnsi";
+ case NS_ooxml::LN_Value_ST_Theme_minorEastAsia:
+ return "minorEastAsia";
+ case NS_ooxml::LN_Value_ST_Theme_minorBidi:
+ return "minorBidi";
+ case NS_ooxml::LN_Value_ST_Theme_minorAscii:
+ return "minorAscii";
+ case NS_ooxml::LN_Value_ST_Theme_minorHAnsi:
+ return "minorHAnsi";
+ }
+ return OUString();
+}
+OUString ThemeTable::getFontNameForTheme(const Id id) const
+{
+ std::map<sal_uInt32, OUString> tmpThemeFontMap;
+ switch (id)
+ {
+ case NS_ooxml::LN_Value_ST_Theme_majorEastAsia:
+ case NS_ooxml::LN_Value_ST_Theme_majorBidi:
+ case NS_ooxml::LN_Value_ST_Theme_majorAscii:
+ case NS_ooxml::LN_Value_ST_Theme_majorHAnsi:
+ tmpThemeFontMap = m_pImpl->m_themeFontMap[NS_ooxml::LN_CT_FontScheme_majorFont];
+ break;
+ case NS_ooxml::LN_Value_ST_Theme_minorEastAsia:
+ case NS_ooxml::LN_Value_ST_Theme_minorBidi:
+ case NS_ooxml::LN_Value_ST_Theme_minorAscii:
+ case NS_ooxml::LN_Value_ST_Theme_minorHAnsi:
+ tmpThemeFontMap = m_pImpl->m_themeFontMap[NS_ooxml::LN_CT_FontScheme_minorFont];
+ break;
+ default:
+ return OUString();
+ }
+
+ switch (id)
+ {
+ case NS_ooxml::LN_Value_ST_Theme_majorAscii:
+ case NS_ooxml::LN_Value_ST_Theme_majorHAnsi:
+ case NS_ooxml::LN_Value_ST_Theme_minorAscii:
+ case NS_ooxml::LN_Value_ST_Theme_minorHAnsi:
+ {
+ std::map<sal_uInt32, OUString>::const_iterator Iter = tmpThemeFontMap.find(NS_ooxml::LN_CT_FontCollection_latin);
+ if (Iter != tmpThemeFontMap.end())
+ return Iter->second;
+ return OUString();
+ }
+ case NS_ooxml::LN_Value_ST_Theme_majorBidi:
+ case NS_ooxml::LN_Value_ST_Theme_minorBidi:
+ {
+ std::map<sal_uInt32, OUString>::const_iterator Iter = tmpThemeFontMap.find(NS_ooxml::LN_CT_FontCollection_cs);
+ if (Iter != tmpThemeFontMap.end())
+ return Iter->second;
+ return OUString();
+ }
+ case NS_ooxml::LN_Value_ST_Theme_majorEastAsia:
+ case NS_ooxml::LN_Value_ST_Theme_minorEastAsia:
+ {
+ std::map<sal_uInt32, OUString>::const_iterator Iter = tmpThemeFontMap.find(NS_ooxml::LN_CT_FontCollection_ea);
+ if (Iter != tmpThemeFontMap.end())
+ return Iter->second;
+ return OUString();
+ }
+ default:
+ return OUString();
+ }
+}
+
+void ThemeTable::setThemeFontLangProperties(const uno::Sequence<beans::PropertyValue>& aPropSeq)
+{
+ for (const auto& rProp : aPropSeq)
+ {
+ OUString sLocaleName;
+ rProp.Value >>= sLocaleName;
+ if (rProp.Name == "eastAsia")
+ m_pImpl->m_themeFontLangEastAsia = fromLocaleToScriptTag(sLocaleName);
+ if (rProp.Name == "bidi")
+ m_pImpl->m_themeFontLangBidi = fromLocaleToScriptTag(sLocaleName);
+
+ }
+}
+
+OUString ThemeTable::fromLocaleToScriptTag(const OUString& sLocale)
+{
+ return fromLCIDToScriptTag(LanguageTag::convertToLanguageType(sLocale));
+}
+
+OUString ThemeTable::fromLCIDToScriptTag(LanguageType lang)
+{
+ // conversion list from:
+ // http://blogs.msdn.com/b/officeinteroperability/archive/2013/04/22/office-open-xml-themes-schemes-and-fonts.aspx
+ switch (static_cast<sal_uInt16>(lang))
+ {
+ case 0x429 : // lidFarsi
+ case 0x401 : // lidArabic
+ case 0x801 : // lidIraq
+ case 0xc01 : // lidEgyptian
+ case 0x1001 : // lidLibya
+ case 0x1401 : // lidAlgerian
+ case 0x1801 : // lidMorocco
+ case 0x1c01 : // lidTunisia
+ case 0x2001 : // lidOman
+ case 0x2401 : // lidYemen
+ case 0x2801 : // lidSyria
+ case 0x2c01 : // lidJordan
+ case 0x3001 : // lidLebanon
+ case 0x3401 : // lidKuwait
+ case 0x3801 : // lidUAE
+ case 0x3c01 : // lidBahrain
+ case 0x4001 : // lidQatar
+ case 0x420 : // lidUrdu
+ case 0x846 : // lidPunjabiPakistan
+ case 0x859 : // lidSindhiPakistan
+ case 0x45f : // lidTamazight
+ case 0x460 : // lidKashmiri
+ case 0x463 : // lidPashto
+ case 0x48c : // lidDari
+ return "Arab";
+ case 0x42b : // lidArmenian
+ return "Armn";
+ case 0x445 : // lidBengali
+ case 0x845 : // lidBengaliBangladesh
+ case 0x44d : // lidAssamese
+ case 0x458 : // lidManipuri
+ return "Beng";
+ case 0x45d : // lidInuktitut
+ return "Cans";
+ case 0x45c : // lidCherokee
+ return "Cher";
+ case 0x419 : // lidRussian
+ case 0x402 : // lidBulgarian
+ case 0x281a : // lidSerbianCyrillic
+ case 0x422 : // lidUkranian
+ case 0x819 : // lidRussianMoldavia
+ case 0xc1a : // lidSerbianCyrillicSerbMont
+ case 0x1c1a : // lidSerbianBosniaHerzegovinaCyrillic
+ case 0x201a : // lidBosnianBosniaHerzegovinaCyrillic
+ case 0x301a : // lidSerbianMontenegroCyrillic
+ case 0x423 : // lidByelorussian
+ case 0x428 : // lidTajik
+ case 0x82c : // lidAzeriCyrillic
+ case 0x42f : // lidMacedonian
+ case 0x43f : // lidKazakh
+ case 0x440 : // lidKyrgyz
+ case 0x843 : // lidUzbekCyrillic
+ case 0x444 : // lidTatar
+ case 0x450 : // lidMongolian
+ case 0x46d : // lidBashkir
+ case 0x485 : // lidSakha
+ return "Cyrl";
+ case 0x439 : // lidHindi
+ case 0x44e : // lidMarathi
+ case 0x44f : // lidSanskrit
+ case 0x457 : // lidKonkani
+ case 0x459 : // lidSindhi
+ case 0x860 : // lidKashmiriIndia
+ case 0x461 : // lidNepali
+ case 0x861 : // lidNepaliIndia
+ return "Deva";
+ case 0x45e : // lidAmharic
+ case 0x473 : // lidTigrignaEthiopic
+ case 0x873 : // lidTigrignaEritrea
+ return "Ethi";
+ case 0x437 : // lidGeorgian
+ return "Geor";
+ case 0x408 : // lidGreek
+ return "Grek";
+ case 0x447 : // lidGujarati
+ return "Gujr";
+ case 0x446 : // lidPunjabi
+ return "Guru";
+ case 0x412 : // lidKoreanExtWansung
+ return "Hang";
+ case 0x804 : // lidChineseSimp
+ case 0x1004 : // lidSingapore
+ return "Hans";
+ case 0x404 : // lidChineseTrad
+ case 0xc04 : // lidHongkong
+ case 0x1404 : // lidMacau
+ return "Hant";
+ case 0x40d : // lidHebrew
+ case 0x43d : // lidYiddish
+ return "Hebr";
+ case 0x411 : // lidJapanese
+ return "Jpan";
+ case 0x453 : // lidKhmer
+ return "Khmr";
+ case 0x44b : // lidKannada
+ return "Knda";
+ case 0x454 : // lidLao
+ return "Laoo";
+ case 0x409 : // lidAmerican
+ case 0xc09 : // lidAustralian
+ case 0x809 : // lidBritish
+ case 0x1009 : // lidEnglishCanadian
+ case 0x403 : // lidCatalan
+ case 0x406 : // lidDanish
+ case 0x413 : // lidDutch
+ case 0x813 : // lidDutchBelgian
+ case 0x479 : // lidPapiamentu
+ case 0x40b : // lidFinnish
+ case 0x40c : // lidFrench
+ case 0xc0c : // lidFrenchCanadian
+ case 0x407 : // lidGerman
+ case 0x807 : // lidSwissGerman
+ case 0xc07 : // lidAustrianGerman
+ case 0x1007 : // lidGermanLuxembourg
+ case 0x1407 : // lidGermanLiechtenstein
+ case 0x410 : // lidItalian
+ case 0x414 : // lidNorskBokmal
+ case 0x814 : // lidNorskNynorsk
+ case 0x416 : // lidPortBrazil
+ case 0x816 : // lidPortIberian
+ case 0x40a : // lidSpanish
+ case 0x41d : // lidSwedish
+ case 0x405 : // lidCzech
+ case 0x40e : // lidHungarian
+ case 0x415 : // lidPolish
+ case 0x41f : // lidTurkish
+ case 0x42d : // lidBasque
+ case 0x424 : // lidSlovenian
+ case 0x426 : // lidLatvian
+ case 0x427 : // lidLithuanian
+ case 0x418 : // lidRomanian
+ case 0x818 : // lidRomanianMoldavia
+ case 0x241a : // lidSerbianLatin
+ case 0x41a : // lidCroatian, lidCroat
+ case 0x491 : // lidGaelicScots
+ case 0x83c : // lidGaelicIrish
+ case 0x430 : // lidSutu
+ case 0x431 : // lidTsonga
+ case 0x432 : // lidTswana
+ case 0x433 : // lidVenda
+ case 0x434 : // lidXhosa
+ case 0x435 : // lidZulu
+ case 0x436 : // lidAfrikaans
+ case 0x425 : // lidEstonian
+ case 0x456 : // lidGalician
+ case 0x41b : // lidSlovak
+ case 0x1409 : // lidEnglishNewZealand
+ case 0x1809 : // lidEnglishIreland
+ case 0x1c09 : // lidEnglishSouthAfrica
+ case 0x2009 : // lidEnglishJamaica
+ case 0x2409 : // lidEnglishCaribbean
+ case 0x2809 : // lidEnglishBelize
+ case 0x2c09 : // lidEnglishTrinidad
+ case 0x3009 : // lidEnglishZimbabwe
+ case 0x3409 : // lidEnglishPhilippines
+ case 0x3809 : // lidEnglishIndonesia
+ case 0x3c09 : // lidEnglishHongKong
+ case 0x4009 : // lidEnglishIndia
+ case 0x4409 : // lidEnglishMalaysia
+ case 0x4809 : // lidEnglishSingapore
+ case 0x80a : // lidSpanishMexican, lidMexican
+ case 0xc0a : // lidSpanishModern
+ case 0x100a : // lidGuatemala
+ case 0x140a : // lidCostaRica
+ case 0x180a : // lidPanama
+ case 0x1c0a : // lidDominicanRepublic
+ case 0x200a : // lidSpanishSA, lidVenezuela
+ case 0x240a : // lidColombia
+ case 0x280a : // lidPeru
+ case 0x2c0a : // lidArgentina
+ case 0x300a : // lidEcuador
+ case 0x340a : // lidChile
+ case 0x380a : // lidUruguay
+ case 0x3c0a : // lidParguay
+ case 0x400a : // lidBolivia
+ case 0x440a : // lidElSalvador
+ case 0x480a : // lidHonduras
+ case 0x4c0a : // lidNicaragua
+ case 0x500a : // lidPuertoRico
+ case 0x540a : // lidSpanishUS
+ case 0x80c : // lidFrenchBelgian
+ case 0x100c : // lidFrenchSwiss
+ case 0x140c : // lidFrenchLuxembourg
+ case 0x180c : // lidFrenchMonaco
+ case 0x1c0c : // lidFrenchWestIndies
+ case 0x200c : // lidFrenchReunion
+ case 0x240c : // lidFrenchCongoDRC, lidFrenchZaire
+ case 0x280c : // lidFrenchSenegal
+ case 0x2c0c : // lidFrenchCameroon
+ case 0x300c : // lidFrenchCotedIvoire
+ case 0x340c : // lidFrenchMali
+ case 0x3c0c : // lidFrenchHaiti
+ case 0x380c : // lidFrenchMorocco
+ case 0x40f : // lidIcelandic
+ case 0x810 : // lidItalianSwiss
+ case 0x417 : // lidRhaetoRomanic, lidRomanic
+ case 0x81a : // lidSerbianLatinSerbMont, lidCroatSerbo
+ case 0x101a : // lidBosniaHerzegovina
+ case 0x141a : // lidBosnianBosniaHerzegovinaLatin
+ case 0x181a : // lidSerbianBosniaHerzegovinaLatin
+ case 0x2c1a : // lidSerbianMontenegroLatin
+ case 0x41c : // lidAlbanian
+ case 0x81d : // lidSwedishFinland
+ case 0x421 : // lidBahasa, lidIndonesian
+ case 0x42c : // lidAzeriLatin
+ case 0x42e : // lidSorbian
+ case 0x82e : // lidLowerSorbian
+ case 0x438 : // lidFaeroese
+ case 0x43a : // lidMaltese
+ case 0x43b : // lidSamiLappish
+ case 0x83b : // lidNorthSamiSwe
+ case 0xc3b : // lidNorthernSamiFi
+ case 0x103b : // lidLuleSamiNor
+ case 0x143b : // lidLuleSamiSwe
+ case 0x183b : // lidSouthSamiNor
+ case 0x1c3b : // lidSouthSamiSwe
+ case 0x203b : // lidSkoltSami
+ case 0x243b : // lidInariSami
+ case 0x43e : // lidMalaysian
+ case 0x83e : // lidMalayBrunei
+ case 0x441 : // lidSwahili
+ case 0x442 : // lidTurkmen
+ case 0x443 : // lidUzbekLatin
+ case 0x452 : // lidWelsh
+ case 0x85d : // lidInuktitutLatin
+ case 0x85f : // lidTamazightLatin
+ case 0x462 : // lidFrisian
+ case 0x464 : // lidFilipino
+ case 0x466 : // lidEdo
+ case 0x467 : // lidFulfulde
+ case 0x468 : // lidHausa
+ case 0x469 : // lidIbibio
+ case 0x46a : // lidYoruba
+ case 0x46b : // lidQuechuaBol
+ case 0x86b : // lidQuechuaEcu
+ case 0xc6b : // lidQuechuaPe
+ case 0x46c : // lidSesothoSaLeboa
+ case 0x46e : // lidLuxembourgish
+ case 0x46f : // lidGreenlandic
+ case 0x470 : // lidIgbo
+ case 0x471 : // lidKanuri
+ case 0x472 : // lidOromo
+ case 0x474 : // lidGuarani
+ case 0x475 : // lidHawaiian
+ case 0x476 : // lidLatin
+ case 0x477 : // lidSomali
+ case 0x47a : // lidMapudungun
+ case 0x47c : // lidMohawk
+ case 0x47e : // lidBreton
+ case 0x481 : // lidMaori
+ case 0x482 : // lidOccitan
+ case 0x483 : // lidCorsican
+ case 0x484 : // lidAlsatian
+ case 0x486 : // lidKiche
+ case 0x487 : // lidKinyarwanda
+ case 0x488 : // lidWolof
+ return "Latn";
+ case 0x44c : // lidMalayalam
+ return "Mlym";
+ case 0x850 : // lidMongolianMongo
+ return "Mong";
+ case 0x455 : // lidBurmese
+ return "Mymr";
+ case 0x448 : // lidOriya
+ return "Orya";
+ case 0x45b : // lidSinhalese
+ return "Sinh";
+ case 0x45a : // lidSyriac
+ return "Syrc";
+ case 0x449 : // lidTamil
+ return "Taml";
+ case 0x44a : // lidTelugu
+ return "Telu";
+ case 0x465 : // lidMaldivian
+ return "Thaa";
+ case 0x41e : // lidThai
+ return "Thai";
+ case 0x451 : // lidTibetan
+ case 0x851 : // lidBhutanese
+ return "Tibt";
+ case 0x480 : // lidUighur
+ return "Uigh";
+ case 0x42a : // lidVietnamese
+ return "Viet";
+ case 0x478 : // lidYi
+ return "Yiii";
+ default:
+ return OUString();
+ }
+}
+
+} //namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/ThemeTable.hxx b/writerfilter/source/dmapper/ThemeTable.hxx
new file mode 100644
index 000000000..164f083c9
--- /dev/null
+++ b/writerfilter/source/dmapper/ThemeTable.hxx
@@ -0,0 +1,58 @@
+/* -*- 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 "LoggedResources.hxx"
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <i18nlangtag/lang.h>
+#include <memory>
+
+namespace writerfilter::dmapper
+{
+struct ThemeTable_Impl;
+
+class ThemeTable : public LoggedProperties, public LoggedTable
+{
+ std::unique_ptr<ThemeTable_Impl> m_pImpl;
+
+public:
+ ThemeTable();
+ virtual ~ThemeTable() override;
+
+ OUString getFontNameForTheme(const Id id) const;
+ static OUString getStringForTheme(const Id id);
+ void setThemeFontLangProperties(const css::uno::Sequence<css::beans::PropertyValue>& aPropSeq);
+
+private:
+ // Properties
+ virtual void lcl_attribute(Id Name, Value& val) override;
+ virtual void lcl_sprm(Sprm& sprm) override;
+
+ // Table
+ virtual void lcl_entry(writerfilter::Reference<Properties>::Pointer_t ref) override;
+
+ // Helper methods
+ static OUString fromLocaleToScriptTag(const OUString& sLocale);
+ static OUString fromLCIDToScriptTag(LanguageType lang);
+};
+typedef tools::SvRef<ThemeTable> ThemeTablePtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TrackChangesHandler.cxx b/writerfilter/source/dmapper/TrackChangesHandler.cxx
new file mode 100644
index 000000000..212f88261
--- /dev/null
+++ b/writerfilter/source/dmapper/TrackChangesHandler.cxx
@@ -0,0 +1,95 @@
+/* -*- 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 "TrackChangesHandler.hxx"
+#include "PropertyMap.hxx"
+#include "ConversionHelper.hxx"
+#include <ooxml/resourceids.hxx>
+#include <oox/token/tokens.hxx>
+#include <osl/diagnose.h>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+using namespace oox;
+
+
+TrackChangesHandler::TrackChangesHandler( sal_Int32 nToken ) :
+ LoggedProperties("TrackChangesHandler"),
+ m_pRedlineParams(new RedlineParams)
+{
+ m_pRedlineParams->m_nToken = nToken;
+}
+
+
+TrackChangesHandler::~TrackChangesHandler()
+{
+}
+
+
+void TrackChangesHandler::lcl_attribute(Id rName, Value & rVal)
+{
+ OUString sStringValue = rVal.getString();
+ switch( rName )
+ {
+ case NS_ooxml::LN_CT_TrackChange_author:
+ {
+ m_pRedlineParams->m_sAuthor = sStringValue;
+ }
+ break;
+ case NS_ooxml::LN_CT_TrackChange_date:
+ {
+ m_pRedlineParams->m_sDate = sStringValue;
+ }
+ break;
+ case NS_ooxml::LN_CT_Markup_id:
+ break;
+ default:
+ OSL_FAIL( "unknown attribute");
+ }
+}
+
+uno::Sequence<beans::PropertyValue> TrackChangesHandler::getRedlineProperties() const
+{
+ uno::Sequence< beans::PropertyValue > aRedlineProperties(3);
+ beans::PropertyValue* pRedlineProperties = aRedlineProperties.getArray();
+
+ OUString sType;
+ switch ( m_pRedlineParams->m_nToken & 0xffff )
+ {
+ case XML_tableRowInsert:
+ sType = getPropertyName( PROP_TABLE_ROW_INSERT );
+ break;
+ case XML_tableRowDelete:
+ sType = getPropertyName( PROP_TABLE_ROW_DELETE );
+ break;
+ case XML_tableCellInsert:
+ sType = getPropertyName( PROP_TABLE_CELL_INSERT );
+ break;
+ case XML_tableCellDelete:
+ sType = getPropertyName( PROP_TABLE_CELL_DELETE );
+ break;
+ }
+
+ pRedlineProperties[0].Name = getPropertyName( PROP_REDLINE_TYPE );
+ pRedlineProperties[0].Value <<= sType;
+ pRedlineProperties[1].Name = getPropertyName( PROP_REDLINE_AUTHOR );
+ pRedlineProperties[1].Value <<= m_pRedlineParams->m_sAuthor;
+ pRedlineProperties[2].Name = getPropertyName( PROP_REDLINE_DATE_TIME );
+ pRedlineProperties[2].Value <<= ConversionHelper::ConvertDateStringToDateTime( m_pRedlineParams->m_sDate );
+ //pRedlineProperties[3].Name = getPropertyName( PROP_REDLINE_REVERT_PROPERTIES );
+ //pRedlineProperties[3].Value <<= pRedline->m_aRevertProperties;
+
+ return aRedlineProperties;
+}
+
+void TrackChangesHandler::lcl_sprm(Sprm &) {}
+
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/TrackChangesHandler.hxx b/writerfilter/source/dmapper/TrackChangesHandler.hxx
new file mode 100644
index 000000000..b3417ccce
--- /dev/null
+++ b/writerfilter/source/dmapper/TrackChangesHandler.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include "LoggedResources.hxx"
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <dmapper/PropertyMap.hxx>
+
+namespace writerfilter::dmapper
+{
+/** Handler for sprms that contain 'track changes' attributes
+ - Author
+ - Date
+ - ID
+ (This class is based on work done in 'MeasureHandler')
+ */
+class TrackChangesHandler : public LoggedProperties
+{
+ RedlineParamsPtr m_pRedlineParams;
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value& val) override;
+ virtual void lcl_sprm(Sprm& sprm) override;
+
+public:
+ explicit TrackChangesHandler(sal_Int32 nToken);
+ virtual ~TrackChangesHandler() override;
+
+ /// Compute the UNO properties for the track changes object based on the received tokens.
+ css::uno::Sequence<css::beans::PropertyValue> getRedlineProperties() const;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/WrapPolygonHandler.cxx b/writerfilter/source/dmapper/WrapPolygonHandler.cxx
new file mode 100644
index 000000000..319fae5c4
--- /dev/null
+++ b/writerfilter/source/dmapper/WrapPolygonHandler.cxx
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/drawing/PointSequence.hpp>
+#include <com/sun/star/text/GraphicCrop.hpp>
+#include <comphelper/sequence.hxx>
+#include <tools/UnitConversion.hxx>
+
+#include <ooxml/resourceids.hxx>
+
+#include "WrapPolygonHandler.hxx"
+#include "util.hxx"
+
+#include <sal/log.hxx>
+
+namespace writerfilter {
+
+using namespace com::sun::star;
+
+namespace dmapper {
+
+WrapPolygon::WrapPolygon()
+{
+}
+
+WrapPolygon::~WrapPolygon()
+{
+}
+
+void WrapPolygon::addPoint(const awt::Point & rPoint)
+{
+ mPoints.push_back(rPoint);
+}
+
+WrapPolygon::Points_t::const_iterator WrapPolygon::begin() const
+{
+ return mPoints.begin();
+}
+
+WrapPolygon::Points_t::const_iterator WrapPolygon::end() const
+{
+ return mPoints.end();
+}
+
+WrapPolygon::Pointer_t WrapPolygon::move(const awt::Point & rPoint) const
+{
+ WrapPolygon::Pointer_t pResult(new WrapPolygon);
+
+ Points_t::const_iterator aIt = begin();
+ Points_t::const_iterator aItEnd = end();
+
+ while (aIt != aItEnd)
+ {
+ awt::Point aPoint(aIt->X + rPoint.X, aIt->Y + rPoint.Y);
+ pResult->addPoint(aPoint);
+ ++aIt;
+ }
+
+ return pResult;
+}
+
+WrapPolygon::Pointer_t WrapPolygon::scale(const Fraction & rFractionX, const Fraction & rFractionY) const
+{
+ WrapPolygon::Pointer_t pResult(new WrapPolygon);
+
+ Points_t::const_iterator aIt = begin();
+ Points_t::const_iterator aItEnd = end();
+
+ while (aIt != aItEnd)
+ {
+ awt::Point aPoint((Fraction(tools::Long(aIt->X)) * rFractionX).operator long(), (Fraction(tools::Long(aIt->Y)) * rFractionY).operator long());
+ pResult->addPoint(aPoint);
+ ++aIt;
+ }
+
+ return pResult;
+}
+
+WrapPolygon::Pointer_t WrapPolygon::correctWordWrapPolygon(const awt::Size & rSrcSize) const
+{
+ WrapPolygon::Pointer_t pResult;
+
+ const tools::Long nWrap100Percent = 21600;
+
+ Fraction aMove(nWrap100Percent, rSrcSize.Width);
+ aMove = aMove * Fraction(convertTwipToMm100(15), 1);
+ awt::Point aMovePoint(aMove.operator long(), 0);
+ pResult = move(aMovePoint);
+
+ Fraction aScaleX = nWrap100Percent / (nWrap100Percent + aMove);
+ Fraction aScaleY = nWrap100Percent / (nWrap100Percent - aMove);
+ pResult = pResult->scale(aScaleX, aScaleY);
+
+ Fraction aScaleSrcX(rSrcSize.Width, nWrap100Percent);
+ Fraction aScaleSrcY(rSrcSize.Height, nWrap100Percent);
+ pResult = pResult->scale(aScaleSrcX, aScaleSrcY);
+
+ return pResult;
+}
+
+WrapPolygon::Pointer_t WrapPolygon::correctWordWrapPolygonPixel(const awt::Size & rSrcSize) const
+{
+ WrapPolygon::Pointer_t pResult;
+
+ /*
+ * https://msdn.microsoft.com/en-us/library/ee342530.aspx
+ *
+ * Image wrapping polygons in Microsoft Word use a fixed coordinate space
+ * that is 21600 units x 21600 units. Coordinate (0,0) is the upper left
+ * corner of the image and coordinate (21600,21600) is the lower right
+ * corner of the image. Microsoft Word scales the size of the wrapping
+ * polygon units to fit the size of the image. The 21600 value is a legacy
+ * artifact from the drawing layer of early versions of Microsoft Office.
+ */
+ const tools::Long nWrap100Percent = 21600;
+
+ Fraction aScaleX(rSrcSize.Width, nWrap100Percent);
+ Fraction aScaleY(rSrcSize.Height, nWrap100Percent);
+ pResult = scale(aScaleX, aScaleY);
+
+ return pResult;
+}
+
+WrapPolygon::Pointer_t WrapPolygon::correctCrop(const awt::Size& rGraphicSize,
+ const text::GraphicCrop& rGraphicCrop) const
+{
+ WrapPolygon::Pointer_t pResult;
+
+ Fraction aScaleX(rGraphicSize.Width - rGraphicCrop.Left - rGraphicCrop.Right,
+ rGraphicSize.Width);
+ Fraction aScaleY(rGraphicSize.Height - rGraphicCrop.Top - rGraphicCrop.Bottom,
+ rGraphicSize.Height);
+ pResult = scale(aScaleX, aScaleY);
+
+ awt::Point aMove(rGraphicCrop.Left, rGraphicCrop.Top);
+ pResult = pResult->move(aMove);
+
+ return pResult;
+}
+
+drawing::PointSequenceSequence WrapPolygon::getPointSequenceSequence() const
+{
+ return { comphelper::containerToSequence(mPoints) };
+}
+
+WrapPolygonHandler::WrapPolygonHandler()
+ : LoggedProperties("WrapPolygonHandler")
+ , mpPolygon(new WrapPolygon)
+ , mnX(0)
+ , mnY(0)
+{
+}
+
+WrapPolygonHandler::~WrapPolygonHandler()
+{
+}
+
+void WrapPolygonHandler::lcl_attribute(Id Name, Value & val)
+{
+ sal_Int32 nIntValue = val.getInt();
+
+ switch(Name)
+ {
+ case NS_ooxml::LN_CT_Point2D_x:
+ mnX = nIntValue;
+ break;
+ case NS_ooxml::LN_CT_Point2D_y:
+ mnY = nIntValue;
+ break;
+ default:
+ SAL_WARN("writerfilter", "WrapPolygonHandler::lcl_attribute: unhandled token: " << Name);
+ break;
+ }
+}
+
+void WrapPolygonHandler::lcl_sprm(Sprm & _sprm)
+{
+ switch (_sprm.getId())
+ {
+ case NS_ooxml::LN_CT_WrapPath_lineTo:
+ case NS_ooxml::LN_CT_WrapPath_start:
+ {
+ resolveSprmProps(*this, _sprm);
+
+ awt::Point aPoint(mnX, mnY);
+ mpPolygon->addPoint(aPoint);
+ }
+ break;
+ default:
+ SAL_WARN("writerfilter", "WrapPolygonHandler::lcl_sprm: unhandled token: " << _sprm.getId());
+ break;
+ }
+}
+
+
+}}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/WrapPolygonHandler.hxx b/writerfilter/source/dmapper/WrapPolygonHandler.hxx
new file mode 100644
index 000000000..8d3e1a3d8
--- /dev/null
+++ b/writerfilter/source/dmapper/WrapPolygonHandler.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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/drawing/PointSequenceSequence.hpp>
+#include "LoggedResources.hxx"
+#include <tools/fract.hxx>
+#include <vector>
+
+namespace com::sun::star::text
+{
+struct GraphicCrop;
+}
+
+namespace writerfilter::dmapper
+{
+/// Handles <wp:wrapPolygon> from DOCX and the pWrapPolygonVertices shape property from RTF.
+class WrapPolygon final : public virtual SvRefBase
+{
+public:
+ typedef std::vector<css::awt::Point> Points_t;
+ typedef ::tools::SvRef<WrapPolygon> Pointer_t;
+
+private:
+ Points_t mPoints;
+
+public:
+ WrapPolygon();
+ ~WrapPolygon() override;
+
+ void addPoint(const css::awt::Point& rPoint);
+
+ Points_t::const_iterator begin() const;
+ Points_t::const_iterator end() const;
+
+ WrapPolygon::Pointer_t move(const css::awt::Point& rMove) const;
+ WrapPolygon::Pointer_t scale(const Fraction& rFractionX, const Fraction& rFractionY) const;
+ WrapPolygon::Pointer_t correctWordWrapPolygon(const css::awt::Size& rSrcSize) const;
+ WrapPolygon::Pointer_t correctWordWrapPolygonPixel(const css::awt::Size& rSrcSize) const;
+ WrapPolygon::Pointer_t correctCrop(const css::awt::Size& rGraphicSize,
+ const css::text::GraphicCrop& rGraphicCrop) const;
+ css::drawing::PointSequenceSequence getPointSequenceSequence() const;
+};
+
+class WrapPolygonHandler : public LoggedProperties
+{
+public:
+ WrapPolygonHandler();
+ virtual ~WrapPolygonHandler() override;
+
+ const WrapPolygon::Pointer_t& getPolygon() const { return mpPolygon; }
+
+private:
+ WrapPolygon::Pointer_t mpPolygon;
+
+ sal_Int32 mnX;
+ sal_Int32 mnY;
+
+ // Properties
+ virtual void lcl_attribute(Id Name, Value& val) override;
+ virtual void lcl_sprm(Sprm& sprm) override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/WriteProtection.cxx b/writerfilter/source/dmapper/WriteProtection.cxx
new file mode 100644
index 000000000..c300ab09e
--- /dev/null
+++ b/writerfilter/source/dmapper/WriteProtection.cxx
@@ -0,0 +1,140 @@
+/* -*- 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 "WriteProtection.hxx"
+#include "TagLogger.hxx"
+
+#include <comphelper/propertyvalue.hxx>
+#include <ooxml/resourceids.hxx>
+
+using namespace com::sun::star;
+
+namespace writerfilter::dmapper
+{
+WriteProtection::WriteProtection()
+ : LoggedProperties("WriteProtection")
+ , m_nCryptProviderType(NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES)
+ , m_CryptSpinCount(0)
+ , m_bRecommended(false)
+{
+}
+
+WriteProtection::~WriteProtection() {}
+
+void WriteProtection::lcl_attribute(Id nName, Value& val)
+{
+ int nIntValue = val.getInt();
+ OUString sStringValue = val.getString();
+
+ switch (nName)
+ {
+ case NS_ooxml::LN_AG_Password_cryptProviderType: // 92025
+ m_nCryptProviderType = nIntValue;
+ break;
+ case NS_ooxml::LN_AG_Password_cryptAlgorithmClass: // 92026
+ if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgClass_hash) // 92023
+ m_sCryptAlgorithmClass = "hash";
+ break;
+ case NS_ooxml::LN_AG_Password_cryptAlgorithmType: // 92027
+ if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgType_typeAny) // 92024
+ m_sCryptAlgorithmType = "typeAny";
+ break;
+ case NS_ooxml::LN_AG_Password_cryptAlgorithmSid: // 92028
+ {
+ sal_Int32 nCryptAlgorithmSid = sStringValue.toInt32();
+ switch (nCryptAlgorithmSid)
+ {
+ case 1:
+ m_sAlgorithmName = "MD2";
+ break;
+ case 2:
+ m_sAlgorithmName = "MD4";
+ break;
+ case 3:
+ m_sAlgorithmName = "MD5";
+ break;
+ case 4:
+ m_sAlgorithmName = "SHA-1";
+ break;
+ case 5:
+ m_sAlgorithmName = "MAC";
+ break;
+ case 6:
+ m_sAlgorithmName = "RIPEMD";
+ break;
+ case 7:
+ m_sAlgorithmName = "RIPEMD-160";
+ break;
+ case 9:
+ m_sAlgorithmName = "HMAC";
+ break;
+ case 12:
+ m_sAlgorithmName = "SHA-256";
+ break;
+ case 13:
+ m_sAlgorithmName = "SHA-384";
+ break;
+ case 14:
+ m_sAlgorithmName = "SHA-512";
+ break;
+ default:; // 8, 10, 11, any other value: Undefined.
+ }
+ }
+ break;
+ case NS_ooxml::LN_AG_Password_cryptSpinCount: // 92029
+ m_CryptSpinCount = nIntValue;
+ break;
+ case NS_ooxml::LN_AG_Password_hash: // 92035
+ m_sHash = sStringValue;
+ break;
+ case NS_ooxml::LN_AG_Password_salt: // 92036
+ m_sSalt = sStringValue;
+ break;
+ case NS_ooxml::LN_CT_WriteProtection_recommended:
+ m_bRecommended = nIntValue;
+ break;
+ default:
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ }
+}
+
+void WriteProtection::lcl_sprm(Sprm& /*rSprm*/) {}
+
+uno::Sequence<beans::PropertyValue> WriteProtection::toSequence() const
+{
+ uno::Sequence<beans::PropertyValue> aResult;
+ if (!m_sAlgorithmName.isEmpty() && !m_sSalt.isEmpty() && !m_sHash.isEmpty()
+ && m_sCryptAlgorithmClass == "hash" && m_sCryptAlgorithmType == "typeAny")
+ {
+ aResult = { comphelper::makePropertyValue("algorithm-name", m_sAlgorithmName),
+ comphelper::makePropertyValue("salt", m_sSalt),
+ comphelper::makePropertyValue("iteration-count", m_CryptSpinCount),
+ comphelper::makePropertyValue("hash", m_sHash) };
+ }
+
+ return aResult;
+}
+
+} //namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/WriteProtection.hxx b/writerfilter/source/dmapper/WriteProtection.hxx
new file mode 100644
index 000000000..21b420b4b
--- /dev/null
+++ b/writerfilter/source/dmapper/WriteProtection.hxx
@@ -0,0 +1,58 @@
+/* -*- 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 "LoggedResources.hxx"
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+namespace writerfilter::dmapper
+{
+class WriteProtection : public LoggedProperties
+{
+private:
+ /** Provider type
+ *
+ * Possible values:
+ * "rsaAES" - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES
+ * "rsaFull" - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaFull
+ */
+ sal_Int32 m_nCryptProviderType;
+ OUString m_sCryptAlgorithmClass;
+ OUString m_sCryptAlgorithmType;
+ sal_Int32 m_CryptSpinCount;
+ OUString m_sAlgorithmName;
+ OUString m_sHash;
+ OUString m_sSalt;
+ bool m_bRecommended;
+
+ virtual void lcl_attribute(Id Name, Value& val) override;
+ virtual void lcl_sprm(Sprm& sprm) override;
+
+public:
+ WriteProtection();
+ virtual ~WriteProtection() override;
+
+ css::uno::Sequence<css::beans::PropertyValue> toSequence() const;
+
+ bool getRecommended() const { return m_bRecommended; }
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/domainmapperfactory.cxx b/writerfilter/source/dmapper/domainmapperfactory.cxx
new file mode 100644
index 000000000..eab017c57
--- /dev/null
+++ b/writerfilter/source/dmapper/domainmapperfactory.cxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "DomainMapper.hxx"
+#include "TagLogger.hxx"
+#include <unotools/mediadescriptor.hxx>
+
+namespace writerfilter::dmapper
+{
+Stream::Pointer_t
+DomainMapperFactory::createMapper(css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ css::uno::Reference<css::io::XInputStream> const& xInputStream,
+ css::uno::Reference<css::lang::XComponent> const& xModel,
+ bool bRepairStorage, SourceDocumentType eDocumentType,
+ utl::MediaDescriptor const& rMediaDesc)
+{
+#ifdef DBG_UTIL
+ OUString sURL
+ = rMediaDesc.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_URL, OUString());
+ ::std::string sURLc = OUStringToOString(sURL, RTL_TEXTENCODING_ASCII_US).getStr();
+
+ if (getenv("SW_DEBUG_WRITERFILTER"))
+ TagLogger::getInstance().setFileName(sURLc);
+ TagLogger::getInstance().startDocument();
+#endif
+
+ return { new DomainMapper(xContext, xInputStream, xModel, bRepairStorage, eDocumentType,
+ rMediaDesc) };
+}
+
+} // namespace writerfilter::dmapper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/util.cxx b/writerfilter/source/dmapper/util.cxx
new file mode 100644
index 000000000..d5b23a3d4
--- /dev/null
+++ b/writerfilter/source/dmapper/util.cxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string>
+#include "util.hxx"
+
+namespace writerfilter::dmapper
+{
+using namespace com::sun::star;
+
+std::string XTextRangeToString(uno::Reference<text::XTextRange> const& textRange)
+{
+ std::string result;
+
+#ifdef DBG_UTIL
+ if (textRange)
+ {
+ OUString aOUStr;
+
+ try
+ {
+ aOUStr = textRange->getString();
+ }
+ catch (const uno::Exception& rException)
+ {
+ result += "(exception: ";
+ result += rException.Message.toUtf8().getStr();
+ result += ")";
+ }
+
+ OString aOStr(aOUStr.getStr(), aOUStr.getLength(), RTL_TEXTENCODING_ASCII_US);
+
+ result = aOStr.getStr();
+ }
+ else
+ {
+ result = "(nil)";
+ }
+#else
+ (void)textRange;
+#endif
+
+ return result;
+}
+
+void resolveSprmProps(Properties& rHandler, Sprm& rSprm)
+{
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ pProperties->resolve(rHandler);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/util.hxx b/writerfilter/source/dmapper/util.hxx
new file mode 100644
index 000000000..9d172331a
--- /dev/null
+++ b/writerfilter/source/dmapper/util.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * 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 <com/sun/star/text/XTextRange.hpp>
+#include <string>
+#include <dmapper/resourcemodel.hxx>
+
+namespace writerfilter::dmapper
+{
+std::string XTextRangeToString(css::uno::Reference<css::text::XTextRange> const& textRange);
+void resolveSprmProps(Properties& rHandler, Sprm& rSprm);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */