[Libreoffice-commits] core.git: sw/qa sw/source
Adam Co
rattles2013 at gmail.com
Thu Jul 18 03:23:01 PDT 2013
sw/qa/extras/ooxmlexport/data/page-borders-export-case-2.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport.cxx | 87 +++++++
sw/source/filter/ww8/docxattributeoutput.cxx | 109 +++++++---
sw/source/filter/ww8/docxattributeoutput.hxx | 19 +
4 files changed, 191 insertions(+), 24 deletions(-)
New commits:
commit 389f017ab17f9ebc613994b9af7c71f9219baf70
Author: Adam Co <rattles2013 at gmail.com>
Date: Wed Jul 17 16:43:52 2013 +0300
fix DOCX export page border - interoperabillity. export case #2
This is a fix for the 2nd case of export of a page's border.
The case is when a border's distance from text is larger than 31pt.
In such a case - LO used to write the value as-is,
which caused a problem in Word.
Now the fix checks the value, and if it is larger than 31pt -
it converts it to a distance from the page margin.
Based on the problem described here:
http://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
Change-Id: I79f721adc71ac744eb332fbf3fea8070e41ddabc
Reviewed-on: https://gerrit.libreoffice.org/4959
diff --git a/sw/qa/extras/ooxmlexport/data/page-borders-export-case-2.docx b/sw/qa/extras/ooxmlexport/data/page-borders-export-case-2.docx
new file mode 100644
index 0000000..f06471e
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/page-borders-export-case-2.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
index 86d6e39..9778100 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
@@ -96,6 +96,7 @@ public:
void testFdo58577();
void testBnc581614();
void testFdo66929();
+ void testPageBorderSpacingExportCase2();
CPPUNIT_TEST_SUITE(Test);
#if !defined(MACOSX) && !defined(WNT)
@@ -162,6 +163,7 @@ void Test::run()
{"fdo58577.odt", &Test::testFdo58577},
{"bnc581614.doc", &Test::testBnc581614},
{"fdo66929.docx", &Test::testFdo66929},
+ {"page-borders-export-case-2.docx", &Test::testPageBorderSpacingExportCase2},
};
// Don't test the first import of these, for some reason those tests fail
const char* aBlacklist[] = {
@@ -969,6 +971,91 @@ void Test::testFdo66929()
CPPUNIT_ASSERT_EQUAL( sal_Int32( 127 ), getProperty< sal_Int32 >( xFrame, "BottomBorderDistance" ) );
}
+void Test::testPageBorderSpacingExportCase2()
+{
+ /*
+ * The problem was that the exporter didn't mirror the workaround of the
+ * importer, regarding the page border's spacing : the <w:pgBorders w:offsetFrom="page">
+ * and the inner nodes like <w:top w:space="24" .... />
+ *
+ * The exporter ALWAYS exported 'w:offsetFrom="text"' even when the spacing values where too large
+ * for Word to handle (larger than 31 points)
+ *
+ * Given that this doesn't affect the result in the importer, we test the
+ * resulting file directly, by opening the zip file, parsing an xml stream,
+ * and asserting an XPath expression. This can be extracted to a helper
+ * method, once it's clear what is common in such tests.
+ */
+
+ // Create the zip file
+ utl::TempFile aTempFile;
+ save("Office Open XML Text", aTempFile);
+
+ // Read the XML stream we're interested in
+ uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), aTempFile.GetURL());
+ uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName("word/document.xml"), uno::UNO_QUERY);
+ boost::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, sal_True));
+ pStream->Seek(STREAM_SEEK_TO_END);
+ sal_Size nSize = pStream->Tell();
+ pStream->Seek(0);
+ OStringBuffer aDocument(nSize);
+
+ char ch;
+ for (sal_Size i = 0; i < nSize; ++i)
+ {
+ *pStream >> ch;
+ aDocument.append(ch);
+ }
+
+ xmlDocPtr pXmlDoc;
+ xmlXPathContextPtr pXmlXpathContext;
+ OString aXPath;
+ xmlXPathObjectPtr pXmlXpathObj;
+ xmlNodeSetPtr pXmlNodes;
+ xmlNodePtr pXmlNode;
+ OString aAttributeName;
+ OUString aAttributeValue;
+
+ // Parse the XML
+ pXmlDoc = xmlParseMemory((const char*)aDocument.getStr(), aDocument.getLength());
+
+ // Assert the XPath expression - page borders
+ pXmlXpathContext = xmlXPathNewContext(pXmlDoc);
+ xmlXPathRegisterNs(pXmlXpathContext, BAD_CAST("w"), BAD_CAST("http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
+ aXPath = "/w:document/w:body/w:sectPr/w:pgBorders";
+ pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathContext);
+ pXmlNodes = pXmlXpathObj->nodesetval;
+ CPPUNIT_ASSERT_EQUAL(1, xmlXPathNodeSetGetLength(pXmlNodes));
+ pXmlNode = pXmlNodes->nodeTab[0];
+ aAttributeName = "offsetFrom";
+ aAttributeValue = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttributeName.getStr())));
+ CPPUNIT_ASSERT_EQUAL(OUString("page"), aAttributeValue);
+
+ // Assert the XPath expression - 'left' border
+ pXmlXpathContext = xmlXPathNewContext(pXmlDoc);
+ xmlXPathRegisterNs(pXmlXpathContext, BAD_CAST("w"), BAD_CAST("http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
+ aXPath = "/w:document/w:body/w:sectPr/w:pgBorders/w:left";
+ pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathContext);
+ pXmlNodes = pXmlXpathObj->nodesetval;
+ CPPUNIT_ASSERT_EQUAL(1, xmlXPathNodeSetGetLength(pXmlNodes));
+ pXmlNode = pXmlNodes->nodeTab[0];
+ aAttributeName = "space";
+ aAttributeValue = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttributeName.getStr())));
+ CPPUNIT_ASSERT_EQUAL(OUString("24"), aAttributeValue);
+
+ // Assert the XPath expression - 'right' border
+ pXmlXpathContext = xmlXPathNewContext(pXmlDoc);
+ xmlXPathRegisterNs(pXmlXpathContext, BAD_CAST("w"), BAD_CAST("http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
+ aXPath = "/w:document/w:body/w:sectPr/w:pgBorders/w:right";
+ pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathContext);
+ pXmlNodes = pXmlXpathObj->nodesetval;
+ CPPUNIT_ASSERT_EQUAL(1, xmlXPathNodeSetGetLength(pXmlNodes));
+ pXmlNode = pXmlNodes->nodeTab[0];
+ aAttributeName = "space";
+ aAttributeValue = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttributeName.getStr())));
+ CPPUNIT_ASSERT_EQUAL(OUString("24"), aAttributeValue);
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(Test);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 0169cf8..1f01d7b 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -1588,6 +1588,7 @@ static OutputBorderOptions lcl_getTableDefaultBorderOptions(bool bEcma)
rOptions.bWriteTag = true;
rOptions.bWriteInsideHV = true;
rOptions.bWriteDistance = false;
+ rOptions.bCheckDistanceSize = false;
return rOptions;
}
@@ -1601,6 +1602,7 @@ static OutputBorderOptions lcl_getTableCellBorderOptions(bool bEcma)
rOptions.bWriteTag = true;
rOptions.bWriteInsideHV = true;
rOptions.bWriteDistance = false;
+ rOptions.bCheckDistanceSize = false;
return rOptions;
}
@@ -1614,11 +1616,22 @@ static OutputBorderOptions lcl_getBoxBorderOptions()
rOptions.bWriteTag = false;
rOptions.bWriteInsideHV = false;
rOptions.bWriteDistance = true;
+ rOptions.bCheckDistanceSize = false;
return rOptions;
}
-static void impl_borders( FSHelperPtr pSerializer, const SvxBoxItem& rBox, const OutputBorderOptions& rOptions)
+static bool boxHasLineLargerThan31(const SvxBoxItem& rBox)
+{
+ return (
+ ( rBox.GetDistance( BOX_LINE_TOP ) / 20 ) > 31 ||
+ ( rBox.GetDistance( BOX_LINE_LEFT ) / 20 ) > 31 ||
+ ( rBox.GetDistance( BOX_LINE_BOTTOM ) / 20 ) > 31 ||
+ ( rBox.GetDistance( BOX_LINE_RIGHT ) / 20 ) > 31
+ );
+}
+
+static void impl_borders( FSHelperPtr pSerializer, const SvxBoxItem& rBox, const OutputBorderOptions& rOptions, PageMargins* pageMargins)
{
static const sal_uInt16 aBorders[] =
{
@@ -1634,6 +1647,17 @@ static void impl_borders( FSHelperPtr pSerializer, const SvxBoxItem& rBox, const
};
bool tagWritten = false;
const sal_uInt16* pBrd = aBorders;
+
+ bool bExportDistanceFromPageEdge = false;
+ if ( rOptions.bCheckDistanceSize == true && boxHasLineLargerThan31(rBox) == true )
+ {
+ // The distance is larger than '31'. This cannot be exported as 'distance from text'.
+ // Instead - it should be exported as 'distance from page edge'.
+ // This is based on http://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
+ // Specifically 'export case #2'
+ bExportDistanceFromPageEdge = true;
+ }
+
for( int i = 0; i < 4; ++i, ++pBrd )
{
const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
@@ -1646,7 +1670,23 @@ static void impl_borders( FSHelperPtr pSerializer, const SvxBoxItem& rBox, const
sal_uInt16 nDist = 0;
if (rOptions.bWriteDistance)
{
- nDist = rBox.GetDistance( *pBrd );
+ if (bExportDistanceFromPageEdge)
+ {
+ // Export 'Distance from Page Edge'
+ if ( *pBrd == BOX_LINE_TOP)
+ nDist = pageMargins->nPageMarginTop - rBox.GetDistance( *pBrd );
+ else if ( *pBrd == BOX_LINE_LEFT)
+ nDist = pageMargins->nPageMarginLeft - rBox.GetDistance( *pBrd );
+ else if ( *pBrd == BOX_LINE_BOTTOM)
+ nDist = pageMargins->nPageMarginBottom - rBox.GetDistance( *pBrd );
+ else if ( *pBrd == BOX_LINE_RIGHT)
+ nDist = pageMargins->nPageMarginRight - rBox.GetDistance( *pBrd );
+ }
+ else
+ {
+ // Export 'Distance from text'
+ nDist = rBox.GetDistance( *pBrd );
+ }
}
impl_borderLine( pSerializer, aXmlElements[i], pLn, nDist );
@@ -1760,7 +1800,7 @@ void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Point
const SvxBoxItem& rDefaultBox = (*tableFirstCells.rbegin())->getTableBox( )->GetFrmFmt( )->GetBox( );
{
// The cell borders
- impl_borders( m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma) );
+ impl_borders( m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma), NULL );
}
TableBackgrounds( pTableTextNodeInfoInner );
@@ -2010,7 +2050,7 @@ void DocxAttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Point
bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
// the defaults of the table are taken from the top-left cell
- impl_borders( m_pSerializer, pFrmFmt->GetBox( ), lcl_getTableDefaultBorderOptions(bEcma) );
+ impl_borders( m_pSerializer, pFrmFmt->GetBox( ), lcl_getTableDefaultBorderOptions(bEcma), NULL );
}
void DocxAttributeOutput::TableDefaultCellMargins( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
@@ -3065,17 +3105,27 @@ void DocxAttributeOutput::SectionPageBorders( const SwFrmFmt* pFmt, const SwFrmF
const SvxBoxItem& rBox = pFmt->GetBox( );
- const SvxBorderLine* pBottom = rBox.GetBottom( );
- const SvxBorderLine* pTop = rBox.GetTop( );
const SvxBorderLine* pLeft = rBox.GetLeft( );
+ const SvxBorderLine* pTop = rBox.GetTop( );
const SvxBorderLine* pRight = rBox.GetRight( );
+ const SvxBorderLine* pBottom = rBox.GetBottom( );
if ( pBottom || pTop || pLeft || pRight )
{
+ bool bExportDistanceFromPageEdge = false;
+ if ( boxHasLineLargerThan31(rBox) == true )
+ {
+ // The distance is larger than '31'. This cannot be exported as 'distance from text'.
+ // Instead - it should be exported as 'distance from page edge'.
+ // This is based on http://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
+ // Specifically 'export case #2'
+ bExportDistanceFromPageEdge = true;
+ }
+
// All distances are relative to the text margins
m_pSerializer->startElementNS( XML_w, XML_pgBorders,
FSNS( XML_w, XML_display ), "allPages",
- FSNS( XML_w, XML_offsetFrom ), "text",
+ FSNS( XML_w, XML_offsetFrom ), bExportDistanceFromPageEdge ? "page" : "text",
FSEND );
m_pSerializer->mark();
@@ -4577,20 +4627,23 @@ void DocxAttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLRSpace )
if ( !m_pSectionSpacingAttrList )
m_pSectionSpacingAttrList = m_pSerializer->createAttrList();
- sal_uInt16 nLDist, nRDist;
+ m_pageMargins.nPageMarginLeft = 0;
+ m_pageMargins.nPageMarginRight = 0;
+
const SfxPoolItem* pItem = m_rExport.HasItem( RES_BOX );
if ( pItem )
{
- nRDist = ((SvxBoxItem*)pItem)->CalcLineSpace( BOX_LINE_LEFT );
- nLDist = ((SvxBoxItem*)pItem)->CalcLineSpace( BOX_LINE_RIGHT );
+ m_pageMargins.nPageMarginRight = ((SvxBoxItem*)pItem)->CalcLineSpace( BOX_LINE_LEFT );
+ m_pageMargins.nPageMarginLeft = ((SvxBoxItem*)pItem)->CalcLineSpace( BOX_LINE_RIGHT );
}
else
- nLDist = nRDist = 0;
- nLDist = nLDist + (sal_uInt16)rLRSpace.GetLeft();
- nRDist = nRDist + (sal_uInt16)rLRSpace.GetRight();
+ m_pageMargins.nPageMarginLeft = m_pageMargins.nPageMarginRight = 0;
+
+ m_pageMargins.nPageMarginLeft = m_pageMargins.nPageMarginLeft + (sal_uInt16)rLRSpace.GetLeft();
+ m_pageMargins.nPageMarginRight = m_pageMargins.nPageMarginRight + (sal_uInt16)rLRSpace.GetRight();
- m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_left ), OString::valueOf( sal_Int32( nLDist ) ) );
- m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_right ), OString::valueOf( sal_Int32( nRDist ) ) );
+ m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_left ), OString::valueOf( sal_Int32( m_pageMargins.nPageMarginLeft ) ) );
+ m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_right ), OString::valueOf( sal_Int32( m_pageMargins.nPageMarginRight ) ) );
}
else
{
@@ -4640,8 +4693,9 @@ void DocxAttributeOutput::FormatULSpace( const SvxULSpaceItem& rULSpace )
m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_header ), OString::valueOf( nHeader ) );
// Page top
+ m_pageMargins.nPageMarginTop = aDistances.dyaTop;
m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_top ),
- OString::valueOf( sal_Int32( aDistances.dyaTop ) ) );
+ OString::valueOf( sal_Int32( m_pageMargins.nPageMarginTop ) ) );
sal_Int32 nFooter = 0;
if ( aDistances.HasFooter() )
@@ -4649,13 +4703,13 @@ void DocxAttributeOutput::FormatULSpace( const SvxULSpaceItem& rULSpace )
m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_footer ), OString::valueOf( nFooter ) );
// Page Bottom
+ m_pageMargins.nPageMarginBottom = aDistances.dyaBottom;
m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_bottom ),
- OString::valueOf( sal_Int32( aDistances.dyaBottom ) ) );
+ OString::valueOf( sal_Int32( m_pageMargins.nPageMarginBottom ) ) );
// FIXME Page Gutter is not handled ATM, setting to 0 as it's mandatory for OOXML
m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_gutter ),
OString::valueOf( sal_Int32( 0 ) ) );
-
}
else
{
@@ -4940,10 +4994,11 @@ void DocxAttributeOutput::FormatBox( const SvxBoxItem& rBox )
{
if (m_bTextFrameSyntax)
{
- const SvxBorderLine* pLeft = rBox.GetLine(BOX_LINE_LEFT);
- const SvxBorderLine* pRight = rBox.GetLine(BOX_LINE_RIGHT);
- const SvxBorderLine* pTop = rBox.GetLine(BOX_LINE_TOP);
- const SvxBorderLine* pBottom = rBox.GetLine(BOX_LINE_BOTTOM);
+ const SvxBorderLine* pLeft = rBox.GetLeft( );
+ const SvxBorderLine* pTop = rBox.GetTop( );
+ const SvxBorderLine* pRight = rBox.GetRight( );
+ const SvxBorderLine* pBottom = rBox.GetBottom( );
+
if (pLeft && pRight && pTop && pBottom &&
*pLeft == *pRight && *pLeft == *pTop && *pLeft == *pBottom)
{
@@ -5000,13 +5055,21 @@ void DocxAttributeOutput::FormatBox( const SvxBoxItem& rBox )
return;
}
+
+ OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions();
+
if ( !m_bOpenedSectPr )
{
// Normally open the borders tag for paragraphs
m_pSerializer->startElementNS( XML_w, XML_pBdr, FSEND );
}
+ else
+ {
+ // If inside a section - check if the distance is larger than 31 points
+ aOutputBorderOptions.bCheckDistanceSize = true;
+ }
- impl_borders( m_pSerializer, rBox, lcl_getBoxBorderOptions() );
+ impl_borders( m_pSerializer, rBox, aOutputBorderOptions, &m_pageMargins );
if ( m_bOpenedSectPr )
{
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 83913a6..316ea6a 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -78,8 +78,23 @@ struct OutputBorderOptions
bool bWriteTag;
bool bWriteInsideHV;
bool bWriteDistance;
+ bool bCheckDistanceSize;
- OutputBorderOptions() : tag(0), bUseStartEnd(false), bWriteTag(true), bWriteInsideHV(false), bWriteDistance(false) {}
+ OutputBorderOptions() : tag(0), bUseStartEnd(false), bWriteTag(true), bWriteInsideHV(false), bWriteDistance(false), bCheckDistanceSize(false) {}
+};
+
+/**
+ * A structure that holds information about the page margins.
+ *
+ */
+struct PageMargins
+{
+ sal_uInt16 nPageMarginLeft;
+ sal_uInt16 nPageMarginRight;
+ sal_uInt16 nPageMarginTop;
+ sal_uInt16 nPageMarginBottom;
+
+ PageMargins() : nPageMarginLeft(0), nPageMarginRight(0), nPageMarginTop(0), nPageMarginBottom(0) {}
};
/// The class that has handlers for various resource types when exporting as DOCX.
@@ -678,6 +693,8 @@ private:
/// Is fake rotation detected, so rotation with 90 degrees should be ignored in this cell?
bool m_bBtLr;
+ PageMargins m_pageMargins;
+
public:
DocxAttributeOutput( DocxExport &rExport, ::sax_fastparser::FSHelperPtr pSerializer, oox::drawingml::DrawingML* pDrawingML );
More information about the Libreoffice-commits
mailing list