sw/qa/core/text/data/redline-image-inline.docx |binary sw/qa/core/text/itrpaint.cxx | 97 ++++++++++++++++--------- sw/source/core/text/porfly.cxx | 10 ++ sw/source/core/text/porlay.cxx | 14 +++ 4 files changed, 84 insertions(+), 37 deletions(-)
New commits: commit 8314046a4fdb706cf9bb4d65c97792ed7410fb0f Author: Miklos Vajna <[email protected]> AuthorDate: Thu Jan 22 08:37:58 2026 +0100 Commit: Miklos Vajna <[email protected]> CommitDate: Thu Jan 22 19:53:58 2026 +0100 cool#13988 sw redline render mode: handle inline images Load the bugdoc, dispatch .uno:RedlineRenderMode, anchored images in deletions are grey, but this doesn't work with inline images. There is some infrastructure for this added in commit d845b91bcc6eb885c55494d4d4fab4ec09577e1d (tdf#78864 sw track changes: cross out deleted images, 2021-04-30), but that crosses out images instead of shading. Fix this by checking for the usual SwRedlineRenderMode flags in sw::FlyContentPortion::Paint() to avoid the unwanted cross. Also extend SwLineLayout::CalcLine() to set the inserted/deleted flags on the fly frame for redlines, so SwGrfNode::GetGraphicAttr() can do its shading as usual. And add a GetMetaFileImages() in the test suite to reduce some duplication. Change-Id: I97f2311ad7e9a6ffc70d76c1811faa2c13e509fe Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197799 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins diff --git a/sw/qa/core/text/data/redline-image-inline.docx b/sw/qa/core/text/data/redline-image-inline.docx new file mode 100644 index 000000000000..beaa9bbad87f Binary files /dev/null and b/sw/qa/core/text/data/redline-image-inline.docx differ diff --git a/sw/qa/core/text/itrpaint.cxx b/sw/qa/core/text/itrpaint.cxx index cf0f37b19f57..479ba5a628bf 100644 --- a/sw/qa/core/text/itrpaint.cxx +++ b/sw/qa/core/text/itrpaint.cxx @@ -158,20 +158,12 @@ bool IsGrayScale(const Bitmap& rBitmap) return aColor.GetRed() == aColor.GetGreen() && aColor.GetRed() == aColor.GetBlue(); } -CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete) +std::vector<Bitmap> GetMetaFileImages(const GDIMetaFile& rMetaFile) { - // Given a document with a normal, a deleted and an inserted image: - createSwDoc("redline-image-anchored.docx"); - - // When using the standard redline render mode: - SwDocShell* pDocShell = getSwDocShell(); - std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile(); - - // Then make sure none of the images are grayscale: std::vector<Bitmap> aImages; - for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); ++nAction) + for (size_t nAction = 0; nAction < rMetaFile.GetActionSize(); ++nAction) { - MetaAction* pAction = xMetaFile->GetAction(nAction); + MetaAction* pAction = rMetaFile.GetAction(nAction); if (pAction->GetType() != MetaActionType::BMPEXSCALE) { continue; @@ -180,6 +172,20 @@ CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete) auto pAct = static_cast<MetaBmpExScaleAction*>(pAction); aImages.push_back(pAct->GetBitmap()); } + return aImages; +} + +CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete) +{ + // Given a document with a normal, a deleted and an inserted image: + createSwDoc("redline-image-anchored.docx"); + + // When using the standard redline render mode: + SwDocShell* pDocShell = getSwDocShell(); + std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile(); + + // Then make sure none of the images are grayscale: + std::vector<Bitmap> aImages = GetMetaFileImages(*xMetaFile); CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aImages.size()); CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); CPPUNIT_ASSERT(!IsGrayScale(aImages[1])); @@ -193,18 +199,7 @@ CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete) xMetaFile = pDocShell->GetPreviewMetaFile(); - aImages.clear(); - for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); ++nAction) - { - MetaAction* pAction = xMetaFile->GetAction(nAction); - if (pAction->GetType() != MetaActionType::BMPEXSCALE) - { - continue; - } - - auto pAct = static_cast<MetaBmpExScaleAction*>(pAction); - aImages.push_back(pAct->GetBitmap()); - } + aImages = GetMetaFileImages(*xMetaFile); CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aImages.size()); CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); CPPUNIT_ASSERT(!IsGrayScale(aImages[1])); @@ -218,18 +213,52 @@ CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete) xMetaFile = pDocShell->GetPreviewMetaFile(); - aImages.clear(); - for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); ++nAction) - { - MetaAction* pAction = xMetaFile->GetAction(nAction); - if (pAction->GetType() != MetaActionType::BMPEXSCALE) - { - continue; - } + aImages = GetMetaFileImages(*xMetaFile); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aImages.size()); + CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); + CPPUNIT_ASSERT(IsGrayScale(aImages[1])); + CPPUNIT_ASSERT(!IsGrayScale(aImages[2])); +} - auto pAct = static_cast<MetaBmpExScaleAction*>(pAction); - aImages.push_back(pAct->GetBitmap()); - } +CPPUNIT_TEST_FIXTURE(Test, testInlineImageRedlineRenderModeOmitInsertDelete) +{ + // Given a document with a normal, a deleted and an inserted image: + createSwDoc("redline-image-inline.docx"); + + // When using the standard redline render mode: + SwDocShell* pDocShell = getSwDocShell(); + std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile(); + + // Then make sure none of the images are grayscale: + std::vector<Bitmap> aImages = GetMetaFileImages(*xMetaFile); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aImages.size()); + CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); + CPPUNIT_ASSERT(!IsGrayScale(aImages[1])); + CPPUNIT_ASSERT(!IsGrayScale(aImages[2])); + + // Omit insert: default, default, grayscale. + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + SwViewOption aOpt(*pWrtShell->GetViewOptions()); + aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitInserts); + pWrtShell->ApplyViewOptions(aOpt); + + xMetaFile = pDocShell->GetPreviewMetaFile(); + + aImages = GetMetaFileImages(*xMetaFile); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aImages.size()); + CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); + CPPUNIT_ASSERT(!IsGrayScale(aImages[1])); + // Without the accompanying fix in place, this test would have failed, the image's center pixel + // wasn't gray. + CPPUNIT_ASSERT(IsGrayScale(aImages[2])); + + // Omit deletes: default, grayscale, default. + aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitDeletes); + pWrtShell->ApplyViewOptions(aOpt); + + xMetaFile = pDocShell->GetPreviewMetaFile(); + + aImages = GetMetaFileImages(*xMetaFile); CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aImages.size()); CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); CPPUNIT_ASSERT(IsGrayScale(aImages[1])); diff --git a/sw/source/core/text/porfly.cxx b/sw/source/core/text/porfly.cxx index b0202f7fecca..da4fcbfdcd4e 100644 --- a/sw/source/core/text/porfly.cxx +++ b/sw/source/core/text/porfly.cxx @@ -40,6 +40,7 @@ #include <sortedobjs.hxx> #include <officecfg/Office/Common.hxx> #include <PostItMgr.hxx> +#include <viewopt.hxx> /** * class SwFlyPortion => we expect a frame-locale SwRect! @@ -223,9 +224,10 @@ void sw::FlyContentPortion::Paint(const SwTextPaintInfo& rInf) const if(rInf.GetTextFrame()->IsVertical()) rInf.GetTextFrame()->SwitchHorizontalToVertical(aRepaintRect); + SwViewShell* pViewShell = m_pFly->getRootFrame()->GetCurrShell(); if(!((m_pFly->IsCompletePaint() || m_pFly->getFrameArea().Overlaps(aRepaintRect)) && - SwFlyFrame::IsPaint(m_pFly->GetVirtDrawObj(), *m_pFly->getRootFrame()->GetCurrShell()))) + SwFlyFrame::IsPaint(m_pFly->GetVirtDrawObj(), *pViewShell))) return; SwRect aRect(m_pFly->getFrameArea()); @@ -239,7 +241,11 @@ void sw::FlyContentPortion::Paint(const SwTextPaintInfo& rInf) const // track changes: cross out the image, if it is deleted const SwFrame *pFrame = m_pFly->Lower(); - if ( GetAuthor() != std::string::npos && IsDeleted() && pFrame ) + const SwViewOption* pViewOptions = pViewShell->GetViewOptions(); + SwRedlineRenderMode eRedlineRenderMode + = pViewOptions ? pViewOptions->GetRedlineRenderMode() : SwRedlineRenderMode::Standard; + if (GetAuthor() != std::string::npos && IsDeleted() && pFrame + && eRedlineRenderMode == SwRedlineRenderMode::Standard) { SwRect aPaintRect( pFrame->GetPaintArea() ); diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx index de25fc8ccc99..78f15f923aa2 100644 --- a/sw/source/core/text/porlay.cxx +++ b/sw/source/core/text/porlay.cxx @@ -672,6 +672,7 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf ) if( pPos->IsFlyCntPortion() ) { bool bDeleted = false; + bool bInserted = false; size_t nAuthor = std::string::npos; if ( bHasRedline ) { @@ -683,10 +684,21 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf ) bool bHasFlyRedline = rLine.GetRedln()->CheckLine(flyStart.first->GetIndex(), flyStart.second, flyStart.first->GetIndex(), flyStart.second, sRedlineText, bHasRedlineEnd, eRedlineEnd, /*pAuthorAtPos=*/&nAuthor); - bDeleted = bHasFlyRedline && eRedlineEnd == RedlineType::Delete; + if (bHasFlyRedline) + { + bDeleted = eRedlineEnd == RedlineType::Delete; + bInserted = eRedlineEnd == RedlineType::Insert; + } } static_cast<SwFlyCntPortion*>(pPos)->SetDeleted(bDeleted); static_cast<SwFlyCntPortion*>(pPos)->SetAuthor(nAuthor); + + if (auto pFlyPortion = dynamic_cast<sw::FlyContentPortion*>(pPos)) + { + SwFlyFrame* pFlyFrame = pFlyPortion->GetFlyFrame(); + pFlyFrame->SetDeleted(bDeleted); + pFlyFrame->SetInserted(bInserted); + } } // anchored to characters else if ( pPos->IsFlyPortion() )
