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
d845b91bcc (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 <vmiklos@collabora.com>
Tested-by: Jenkins
This commit is contained in:
Miklos Vajna
2026-01-22 19:53:58 +01:00
parent c435ca3be8
commit 8314046a4f
4 changed files with 85 additions and 38 deletions
Binary file not shown.
+64 -35
View File
@@ -158,6 +158,23 @@ bool IsGrayScale(const Bitmap& rBitmap)
return aColor.GetRed() == aColor.GetGreen() && aColor.GetRed() == aColor.GetBlue();
}
std::vector<Bitmap> GetMetaFileImages(const GDIMetaFile& rMetaFile)
{
std::vector<Bitmap> aImages;
for (size_t nAction = 0; nAction < rMetaFile.GetActionSize(); ++nAction)
{
MetaAction* pAction = rMetaFile.GetAction(nAction);
if (pAction->GetType() != MetaActionType::BMPEXSCALE)
{
continue;
}
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:
@@ -168,18 +185,7 @@ CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete)
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)
{
MetaAction* pAction = xMetaFile->GetAction(nAction);
if (pAction->GetType() != MetaActionType::BMPEXSCALE)
{
continue;
}
auto pAct = static_cast<MetaBmpExScaleAction*>(pAction);
aImages.push_back(pAct->GetBitmap());
}
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]));
+8 -2
View File
@@ -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() );
+13 -1
View File
@@ -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() )