summaryrefslogtreecommitdiff
path: root/sw
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2022-01-06 15:45:02 +0100
committerMike Kaganski <mike.kaganski@collabora.com>2022-02-11 09:19:07 +0100
commit0dbecd2d2ebe18a262cfab96e105637840b5b7fe (patch)
treeb782459ab4d3bbb4f305c75e29c52b2beea432a3 /sw
parent00acb0d7479770f7828c60e22dbc9186ba7f85fe (diff)
sw: fix too long inner borders intersecting with outer borders for Word cells
This is similar to commit 66ac8e60896f6306bed8fbb34606fd14474f19ce (sw: fix unwanted long vertical border around vertically merged Word cell, 2021-03-04), but this one is about how we handle table border painting when an inner border intersects with an outer border. Previously we used to paint the full outer border and the full inner border, which looks silly in case you have e.g. double border outside and a single border inside -- the inner line stops at the edge of the thick outer border in Word. Do the same by limiting the start of a horizontal line if its start would match the X coordinate of a vertical line (and the remaining 3 combinations of hori/vert line start/end). We always limit the inner line, so this needs extending SwLineEntry if the line is an outer one or not. (cherry picked from commit 526c8bdb54eff942d5213030d1455f97720a1ba7) Change-Id: I669a271ce3a4c3c69916779d4f3167208e999f05 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129746 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Diffstat (limited to 'sw')
-rw-r--r--sw/qa/core/layout/data/inner-border.docxbin0 -> 12391 bytes
-rw-r--r--sw/qa/core/layout/layout.cxx53
-rw-r--r--sw/source/core/layout/paintfrm.cxx83
3 files changed, 116 insertions, 20 deletions
diff --git a/sw/qa/core/layout/data/inner-border.docx b/sw/qa/core/layout/data/inner-border.docx
new file mode 100644
index 000000000000..1d8adc9fe818
--- /dev/null
+++ b/sw/qa/core/layout/data/inner-border.docx
Binary files differ
diff --git a/sw/qa/core/layout/layout.cxx b/sw/qa/core/layout/layout.cxx
index b1cb3136b963..2b1dc8be0b3f 100644
--- a/sw/qa/core/layout/layout.cxx
+++ b/sw/qa/core/layout/layout.cxx
@@ -525,6 +525,59 @@ CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testLinkedBullet)
assertXPath(pXmlDoc, "//bmpexscale", 1);
}
+CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testInnerCellBorderIntersect)
+{
+ // Given a table with both outer and inner borders:
+ SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "inner-border.docx");
+ SwDocShell* pShell = pDoc->GetDocShell();
+
+ // When rendering table borders:
+ std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+
+ // Then make sure that that inner and outer borders don't overlap in Word compatibility mode,
+ // and inner borders are reduced to prevent an overlap:
+ MetafileXmlDump dumper;
+ xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+ // Collect start/end (vertical) positions of horizontal borders.
+ xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
+ xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
+ std::vector<std::pair<sal_Int32, sal_Int32>> aBorderStartEnds;
+ for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); i += 2)
+ {
+ xmlNodePtr pStart = pXmlNodes->nodeTab[i];
+ xmlNodePtr pEnd = pXmlNodes->nodeTab[i + 1];
+ xmlChar* pStartY = xmlGetProp(pStart, BAD_CAST("y"));
+ xmlChar* pEndY = xmlGetProp(pEnd, BAD_CAST("y"));
+ sal_Int32 nStartY = OString(reinterpret_cast<char const*>(pStartY)).toInt32();
+ sal_Int32 nEndY = OString(reinterpret_cast<char const*>(pEndY)).toInt32();
+ if (nStartY != nEndY)
+ {
+ // Vertical border.
+ continue;
+ }
+ xmlChar* pStartX = xmlGetProp(pStart, BAD_CAST("x"));
+ xmlChar* pEndX = xmlGetProp(pEnd, BAD_CAST("x"));
+ sal_Int32 nStartX = OString(reinterpret_cast<char const*>(pStartX)).toInt32();
+ sal_Int32 nEndX = OString(reinterpret_cast<char const*>(pEndX)).toInt32();
+ aBorderStartEnds.emplace_back(nStartX, nEndX);
+ }
+ xmlXPathFreeObject(pXmlObj);
+ // We have 3 lines: top, middle and bottom. The top and the bottom one is a full line, since
+ // it's an outer border. The middle one has increased start and decreased end to avoid an
+ // overlap.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aBorderStartEnds.size());
+ CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[2].first);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected greater than: 1724
+ // - Actual : 1724
+ // i.e. the middle line's start was the same as the top line start, while what we want is a
+ // larger X position, so the start of the middle line doesn't overlap with the thick vertical
+ // outer border on the left of the table.
+ CPPUNIT_ASSERT_GREATER(aBorderStartEnds[0].first, aBorderStartEnds[1].first);
+ CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[2].second);
+ CPPUNIT_ASSERT_LESS(aBorderStartEnds[0].second, aBorderStartEnds[1].second);
+}
+
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 e356266b8795..41cbb9c47e71 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -2242,6 +2242,7 @@ struct SwLineEntry
SwTwips mnStartPos;
SwTwips mnEndPos;
SwTwips mnLimitedEndPos;
+ bool mbOuter;
svx::frame::Style maAttribute;
@@ -2253,6 +2254,7 @@ public:
SwLineEntry( SwTwips nKey,
SwTwips nStartPos,
SwTwips nEndPos,
+ bool bOuter,
const svx::frame::Style& rAttribute );
OverlapType Overlaps( const SwLineEntry& rComp ) const;
@@ -2269,11 +2271,13 @@ public:
SwLineEntry::SwLineEntry( SwTwips nKey,
SwTwips nStartPos,
SwTwips nEndPos,
+ bool bOuter,
const svx::frame::Style& rAttribute )
: mnKey( nKey ),
mnStartPos( nStartPos ),
mnEndPos( nEndPos ),
mnLimitedEndPos(0),
+ mbOuter(bOuter),
maAttribute( rAttribute )
{
}
@@ -2384,10 +2388,11 @@ class SwTabFramePainter
void Insert( SwLineEntry&, bool bHori );
void Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect &rPaintArea);
void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
- void FindStylesForLine( const Point&,
- const Point&,
+ void FindStylesForLine( Point&,
+ Point&,
svx::frame::Style*,
- bool bHori ) const;
+ bool bHori,
+ bool bOuter ) const;
public:
explicit SwTabFramePainter( const SwTabFrame& rTabFrame );
@@ -2501,7 +2506,7 @@ void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) cons
svx::frame::Style aStyles[ 7 ];
aStyles[ 0 ] = rEntryStyle;
- FindStylesForLine( aStart, aEnd, aStyles, bHori );
+ FindStylesForLine(aStart, aEnd, aStyles, bHori, rEntry.mbOuter);
if (!bHori && rEntry.mnLimitedEndPos)
{
@@ -2675,10 +2680,10 @@ void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) cons
* StartPoint or Endpoint. The styles of these lines are required for DR's magic
* line painting functions
*/
-void SwTabFramePainter::FindStylesForLine( const Point& rStartPoint,
- const Point& rEndPoint,
+void SwTabFramePainter::FindStylesForLine( Point& rStartPoint,
+ Point& rEndPoint,
svx::frame::Style* pStyles,
- bool bHori ) const
+ bool bHori, bool bOuter ) const
{
// For example, aLFromB means: this vertical line intersects my horizontal line at its left end,
// from bottom.
@@ -2689,6 +2694,14 @@ void SwTabFramePainter::FindStylesForLine( const Point& rStartPoint,
// pStyles[ 5 ] = bHori ? aRFromR : BFromB,
// pStyles[ 6 ] = bHori ? aRFromB : BFromR,
+ bool bWordTableCell = false;
+ SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
+ if (pShell)
+ {
+ const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
+ bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+ }
+
SwLineEntryMap::const_iterator aMapIter = maVertLines.find( rStartPoint.X() );
OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
const SwLineEntrySet& rVertSet = (*aMapIter).second;
@@ -2701,6 +2714,11 @@ void SwTabFramePainter::FindStylesForLine( const Point& rStartPoint,
pStyles[ 3 ] = rEntry.maAttribute;
else if ( rStartPoint.Y() == rEntry.mnEndPos )
pStyles[ 1 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rStartPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rStartPoint.AdjustX(rEntry.maAttribute.GetWidth());
+ }
}
else
{
@@ -2730,6 +2748,11 @@ void SwTabFramePainter::FindStylesForLine( const Point& rStartPoint,
pStyles[ 1 ] = rEntry.maAttribute;
else if ( rStartPoint.X() == rEntry.mnStartPos )
pStyles[ 3 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rStartPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rStartPoint.AdjustY(rEntry.maAttribute.GetWidth());
+ }
}
}
@@ -2745,6 +2768,11 @@ void SwTabFramePainter::FindStylesForLine( const Point& rStartPoint,
pStyles[ 6 ] = rEntry.maAttribute;
else if ( rEndPoint.Y() == rEntry.mnEndPos )
pStyles[ 4 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rEndPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rEndPoint.AdjustX(-rEntry.maAttribute.GetWidth());
+ }
}
}
else
@@ -2759,6 +2787,11 @@ void SwTabFramePainter::FindStylesForLine( const Point& rStartPoint,
pStyles[ 4 ] = rEntry.maAttribute;
else if ( rEndPoint.X() == rEntry.mnStartPos )
pStyles[ 6 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rEndPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rEndPoint.AdjustY(-rEntry.maAttribute.GetWidth());
+ }
}
}
}
@@ -2826,22 +2859,32 @@ void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem
aT.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
aB.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
- SwLineEntry aLeft (nLeft, nTop, nBottom,
+ // First cell in a row.
+ bool bOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetLower() == &rFrame;
+ SwLineEntry aLeft (nLeft, nTop, nBottom, bOuter,
bVert ? aB : (bR2L ? aR : aL));
if (bWordTableCell && rBoxItem.GetLeft())
{
aLeft.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::LEFT);
}
- SwLineEntry aRight (nRight, nTop, nBottom,
+ // Last cell in a row.
+ bOuter = rFrame.IsCellFrame() && rFrame.GetNext() == nullptr;
+ SwLineEntry aRight (nRight, nTop, nBottom, bOuter,
bVert ? (bBottomAsTop ? aB : aT) : (bR2L ? aL : aR));
if (bWordTableCell && rBoxItem.GetRight())
{
aRight.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::RIGHT);
}
- SwLineEntry aTop (nTop, nLeft, nRight,
+
+ // First row in a table.
+ bOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetUpper()->GetLower() == rFrame.GetUpper();
+ SwLineEntry aTop (nTop, nLeft, nRight, bOuter,
bVert ? aL : (bBottomAsTop ? aB : aT));
- SwLineEntry aBottom(nBottom, nLeft, nRight,
+
+ // Last row in a table.
+ bOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetNext() == nullptr;
+ SwLineEntry aBottom(nBottom, nLeft, nRight, bOuter,
bVert ? aR : aB);
Insert( aLeft, false );
@@ -2870,7 +2913,7 @@ void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
{
const SwLineEntry& rOld = *aIter;
- if (rOld.mnLimitedEndPos)
+ if (rOld.mnLimitedEndPos || rOld.mbOuter != rNew.mbOuter)
{
// Don't merge with this line entry as it ends sooner than mnEndPos.
++aIter;
@@ -2888,10 +2931,10 @@ void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
OSL_ENSURE( rNew.mnStartPos >= rOld.mnStartPos, "Overlap type 3? How this?" );
// new left segment
- const SwLineEntry aLeft( nKey, rOld.mnStartPos, rNew.mnStartPos, rOldAttr );
+ const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
// new middle segment
- const SwLineEntry aMiddle( nKey, rNew.mnStartPos, rOld.mnEndPos, rCmpAttr );
+ const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rOld.mnEndPos, rOld.mbOuter, rCmpAttr);
// new right segment
rNew.mnStartPos = rOld.mnEndPos;
@@ -2908,13 +2951,13 @@ void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
else if ( SwLineEntry::OVERLAP2 == nOverlapType )
{
// new left segment
- const SwLineEntry aLeft( nKey, rOld.mnStartPos, rNew.mnStartPos, rOldAttr );
+ const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
// new middle segment
- const SwLineEntry aMiddle( nKey, rNew.mnStartPos, rNew.mnEndPos, rCmpAttr );
+ const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
// new right segment
- const SwLineEntry aRight( nKey, rNew.mnEndPos, rOld.mnEndPos, rOldAttr );
+ const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
// update current lines set
pLineSet->erase( aIter );
@@ -2929,13 +2972,13 @@ void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
else if ( SwLineEntry::OVERLAP3 == nOverlapType )
{
// new left segment
- const SwLineEntry aLeft( nKey, rNew.mnStartPos, rOld.mnStartPos, rNewAttr );
+ const SwLineEntry aLeft(nKey, rNew.mnStartPos, rOld.mnStartPos, rOld.mbOuter, rNewAttr);
// new middle segment
- const SwLineEntry aMiddle( nKey, rOld.mnStartPos, rNew.mnEndPos, rCmpAttr );
+ const SwLineEntry aMiddle(nKey, rOld.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
// new right segment
- const SwLineEntry aRight( nKey, rNew.mnEndPos, rOld.mnEndPos, rOldAttr );
+ const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
// update current lines set
pLineSet->erase( aIter );