sw/qa/extras/ooxmlexport/data/image-comment-at-char.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport11.cxx | 18 ++++++ sw/source/filter/ww8/docxexport.cxx | 4 - sw/source/filter/ww8/docxexport.hxx | 2 sw/source/filter/ww8/rtfexport.cxx | 5 + sw/source/filter/ww8/rtfexport.hxx | 3 - sw/source/filter/ww8/wrtw8nds.cxx | 42 ++++++++++++--- sw/source/filter/ww8/wrtww8.cxx | 4 - sw/source/filter/ww8/wrtww8.hxx | 11 ++- writerfilter/source/dmapper/DomainMapper_Impl.cxx | 19 ++++++ 10 files changed, 88 insertions(+), 20 deletions(-)
New commits: commit 9ffd2d9ade3e9c4ecf50a1948637c902486f88bf Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Jul 2 16:26:03 2019 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Thu Jul 4 09:02:48 2019 +0200 sw comments on frames: fix DOCX handling We used to ignore annotation marks which just cover the comment anchor since commit fff019debf14a0bf8cd358591a686191347f1542 (MSWordExportBase: ignore empty annotation marks, 2014-09-17), but this means comments on images are lost. Pass around SwWW8AttrIter, so we can decide if we have a relevant at-char anchored frame in MSWordExportBase::GetAnnotationMarks(), without iterating over all frames in the document, which would be slow for large documents. Regarding the import side, the only problem was that the empty comment range resulted in a loss of annotation marks; fix that by using a marker while inserting. (cherry picked from commit 7fa96a3e4bdd384ad411e0bdc4e7c3f2ab920279) Conflicts: sw/qa/extras/ooxmlexport/ooxmlexport13.cxx sw/source/filter/ww8/docxexport.cxx sw/source/filter/ww8/docxexport.hxx sw/source/filter/ww8/rtfexport.cxx sw/source/filter/ww8/rtfexport.hxx sw/source/filter/ww8/wrtw8nds.cxx sw/source/filter/ww8/wrtww8.cxx sw/source/filter/ww8/wrtww8.hxx writerfilter/source/dmapper/DomainMapper_Impl.cxx Change-Id: I385677d74423bc05824dac4a12d1a991bb3983c4 Reviewed-on: https://gerrit.libreoffice.org/75039 Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Tested-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/extras/ooxmlexport/data/image-comment-at-char.docx b/sw/qa/extras/ooxmlexport/data/image-comment-at-char.docx new file mode 100644 index 000000000000..677464de49f7 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/image-comment-at-char.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx index b8214f664e42..f77320436ea3 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx @@ -450,6 +450,24 @@ DECLARE_OOXMLEXPORT_TEST(testTdf123636_newlinePageBreak4, "tdf123636_newlinePage assertXPath(pDump, "/root/page[2]/body/txt[1]/Text", 0); } +DECLARE_OOXMLEXPORT_TEST(testImageCommentAtChar, "image-comment-at-char.docx") +{ + uno::Reference<text::XTextRange> xPara = getParagraph(1); + CPPUNIT_ASSERT_EQUAL(OUString("Text"), + getProperty<OUString>(getRun(xPara, 1), "TextPortionType")); + // Without the accompanying fix in place, this test would have failed with 'Expected: + // Annotation; Actual: Frame', i.e. the comment start before the image was lost. + CPPUNIT_ASSERT_EQUAL(OUString("Annotation"), + getProperty<OUString>(getRun(xPara, 2), "TextPortionType")); + CPPUNIT_ASSERT_EQUAL(OUString("Frame"), + getProperty<OUString>(getRun(xPara, 3), "TextPortionType")); + CPPUNIT_ASSERT_EQUAL(OUString("AnnotationEnd"), + getProperty<OUString>(getRun(xPara, 4), "TextPortionType")); + CPPUNIT_ASSERT_EQUAL(OUString("Text"), + getProperty<OUString>(getRun(xPara, 5), "TextPortionType")); +} + + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index becf669cf2fc..ccfce360f49c 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -187,13 +187,13 @@ void DocxExport::AppendBookmark( const OUString& rName ) m_pAttrOutput->WriteBookmarks_Impl( aStarts, aEnds ); } -void DocxExport::AppendAnnotationMarks( const SwTextNode& rNode, sal_Int32 nAktPos, sal_Int32 nLen ) +void DocxExport::AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nAktPos, sal_Int32 nLen ) { std::vector< OUString > aStarts; std::vector< OUString > aEnds; IMarkVector aMarks; - if ( GetAnnotationMarks( rNode, nAktPos, nAktPos + nLen, aMarks ) ) + if ( GetAnnotationMarks( rAttrs, nAktPos, nAktPos + nLen, aMarks ) ) { for ( IMarkVector::const_iterator it = aMarks.begin(), end = aMarks.end(); it != end; ++it ) diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx index 22b5dca7934e..f859a1e75278 100644 --- a/sw/source/filter/ww8/docxexport.hxx +++ b/sw/source/filter/ww8/docxexport.hxx @@ -141,7 +141,7 @@ public: virtual void AppendBookmark( const OUString& rName ) override; - virtual void AppendAnnotationMarks( const SwTextNode& rNode, sal_Int32 nAktPos, sal_Int32 nLen ) override; + virtual void AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nAktPos, sal_Int32 nLen ) override; virtual void ExportGrfBullet(const SwTextNode&) override; diff --git a/sw/source/filter/ww8/rtfexport.cxx b/sw/source/filter/ww8/rtfexport.cxx index 679b96ac9a77..e265f2618a06 100644 --- a/sw/source/filter/ww8/rtfexport.cxx +++ b/sw/source/filter/ww8/rtfexport.cxx @@ -131,13 +131,14 @@ void RtfExport::AppendBookmark(const OUString& rName) m_pAttrOutput->WriteBookmarks_Impl(aStarts, aEnds); } -void RtfExport::AppendAnnotationMarks(const SwTextNode& rNode, sal_Int32 nAktPos, sal_Int32 nLen) +void RtfExport::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nAktPos, + sal_Int32 nLen) { std::vector<OUString> aStarts; std::vector<OUString> aEnds; IMarkVector aMarks; - if (GetAnnotationMarks(rNode, nAktPos, nAktPos + nLen, aMarks)) + if (GetAnnotationMarks(rAttrs, nAktPos, nAktPos + nLen, aMarks)) { for (const auto& pMark : aMarks) { diff --git a/sw/source/filter/ww8/rtfexport.hxx b/sw/source/filter/ww8/rtfexport.hxx index b470ef593f30..ab86e9bdf794 100644 --- a/sw/source/filter/ww8/rtfexport.hxx +++ b/sw/source/filter/ww8/rtfexport.hxx @@ -73,7 +73,8 @@ public: void AppendBookmark(const OUString& rName) override; - void AppendAnnotationMarks(const SwTextNode& rNode, sal_Int32 nAktPos, sal_Int32 nLen) override; + void AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nAktPos, + sal_Int32 nLen) override; //For i120928,add an interface to export graphic of bullet void ExportGrfBullet(const SwTextNode& rNd) override; diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index 9361943a7636..2ec8a8c90ef3 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -571,6 +571,21 @@ bool SwWW8AttrIter::IsAnchorLinkedToThisNode( sal_uLong nNodePos ) return false ; } +bool SwWW8AttrIter::HasFlysAt(sal_Int32 nSwPos) const +{ + for (const auto& rFly : maFlyFrames) + { + const SwPosition& rAnchor = rFly.GetPosition(); + const sal_Int32 nPos = rAnchor.nContent.GetIndex(); + if (nPos == nSwPos) + { + return true; + } + } + + return false; +} + FlyProcessingState SwWW8AttrIter::OutFlys(sal_Int32 nSwPos) { // collection point to first gather info about all of the potentially linked textboxes: to be analyzed later. @@ -1934,7 +1949,7 @@ sal_Int32 MSWordExportBase::GetNextPos( SwWW8AttrIter const * aAttrIter, const S { GetSortedBookmarks( rNode, nAktPos, nNextBookmark - nAktPos ); NearestBookmark( nNextBookmark, nAktPos, false ); - GetSortedAnnotationMarks( rNode, nAktPos, nNextAnnotationMark - nAktPos ); + GetSortedAnnotationMarks( *aAttrIter, nAktPos, nNextAnnotationMark - nAktPos ); NearestAnnotationMark( nNextAnnotationMark, nAktPos, false ); } return std::min( nNextPos, std::min( nNextBookmark, nNextAnnotationMark ) ); @@ -1988,11 +2003,11 @@ bool MSWordExportBase::GetBookmarks( const SwTextNode& rNd, sal_Int32 nStt, return ( rArr.size() > 0 ); } -bool MSWordExportBase::GetAnnotationMarks( const SwTextNode& rNd, sal_Int32 nStt, +bool MSWordExportBase::GetAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nStt, sal_Int32 nEnd, IMarkVector& rArr ) { IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); - sal_uLong nNd = rNd.GetIndex( ); + sal_uLong nNd = rAttrs.GetNode().GetIndex(); const sal_Int32 nMarks = pMarkAccess->getAnnotationMarksCount(); for ( sal_Int32 i = 0; i < nMarks; i++ ) @@ -2015,6 +2030,16 @@ bool MSWordExportBase::GetAnnotationMarks( const SwTextNode& rNd, sal_Int32 nStt // comment field, so ignore the annotation mark itself. bool bSingleChar = pMark->GetMarkStart().nNode == pMark->GetMarkEnd().nNode && nBStart + 1 == nBEnd; + if (bSingleChar) + { + if (rAttrs.HasFlysAt(nBStart)) + { + // There is content (an at-char anchored frame) between the annotation mark + // start/end, so still emit range start/end. + bSingleChar = false; + } + } + if ( ( bIsStartOk || bIsEndOk ) && !bSingleChar ) { rArr.push_back( pMark ); @@ -2097,10 +2122,10 @@ void MSWordExportBase::NearestAnnotationMark( sal_Int32& rNearest, const sal_Int } } -void MSWordExportBase::GetSortedAnnotationMarks( const SwTextNode& rNode, sal_Int32 nAktPos, sal_Int32 nLen ) +void MSWordExportBase::GetSortedAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nAktPos, sal_Int32 nLen ) { IMarkVector aMarksStart; - if ( GetAnnotationMarks( rNode, nAktPos, nAktPos + nLen, aMarksStart ) ) + if ( GetAnnotationMarks( rAttrs, nAktPos, nAktPos + nLen, aMarksStart ) ) { IMarkVector aSortedEnd; IMarkVector aSortedStart; @@ -2113,6 +2138,7 @@ void MSWordExportBase::GetSortedAnnotationMarks( const SwTextNode& rNode, sal_In const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex(); const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex(); + const SwTextNode& rNode = rAttrs.GetNode(); if ( nStart > nAktPos && ( pMark->GetMarkStart().nNode == rNode.GetIndex()) ) aSortedStart.push_back( pMark ); @@ -2383,7 +2409,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) // Append bookmarks in this range after flys, exclusive of final // position of this range AppendBookmarks( rNode, nAktPos, nNextAttr - nAktPos ); - AppendAnnotationMarks( rNode, nAktPos, nNextAttr - nAktPos ); + AppendAnnotationMarks( aAttrIter, nAktPos, nNextAttr - nAktPos ); // At the moment smarttags are only written for paragraphs, at the // beginning of the paragraph. @@ -2553,7 +2579,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) nStateOfFlyFrame = aAttrIter.OutFlys( nEnd ); // insert final bookmarks if any before CR and after flys AppendBookmarks( rNode, nEnd, 1 ); - AppendAnnotationMarks( rNode, nEnd, 1 ); + AppendAnnotationMarks(aAttrIter, nEnd, 1); if ( pTOXSect ) { m_aCurrentCharPropStarts.pop(); @@ -2607,7 +2633,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) nStateOfFlyFrame = aAttrIter.OutFlys( nEnd ); // insert final bookmarks if any before CR and after flys AppendBookmarks( rNode, nEnd, 1 ); - AppendAnnotationMarks( rNode, nEnd, 1 ); + AppendAnnotationMarks(aAttrIter, nEnd, 1); WriteCR( pTextNodeInfoInner ); // #i120928 - position of the bullet's graphic is at end of doc if (bLastCR && (!bExported)) diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx index 64bf90dfdac5..612dee47e903 100644 --- a/sw/source/filter/ww8/wrtww8.cxx +++ b/sw/source/filter/ww8/wrtww8.cxx @@ -1474,10 +1474,10 @@ void WW8Export::AppendBookmarks( const SwTextNode& rNd, sal_Int32 nAktPos, sal_I } } -void WW8Export::AppendAnnotationMarks(const SwTextNode& rNode, sal_Int32 nAktPos, sal_Int32 nLen) +void WW8Export::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nAktPos, sal_Int32 nLen) { IMarkVector aMarks; - if (GetAnnotationMarks(rNode, nAktPos, nAktPos + nLen, aMarks)) + if (GetAnnotationMarks(rAttrs, nAktPos, nAktPos + nLen, aMarks)) { for (IMarkVector::const_iterator it = aMarks.begin(), end = aMarks.end(); it != end; ++it) { diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index c95323f6edfc..755c9fdf39bc 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -679,7 +679,7 @@ public: virtual void AppendBookmark( const OUString& rName ) = 0; - virtual void AppendAnnotationMarks( const SwTextNode& rNd, sal_Int32 nAktPos, sal_Int32 nLen ) = 0; + virtual void AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nAktPos, sal_Int32 nLen ) = 0; virtual void AppendSmartTags(SwTextNode& /*rTextNode*/) { } @@ -858,9 +858,9 @@ protected: /// void NearestAnnotationMark( sal_Int32& rNearest, const sal_Int32 nAktPos, bool bNextPositionOnly ); - void GetSortedAnnotationMarks( const SwTextNode& rNd, sal_Int32 nAktPos, sal_Int32 nLen ); + void GetSortedAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nAktPos, sal_Int32 nLen ); - bool GetAnnotationMarks( const SwTextNode& rNd, sal_Int32 nStt, sal_Int32 nEnd, + bool GetAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nStt, sal_Int32 nEnd, IMarkVector& rArr ); const NfKeywordTable & GetNfKeywordTable(); @@ -1047,7 +1047,7 @@ public: virtual void AppendBookmark( const OUString& rName ) override; void AppendBookmarkEndWithCorrection( const OUString& rName ); - virtual void AppendAnnotationMarks( const SwTextNode& rNd, sal_Int32 nAktPos, sal_Int32 nLen ) override; + virtual void AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nAktPos, sal_Int32 nLen ) override; virtual void AppendSmartTags(SwTextNode& rTextNode) override; @@ -1508,6 +1508,7 @@ public: const SwRedlineData* GetParagraphLevelRedline( ); const SwRedlineData* GetRunLevelRedline( sal_Int32 nPos ); FlyProcessingState OutFlys(sal_Int32 nSwPos); + bool HasFlysAt(sal_Int32 nSwPos) const; sal_Int32 WhereNext() const { return nAktSwPos; } sal_uInt16 GetScript() const { return mnScript; } @@ -1521,6 +1522,8 @@ public: bool IsAnchorLinkedToThisNode( sal_uLong nNodePos ); void SplitRun( sal_Int32 nSplitEndPos ); + + const SwTextNode& GetNode() const { return rNd; } }; /// Class to collect and output the styles table. diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index f17a52da1697..2d328e299e09 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -57,6 +57,7 @@ #include <com/sun/star/text/XParagraphCursor.hpp> #include <com/sun/star/text/XRedline.hpp> #include <com/sun/star/text/XTextFieldsSupplier.hpp> +#include <com/sun/star/text/XTextRangeCompare.hpp> #include <com/sun/star/style/DropCapFormat.hpp> #include <com/sun/star/util/NumberFormatter.hpp> #include <com/sun/star/util/XNumberFormatsSupplier.hpp> @@ -1942,12 +1943,30 @@ void DomainMapper_Impl::PopAnnotation() // Create a range that points to the annotation start/end. uno::Reference<text::XText> const xText = aAnnotationPosition.m_xStart->getText(); uno::Reference<text::XTextCursor> const xCursor = xText->createTextCursorByRange(aAnnotationPosition.m_xStart); + + bool bMarker = false; + uno::Reference<text::XTextRangeCompare> xTextRangeCompare(xText, uno::UNO_QUERY); + if (xTextRangeCompare->compareRegionStarts(aAnnotationPosition.m_xStart, aAnnotationPosition.m_xEnd) == 0) + { + // Insert a marker so that comment around an anchored image is not collapsed during + // insertion. + xText->insertString(xCursor, "x", false); + bMarker = true; + } + xCursor->gotoRange(aAnnotationPosition.m_xEnd, true); uno::Reference<text::XTextRange> const xTextRange(xCursor, uno::UNO_QUERY_THROW); // Attach the annotation to the range. uno::Reference<text::XTextAppend> const xTextAppend = m_aTextAppendStack.top().xTextAppend; xTextAppend->insertTextContent(xTextRange, uno::Reference<text::XTextContent>(m_xAnnotationField, uno::UNO_QUERY_THROW), !xCursor->isCollapsed()); + + if (bMarker) + { + // Remove the marker. + xCursor->goLeft(1, true); + xCursor->setString(OUString()); + } } m_aAnnotationPositions.erase( m_nAnnotationId ); } _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits