sw table cell borders: add optional Word-compatible border collapsing

We always compared border width and other aspects only after that, Word
works with border weight according to their implementer notes.

So extend svx::frame::Style to be able to collapse borders using weights
and opt in for that from sw/ in case a compat mode (which is related to
tables, off by default and is set by the DOC/DOCX import) is set.

Change-Id: I1f682789302c88a0d326c6c0263ad3007441cb24
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/89052
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
This commit is contained in:
Miklos Vajna
2020-02-19 18:03:59 +01:00
parent 7448b69b4a
commit e6fa52c2c3
5 changed files with 155 additions and 1 deletions

View File

@@ -116,6 +116,7 @@ private:
double mfSecn; /// Width of secondary (right or bottom) line.
double mfPatternScale; /// Scale used for line pattern spacing.
SvxBorderLineStyle mnType;
bool mbWordTableCell;
public:
/** Constructs an invisible frame style. */
@@ -129,7 +130,8 @@ private:
mfDist(0.0),
mfSecn(0.0),
mfPatternScale(1.0),
mnType(SvxBorderLineStyle::SOLID)
mnType(SvxBorderLineStyle::SOLID),
mbWordTableCell(false)
{}
};
@@ -187,6 +189,9 @@ public:
/** Mirrors this style (exchanges primary and secondary), if it is a double frame style. */
Style& MirrorSelf();
/** Enables the Word-compatible Style comparison code. */
void SetWordTableCell(bool bWordTableCell);
bool operator==( const Style& rOther) const;
bool operator<( const Style& rOther) const;
};

View File

@@ -260,6 +260,16 @@ Style& Style::MirrorSelf()
return *this;
}
void Style::SetWordTableCell(bool bWordTableCell)
{
if (!maImplStyle)
{
implEnsureImplStyle();
}
maImplStyle->mbWordTableCell = bWordTableCell;
}
bool Style::operator==( const Style& rOther) const
{
if(!maImplStyle && !rOther.maImplStyle)
@@ -283,6 +293,101 @@ bool Style::operator==( const Style& rOther) const
&& Type() == rOther.Type());
}
namespace
{
/**
* Gets the weight of rStyle, according to [MS-OI29500] v20171130, 2.1.168 Part 1 Section 17.4.66,
* tcBorders (Table Cell Borders).
*/
double GetWordTableCellBorderWeight(const Style& rStyle)
{
double fWidth = rStyle.GetWidth();
int nBorderNumber = 0;
// See lcl_convertBorderStyleFromToken() in writerfilter/ and ConvertBorderStyleFromWord() in
// editeng/, this is the opposite of the combination of those functions.
switch (rStyle.Type())
{
case SvxBorderLineStyle::NONE:
return 0.0;
case SvxBorderLineStyle::DOTTED:
case SvxBorderLineStyle::DASHED:
return 1.0;
case SvxBorderLineStyle::SOLID:
// single = 1
// thick = 2
// wave = 20
nBorderNumber = 1;
break;
case SvxBorderLineStyle::DOUBLE:
case SvxBorderLineStyle::DOUBLE_THIN:
// double = 3
// triple = 10
// doubleWave = 21
// dashDotStroked = 23
nBorderNumber = 3;
break;
case SvxBorderLineStyle::DASH_DOT:
// dotDash = 8
nBorderNumber = 8;
break;
case SvxBorderLineStyle::DASH_DOT_DOT:
// dotDotDash = 9
nBorderNumber = 9;
break;
case SvxBorderLineStyle::THINTHICK_SMALLGAP:
// thinThickSmallGap = 11
nBorderNumber = 11;
break;
case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
// thickThinSmallGap = 12
// thinThickThinSmallGap = 13
nBorderNumber = 12;
break;
case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
// thinThickMediumGap = 14
nBorderNumber = 14;
break;
case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
// thickThinMediumGap = 15
// thinThickThinMediumGap = 16
nBorderNumber = 15;
break;
case SvxBorderLineStyle::THINTHICK_LARGEGAP:
// thinThickLargeGap = 17
nBorderNumber = 17;
break;
case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
// thickThinLargeGap = 18
// thinThickThinLargeGap = 19
nBorderNumber = 18;
break;
case SvxBorderLineStyle::FINE_DASHED:
// dashSmallGap = 22
nBorderNumber = 22;
break;
case SvxBorderLineStyle::EMBOSSED:
// threeDEmboss = 24
nBorderNumber = 24;
break;
case SvxBorderLineStyle::ENGRAVED:
// threeDEngrave = 25
nBorderNumber = 25;
break;
case SvxBorderLineStyle::OUTSET:
// outset = 26
nBorderNumber = 25;
break;
case SvxBorderLineStyle::INSET:
// inset = 27
nBorderNumber = 27;
break;
}
return nBorderNumber * fWidth;
}
}
bool Style::operator<( const Style& rOther) const
{
if(!maImplStyle && !rOther.maImplStyle)
@@ -291,6 +396,18 @@ bool Style::operator<( const Style& rOther) const
return false;
}
if (maImplStyle && maImplStyle->mbWordTableCell)
{
// The below code would first compare based on the border width, Word compares based on its
// calculated weight, do that in the compat case.
double fLW = GetWordTableCellBorderWeight(*this);
double fRW = GetWordTableCellBorderWeight(rOther);
if (!rtl::math::approxEqual(fLW, fRW))
{
return fLW < fRW;
}
}
// different total widths -> this<rOther, if this is thinner
double nLW = GetWidth();
double nRW = rOther.GetWidth();

Binary file not shown.

View File

@@ -43,6 +43,26 @@ CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTableFlyOverlap)
CPPUNIT_ASSERT_GREATEREQUAL(nFlyBottom, nTableTop);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBorderCollapseCompat)
{
// Load a document with a border conflict: top cell has a dotted bottom border, bottom cell has
// a solid upper border.
load(DATA_DIRECTORY, "border-collapse-compat.docx");
SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
SwDocShell* pShell = pTextDoc->GetDocShell();
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
MetafileXmlDump aDumper;
xmlDocPtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
// Make sure the solid border has priority.
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1
// - Actual : 48
// i.e. there was no single cell border with width=20, rather there were 48 border parts
// (forming a dotted border), all with width=40.
assertXPath(pXmlDoc, "//polyline[@style='solid']", "width", "20");
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@@ -2744,11 +2744,23 @@ void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem
bool const bVert = mrTabFrame.IsVertical();
bool const bR2L = mrTabFrame.IsRightToLeft();
bool bWordTableCell = false;
SwViewShell* pShell = rFrame.getRootFrame()->GetCurrShell();
if (pShell)
{
const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
}
// no scaling needed, it's all in the primitives and the target device
svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
aL.SetWordTableCell(bWordTableCell);
svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
aR.SetWordTableCell(bWordTableCell);
svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
aT.SetWordTableCell(bWordTableCell);
svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);
aB.SetWordTableCell(bWordTableCell);
aR.MirrorSelf();
aB.MirrorSelf();