[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