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:
@@ -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;
|
||||
};
|
||||
|
@@ -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();
|
||||
|
BIN
sw/qa/core/layout/data/border-collapse-compat.docx
Normal file
BIN
sw/qa/core/layout/data/border-collapse-compat.docx
Normal file
Binary file not shown.
@@ -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: */
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user