summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.co.uk>2018-02-14 15:31:35 +0100
committerMiklos Vajna <vmiklos@collabora.co.uk>2018-02-14 20:21:57 +0100
commit8b73bafbc18acb4dd8911d2f2de8158d98eb6144 (patch)
tree5bb43213b1c7ef8802d8f6b50bffdf9742117ad2
parentb13678b1e1d6f4cac548ae7e088b6030c31cf081 (diff)
tdf#115719 DOCX import: increase paragraph spacing for anchored objects
... like Word 2013 does, when the version string indicates that the new layout is wanted. An alternative to this change would be to add a new sw layout compatibility flag and handle this at a layout level (somewhere in SwAnchoredObject::GetObjRectWithSpaces()). The downside of that approach is that once a layout flag is added, it's not preferred to tweak its behavior, while doing the same at import time is not a problem. Also it's better to have a flag for something which has clear behavior in some spec / implementer notes, which is not the case for this problem. (I've mailed dochelp@microsoft, no answer so far.) Change-Id: Ibad28d27e4bcbe1991a3be1c686064e18e9ffa4d Reviewed-on: https://gerrit.libreoffice.org/49733 Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk> Tested-by: Jenkins <ci@libreoffice.org>
-rw-r--r--sw/qa/extras/ooxmlexport/data/tdf115719.docxbin0 -> 11905 bytes
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport11.cxx7
-rw-r--r--writerfilter/source/dmapper/DomainMapperTableHandler.cxx30
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.cxx17
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.hxx16
-rw-r--r--writerfilter/source/dmapper/PropertyMap.cxx68
-rw-r--r--writerfilter/source/dmapper/PropertyMap.hxx4
-rw-r--r--writerfilter/source/dmapper/SettingsTable.cxx27
-rw-r--r--writerfilter/source/dmapper/SettingsTable.hxx2
9 files changed, 142 insertions, 29 deletions
diff --git a/sw/qa/extras/ooxmlexport/data/tdf115719.docx b/sw/qa/extras/ooxmlexport/data/tdf115719.docx
new file mode 100644
index 000000000000..9519a2a14524
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf115719.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index 134576b6275c..661a6a7597e0 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -101,6 +101,13 @@ DECLARE_OOXMLEXPORT_TEST(testTdf67207_MERGEFIELD, "mailmerge.docx")
CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.text.fieldmaster.DataBase.Name"), sValue);
}
+DECLARE_OOXMLEXPORT_TEST(testTdf115719, "tdf115719.docx")
+{
+ // This was a single page, instead of pushing the textboxes to the second
+ // page.
+ CPPUNIT_ASSERT_EQUAL(2, getPages());
+}
+
DECLARE_OOXMLEXPORT_TEST(testParagraphSplitOnSectionBorder, "parasplit-on-section-border.odt")
{
xmlDocPtr pXmlDoc = parseExport("word/document.xml");
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index 51111fd55f41..851dba8e165e 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -312,34 +312,6 @@ void lcl_DecrementHoriOrientPosition(std::vector<beans::PropertyValue>& rFramePr
}
}
-sal_Int32 lcl_getWordCompatibilityMode( const css::uno::Sequence< css::beans::PropertyValue >& rCompatSettings )
-{
- for ( int i = 0; i < rCompatSettings.getLength(); ++i )
- {
- const css::beans::PropertyValue& rProp = rCompatSettings[i];
- if ( rProp.Name == "compatSetting" )
- {
- css::uno::Sequence< css::beans::PropertyValue > aCurrentCompatSettings;
- rProp.Value >>= aCurrentCompatSettings;
-
- OUString sName;
- OUString sUri;
- OUString sVal;
-
- aCurrentCompatSettings[0].Value >>= sName;
- aCurrentCompatSettings[1].Value >>= sUri;
- aCurrentCompatSettings[2].Value >>= sVal;
-
- if ( sName == "compatibilityMode" && sUri == "http://schemas.microsoft.com/office/word" )
- {
- return sVal.toInt32();
- }
- }
- }
-
- return -1; // Word compatibility mode not found
-}
-
TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo, std::vector<beans::PropertyValue>& rFrameProperties)
{
// will receive the table style if any
@@ -574,7 +546,7 @@ TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo
// tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
// this is also the default behavior in LO when DOCX doesn't define "compatibilityMode" option
- sal_Int32 nMode = lcl_getWordCompatibilityMode( m_rDMapper_Impl.GetSettingsTable()->GetCompatSettings() );
+ sal_Int32 nMode = m_rDMapper_Impl.GetSettingsTable()->GetWordCompatibilityMode();
if ( nMode > 0 && nMode <= 14 && rInfo.nNestLevel == 1 )
{
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index bb15f12c824c..355408d4a07e 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -1330,6 +1330,17 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap )
xTextRange = xTextAppend->finishParagraph( comphelper::containerToSequence(aProperties) );
m_xPreviousParagraph.set(xTextRange, uno::UNO_QUERY);
+
+ if (!rAppendContext.m_aAnchoredObjects.empty())
+ {
+ // Remember what objects are anchored to this paragraph.
+ AnchoredObjectInfo 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;
@@ -5222,8 +5233,14 @@ void DomainMapper_Impl::ImportGraphic(const writerfilter::Reference< Properties
//insert it into the document at the current cursor position
OSL_ENSURE( xTextContent.is(), "DomainMapper_Impl::ImportGraphic");
if( xTextContent.is())
+ {
appendTextContent( xTextContent, uno::Sequence< beans::PropertyValue >() );
+ if (eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR && !m_aTextAppendStack.empty())
+ // Remember this object is anchored to the current paragraph.
+ m_aTextAppendStack.top().m_aAnchoredObjects.push_back(xTextContent);
+ }
+
// 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.
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index d7fb9b4e8c40..911d2e8ee100 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -215,6 +215,12 @@ struct TextAppendContext
css::uno::Reference<css::text::XParagraphCursor> xCursor;
ParagraphPropertiesPtr pLastParagraphProperties;
+ /**
+ * Objects anchored to the current paragraph, may affect the paragraph
+ * spacing.
+ */
+ std::vector<css::uno::Reference<css::text::XTextContent>> m_aAnchoredObjects;
+
TextAppendContext(const css::uno::Reference<css::text::XTextAppend>& xAppend, const css::uno::Reference<css::text::XTextCursor>& xCur)
: xTextAppend(xAppend)
{
@@ -374,6 +380,13 @@ struct FloatingTableInfo
css::uno::Any getPropertyValue(const OUString &propertyName);
};
+/// Stores info about objects anchored to a given paragraph.
+struct AnchoredObjectInfo
+{
+ css::uno::Reference<css::text::XTextRange> m_xParagraph;
+ std::vector<css::uno::Reference<css::text::XTextContent>> m_aAnchoredObjects;
+};
+
struct SymbolData
{
sal_Unicode cSymbol;
@@ -919,6 +932,9 @@ public:
/// 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<AnchoredObjectInfo> 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);
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx
index 27ff89bdd0f5..2d323d320c1e 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -1127,6 +1127,72 @@ void SectionPropertyMap::InheritOrFinalizePageStyles( DomainMapper_Impl& rDM_Imp
}
}
+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<AnchoredObjectInfo>& rAnchoredObjectAnchors = rDM_Impl.m_aAnchoredObjectAnchors;
+ for (auto& rAnchor : rAnchoredObjectAnchors)
+ {
+ // 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, uno::UNO_QUERY);
+ if (!xShape.is())
+ continue;
+
+ uno::Reference<beans::XPropertySet> xPropertySet(rAnchored, 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;
+
+ sal_Int32 nLeftMargin = 0;
+ xPropertySet->getPropertyValue("LeftMargin") >>= 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, 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::makeAny(nTopMargin));
+ }
+ }
+ rAnchoredObjectAnchors.clear();
+}
+
void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
{
// The default section type is nextPage.
@@ -1144,6 +1210,8 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
}
rPendingFloatingTables.clear();
+ HandleIncreasedAnchoredObjectSpacing(rDM_Impl);
+
if ( m_nLnnMod )
{
bool bFirst = rDM_Impl.IsLineNumberingSet();
diff --git a/writerfilter/source/dmapper/PropertyMap.hxx b/writerfilter/source/dmapper/PropertyMap.hxx
index 3e09007d8f6a..c373522d2792 100644
--- a/writerfilter/source/dmapper/PropertyMap.hxx
+++ b/writerfilter/source/dmapper/PropertyMap.hxx
@@ -59,6 +59,7 @@ namespace dmapper {
class DomainMapper_Impl;
struct FloatingTableInfo;
+struct AnchoredObjectInfo;
enum BorderPosition
{
@@ -280,6 +281,9 @@ private:
// Determines if conversion of a given floating table is wanted or not.
bool FloatingTableConversion( 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
{
diff --git a/writerfilter/source/dmapper/SettingsTable.cxx b/writerfilter/source/dmapper/SettingsTable.cxx
index 76df6f81ab77..12c26049b139 100644
--- a/writerfilter/source/dmapper/SettingsTable.cxx
+++ b/writerfilter/source/dmapper/SettingsTable.cxx
@@ -617,6 +617,33 @@ void SettingsTable::ApplyProperties(uno::Reference<text::XTextDocument> const& x
}
}
+sal_Int32 SettingsTable::GetWordCompatibilityMode() const
+{
+ for (const auto& rProp : m_pImpl->m_aCompatSettings)
+ {
+ if (rProp.Name == "compatSetting")
+ {
+ css::uno::Sequence<css::beans::PropertyValue> aCurrentCompatSettings;
+ rProp.Value >>= aCurrentCompatSettings;
+
+ OUString sName;
+ OUString sUri;
+ OUString sVal;
+
+ aCurrentCompatSettings[0].Value >>= sName;
+ aCurrentCompatSettings[1].Value >>= sUri;
+ aCurrentCompatSettings[2].Value >>= sVal;
+
+ if (sName == "compatibilityMode" && sUri == "http://schemas.microsoft.com/office/word")
+ {
+ return sVal.toInt32();
+ }
+ }
+ }
+
+ return -1; // Word compatibility mode not found
+}
+
}//namespace dmapper
} //namespace writerfilter
diff --git a/writerfilter/source/dmapper/SettingsTable.hxx b/writerfilter/source/dmapper/SettingsTable.hxx
index 4dcfd2dcaf6a..a36115616e4f 100644
--- a/writerfilter/source/dmapper/SettingsTable.hxx
+++ b/writerfilter/source/dmapper/SettingsTable.hxx
@@ -83,6 +83,8 @@ class SettingsTable : public LoggedProperties, public LoggedTable
void ApplyProperties(css::uno::Reference<css::text::XTextDocument> const& xDoc);
+ sal_Int32 GetWordCompatibilityMode() const;
+
private:
// Properties
virtual void lcl_attribute(Id Name, Value & val) override;