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 mfSecn; /// Width of secondary (right or bottom) line.
|
||||||
double mfPatternScale; /// Scale used for line pattern spacing.
|
double mfPatternScale; /// Scale used for line pattern spacing.
|
||||||
SvxBorderLineStyle mnType;
|
SvxBorderLineStyle mnType;
|
||||||
|
bool mbWordTableCell;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Constructs an invisible frame style. */
|
/** Constructs an invisible frame style. */
|
||||||
@@ -129,7 +130,8 @@ private:
|
|||||||
mfDist(0.0),
|
mfDist(0.0),
|
||||||
mfSecn(0.0),
|
mfSecn(0.0),
|
||||||
mfPatternScale(1.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. */
|
/** Mirrors this style (exchanges primary and secondary), if it is a double frame style. */
|
||||||
Style& MirrorSelf();
|
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;
|
||||||
bool operator<( const Style& rOther) const;
|
bool operator<( const Style& rOther) const;
|
||||||
};
|
};
|
||||||
|
@@ -260,6 +260,16 @@ Style& Style::MirrorSelf()
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Style::SetWordTableCell(bool bWordTableCell)
|
||||||
|
{
|
||||||
|
if (!maImplStyle)
|
||||||
|
{
|
||||||
|
implEnsureImplStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
maImplStyle->mbWordTableCell = bWordTableCell;
|
||||||
|
}
|
||||||
|
|
||||||
bool Style::operator==( const Style& rOther) const
|
bool Style::operator==( const Style& rOther) const
|
||||||
{
|
{
|
||||||
if(!maImplStyle && !rOther.maImplStyle)
|
if(!maImplStyle && !rOther.maImplStyle)
|
||||||
@@ -283,6 +293,101 @@ bool Style::operator==( const Style& rOther) const
|
|||||||
&& Type() == rOther.Type());
|
&& 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
|
bool Style::operator<( const Style& rOther) const
|
||||||
{
|
{
|
||||||
if(!maImplStyle && !rOther.maImplStyle)
|
if(!maImplStyle && !rOther.maImplStyle)
|
||||||
@@ -291,6 +396,18 @@ bool Style::operator<( const Style& rOther) const
|
|||||||
return false;
|
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
|
// different total widths -> this<rOther, if this is thinner
|
||||||
double nLW = GetWidth();
|
double nLW = GetWidth();
|
||||||
double nRW = rOther.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_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();
|
CPPUNIT_PLUGIN_IMPLEMENT();
|
||||||
|
|
||||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
/* 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 bVert = mrTabFrame.IsVertical();
|
||||||
bool const bR2L = mrTabFrame.IsRightToLeft();
|
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
|
// no scaling needed, it's all in the primitives and the target device
|
||||||
svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
|
svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
|
||||||
|
aL.SetWordTableCell(bWordTableCell);
|
||||||
svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
|
svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
|
||||||
|
aR.SetWordTableCell(bWordTableCell);
|
||||||
svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
|
svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
|
||||||
|
aT.SetWordTableCell(bWordTableCell);
|
||||||
svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);
|
svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);
|
||||||
|
aB.SetWordTableCell(bWordTableCell);
|
||||||
|
|
||||||
aR.MirrorSelf();
|
aR.MirrorSelf();
|
||||||
aB.MirrorSelf();
|
aB.MirrorSelf();
|
||||||
|
Reference in New Issue
Block a user