diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2020-02-19 18:03:59 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2020-02-19 20:15:29 +0100 |
commit | e6fa52c2c371c7adc9c2c2cb18c3a8cf782cfa4b (patch) | |
tree | f090c50815727c468bebe50311246ab9424d1250 | |
parent | 7448b69b4aaa982940e49c5ace9842f07dd81938 (diff) |
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
-rw-r--r-- | include/svx/framelink.hxx | 7 | ||||
-rw-r--r-- | svx/source/dialog/framelink.cxx | 117 | ||||
-rw-r--r-- | sw/qa/core/layout/data/border-collapse-compat.docx | bin | 0 -> 12669 bytes | |||
-rw-r--r-- | sw/qa/core/layout/layout.cxx | 20 | ||||
-rw-r--r-- | sw/source/core/layout/paintfrm.cxx | 12 |
5 files changed, 155 insertions, 1 deletions
diff --git a/include/svx/framelink.hxx b/include/svx/framelink.hxx index 5296ab88d56e..ec94bda63f01 100644 --- a/include/svx/framelink.hxx +++ b/include/svx/framelink.hxx @@ -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; }; diff --git a/svx/source/dialog/framelink.cxx b/svx/source/dialog/framelink.cxx index 68d52462025e..286113e01dfb 100644 --- a/svx/source/dialog/framelink.cxx +++ b/svx/source/dialog/framelink.cxx @@ -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(); diff --git a/sw/qa/core/layout/data/border-collapse-compat.docx b/sw/qa/core/layout/data/border-collapse-compat.docx Binary files differnew file mode 100644 index 000000000000..a3f29cbe8d1c --- /dev/null +++ b/sw/qa/core/layout/data/border-collapse-compat.docx diff --git a/sw/qa/core/layout/layout.cxx b/sw/qa/core/layout/layout.cxx index cbea578be4cc..4c16a3bbcd97 100644 --- a/sw/qa/core/layout/layout.cxx +++ b/sw/qa/core/layout/layout.cxx @@ -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: */ diff --git a/sw/source/core/layout/paintfrm.cxx b/sw/source/core/layout/paintfrm.cxx index d0c72f9b4f91..50cc2aaac8a7 100644 --- a/sw/source/core/layout/paintfrm.cxx +++ b/sw/source/core/layout/paintfrm.cxx @@ -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(); |