summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2023-02-22 10:37:53 +0100
committerMiklos Vajna <vmiklos@collabora.com>2023-02-22 10:32:34 +0000
commit3a8ecbc70320151cb7dde7d8f89dee67a7c6e3e5 (patch)
tree01233c08654ca2197ed169bb80fb16afe8f5eedf
parenta1d3a0f8ec2107da739f50ae0606d176c28a32d0 (diff)
DOCX export: move the bulk of the table export code to a new file
docxattributeoutput.cxx was already more than 10k lines long, which means it's really slow to recompile it, compared to other files in the same directory. Split out some of the table handling functionality into a new docxtableexport.cxx, so touching that (floating) table code has much improved rebuild time when developing. Change-Id: I4fe94a572ae8f684c78799dedbaa346d9a1bdae9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147455 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
-rw-r--r--sw/Library_msword.mk1
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.cxx842
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.hxx26
-rw-r--r--sw/source/filter/ww8/docxtableexport.cxx869
4 files changed, 916 insertions, 822 deletions
diff --git a/sw/Library_msword.mk b/sw/Library_msword.mk
index bf1ed7d09322..526d25fd96e4 100644
--- a/sw/Library_msword.mk
+++ b/sw/Library_msword.mk
@@ -82,6 +82,7 @@ $(eval $(call gb_Library_add_exception_objects,msword,\
sw/source/filter/ww8/docxexport \
sw/source/filter/ww8/docxexportfilter \
sw/source/filter/ww8/docxsdrexport \
+ sw/source/filter/ww8/docxtableexport \
sw/source/filter/ww8/docxtablestyleexport \
sw/source/filter/ww8/rtfattributeoutput \
sw/source/filter/ww8/rtfexport \
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index ff69fb0b8e2c..c36b57a0df18 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -34,7 +34,6 @@
#include <fmtanchr.hxx>
#include <breakit.hxx>
#include <redline.hxx>
-#include <unocoll.hxx>
#include <unoframe.hxx>
#include <textboxhelper.hxx>
#include <rdfhelper.hxx>
@@ -97,7 +96,6 @@
#include <svx/xflgrit.hxx>
#include <svx/svdouno.hxx>
#include <svx/unobrushitemhelper.hxx>
-#include <svx/svdogrp.hxx>
#include <svl/grabbagitem.hxx>
#include <tools/date.hxx>
#include <tools/datetime.hxx>
@@ -112,7 +110,6 @@
#include <flddropdown.hxx>
#include <fmtclds.hxx>
#include <fmtinfmt.hxx>
-#include <fmtrowsplt.hxx>
#include <fmtline.hxx>
#include <ftninfo.hxx>
#include <htmltbl.hxx>
@@ -171,24 +168,6 @@ using namespace sw::util;
using namespace ::com::sun::star;
using namespace ::com::sun::star::drawing;
-const sal_Int32 Tag_StartParagraph_1 = 1;
-const sal_Int32 Tag_StartParagraph_2 = 2;
-const sal_Int32 Tag_WriteSdtBlock = 3;
-const sal_Int32 Tag_StartParagraphProperties = 4;
-const sal_Int32 Tag_InitCollectedParagraphProperties = 5;
-const sal_Int32 Tag_StartRun_1 = 6;
-const sal_Int32 Tag_StartRun_2 = 7;
-const sal_Int32 Tag_StartRun_3 = 8;
-const sal_Int32 Tag_EndRun_1 = 9;
-const sal_Int32 Tag_EndRun_2 = 10;
-const sal_Int32 Tag_StartRunProperties = 11;
-const sal_Int32 Tag_InitCollectedRunProperties = 12;
-const sal_Int32 Tag_Redline_1 = 13;
-const sal_Int32 Tag_Redline_2 = 14;
-const sal_Int32 Tag_TableDefinition = 15;
-const sal_Int32 Tag_OutputFlyFrame = 16;
-const sal_Int32 Tag_StartSection = 17;
-
namespace {
class FFDataWriterHelper
@@ -319,7 +298,7 @@ static bool lcl_isOnelinerSdt(std::u16string_view rName)
return rName == u"Title" || rName == u"Subtitle" || rName == u"Company";
}
-static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ...)
+void DocxAttributeOutput::AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ...)
{
if (!pAttrList.is())
pAttrList = FastSerializerHelper::createAttrList();
@@ -336,7 +315,7 @@ static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAt
va_end(args);
}
-static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const char* sAttrValue)
+void DocxAttributeOutput::AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const char* sAttrValue)
{
AddToAttrList(pAttrList, 1, nAttrName, sAttrValue);
}
@@ -541,7 +520,7 @@ sal_Int32 DocxAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t p
return nParaId;
}
-static OString convertToOOXMLVertOrient(sal_Int16 nOrient)
+OString DocxAttributeOutput::convertToOOXMLVertOrient(sal_Int16 nOrient)
{
switch( nOrient )
{
@@ -561,7 +540,7 @@ static OString convertToOOXMLVertOrient(sal_Int16 nOrient)
}
}
-static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle)
+OString DocxAttributeOutput::convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle)
{
switch( nOrient )
{
@@ -581,7 +560,7 @@ static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle)
}
}
-static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)
+OString DocxAttributeOutput::convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)
{
switch (nOrientRel)
{
@@ -596,7 +575,7 @@ static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)
}
}
-static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel)
+OString DocxAttributeOutput::convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel)
{
switch (nOrientRel)
{
@@ -643,7 +622,7 @@ void SdtBlockHelper::WriteSdtBlock(const ::sax_fastparser::FSHelperPtr& pSeriali
return;
// sdt start mark
- pSerializer->mark(Tag_WriteSdtBlock);
+ pSerializer->mark(DocxAttributeOutput::Tag_WriteSdtBlock);
pSerializer->startElementNS(XML_w, XML_sdt);
@@ -687,7 +666,7 @@ void SdtBlockHelper::WriteSdtBlock(const ::sax_fastparser::FSHelperPtr& pSeriali
pSerializer->startElementNS(XML_w, XML_sdtContent);
// prepend the tags since the sdt start mark before the paragraph
- pSerializer->mergeTopMarks(Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND);
+ pSerializer->mergeTopMarks(DocxAttributeOutput::Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND);
// write the ending tags after the paragraph
m_bStartedSdt = true;
@@ -770,15 +749,15 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::Property
{
OUString sValue = rProp.Value.get<OUString>();
if (rProp.Name == "ooxml:CT_SdtCheckbox_checked")
- AddToAttrList(m_pTokenChildren,
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
FSNS(XML_w14, XML_checked),
OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState")
- AddToAttrList(m_pTokenChildren,
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
FSNS(XML_w14, XML_checkedState),
OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState")
- AddToAttrList(m_pTokenChildren,
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
FSNS(XML_w14, XML_uncheckedState),
OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
}
@@ -791,15 +770,15 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::Property
{
OUString sValue = rProp.Value.get<OUString>();
if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings")
- AddToAttrList( m_pDataBindingAttrs,
+ DocxAttributeOutput::AddToAttrList( m_pDataBindingAttrs,
FSNS( XML_w, XML_prefixMappings ),
OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
else if (rProp.Name == "ooxml:CT_DataBinding_xpath")
- AddToAttrList( m_pDataBindingAttrs,
+ DocxAttributeOutput::AddToAttrList( m_pDataBindingAttrs,
FSNS( XML_w, XML_xpath ),
OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID")
- AddToAttrList( m_pDataBindingAttrs,
+ DocxAttributeOutput::AddToAttrList( m_pDataBindingAttrs,
FSNS( XML_w, XML_storeItemID ),
OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
}
@@ -814,7 +793,7 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::Property
{
OUString sValue = rProp.Value.get<OUString>();
if (rProp.Name == "ooxml:CT_SdtText_multiLine")
- AddToAttrList(m_pTextAttrs,
+ DocxAttributeOutput::AddToAttrList(m_pTextAttrs,
FSNS(XML_w, XML_multiLine),
OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
}
@@ -898,18 +877,18 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::Property
{
OUString sValue = rProp.Value.get<OUString>();
if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery")
- AddToAttrList(m_pTokenChildren,
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
FSNS(XML_w, XML_docPartGallery),
OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory")
- AddToAttrList(m_pTokenChildren,
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
FSNS(XML_w, XML_docPartCategory),
OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique")
{
if (sValue.isEmpty())
sValue = "true";
- AddToAttrList(m_pTokenChildren, FSNS(XML_w, XML_docPartUnique),
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren, FSNS(XML_w, XML_docPartUnique),
OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
}
}
@@ -4271,7 +4250,7 @@ static void impl_borders( FSHelperPtr const & pSerializer,
}
}
-static void impl_cellMargins( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins = nullptr)
+void DocxAttributeOutput::ImplCellMargins( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins)
{
static const SvxBoxItemLine aBorders[] =
{
@@ -4384,7 +4363,7 @@ void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Point
{
// Cell margins
- impl_cellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox );
+ DocxAttributeOutput::ImplCellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox );
}
TableVerticalCell( pTableTextNodeInfoInner );
@@ -4522,787 +4501,6 @@ void DocxAttributeOutput::EndTableCell(sal_uInt32 nCell)
m_tableReference->m_bTableCellParaSdtOpen = false;
}
-void DocxAttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
-{
-}
-
-void DocxAttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/ )
-{
-}
-
-namespace
-{
-
-/// Does the same as comphelper::string::padToLength(), but extends the start, not the end.
-OString lcl_padStartToLength(OString const & aString, sal_Int32 nLen, char cFill)
-{
- if (nLen > aString.getLength())
- {
- sal_Int32 nDiff = nLen - aString.getLength();
- OStringBuffer aBuffer;
- comphelper::string::padToLength(aBuffer, nDiff, cFill);
- aBuffer.append(aString);
- return aBuffer.makeStringAndClear();
- }
- else
- return aString;
-}
-
-//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
-//Since this is not import code, "-1" needs to be handled as the mode that LO will save as.
-//To identify how your code should handle a "-1", look in DocxExport::WriteSettings().
-sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& rDocExport)
-{
- sal_Int32 nWordCompatibilityMode = rDocExport.getWordCompatibilityModeFromGrabBag();
-
- // TODO: this is duplicated, better store it in DocxExport member?
- if (!rDocExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
- {
- if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
- {
- nWordCompatibilityMode = 14;
- }
- }
-
- return nWordCompatibilityMode;
-}
-
-void CollectFloatingTableAttributes(DocxExport& rExport, const ww8::Frame& rFrame,
- ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner,
- rtl::Reference<FastAttributeList>& pAttributes)
-{
- // we export the values of the surrounding Frame
- OString sOrientation;
- sal_Int32 nValue;
-
- // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY!
- OString sTblpXSpec
- = convertToOOXMLHoriOrient(rFrame.GetFrameFormat().GetHoriOrient().GetHoriOrient(),
- rFrame.GetFrameFormat().GetHoriOrient().IsPosToggle());
- OString sTblpYSpec
- = convertToOOXMLVertOrient(rFrame.GetFrameFormat().GetVertOrient().GetVertOrient());
-
- sOrientation
- = convertToOOXMLVertOrientRel(rFrame.GetFrameFormat().GetVertOrient().GetRelationOrient());
- pAttributes->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
-
- if (!sTblpYSpec.isEmpty())
- pAttributes->add(FSNS(XML_w, XML_tblpYSpec), sTblpYSpec);
-
- sOrientation
- = convertToOOXMLHoriOrientRel(rFrame.GetFrameFormat().GetHoriOrient().GetRelationOrient());
- pAttributes->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
-
- if (!sTblpXSpec.isEmpty())
- pAttributes->add(FSNS(XML_w, XML_tblpXSpec), sTblpXSpec);
-
- nValue = rFrame.GetFrameFormat().GetULSpace().GetLower();
- if (nValue != 0)
- pAttributes->add(FSNS(XML_w, XML_bottomFromText), OString::number(nValue));
-
- nValue = rFrame.GetFrameFormat().GetLRSpace().GetLeft();
- if (nValue != 0)
- pAttributes->add(FSNS(XML_w, XML_leftFromText), OString::number(nValue));
-
- nValue = rFrame.GetFrameFormat().GetLRSpace().GetRight();
- if (nValue != 0)
- pAttributes->add(FSNS(XML_w, XML_rightFromText), OString::number(nValue));
-
- nValue = rFrame.GetFrameFormat().GetULSpace().GetUpper();
- if (nValue != 0)
- pAttributes->add(FSNS(XML_w, XML_topFromText), OString::number(nValue));
-
- if (sTblpXSpec.isEmpty()) // do not write tblpX if tblpXSpec is present
- {
- nValue = rFrame.GetFrameFormat().GetHoriOrient().GetPos();
- // we need to revert the additional shift introduced by
- // lcl_DecrementHoriOrientPosition() in writerfilter
- // 1st: left distance of the table
- const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
- const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
- const SvxBoxItem& rBox = pFrameFormat->GetBox();
- sal_Int32 nMode = lcl_getWordCompatibilityMode(rExport);
- if (nMode < 15)
- {
- sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT);
- nValue += nLeftDistance;
- }
-
- // 2nd: if a left border is given, revert the shift by half the width
- // from lcl_DecrementHoriOrientPosition() in writerfilter
- if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft())
- {
- tools::Long nWidth = pLeftBorder->GetWidth();
- nValue += (nWidth / 2);
- }
-
- pAttributes->add(FSNS(XML_w, XML_tblpX), OString::number(nValue));
- }
-
- if (sTblpYSpec.isEmpty()) // do not write tblpY if tblpYSpec is present
- {
- nValue = rFrame.GetFrameFormat().GetVertOrient().GetPos();
- pAttributes->add(FSNS(XML_w, XML_tblpY), OString::number(nValue));
- }
-}
-}
-
-void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
- bool const bEcma = GetExport().GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION;
-
- // Write the table properties
- m_pSerializer->startElementNS(XML_w, XML_tblPr);
-
- static const sal_Int32 aOrder[] =
- {
- FSNS( XML_w, XML_tblStyle ),
- FSNS( XML_w, XML_tblpPr ),
- FSNS( XML_w, XML_tblOverlap ),
- FSNS( XML_w, XML_bidiVisual ),
- FSNS( XML_w, XML_tblStyleRowBandSize ),
- FSNS( XML_w, XML_tblStyleColBandSize ),
- FSNS( XML_w, XML_tblW ),
- FSNS( XML_w, XML_jc ),
- FSNS( XML_w, XML_tblCellSpacing ),
- FSNS( XML_w, XML_tblInd ),
- FSNS( XML_w, XML_tblBorders ),
- FSNS( XML_w, XML_shd ),
- FSNS( XML_w, XML_tblLayout ),
- FSNS( XML_w, XML_tblCellMar ),
- FSNS( XML_w, XML_tblLook ),
- FSNS( XML_w, XML_tblPrChange )
- };
-
- // postpone the output so that we can later []
- // prepend the properties before the run
- // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
- m_pSerializer->mark(Tag_TableDefinition, comphelper::containerToSequence(aOrder));
-
- tools::Long nPageSize = 0;
- const char* widthType = "dxa";
-
- // If actual width of table is relative it should export is as "pct".`
- const SwTable *pTable = pTableTextNodeInfoInner->getTable();
- SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
- const SwFormatFrameSize &rSize = pTableFormat->GetFrameSize();
- int nWidthPercent = rSize.GetWidthPercent();
- // If we export a floating table: we use the widthPercent of the surrounding frame
- const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame();
- if (pFloatingTableFrame)
- {
- const SwFormatFrameSize &rFrameSize = pFloatingTableFrame->GetFrameFormat().GetFrameSize();
- nWidthPercent = rFrameSize.GetWidthPercent();
- }
-
- uno::Reference<beans::XPropertySet> xPropertySet(SwXTextTables::GetObject(*pTable->GetFrameFormat( )),uno::UNO_QUERY);
- bool isWidthRelative = false;
- xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative;
- if (!isWidthRelative && !nWidthPercent)
- {
- // The best fit for "automatic" table placement is relative 100%
- short nHoriOrient = -1;
- xPropertySet->getPropertyValue("HoriOrient") >>= nHoriOrient;
- isWidthRelative = nHoriOrient == text::HoriOrientation::FULL;
- if (isWidthRelative)
- nWidthPercent = 100;
- }
-
- if(isWidthRelative)
- {
- /**
- * As per ECMA Specification : ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference [ 17.18.90 ST_TableWidth (Table Width Units)]
- * http://www.schemacentral.com/sc/ooxml/a-w_type-7.html
- *
- * Fiftieths of a Percent :
- * http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
- * pct Width is in Fiftieths of a Percent
- *
- * ex. If the Table width is 50% then
- * Width in Fiftieths of a percent is (50 * 50) % or 0.5 * 5000 = 2500pct
- **/
- nPageSize = nWidthPercent * 50 ;
- widthType = "pct" ;
- }
- else
- {
- bool bRelBoxSize = false;
- // Create the SwWriteTable instance to use col spans (and maybe other infos)
- GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
- if(nPageSize == 0)
- widthType = "auto";
- }
-
- // Output the table preferred width
- m_pSerializer->singleElementNS( XML_w, XML_tblW,
- FSNS( XML_w, XML_w ), OString::number(nPageSize),
- FSNS( XML_w, XML_type ), widthType );
-
- // Disable layout autofit, as it does not exist in LibreOffice yet
- m_pSerializer->singleElementNS( XML_w, XML_tblLayout,
- FSNS( XML_w, XML_type ), "fixed" );
-
- // Look for the table style property in the table grab bag
- std::map<OUString, css::uno::Any> aGrabBag =
- pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
-
- // We should clear the TableStyle map. In case of Table inside multiple tables it contains the
- // table border style of the previous table.
- std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = m_aTableStyleConfs.back();
- rTableStyleConf.clear();
-
- bool bFloatingTableWritten = false;
- if (pFloatingTableFrame && pFloatingTableFrame->GetFrameFormat().GetFlySplit().GetValue())
- {
- rtl::Reference<FastAttributeList> pAttributes = FastSerializerHelper::createAttrList();
- CollectFloatingTableAttributes(m_rExport, *pFloatingTableFrame, pTableTextNodeInfoInner,
- pAttributes);
- m_pSerializer->singleElementNS(XML_w, XML_tblpPr, pAttributes);
- bFloatingTableWritten = true;
- }
-
- // Extract properties from grab bag
- for( const auto & rGrabBagElement : aGrabBag )
- {
- if( rGrabBagElement.first == "TableStyleName")
- {
- OString sStyleName = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
- m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, XML_val), sStyleName);
- }
- else if( rGrabBagElement.first == "TableStyleTopBorder" )
- rTableStyleConf[SvxBoxItemLine::TOP] = rGrabBagElement.second.get<table::BorderLine2>();
- else if( rGrabBagElement.first == "TableStyleBottomBorder" )
- rTableStyleConf[SvxBoxItemLine::BOTTOM]
- = rGrabBagElement.second.get<table::BorderLine2>();
- else if( rGrabBagElement.first == "TableStyleLeftBorder" )
- rTableStyleConf[SvxBoxItemLine::LEFT]
- = rGrabBagElement.second.get<table::BorderLine2>();
- else if( rGrabBagElement.first == "TableStyleRightBorder" )
- rTableStyleConf[SvxBoxItemLine::RIGHT]
- = rGrabBagElement.second.get<table::BorderLine2>();
- else if (rGrabBagElement.first == "TableStyleLook")
- {
- rtl::Reference<FastAttributeList> pAttributeList = FastSerializerHelper::createAttrList();
- const uno::Sequence<beans::PropertyValue> aAttributeList = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
-
- for (const auto& rAttribute : aAttributeList)
- {
- if (rAttribute.Name == "val")
- pAttributeList->add(FSNS(XML_w, XML_val), lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16), 4, '0'));
- else
- {
- static DocxStringTokenMap const aTokens[] =
- {
- {"firstRow", XML_firstRow},
- {"lastRow", XML_lastRow},
- {"firstColumn", XML_firstColumn},
- {"lastColumn", XML_lastColumn},
- {"noHBand", XML_noHBand},
- {"noVBand", XML_noVBand},
- {nullptr, 0}
- };
-
- if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name))
- pAttributeList->add(FSNS(XML_w, nToken), (rAttribute.Value.get<sal_Int32>() ? "1" : "0"));
- }
- }
-
- m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList);
- }
- else if (rGrabBagElement.first == "TablePosition" &&
- // skip empty table position (tables in footnotes converted to
- // floating tables temporarily, don't export this)
- rGrabBagElement.second != uno::Any() )
- {
- rtl::Reference<FastAttributeList> attrListTablePos = FastSerializerHelper::createAttrList( );
- const uno::Sequence<beans::PropertyValue> aTablePosition = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue> >();
- // look for a surrounding frame and take it's position values
- const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame();
- if( pFrame )
- {
- CollectFloatingTableAttributes(m_rExport, *pFrame, pTableTextNodeInfoInner,
- attrListTablePos);
- }
- else // ( pFrame = 0 )
- {
- // we export the values from the grabBag
- for (const auto& rProp : aTablePosition)
- {
- if (rProp.Name == "vertAnchor" && !rProp.Value.get<OUString>().isEmpty())
- {
- OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
- attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
- }
- else if (rProp.Name == "tblpYSpec" && !rProp.Value.get<OUString>().isEmpty())
- {
- OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
- attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), sOrientation);
- }
- else if (rProp.Name == "horzAnchor" && !rProp.Value.get<OUString>().isEmpty())
- {
- OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
- attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
- }
- else if (rProp.Name == "tblpXSpec" && !rProp.Value.get<OUString>().isEmpty())
- {
- OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
- attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), sOrientation);
- }
- else if (rProp.Name == "bottomFromText")
- {
- sal_Int32 nValue = rProp.Value.get<sal_Int32>();
- attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) );
- }
- else if (rProp.Name == "leftFromText")
- {
- sal_Int32 nValue = rProp.Value.get<sal_Int32>();
- attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) );
- }
- else if (rProp.Name == "rightFromText")
- {
- sal_Int32 nValue = rProp.Value.get<sal_Int32>();
- attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) );
- }
- else if (rProp.Name == "topFromText")
- {
- sal_Int32 nValue = rProp.Value.get<sal_Int32>();
- attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) );
- }
- else if (rProp.Name == "tblpX")
- {
- sal_Int32 nValue = rProp.Value.get<sal_Int32>();
- attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) );
- }
- else if (rProp.Name == "tblpY")
- {
- sal_Int32 nValue = rProp.Value.get<sal_Int32>();
- attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) );
- }
- }
- }
-
- if (!bFloatingTableWritten)
- {
- m_pSerializer->singleElementNS(XML_w, XML_tblpPr, attrListTablePos);
- }
- attrListTablePos = nullptr;
- }
- else
- SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: unhandled property: " << rGrabBagElement.first);
- }
-
- // Output the table alignment
- const char* pJcVal;
- sal_Int32 nIndent = 0;
- switch ( pTableFormat->GetHoriOrient( ).GetHoriOrient( ) )
- {
- case text::HoriOrientation::CENTER:
- pJcVal = "center";
- break;
- case text::HoriOrientation::RIGHT:
- if ( bEcma )
- pJcVal = "right";
- else
- pJcVal = "end";
- break;
- default:
- case text::HoriOrientation::NONE:
- case text::HoriOrientation::LEFT_AND_WIDTH:
- {
- if ( bEcma )
- pJcVal = "left";
- else
- pJcVal = "start";
- nIndent = sal_Int32( pTableFormat->GetLRSpace().GetLeft() );
-
- // Table indentation has different meaning in Word, depending if the table is nested or not.
- // If nested, tblInd is added to parent table's left spacing and defines left edge position
- // If not nested, text position of left-most cell must be at absolute X = tblInd
- // so, table_spacing + table_spacing_to_content = tblInd
-
- // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
- // if no compatibilityMode is defined (which now should only happen on a new export to .docx),
- // LO uses a higher compatibility than 2010's 14.
- sal_Int32 nMode = lcl_getWordCompatibilityMode(m_rExport);
-
- const SwFrameFormat* pFrameFormat = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();
- if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth == 0)
- nIndent += pFrameFormat->GetBox().GetDistance( SvxBoxItemLine::LEFT );
- else
- {
- // adjust for SW considering table to start mid-border instead of nested/2013's left-side-of-border.
- nIndent -= pFrameFormat->GetBox().CalcLineWidth( SvxBoxItemLine::LEFT ) / 2;
- }
-
- break;
- }
- }
- m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pJcVal);
-
- // Output the table background color (although cell value still needs to be specified)
- const SvxBrushItem *pColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
- Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
- if ( aColor != COL_AUTO )
- {
- OString sColor = msfilter::util::ConvertColor( aColor );
- m_pSerializer->singleElementNS( XML_w, XML_shd,
- FSNS( XML_w, XML_fill ), sColor,
- FSNS( XML_w, XML_val ), "clear" );
- }
-
- // Output the table borders
- TableDefaultBorders( pTableTextNodeInfoInner );
-
- // Output the default cell margins
- TableDefaultCellMargins( pTableTextNodeInfoInner );
-
- TableBidi( pTableTextNodeInfoInner );
-
- // Table indent (need to get written even if == 0)
- m_pSerializer->singleElementNS( XML_w, XML_tblInd,
- FSNS( XML_w, XML_w ), OString::number(nIndent),
- FSNS( XML_w, XML_type ), "dxa" );
-
- // Merge the marks for the ordered elements
- m_pSerializer->mergeTopMarks(Tag_TableDefinition);
-
- m_pSerializer->endElementNS( XML_w, XML_tblPr );
-
- // Write the table grid infos
- m_pSerializer->startElementNS(XML_w, XML_tblGrid);
- sal_Int32 nPrv = 0;
- ww8::WidthsPtr pColumnWidths = GetColumnWidths( pTableTextNodeInfoInner );
- for ( auto aColumnWidth : *pColumnWidths )
- {
- sal_Int32 nWidth = sal_Int32( aColumnWidth ) - nPrv;
- m_pSerializer->singleElementNS( XML_w, XML_gridCol,
- FSNS( XML_w, XML_w ), OString::number(nWidth) );
- nPrv = sal_Int32( aColumnWidth );
- }
-
- m_pSerializer->endElementNS( XML_w, XML_tblGrid );
-}
-
-void DocxAttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
-{
- // Table defaults should only be created IF m_aTableStyleConf contents haven't come from a table style.
- // Previously this function wrote out Cell A1 as the table default, causing problems with no benefit.
-}
-
-void DocxAttributeOutput::TableDefaultCellMargins( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
-{
- const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
- const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
- const SvxBoxItem& rBox = pFrameFormat->GetBox( );
- const bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_376_1ST_EDITION;
-
- impl_cellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma);
-}
-
-void DocxAttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
- const SwTable *pTable = pTableTextNodeInfoInner->getTable();
- const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
- const SwTableLine *pTableRow = pTableBox->GetUpper();
- const SwFrameFormat *pFormat = pTableBox->GetFrameFormat( );
-
- const SvxBrushItem *pColorProp = pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
- Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
-
- const SwFrameFormat *pRowFormat = pTableRow->GetFrameFormat( );
- const SvxBrushItem *pRowColorProp = pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
- if ( pRowColorProp && aColor == COL_AUTO)
- aColor = pRowColorProp->GetColor();
-
- const SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
- const SvxBrushItem *pTableColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
- if ( pTableColorProp && aColor == COL_AUTO )
- aColor = pTableColorProp->GetColor();
-
- const OString sColor = msfilter::util::ConvertColor( aColor );
-
- std::map<OUString, css::uno::Any> aGrabBag =
- pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
-
- OString sOriginalColor;
- std::map<OUString, css::uno::Any>::iterator aGrabBagElement = aGrabBag.find("originalColor");
- if( aGrabBagElement != aGrabBag.end() )
- sOriginalColor = OUStringToOString( aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
-
- if ( sOriginalColor != sColor )
- {
- // color changed by the user, or no grab bag: write sColor
- if ( sColor != "auto" )
- {
- m_pSerializer->singleElementNS( XML_w, XML_shd,
- FSNS( XML_w, XML_fill ), sColor,
- FSNS( XML_w, XML_val ), "clear" );
- }
- }
- else
- {
- rtl::Reference<sax_fastparser::FastAttributeList> pAttrList;
-
- for( const auto & rGrabBagElement : aGrabBag )
- {
- if (!rGrabBagElement.second.has<OUString>())
- continue;
-
- OString sValue = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
- if( rGrabBagElement.first == "themeFill")
- AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFill ), sValue.getStr() );
- else if( rGrabBagElement.first == "themeFillTint")
- AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillTint ), sValue.getStr() );
- else if( rGrabBagElement.first == "themeFillShade")
- AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillShade ), sValue.getStr() );
- else if( rGrabBagElement.first == "fill" )
- AddToAttrList( pAttrList, FSNS( XML_w, XML_fill ), sValue.getStr() );
- else if( rGrabBagElement.first == "themeColor")
- AddToAttrList( pAttrList, FSNS( XML_w, XML_themeColor ), sValue.getStr() );
- else if( rGrabBagElement.first == "themeTint")
- AddToAttrList( pAttrList, FSNS( XML_w, XML_themeTint ), sValue.getStr() );
- else if( rGrabBagElement.first == "themeShade")
- AddToAttrList( pAttrList, FSNS( XML_w, XML_themeShade ), sValue.getStr() );
- else if( rGrabBagElement.first == "color")
- AddToAttrList( pAttrList, FSNS( XML_w, XML_color ), sValue.getStr() );
- else if( rGrabBagElement.first == "val")
- AddToAttrList( pAttrList, FSNS( XML_w, XML_val ), sValue.getStr() );
- }
- m_pSerializer->singleElementNS( XML_w, XML_shd, pAttrList.get() );
- }
-}
-
-void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
- const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
- const SwTableLine * pTabLine = pTabBox->GetUpper();
-
- bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
- SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
-
- // check table row property "HasTextChangesOnly"
- SwRedlineTable::size_type nPos(0);
- SwRedlineTable::size_type nChange = pTabLine->UpdateTextChangesOnly(nPos);
- if ( nChange != SwRedlineTable::npos )
- {
- const SwRedlineTable& aRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
- const SwRangeRedline* pRedline = aRedlineTable[ nChange ];
- SwTableRowRedline* pTableRowRedline = nullptr;
- bool bIsInExtra = false;
-
- // use the original DOCX redline data stored in ExtraRedlineTable,
- // if it exists and its type wasn't changed
- const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
- for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
- {
- SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
- pTableRowRedline = dynamic_cast<SwTableRowRedline*>(pExtraRedline);
- if (pTableRowRedline && &pTableRowRedline->GetTableLine() == pTabLine)
- {
- bIsInExtra = true;
- break;
- }
- }
-
- const SwRedlineData& aRedlineData = bIsInExtra &&
- // still the same type (an inserted row could become a tracked deleted one)
- pTableRowRedline->GetRedlineData().GetType() == pRedline->GetRedlineData().GetType()
- ? pTableRowRedline->GetRedlineData()
- : pRedline->GetRedlineData();
-
- // Note: all redline ranges and table row redline (with the same author and timestamp)
- // use the same redline id in OOXML exported by MSO, but it seems, the recent solution
- // (different IDs for different ranges, also row changes) is also portable.
- OString aId( OString::number( m_nRedlineId++ ) );
- const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
- OString aAuthor( OUStringToOString( bRemovePersonalInfo
- ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
- : rAuthor, RTL_TEXTENCODING_UTF8 ) );
-
- const DateTime aDateTime = aRedlineData.GetTimeStamp();
- bool bNoDate = bRemovePersonalInfo ||
- ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
-
- if ( bNoDate )
- m_pSerializer->singleElementNS( XML_w,
- RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins,
- FSNS( XML_w, XML_id ), aId,
- FSNS( XML_w, XML_author ), aAuthor );
- else
- m_pSerializer->singleElementNS( XML_w,
- RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins,
- FSNS( XML_w, XML_id ), aId,
- FSNS( XML_w, XML_author ), aAuthor,
- FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) );
- return;
- }
-}
-
-void DocxAttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
- const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
-
- bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
- SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
-
- // search next Redline
- const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
- for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
- {
- SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
- const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
- if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox)
- {
- // Redline for this table cell
- const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
- RedlineType nRedlineType = aRedlineData.GetType();
- switch (nRedlineType)
- {
- case RedlineType::TableCellInsert:
- case RedlineType::TableCellDelete:
- {
- OString aId( OString::number( m_nRedlineId++ ) );
- const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
- OString aAuthor( OUStringToOString( bRemovePersonalInfo
- ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
- : rAuthor, RTL_TEXTENCODING_UTF8 ) );
-
- sal_Int32 nElement = nRedlineType == RedlineType::TableCellInsert
- ? XML_cellIns
- : XML_cellDel;
- const DateTime aDateTime = aRedlineData.GetTimeStamp();
- bool bNoDate = bRemovePersonalInfo || ( aDateTime.GetYear() == 1970 &&
- aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
- if ( bNoDate )
- m_pSerializer->singleElementNS( XML_w, nElement,
- FSNS( XML_w, XML_id ), aId,
- FSNS( XML_w, XML_author ), aAuthor );
- else
- m_pSerializer->singleElementNS( XML_w, nElement,
- FSNS( XML_w, XML_id ), aId,
- FSNS( XML_w, XML_author ), aAuthor,
- FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) );
- }
- break;
- default: break;
- }
- }
- }
-}
-
-void DocxAttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
- const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
- const SwTableLine * pTabLine = pTabBox->GetUpper();
- const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
-
- const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
- if ( !(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()) )
- return;
-
- sal_Int32 nHeight = rLSz.GetHeight();
- const char *pRule = nullptr;
-
- switch ( rLSz.GetHeightSizeType() )
- {
- case SwFrameSize::Fixed: pRule = "exact"; break;
- case SwFrameSize::Minimum: pRule = "atLeast"; break;
- default: break;
- }
-
- if ( pRule )
- m_pSerializer->singleElementNS( XML_w, XML_trHeight,
- FSNS( XML_w, XML_val ), OString::number(nHeight),
- FSNS( XML_w, XML_hRule ), pRule );
-}
-
-void DocxAttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
- const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
- const SwTableLine * pTabLine = pTabBox->GetUpper();
- const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
-
- const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit( );
- // if rSplittable is true then no need to write <w:cantSplit w:val="false"/>
- // as default row prop is allow row to break across page.
- if( !rSplittable.GetValue( ) )
- m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w, XML_val), "true");
-}
-
-void DocxAttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
- const SwTable * pTable = pTableTextNodeInfoInner->getTable();
- const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
-
- if ( m_rExport.TrueFrameDirection( *pFrameFormat ) == SvxFrameDirection::Horizontal_RL_TB )
- {
- m_pSerializer->singleElementNS(XML_w, XML_bidiVisual, FSNS(XML_w, XML_val), "true");
- }
-}
-
-void DocxAttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
-{
- const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
- const SwFrameFormat *pFrameFormat = pTabBox->GetFrameFormat( );
-
- if ( SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection( *pFrameFormat ) )
- m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "tbRl");
- else if ( SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection( *pFrameFormat ) )
- {
- m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "btLr");
- }
-
- const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
- const auto nRow = pTableTextNodeInfoInner->getRow();
- if (nRow >= rRows.size())
- {
- SAL_WARN("sw.ww8", "DocxAttributeOutput::TableCellProperties: out of range row: " << nRow);
- return;
- }
- SwWriteTableRow *pRow = rRows[nRow].get();
- sal_uInt32 nCell = pTableTextNodeInfoInner->getCell();
- const SwWriteTableCells& rTableCells = pRow->GetCells();
- if (nCell >= rTableCells.size() )
- return;
-
- const SwWriteTableCell *const pCell = pRow->GetCells()[ nCell ].get();
- switch( pCell->GetVertOri())
- {
- case text::VertOrientation::TOP:
- break;
- case text::VertOrientation::CENTER:
- m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center");
- break;
- case text::VertOrientation::BOTTOM:
- m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom");
- break;
- }
-}
-
-void DocxAttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner )
-{
- // This is called when the nested table ends in a cell, and there's no
- // paragraph behind that; so we must check for the ends of cell, rows,
- // tables
- // ['true' to write an empty paragraph, MS Word insists on that]
- FinishTableRowCell( pNodeInfoInner, true );
-}
-
-void DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
-{
- SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
-}
-
-void DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
-{
- SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
-}
-
-void DocxAttributeOutput::TableRowEnd( sal_uInt32 /*nDepth*/ )
-{
- SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 nDepth = 1 )" );
-}
-
void DocxAttributeOutput::StartStyles()
{
m_pSerializer->startElementNS( XML_w, XML_styles,
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 29abf6badce8..867c6d72e810 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -1097,6 +1097,32 @@ public:
void pushToTableExportContext(DocxTableExportContext& rContext);
/// Restores from the remembered state.
void popFromTableExportContext(DocxTableExportContext const & rContext);
+
+ static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle);
+ static OString convertToOOXMLVertOrient(sal_Int16 nOrient);
+ static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel);
+ static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel);
+ static void ImplCellMargins( sax_fastparser::FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins = nullptr);
+ static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ...);
+ static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const char* sAttrValue);
+
+ static const sal_Int32 Tag_StartParagraph_1 = 1;
+ static const sal_Int32 Tag_StartParagraph_2 = 2;
+ static const sal_Int32 Tag_WriteSdtBlock = 3;
+ static const sal_Int32 Tag_StartParagraphProperties = 4;
+ static const sal_Int32 Tag_InitCollectedParagraphProperties = 5;
+ static const sal_Int32 Tag_StartRun_1 = 6;
+ static const sal_Int32 Tag_StartRun_2 = 7;
+ static const sal_Int32 Tag_StartRun_3 = 8;
+ static const sal_Int32 Tag_EndRun_1 = 9;
+ static const sal_Int32 Tag_EndRun_2 = 10;
+ static const sal_Int32 Tag_StartRunProperties = 11;
+ static const sal_Int32 Tag_InitCollectedRunProperties = 12;
+ static const sal_Int32 Tag_Redline_1 = 13;
+ static const sal_Int32 Tag_Redline_2 = 14;
+ static const sal_Int32 Tag_TableDefinition = 15;
+ static const sal_Int32 Tag_OutputFlyFrame = 16;
+ static const sal_Int32 Tag_StartSection = 17;
};
/**
diff --git a/sw/source/filter/ww8/docxtableexport.cxx b/sw/source/filter/ww8/docxtableexport.cxx
new file mode 100644
index 000000000000..45cd8f3fbb1c
--- /dev/null
+++ b/sw/source/filter/ww8/docxtableexport.cxx
@@ -0,0 +1,869 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "docxattributeoutput.hxx"
+
+#include <com/sun/star/text/XTextTable.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <svl/grabbagitem.hxx>
+#include <sax/fshelper.hxx>
+#include <editeng/ulspitem.hxx>
+#include <comphelper/string.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <tools/datetimeutils.hxx>
+
+#include <fmtfsize.hxx>
+#include <unocoll.hxx>
+#include <formatflysplit.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <frmatr.hxx>
+#include <swmodule.hxx>
+#include <fmtrowsplt.hxx>
+
+#include "docxexportfilter.hxx"
+#include "docxhelper.hxx"
+
+using namespace com::sun::star;
+using namespace sax_fastparser;
+using namespace oox;
+
+namespace
+{
+/// Does the same as comphelper::string::padToLength(), but extends the start, not the end.
+OString lcl_padStartToLength(OString const& aString, sal_Int32 nLen, char cFill)
+{
+ if (nLen > aString.getLength())
+ {
+ sal_Int32 nDiff = nLen - aString.getLength();
+ OStringBuffer aBuffer;
+ comphelper::string::padToLength(aBuffer, nDiff, cFill);
+ aBuffer.append(aString);
+ return aBuffer.makeStringAndClear();
+ }
+ else
+ return aString;
+}
+
+//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
+//Since this is not import code, "-1" needs to be handled as the mode that LO will save as.
+//To identify how your code should handle a "-1", look in DocxExport::WriteSettings().
+sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& rDocExport)
+{
+ sal_Int32 nWordCompatibilityMode = rDocExport.getWordCompatibilityModeFromGrabBag();
+
+ // TODO: this is duplicated, better store it in DocxExport member?
+ if (!rDocExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
+ {
+ if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
+ {
+ nWordCompatibilityMode = 14;
+ }
+ }
+
+ return nWordCompatibilityMode;
+}
+
+void CollectFloatingTableAttributes(DocxExport& rExport, const ww8::Frame& rFrame,
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner,
+ rtl::Reference<FastAttributeList>& pAttributes)
+{
+ // we export the values of the surrounding Frame
+ OString sOrientation;
+ sal_Int32 nValue;
+
+ // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY!
+ OString sTblpXSpec = DocxAttributeOutput::convertToOOXMLHoriOrient(
+ rFrame.GetFrameFormat().GetHoriOrient().GetHoriOrient(),
+ rFrame.GetFrameFormat().GetHoriOrient().IsPosToggle());
+ OString sTblpYSpec = DocxAttributeOutput::convertToOOXMLVertOrient(
+ rFrame.GetFrameFormat().GetVertOrient().GetVertOrient());
+
+ sOrientation = DocxAttributeOutput::convertToOOXMLVertOrientRel(
+ rFrame.GetFrameFormat().GetVertOrient().GetRelationOrient());
+ pAttributes->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
+
+ if (!sTblpYSpec.isEmpty())
+ pAttributes->add(FSNS(XML_w, XML_tblpYSpec), sTblpYSpec);
+
+ sOrientation = DocxAttributeOutput::convertToOOXMLHoriOrientRel(
+ rFrame.GetFrameFormat().GetHoriOrient().GetRelationOrient());
+ pAttributes->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
+
+ if (!sTblpXSpec.isEmpty())
+ pAttributes->add(FSNS(XML_w, XML_tblpXSpec), sTblpXSpec);
+
+ nValue = rFrame.GetFrameFormat().GetULSpace().GetLower();
+ if (nValue != 0)
+ pAttributes->add(FSNS(XML_w, XML_bottomFromText), OString::number(nValue));
+
+ nValue = rFrame.GetFrameFormat().GetLRSpace().GetLeft();
+ if (nValue != 0)
+ pAttributes->add(FSNS(XML_w, XML_leftFromText), OString::number(nValue));
+
+ nValue = rFrame.GetFrameFormat().GetLRSpace().GetRight();
+ if (nValue != 0)
+ pAttributes->add(FSNS(XML_w, XML_rightFromText), OString::number(nValue));
+
+ nValue = rFrame.GetFrameFormat().GetULSpace().GetUpper();
+ if (nValue != 0)
+ pAttributes->add(FSNS(XML_w, XML_topFromText), OString::number(nValue));
+
+ if (sTblpXSpec.isEmpty()) // do not write tblpX if tblpXSpec is present
+ {
+ nValue = rFrame.GetFrameFormat().GetHoriOrient().GetPos();
+ // we need to revert the additional shift introduced by
+ // lcl_DecrementHoriOrientPosition() in writerfilter
+ // 1st: left distance of the table
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
+ const SvxBoxItem& rBox = pFrameFormat->GetBox();
+ sal_Int32 nMode = lcl_getWordCompatibilityMode(rExport);
+ if (nMode < 15)
+ {
+ sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT);
+ nValue += nLeftDistance;
+ }
+
+ // 2nd: if a left border is given, revert the shift by half the width
+ // from lcl_DecrementHoriOrientPosition() in writerfilter
+ if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft())
+ {
+ tools::Long nWidth = pLeftBorder->GetWidth();
+ nValue += (nWidth / 2);
+ }
+
+ pAttributes->add(FSNS(XML_w, XML_tblpX), OString::number(nValue));
+ }
+
+ if (sTblpYSpec.isEmpty()) // do not write tblpY if tblpYSpec is present
+ {
+ nValue = rFrame.GetFrameFormat().GetVertOrient().GetPos();
+ pAttributes->add(FSNS(XML_w, XML_tblpY), OString::number(nValue));
+ }
+}
+}
+
+void DocxAttributeOutput::TableInfoCell(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+}
+
+void DocxAttributeOutput::TableInfoRow(ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/)
+{
+}
+
+void DocxAttributeOutput::TableDefinition(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ bool const bEcma = GetExport().GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION;
+
+ // Write the table properties
+ m_pSerializer->startElementNS(XML_w, XML_tblPr);
+
+ static const sal_Int32 aOrder[] = { FSNS(XML_w, XML_tblStyle),
+ FSNS(XML_w, XML_tblpPr),
+ FSNS(XML_w, XML_tblOverlap),
+ FSNS(XML_w, XML_bidiVisual),
+ FSNS(XML_w, XML_tblStyleRowBandSize),
+ FSNS(XML_w, XML_tblStyleColBandSize),
+ FSNS(XML_w, XML_tblW),
+ FSNS(XML_w, XML_jc),
+ FSNS(XML_w, XML_tblCellSpacing),
+ FSNS(XML_w, XML_tblInd),
+ FSNS(XML_w, XML_tblBorders),
+ FSNS(XML_w, XML_shd),
+ FSNS(XML_w, XML_tblLayout),
+ FSNS(XML_w, XML_tblCellMar),
+ FSNS(XML_w, XML_tblLook),
+ FSNS(XML_w, XML_tblPrChange) };
+
+ // postpone the output so that we can later []
+ // prepend the properties before the run
+ // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
+ m_pSerializer->mark(Tag_TableDefinition, comphelper::containerToSequence(aOrder));
+
+ tools::Long nPageSize = 0;
+ const char* widthType = "dxa";
+
+ // If actual width of table is relative it should export is as "pct".`
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ SwFrameFormat* pTableFormat = pTable->GetFrameFormat();
+ const SwFormatFrameSize& rSize = pTableFormat->GetFrameSize();
+ int nWidthPercent = rSize.GetWidthPercent();
+ // If we export a floating table: we use the widthPercent of the surrounding frame
+ const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame();
+ if (pFloatingTableFrame)
+ {
+ const SwFormatFrameSize& rFrameSize = pFloatingTableFrame->GetFrameFormat().GetFrameSize();
+ nWidthPercent = rFrameSize.GetWidthPercent();
+ }
+
+ uno::Reference<beans::XPropertySet> xPropertySet(
+ SwXTextTables::GetObject(*pTable->GetFrameFormat()), uno::UNO_QUERY);
+ bool isWidthRelative = false;
+ xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative;
+ if (!isWidthRelative && !nWidthPercent)
+ {
+ // The best fit for "automatic" table placement is relative 100%
+ short nHoriOrient = -1;
+ xPropertySet->getPropertyValue("HoriOrient") >>= nHoriOrient;
+ isWidthRelative = nHoriOrient == text::HoriOrientation::FULL;
+ if (isWidthRelative)
+ nWidthPercent = 100;
+ }
+
+ if (isWidthRelative)
+ {
+ /**
+ * As per ECMA Specification : ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference [ 17.18.90 ST_TableWidth (Table Width Units)]
+ * http://www.schemacentral.com/sc/ooxml/a-w_type-7.html
+ *
+ * Fiftieths of a Percent :
+ * http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
+ * pct Width is in Fiftieths of a Percent
+ *
+ * ex. If the Table width is 50% then
+ * Width in Fiftieths of a percent is (50 * 50) % or 0.5 * 5000 = 2500pct
+ **/
+ nPageSize = nWidthPercent * 50;
+ widthType = "pct";
+ }
+ else
+ {
+ bool bRelBoxSize = false;
+ // Create the SwWriteTable instance to use col spans (and maybe other infos)
+ GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize);
+ if (nPageSize == 0)
+ widthType = "auto";
+ }
+
+ // Output the table preferred width
+ m_pSerializer->singleElementNS(XML_w, XML_tblW, FSNS(XML_w, XML_w), OString::number(nPageSize),
+ FSNS(XML_w, XML_type), widthType);
+
+ // Disable layout autofit, as it does not exist in LibreOffice yet
+ m_pSerializer->singleElementNS(XML_w, XML_tblLayout, FSNS(XML_w, XML_type), "fixed");
+
+ // Look for the table style property in the table grab bag
+ std::map<OUString, css::uno::Any> aGrabBag
+ = pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
+
+ // We should clear the TableStyle map. In case of Table inside multiple tables it contains the
+ // table border style of the previous table.
+ std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = m_aTableStyleConfs.back();
+ rTableStyleConf.clear();
+
+ bool bFloatingTableWritten = false;
+ if (pFloatingTableFrame && pFloatingTableFrame->GetFrameFormat().GetFlySplit().GetValue())
+ {
+ rtl::Reference<FastAttributeList> pAttributes = FastSerializerHelper::createAttrList();
+ CollectFloatingTableAttributes(m_rExport, *pFloatingTableFrame, pTableTextNodeInfoInner,
+ pAttributes);
+ m_pSerializer->singleElementNS(XML_w, XML_tblpPr, pAttributes);
+ bFloatingTableWritten = true;
+ }
+
+ // Extract properties from grab bag
+ for (const auto& rGrabBagElement : aGrabBag)
+ {
+ if (rGrabBagElement.first == "TableStyleName")
+ {
+ OString sStyleName
+ = OUStringToOString(rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, XML_val), sStyleName);
+ }
+ else if (rGrabBagElement.first == "TableStyleTopBorder")
+ rTableStyleConf[SvxBoxItemLine::TOP] = rGrabBagElement.second.get<table::BorderLine2>();
+ else if (rGrabBagElement.first == "TableStyleBottomBorder")
+ rTableStyleConf[SvxBoxItemLine::BOTTOM]
+ = rGrabBagElement.second.get<table::BorderLine2>();
+ else if (rGrabBagElement.first == "TableStyleLeftBorder")
+ rTableStyleConf[SvxBoxItemLine::LEFT]
+ = rGrabBagElement.second.get<table::BorderLine2>();
+ else if (rGrabBagElement.first == "TableStyleRightBorder")
+ rTableStyleConf[SvxBoxItemLine::RIGHT]
+ = rGrabBagElement.second.get<table::BorderLine2>();
+ else if (rGrabBagElement.first == "TableStyleLook")
+ {
+ rtl::Reference<FastAttributeList> pAttributeList
+ = FastSerializerHelper::createAttrList();
+ const uno::Sequence<beans::PropertyValue> aAttributeList
+ = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue>>();
+
+ for (const auto& rAttribute : aAttributeList)
+ {
+ if (rAttribute.Name == "val")
+ pAttributeList->add(
+ FSNS(XML_w, XML_val),
+ lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16),
+ 4, '0'));
+ else
+ {
+ static DocxStringTokenMap const aTokens[]
+ = { { "firstRow", XML_firstRow },
+ { "lastRow", XML_lastRow },
+ { "firstColumn", XML_firstColumn },
+ { "lastColumn", XML_lastColumn },
+ { "noHBand", XML_noHBand },
+ { "noVBand", XML_noVBand },
+ { nullptr, 0 } };
+
+ if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name))
+ pAttributeList->add(FSNS(XML_w, nToken),
+ (rAttribute.Value.get<sal_Int32>() ? "1" : "0"));
+ }
+ }
+
+ m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList);
+ }
+ else if (rGrabBagElement.first == "TablePosition" &&
+ // skip empty table position (tables in footnotes converted to
+ // floating tables temporarily, don't export this)
+ rGrabBagElement.second != uno::Any())
+ {
+ rtl::Reference<FastAttributeList> attrListTablePos
+ = FastSerializerHelper::createAttrList();
+ const uno::Sequence<beans::PropertyValue> aTablePosition
+ = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue>>();
+ // look for a surrounding frame and take it's position values
+ const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame();
+ if (pFrame)
+ {
+ CollectFloatingTableAttributes(m_rExport, *pFrame, pTableTextNodeInfoInner,
+ attrListTablePos);
+ }
+ else // ( pFrame = 0 )
+ {
+ // we export the values from the grabBag
+ for (const auto& rProp : aTablePosition)
+ {
+ if (rProp.Name == "vertAnchor" && !rProp.Value.get<OUString>().isEmpty())
+ {
+ OString sOrientation
+ = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
+ }
+ else if (rProp.Name == "tblpYSpec" && !rProp.Value.get<OUString>().isEmpty())
+ {
+ OString sOrientation
+ = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), sOrientation);
+ }
+ else if (rProp.Name == "horzAnchor" && !rProp.Value.get<OUString>().isEmpty())
+ {
+ OString sOrientation
+ = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
+ }
+ else if (rProp.Name == "tblpXSpec" && !rProp.Value.get<OUString>().isEmpty())
+ {
+ OString sOrientation
+ = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), sOrientation);
+ }
+ else if (rProp.Name == "bottomFromText")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_bottomFromText),
+ OString::number(nValue));
+ }
+ else if (rProp.Name == "leftFromText")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_leftFromText),
+ OString::number(nValue));
+ }
+ else if (rProp.Name == "rightFromText")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_rightFromText),
+ OString::number(nValue));
+ }
+ else if (rProp.Name == "topFromText")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_topFromText),
+ OString::number(nValue));
+ }
+ else if (rProp.Name == "tblpX")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_tblpX), OString::number(nValue));
+ }
+ else if (rProp.Name == "tblpY")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_tblpY), OString::number(nValue));
+ }
+ }
+ }
+
+ if (!bFloatingTableWritten)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_tblpPr, attrListTablePos);
+ }
+ attrListTablePos = nullptr;
+ }
+ else
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: unhandled property: "
+ << rGrabBagElement.first);
+ }
+
+ // Output the table alignment
+ const char* pJcVal;
+ sal_Int32 nIndent = 0;
+ switch (pTableFormat->GetHoriOrient().GetHoriOrient())
+ {
+ case text::HoriOrientation::CENTER:
+ pJcVal = "center";
+ break;
+ case text::HoriOrientation::RIGHT:
+ if (bEcma)
+ pJcVal = "right";
+ else
+ pJcVal = "end";
+ break;
+ default:
+ case text::HoriOrientation::NONE:
+ case text::HoriOrientation::LEFT_AND_WIDTH:
+ {
+ if (bEcma)
+ pJcVal = "left";
+ else
+ pJcVal = "start";
+ nIndent = sal_Int32(pTableFormat->GetLRSpace().GetLeft());
+
+ // Table indentation has different meaning in Word, depending if the table is nested or not.
+ // If nested, tblInd is added to parent table's left spacing and defines left edge position
+ // If not nested, text position of left-most cell must be at absolute X = tblInd
+ // so, table_spacing + table_spacing_to_content = tblInd
+
+ // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
+ // if no compatibilityMode is defined (which now should only happen on a new export to .docx),
+ // LO uses a higher compatibility than 2010's 14.
+ sal_Int32 nMode = lcl_getWordCompatibilityMode(m_rExport);
+
+ const SwFrameFormat* pFrameFormat
+ = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();
+ if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth == 0)
+ nIndent += pFrameFormat->GetBox().GetDistance(SvxBoxItemLine::LEFT);
+ else
+ {
+ // adjust for SW considering table to start mid-border instead of nested/2013's left-side-of-border.
+ nIndent -= pFrameFormat->GetBox().CalcLineWidth(SvxBoxItemLine::LEFT) / 2;
+ }
+
+ break;
+ }
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pJcVal);
+
+ // Output the table background color (although cell value still needs to be specified)
+ const SvxBrushItem* pColorProp
+ = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
+ if (aColor != COL_AUTO)
+ {
+ OString sColor = msfilter::util::ConvertColor(aColor);
+ m_pSerializer->singleElementNS(XML_w, XML_shd, FSNS(XML_w, XML_fill), sColor,
+ FSNS(XML_w, XML_val), "clear");
+ }
+
+ // Output the table borders
+ TableDefaultBorders(pTableTextNodeInfoInner);
+
+ // Output the default cell margins
+ TableDefaultCellMargins(pTableTextNodeInfoInner);
+
+ TableBidi(pTableTextNodeInfoInner);
+
+ // Table indent (need to get written even if == 0)
+ m_pSerializer->singleElementNS(XML_w, XML_tblInd, FSNS(XML_w, XML_w), OString::number(nIndent),
+ FSNS(XML_w, XML_type), "dxa");
+
+ // Merge the marks for the ordered elements
+ m_pSerializer->mergeTopMarks(Tag_TableDefinition);
+
+ m_pSerializer->endElementNS(XML_w, XML_tblPr);
+
+ // Write the table grid infos
+ m_pSerializer->startElementNS(XML_w, XML_tblGrid);
+ sal_Int32 nPrv = 0;
+ ww8::WidthsPtr pColumnWidths = GetColumnWidths(pTableTextNodeInfoInner);
+ for (auto aColumnWidth : *pColumnWidths)
+ {
+ sal_Int32 nWidth = sal_Int32(aColumnWidth) - nPrv;
+ m_pSerializer->singleElementNS(XML_w, XML_gridCol, FSNS(XML_w, XML_w),
+ OString::number(nWidth));
+ nPrv = sal_Int32(aColumnWidth);
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_tblGrid);
+}
+
+void DocxAttributeOutput::TableDefaultBorders(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+ // Table defaults should only be created IF m_aTableStyleConf contents haven't come from a table style.
+ // Previously this function wrote out Cell A1 as the table default, causing problems with no benefit.
+}
+
+void DocxAttributeOutput::TableDefaultCellMargins(
+ ww8::WW8TableNodeInfoInner::Pointer_t const& pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
+ const SvxBoxItem& rBox = pFrameFormat->GetBox();
+ const bool bEcma = GetExport().GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION;
+
+ DocxAttributeOutput::ImplCellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma);
+}
+
+void DocxAttributeOutput::TableBackgrounds(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTableRow = pTableBox->GetUpper();
+ const SwFrameFormat* pFormat = pTableBox->GetFrameFormat();
+
+ const SvxBrushItem* pColorProp = pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
+
+ const SwFrameFormat* pRowFormat = pTableRow->GetFrameFormat();
+ const SvxBrushItem* pRowColorProp
+ = pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if (pRowColorProp && aColor == COL_AUTO)
+ aColor = pRowColorProp->GetColor();
+
+ const SwFrameFormat* pTableFormat = pTable->GetFrameFormat();
+ const SvxBrushItem* pTableColorProp
+ = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if (pTableColorProp && aColor == COL_AUTO)
+ aColor = pTableColorProp->GetColor();
+
+ const OString sColor = msfilter::util::ConvertColor(aColor);
+
+ std::map<OUString, css::uno::Any> aGrabBag
+ = pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
+
+ OString sOriginalColor;
+ std::map<OUString, css::uno::Any>::iterator aGrabBagElement = aGrabBag.find("originalColor");
+ if (aGrabBagElement != aGrabBag.end())
+ sOriginalColor
+ = OUStringToOString(aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8);
+
+ if (sOriginalColor != sColor)
+ {
+ // color changed by the user, or no grab bag: write sColor
+ if (sColor != "auto")
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_shd, FSNS(XML_w, XML_fill), sColor,
+ FSNS(XML_w, XML_val), "clear");
+ }
+ }
+ else
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList;
+
+ for (const auto& rGrabBagElement : aGrabBag)
+ {
+ if (!rGrabBagElement.second.has<OUString>())
+ continue;
+
+ OString sValue
+ = OUStringToOString(rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ if (rGrabBagElement.first == "themeFill")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeFill), sValue.getStr());
+ else if (rGrabBagElement.first == "themeFillTint")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeFillTint), sValue.getStr());
+ else if (rGrabBagElement.first == "themeFillShade")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeFillShade), sValue.getStr());
+ else if (rGrabBagElement.first == "fill")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_fill), sValue.getStr());
+ else if (rGrabBagElement.first == "themeColor")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeColor), sValue.getStr());
+ else if (rGrabBagElement.first == "themeTint")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeTint), sValue.getStr());
+ else if (rGrabBagElement.first == "themeShade")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeShade), sValue.getStr());
+ else if (rGrabBagElement.first == "color")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_color), sValue.getStr());
+ else if (rGrabBagElement.first == "val")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_val), sValue.getStr());
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_shd, pAttrList.get());
+ }
+}
+
+void DocxAttributeOutput::TableRowRedline(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTabLine = pTabBox->GetUpper();
+
+ bool bRemovePersonalInfo
+ = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo);
+
+ // check table row property "HasTextChangesOnly"
+ SwRedlineTable::size_type nPos(0);
+ SwRedlineTable::size_type nChange = pTabLine->UpdateTextChangesOnly(nPos);
+ if (nChange != SwRedlineTable::npos)
+ {
+ const SwRedlineTable& aRedlineTable
+ = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ const SwRangeRedline* pRedline = aRedlineTable[nChange];
+ SwTableRowRedline* pTableRowRedline = nullptr;
+ bool bIsInExtra = false;
+
+ // use the original DOCX redline data stored in ExtraRedlineTable,
+ // if it exists and its type wasn't changed
+ const SwExtraRedlineTable& aExtraRedlineTable
+ = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
+ for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize();
+ ++nCurRedlinePos)
+ {
+ SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
+ pTableRowRedline = dynamic_cast<SwTableRowRedline*>(pExtraRedline);
+ if (pTableRowRedline && &pTableRowRedline->GetTableLine() == pTabLine)
+ {
+ bIsInExtra = true;
+ break;
+ }
+ }
+
+ const SwRedlineData& aRedlineData
+ = bIsInExtra &&
+ // still the same type (an inserted row could become a tracked deleted one)
+ pTableRowRedline->GetRedlineData().GetType()
+ == pRedline->GetRedlineData().GetType()
+ ? pTableRowRedline->GetRedlineData()
+ : pRedline->GetRedlineData();
+
+ // Note: all redline ranges and table row redline (with the same author and timestamp)
+ // use the same redline id in OOXML exported by MSO, but it seems, the recent solution
+ // (different IDs for different ranges, also row changes) is also portable.
+ OString aId(OString::number(m_nRedlineId++));
+ const OUString& rAuthor(SW_MOD()->GetRedlineAuthor(aRedlineData.GetAuthor()));
+ OString aAuthor(OUStringToOString(
+ bRemovePersonalInfo ? "Author" + OUString::number(GetExport().GetInfoID(rAuthor))
+ : rAuthor,
+ RTL_TEXTENCODING_UTF8));
+
+ const DateTime aDateTime = aRedlineData.GetTimeStamp();
+ bool bNoDate = bRemovePersonalInfo
+ || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1
+ && aDateTime.GetDay() == 1);
+
+ if (bNoDate)
+ m_pSerializer->singleElementNS(
+ XML_w, RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins,
+ FSNS(XML_w, XML_id), aId, FSNS(XML_w, XML_author), aAuthor);
+ else
+ m_pSerializer->singleElementNS(
+ XML_w, RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins,
+ FSNS(XML_w, XML_id), aId, FSNS(XML_w, XML_author), aAuthor, FSNS(XML_w, XML_date),
+ DateTimeToOString(aDateTime));
+ return;
+ }
+}
+
+void DocxAttributeOutput::TableCellRedline(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+
+ bool bRemovePersonalInfo
+ = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo);
+
+ // search next Redline
+ const SwExtraRedlineTable& aExtraRedlineTable
+ = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
+ for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize();
+ ++nCurRedlinePos)
+ {
+ SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
+ const SwTableCellRedline* pTableCellRedline
+ = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
+ if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox)
+ {
+ // Redline for this table cell
+ const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
+ RedlineType nRedlineType = aRedlineData.GetType();
+ switch (nRedlineType)
+ {
+ case RedlineType::TableCellInsert:
+ case RedlineType::TableCellDelete:
+ {
+ OString aId(OString::number(m_nRedlineId++));
+ const OUString& rAuthor(SW_MOD()->GetRedlineAuthor(aRedlineData.GetAuthor()));
+ OString aAuthor(OUStringToOString(
+ bRemovePersonalInfo
+ ? "Author" + OUString::number(GetExport().GetInfoID(rAuthor))
+ : rAuthor,
+ RTL_TEXTENCODING_UTF8));
+
+ sal_Int32 nElement
+ = nRedlineType == RedlineType::TableCellInsert ? XML_cellIns : XML_cellDel;
+ const DateTime aDateTime = aRedlineData.GetTimeStamp();
+ bool bNoDate = bRemovePersonalInfo
+ || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1
+ && aDateTime.GetDay() == 1);
+ if (bNoDate)
+ m_pSerializer->singleElementNS(XML_w, nElement, FSNS(XML_w, XML_id), aId,
+ FSNS(XML_w, XML_author), aAuthor);
+ else
+ m_pSerializer->singleElementNS(
+ XML_w, nElement, FSNS(XML_w, XML_id), aId, FSNS(XML_w, XML_author),
+ aAuthor, FSNS(XML_w, XML_date), DateTimeToOString(aDateTime));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+void DocxAttributeOutput::TableHeight(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTabLine = pTabBox->GetUpper();
+ const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
+
+ const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
+ if (!(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()))
+ return;
+
+ sal_Int32 nHeight = rLSz.GetHeight();
+ const char* pRule = nullptr;
+
+ switch (rLSz.GetHeightSizeType())
+ {
+ case SwFrameSize::Fixed:
+ pRule = "exact";
+ break;
+ case SwFrameSize::Minimum:
+ pRule = "atLeast";
+ break;
+ default:
+ break;
+ }
+
+ if (pRule)
+ m_pSerializer->singleElementNS(XML_w, XML_trHeight, FSNS(XML_w, XML_val),
+ OString::number(nHeight), FSNS(XML_w, XML_hRule), pRule);
+}
+
+void DocxAttributeOutput::TableCanSplit(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTabLine = pTabBox->GetUpper();
+ const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
+
+ const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
+ // if rSplittable is true then no need to write <w:cantSplit w:val="false"/>
+ // as default row prop is allow row to break across page.
+ if (!rSplittable.GetValue())
+ m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w, XML_val), "true");
+}
+
+void DocxAttributeOutput::TableBidi(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ const SwFrameFormat* pFrameFormat = pTable->GetFrameFormat();
+
+ if (m_rExport.TrueFrameDirection(*pFrameFormat) == SvxFrameDirection::Horizontal_RL_TB)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_bidiVisual, FSNS(XML_w, XML_val), "true");
+ }
+}
+
+void DocxAttributeOutput::TableVerticalCell(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
+
+ if (SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection(*pFrameFormat))
+ m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "tbRl");
+ else if (SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection(*pFrameFormat))
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "btLr");
+ }
+
+ const SwWriteTableRows& rRows = m_xTableWrt->GetRows();
+ const auto nRow = pTableTextNodeInfoInner->getRow();
+ if (nRow >= rRows.size())
+ {
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::TableCellProperties: out of range row: " << nRow);
+ return;
+ }
+ SwWriteTableRow* pRow = rRows[nRow].get();
+ sal_uInt32 nCell = pTableTextNodeInfoInner->getCell();
+ const SwWriteTableCells& rTableCells = pRow->GetCells();
+ if (nCell >= rTableCells.size())
+ return;
+
+ const SwWriteTableCell* const pCell = pRow->GetCells()[nCell].get();
+ switch (pCell->GetVertOri())
+ {
+ case text::VertOrientation::TOP:
+ break;
+ case text::VertOrientation::CENTER:
+ m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center");
+ break;
+ case text::VertOrientation::BOTTOM:
+ m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom");
+ break;
+ }
+}
+
+void DocxAttributeOutput::TableNodeInfoInner(ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner)
+{
+ // This is called when the nested table ends in a cell, and there's no
+ // paragraph behind that; so we must check for the ends of cell, rows,
+ // tables
+ // ['true' to write an empty paragraph, MS Word insists on that]
+ FinishTableRowCell(pNodeInfoInner, true);
+}
+
+void DocxAttributeOutput::TableOrientation(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+ SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableOrientation( "
+ "ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )");
+}
+
+void DocxAttributeOutput::TableSpacing(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+ SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableSpacing( "
+ "ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )");
+}
+
+void DocxAttributeOutput::TableRowEnd(sal_uInt32 /*nDepth*/)
+{
+ SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 nDepth = 1 )");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */