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 <[email protected]> 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 ); _______________________________________________ Libreoffice-commits mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
