Files
odfstudio/sw/qa/core/layout/layout.cxx
T
Andrea Gelmini 4ee0d493d0 Fix typos in code + comments
Change-Id: I66a422f3519dceed4f5e253a2895eb2e0d943662
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210574
Reviewed-by: Julien Nabet <serval2412@yahoo.fr>
Tested-by: Jenkins
2026-09-11 02:53:38 +02:00

1038 lines
46 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <swmodeltestbase.hxx>
#include <com/sun/star/style/PageStyleLayout.hpp>
#include <vcl/gdimtf.hxx>
#include <svx/svdpage.hxx>
#include <o3tl/string_view.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/dispatch.hxx>
#include <editeng/frmdiritem.hxx>
#include <wrtsh.hxx>
#include <docsh.hxx>
#include <unotxdoc.hxx>
#include <drawdoc.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentLinksAdministration.hxx>
#include <IDocumentState.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <rootfrm.hxx>
#include <itabenum.hxx>
#include <fmtanchr.hxx>
#include <fmtsrnd.hxx>
#include <IDocumentSettingAccess.hxx>
#include <fmtfsize.hxx>
#include <fmtfollowtextflow.hxx>
#include <view.hxx>
#include <cmdid.h>
#include <node.hxx>
#include <ndtxt.hxx>
#include <tabfrm.hxx>
#include <cntfrm.hxx>
/// Covers sw/source/core/layout/ fixes.
class SwCoreLayoutTest : public SwModelTestBase
{
public:
SwCoreLayoutTest()
: SwModelTestBase(u"/sw/qa/core/layout/data/"_ustr)
{
}
};
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTableFlyOverlap)
{
// Load a document that has an image anchored in the header.
// It also has a table which has the wrap around the image.
createSwDoc("table-fly-overlap.docx");
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
SwTwips nFlyTop = getXPath(pXmlDoc, "//header/txt/anchored/fly/infos/bounds", "top").toInt32();
SwTwips nFlyHeight
= getXPath(pXmlDoc, "//header/txt/anchored/fly/infos/bounds", "height").toInt32();
SwTwips nFlyBottom = nFlyTop + nFlyHeight;
SwTwips nTableFrameTop = getXPath(pXmlDoc, "//tab/infos/bounds", "top").toInt32();
SwTwips nTablePrintTop = getXPath(pXmlDoc, "//tab/infos/prtBounds", "top").toInt32();
SwTwips nTableTop = nTableFrameTop + nTablePrintTop;
// Without the accompanying fix in place, this test would have failed with:
// - Expected greater or equal than: 3579
// - Actual : 2210
// i.e. the table's top border overlapped with the image, even if the image's wrap mode was set
// to parallel.
CPPUNIT_ASSERT_GREATEREQUAL(nFlyBottom, nTableTop);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTdf128195)
{
// Load a document that has two paragraphs in the header.
// The second paragraph should have its bottom spacing applied.
createSwDoc("tdf128195.docx");
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
sal_Int32 nTxtHeight = getXPath(pXmlDoc, "//header/txt[2]/infos/bounds", "height").toInt32();
sal_Int32 nTxtBottom = getXPath(pXmlDoc, "//header/txt[2]/infos/bounds", "bottom").toInt32();
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2269), nTxtHeight);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3529), nTxtBottom);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBIRT)
{
// this looped
createSwDoc("birt_min.odt");
}
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.
createSwDoc("border-collapse-compat.docx");
SwDocShell* pShell = getSwDocShell();
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
MetafileXmlDump aDumper;
xmlDocUniquePtr 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", u"20");
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBtlrTableRowSpan)
{
// Load a document which has a table. The A1 cell has btlr text direction, and the A1..A3 cells
// are merged.
createSwDoc("btlr-table-row-span.odt");
SwDocShell* pShell = getSwDocShell();
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
MetafileXmlDump aDumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: USA
// - Actual : West
// i.e. the "USA" text completely disappeared.
assertXPathContent(pXmlDoc, "//textarray[1]/text", u"USA");
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTableFlyOverlapSpacing)
{
// Load a document that has an image on the right of a table. The table wraps around the image.
createSwDoc("table-fly-overlap-spacing.docx");
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
SwTwips nFlyTop = getXPath(pXmlDoc, "//body/txt/anchored/fly/infos/bounds", "top").toInt32();
SwTwips nFlyHeight
= getXPath(pXmlDoc, "//body/txt/anchored/fly/infos/bounds", "height").toInt32();
SwTwips nFlyBottom = nFlyTop + nFlyHeight;
SwTwips nTableFrameTop = getXPath(pXmlDoc, "//tab/infos/bounds", "top").toInt32();
SwTwips nTablePrintTop = getXPath(pXmlDoc, "//tab/infos/prtBounds", "top").toInt32();
SwTwips nTableTop = nTableFrameTop + nTablePrintTop;
// Without the accompanying fix in place, this test would have failed with:
// - Expected greater or equal than: 3993
// - Actual : 3993
// i.e. the table was below the image, not on the left of the image.
CPPUNIT_ASSERT_LESS(nFlyBottom, nTableTop);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTablesMoveBackwards)
{
// Load a document with 1 pages: empty content on first page, then 21 tables on the second page.
createSwDoc("tables-move-backwards.odt");
SwDocShell* pDocShell = getSwDocShell();
SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
// Delete the content on the first page.
pWrtShell->SttEndDoc(/*bStart=*/true);
pWrtShell->EndPg(/*bSelect=*/true);
pWrtShell->DelLeft();
// Calc the layout and check the number of pages.
calcLayout();
xmlDocUniquePtr pLayout = parseLayoutDump();
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1
// - Actual : 2
// i.e. there was an unexpected 2nd page, as only 20 out of 21 tables were moved to the first
// page.
assertXPath(pLayout, "//page", 1);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testContinuousEndnotesMoveBackwards)
{
// Load a document with the ContinuousEndnotes flag turned on.
createSwDoc("continuous-endnotes-move-backwards.doc");
xmlDocUniquePtr pLayout = parseLayoutDump();
// We have 2 pages.
assertXPath(pLayout, "/root/page", 2);
// No endnote container on page 1.
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 0
// - Actual : 1
// i.e. there were unexpected endnotes on page 1.
assertXPath(pLayout, "/root/page[1]//ftncont", 0);
// All endnotes are in a container on page 2.
assertXPath(pLayout, "/root/page[2]//ftncont", 1);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testAnchorPositionBasedOnParagraph)
{
// tdf#134783 check whether position of shape is good if it is anchored to paragraph and
// the "Don't add space between paragraphs of the same style" option is set
createSwDoc("tdf134783_testAnchorPositionBasedOnParagraph.fodt");
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
CPPUNIT_ASSERT(pXmlDoc);
assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[1]/bounds", "top", u"1671");
assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[1]/bounds", "bottom", u"1732");
assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[2]/bounds", "top", u"1947");
assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[2]/bounds", "bottom", u"2008");
assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[3]/bounds", "top", u"3783");
assertXPath(pXmlDoc, "(//anchored/SwAnchoredDrawObject)[3]/bounds", "bottom", u"3844");
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxStaysInsideShape)
{
// tdf#135198: check whether text box stays inside shape after moving it upwards
createSwDoc("shape-textbox.odt");
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
CPPUNIT_ASSERT(pXmlDoc);
// Without the fix in place, this test would have failed with
// - Expected: 1932
// - Actual : 7476
assertXPath(pXmlDoc, "//anchored/fly/infos/bounds", "top", u"1932");
assertXPath(pXmlDoc, "//anchored/fly/infos/bounds", "bottom", u"7184");
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxNotModifiedOnOpen)
{
// tdf#138050: a freshly opened document containing a shape with a text box
// should not appear to be modified
createSwDoc("textbox-phantom-change.docx");
SwDoc* pDoc = getSwDoc();
// Without the fix in place this test would have shown that the document
// was modified due to a fix to tdf#135198
CPPUNIT_ASSERT(!pDoc->getIDocumentState().IsModified());
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxAutoGrowVertical)
{
createSwDoc("textbox-autogrow-vertical.docx");
SwDoc* pDoc = getSwDoc();
SdrPage* pPage = pDoc->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pShape = pPage->GetObj(0);
tools::Rectangle aShapeRect = pShape->GetCurrentBoundRect();
xmlDocUniquePtr pLayout = parseLayoutDump();
CPPUNIT_ASSERT(pLayout);
sal_Int32 nFlyLeft = getXPath(pLayout, "//anchored/fly/infos/bounds", "left").toInt32();
sal_Int32 nFlyTop = getXPath(pLayout, "//anchored/fly/infos/bounds", "top").toInt32();
sal_Int32 nFlyRight = getXPath(pLayout, "//anchored/fly/infos/bounds", "right").toInt32();
sal_Int32 nFlyBottom = getXPath(pLayout, "//anchored/fly/infos/bounds", "bottom").toInt32();
tools::Rectangle aFlyRect(nFlyLeft, nFlyTop, nFlyRight, nFlyBottom);
// Without the accompanying fix in place, this test would have failed, as aFlyRect was too wide,
// so it was not inside aShapeRect anymore.
CPPUNIT_ASSERT(aShapeRect.Contains(aFlyRect));
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextboxModification)
{
// Load a document with a textbox in it: the layout will have to position the shape part.
createSwDoc("textbox-modification.docx");
SwDocShell* pDocShell = getSwDocShell();
// Without the accompanying fix in place, this test would have failed, as the document was
// marked as modified right after the import.
CPPUNIT_ASSERT(!pDocShell->IsModified());
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTextBoxInHeaderIsPositioned)
{
// Load a document with a floating text box in the header
createSwDoc("header-textbox.docx");
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
CPPUNIT_ASSERT(pXmlDoc);
// Without the fix in place, this test would have failed with
// - Expected: 8051
// - Actual : 1418
// Comparison with 7000 chosen due to variability between devices
CPPUNIT_ASSERT_GREATEREQUAL(
double(7000), getXPath(pXmlDoc, "//anchored/fly/infos/bounds", "left").toDouble());
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testBtlrNestedCell)
{
// Load a document with a nested table, the inner A1 cell has a btlr text direction.
createSwDoc("btlr-nested-cell.odt");
SwDoc* pDoc = getSwDoc();
SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
SwFrame* pPage = pLayout->GetLower();
SwFrame* pBody = pPage->GetLower();
SwFrame* pOuterTable = pBody->GetLower()->GetNext();
SwFrame* pInnerTable = pOuterTable->GetLower()->GetLower()->GetLower();
// Check the paint area of the only text frame in the cell.
SwFrame* pTextFrame = pInnerTable->GetLower()->GetLower()->GetLower();
tools::Long nFrameBottom = pTextFrame->getFrameArea().Bottom();
SwRect aPaintArea = pTextFrame->GetPaintArea();
// Without the accompanying fix in place, this test would have failed with:
// - Expected greater or equal than: 2829
// - Actual : 2080
// i.e. part of the text frame area was not painted, hiding the actual text.
CPPUNIT_ASSERT_GREATEREQUAL(nFrameBottom, aPaintArea.Bottom());
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testKeepwithnextFullheight)
{
// The document has a heading (keep with next) and a full-page image in the next paragraph, i.e.
// conflicting requirements.
// Without the accompanying fix in place, this test would have failed with a layout loop in
// SwEditShell::CalcLayout().
createSwDoc("keepwithnext-fullheight.fodt");
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
CPPUNIT_ASSERT(pXmlDoc);
// Make sure the document has 2 pages.
assertXPath(pXmlDoc, "//page", 2);
// Heading stays on page 1 to avoid a layout loop.
assertXPathContent(pXmlDoc, "//page[1]/body/txt[2]", u"Heading");
// Image stays on page 2.
assertXPath(pXmlDoc, "//page[2]/body/txt/anchored/fly", 1);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterMargin)
{
// Create a document, remember the old left edge of the page print area (the rectangle that is
// inside margins).
createSwDoc();
SwDoc* pDoc = getSwDoc();
uno::Reference<beans::XPropertySet> xStandard(
getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
SwFrame* pPage = pLayout->GetLower();
tools::Long nOldLeft = pPage->getFramePrintArea().Left();
// Set the gutter margin to 2cm.
sal_Int32 nGutterMm100 = 2000;
xStandard->setPropertyValue(u"GutterMargin"_ustr, uno::Any(nGutterMm100));
// Verify that the new left edge is larger.
tools::Long nNewLeft = pPage->getFramePrintArea().Left();
tools::Long nGutterTwips = o3tl::toTwips(nGutterMm100, o3tl::Length::mm100);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1134
// - Actual : 0
// i.e. the gutter was not added to the left margin.
CPPUNIT_ASSERT_EQUAL(nGutterTwips, nNewLeft - nOldLeft);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterTopMargin)
{
// Create a document, remember the old top edge of the page print area (the rectangle that is
// inside margins).
createSwDoc();
SwDoc* pDoc = getSwDoc();
uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
uno::Reference<beans::XPropertySet> xSettings(
xFactory->createInstance(u"com.sun.star.document.Settings"_ustr), uno::UNO_QUERY);
xSettings->setPropertyValue(u"GutterAtTop"_ustr, uno::Any(true));
uno::Reference<beans::XPropertySet> xStandard(
getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
SwFrame* pPage = pLayout->GetLower();
tools::Long nOldTop = pPage->getFramePrintArea().Top();
// Set the gutter margin to 2cm.
sal_Int32 nGutterMm100 = 2000;
xStandard->setPropertyValue(u"GutterMargin"_ustr, uno::Any(nGutterMm100));
// Verify that the new top edge is larger.
tools::Long nNewTop = pPage->getFramePrintArea().Top();
tools::Long nGutterTwips = o3tl::toTwips(nGutterMm100, o3tl::Length::mm100);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1134
// - Actual : 0
// i.e. the gutter was not added to the left margin.
CPPUNIT_ASSERT_EQUAL(nGutterTwips, nNewTop - nOldTop);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterMirrorMargin)
{
createSwDoc();
SwDoc* pDoc = getSwDoc();
SwDocShell* pDocShell = getSwDocShell();
SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
pWrtShell->InsertPageBreak();
SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
SwFrame* pPage = pLayout->GetLower();
tools::Long nOldLeft = pPage->getFramePrintArea().Left();
SwFrame* pPage2 = pPage->GetNext();
tools::Long nOldRight = pPage2->getFramePrintArea().Right();
uno::Reference<beans::XPropertySet> xStandard(
getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
xStandard->setPropertyValue(u"PageStyleLayout"_ustr, uno::Any(style::PageStyleLayout_MIRRORED));
sal_Int32 nGutterMm100 = 2000;
xStandard->setPropertyValue(u"GutterMargin"_ustr, uno::Any(nGutterMm100));
tools::Long nNewLeft = pPage->getFramePrintArea().Left();
tools::Long nGutterTwips = o3tl::toTwips(nGutterMm100, o3tl::Length::mm100);
CPPUNIT_ASSERT_EQUAL(nGutterTwips, nNewLeft - nOldLeft);
tools::Long nNewRight = pPage2->getFramePrintArea().Right();
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1134
// - Actual : 0
// i.e. the gutter was missing on the second, mirrored page.
CPPUNIT_ASSERT_EQUAL(nGutterTwips, nOldRight - nNewRight);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testRtlGutterMargin)
{
// Given a document with a right margin:
createSwDoc();
SwDoc* pDoc = getSwDoc();
uno::Reference<beans::XPropertySet> xStandard(
getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
SwFrame* pPage = pLayout->GetLower();
tools::Long nOldRight = pPage->getFramePrintArea().Right();
// When setting enable RTL gutter mode and setting a gutter margin:
xStandard->setPropertyValue(u"RtlGutter"_ustr, uno::Any(true));
sal_Int32 nGutterMm100 = 2000;
xStandard->setPropertyValue(u"GutterMargin"_ustr, uno::Any(nGutterMm100));
// Then make sure the new right edge of the print area is decreased:
tools::Long nNewRight = pPage->getFramePrintArea().Right();
tools::Long nGutterTwips = o3tl::toTwips(nGutterMm100, o3tl::Length::mm100);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1134
// - Actual : 0
// i.e. the gutter was missing on the right side.
CPPUNIT_ASSERT_EQUAL(nGutterTwips, nOldRight - nNewRight);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testGutterMarginPageBorder)
{
// FIXME this is 3369 on macOS -- calculate this number dynamically?
// FIXME this is random on Windows at the moment (in two subsequent tests without any scaling,
// the actual values were 6346, 10066) - something broke metafile generation on Windows?
#if !defined(MACOSX) && !defined(_WIN32)
// Given a document with a non-0 gutter margin.
createSwDoc();
uno::Reference<beans::XPropertySet> xStandard(
getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
sal_Int32 nGutterMm100 = 2000;
xStandard->setPropertyValue(u"GutterMargin"_ustr, uno::Any(nGutterMm100));
// When setting a left border.
table::BorderLine2 aBorder;
aBorder.LineWidth = 2;
aBorder.OuterLineWidth = 2;
xStandard->setPropertyValue(u"LeftBorder"_ustr, uno::Any(aBorder));
// Then make sure border is at the left edge of the text area.
SwDocShell* pShell = getSwDocShell();
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
MetafileXmlDump dumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 2565
// - Actual : 1425
// Where 2565 is close to the left edge of the text area (2553).
assertXPath(pXmlDoc, "//polyline[@style='solid']/point[1]", "x", u"2565");
#endif
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testTdf45908_invoice)
{
// without the fix, this was hanging (and slowly consuming memory) on fileopen.
createSwDoc("tdf45908_invoice.odt");
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testVerticallyMergedCellBorder)
{
// Given a document with a table: 2 columns, 5 rows. B2 -> B5 is merged:
createSwDoc("vmerge-cell-border.docx");
SwDocShell* pShell = getSwDocShell();
// When rendering the table:
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
// Make sure that B4->B5 has no borders.
MetafileXmlDump dumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
// Collect vertical positions of all border points.
xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
std::vector<sal_Int32> aBorderPositions;
for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); ++i)
{
xmlNodePtr pXmlNode = pXmlNodes->nodeTab[i];
xmlChar* pValue = xmlGetProp(pXmlNode, BAD_CAST("y"));
sal_Int32 nValue = o3tl::toInt32(reinterpret_cast<char const*>(pValue));
aBorderPositions.push_back(nValue);
}
xmlXPathFreeObject(pXmlObj);
// Collect top and bottom of the B1->B3 rows.
xmlDocUniquePtr pLayout = parseLayoutDump();
pXmlObj = getXPathNode(pLayout, "//tab/row/infos/bounds");
pXmlNodes = pXmlObj->nodesetval;
std::vector<sal_Int32> aLayoutPositions;
for (int i = 0; i < 3; ++i)
{
xmlNodePtr pXmlNode = pXmlNodes->nodeTab[i];
if (i == 0)
{
xmlChar* pValue = xmlGetProp(pXmlNode, BAD_CAST("top"));
sal_Int32 nValue = o3tl::toInt32(reinterpret_cast<char const*>(pValue));
aLayoutPositions.push_back(nValue);
}
xmlChar* pValue = xmlGetProp(pXmlNode, BAD_CAST("bottom"));
sal_Int32 nValue = o3tl::toInt32(reinterpret_cast<char const*>(pValue));
aLayoutPositions.push_back(nValue);
}
xmlXPathFreeObject(pXmlObj);
// Check if any border is outside the B1->B3 range.
for (const auto nBorderPosition : aBorderPositions)
{
bool bFound = false;
for (const auto nLayoutPosition : aLayoutPositions)
{
if (std::abs(nBorderPosition - nLayoutPosition) <= 30)
{
bFound = true;
break;
}
}
std::stringstream ss;
ss << "Bad vertical position for border point: " << nBorderPosition;
ss << " Expected positions: ";
for (size_t i = 0; i < aLayoutPositions.size(); ++i)
{
if (i > 0)
{
ss << ", ";
}
ss << aLayoutPositions[i];
}
// Without the accompanying fix in place, this test would have failed with:
// - Bad vertical position for border point: 5624 Expected positions: 3022, 3540, 4059, 4578
// i.e. the middle vertical border end was the bottom of B5, not bottom of B3.
CPPUNIT_ASSERT_MESSAGE(ss.str(), bFound);
}
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testCrashRemoveFromLayout)
{
createSwDoc("tdf122894-4.doc");
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testLinkedBullet)
{
// Given a document with a graphic bullet, where the image is a linked one:
createSwDoc("linked-bullet.odt");
SwDocShell* pShell = getSwDocShell();
// allow link updates and trigger fetch so the linked bullet image
// is loaded before rendering
pShell->getEmbeddedObjectContainer().setUserAllowsLinkUpdate(true);
sfx2::LinkManager& rLinkMgr = getSwDoc()->getIDocumentLinksAdministration().GetLinkManager();
rLinkMgr.UpdateAllLinks(false, u""_ustr);
// When rendering that document:
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
// Then make sure the render result contains exactly one bitmap:
MetafileXmlDump aDumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1
// - Actual : 0
// i.e. the bullet's bitmap was lost.
assertXPath(pXmlDoc, "//bmpexscale", 1);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testInnerCellBorderIntersect)
{
// Given a table with both outer and inner borders:
createSwDoc("inner-border.docx");
SwDocShell* pShell = getSwDocShell();
// When rendering table borders:
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
// Then make sure 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 = o3tl::toInt32(reinterpret_cast<char const*>(pStartY));
sal_Int32 nEndY = o3tl::toInt32(reinterpret_cast<char const*>(pEndY));
if (nStartY != nEndY)
{
// Vertical border.
continue;
}
xmlChar* pStartX = xmlGetProp(pStart, BAD_CAST("x"));
xmlChar* pEndX = xmlGetProp(pEnd, BAD_CAST("x"));
sal_Int32 nStartX = o3tl::toInt32(reinterpret_cast<char const*>(pStartX));
sal_Int32 nEndX = o3tl::toInt32(reinterpret_cast<char const*>(pEndX));
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_TEST_FIXTURE(SwCoreLayoutTest, testInnerCellBorderNocompatIntersect)
{
// Given a table with both outer and inner borders:
createSwDoc();
SwDocShell* pShell = getSwDocShell();
SwDoc* pDoc = pShell->GetDoc();
// Set the default page style's writing direction to vertical:
SwPageDesc aStandard(pDoc->GetPageDesc(0));
SvxFrameDirectionItem aDirection(SvxFrameDirection::Vertical_RL_TB, RES_FRAMEDIR);
aStandard.GetMaster().SetFormatAttr(aDirection);
pDoc->ChgPageDesc(0, aStandard);
// Insert a 4x4 table:
SwWrtShell* pWrtShell = pShell->GetWrtShell();
SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
pWrtShell->InsertTable(aTableOptions, 4, 4);
// When rendering table borders:
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
// Then make sure that all horizontal lines have the same length:
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 = o3tl::toInt32(reinterpret_cast<char const*>(pStartY));
sal_Int32 nEndY = o3tl::toInt32(reinterpret_cast<char const*>(pEndY));
if (nStartY != nEndY)
{
// Vertical border.
continue;
}
xmlChar* pStartX = xmlGetProp(pStart, BAD_CAST("x"));
xmlChar* pEndX = xmlGetProp(pEnd, BAD_CAST("x"));
sal_Int32 nStartX = o3tl::toInt32(reinterpret_cast<char const*>(pStartX));
sal_Int32 nEndX = o3tl::toInt32(reinterpret_cast<char const*>(pEndX));
aBorderStartEnds.emplace_back(nStartX, nEndX);
}
xmlXPathFreeObject(pXmlObj);
// We have 5 lines: top of 4 cells + bottom.
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), aBorderStartEnds.size());
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 9719
// - Actual : 10064
// i.e. the 2nd line started later than the first one, which is incorrect.
CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[1].first);
CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[2].first);
CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[3].first);
CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].first, aBorderStartEnds[4].first);
CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[1].second);
CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[2].second);
CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[3].second);
CPPUNIT_ASSERT_EQUAL(aBorderStartEnds[0].second, aBorderStartEnds[4].second);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testDoubleBorderVertical)
{
// Given a table with a left and right double border, outer is thick, inner is thin:
createSwDoc("double-border-vertical.docx");
SwDocShell* pShell = getSwDocShell();
// When rendering that document:
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
// Then make sure the left border is thick+thin and the right border is thin+thick (from left to
// right):
MetafileXmlDump dumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
// Collect widths of vertical lines.
xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
// Horizontal position -> width.
std::map<sal_Int32, sal_Int32> aBorderWidths;
for (int i = 0; i < xmlXPathNodeSetGetLength(pXmlNodes); i += 2)
{
xmlNodePtr pStart = pXmlNodes->nodeTab[i];
xmlNodePtr pEnd = pXmlNodes->nodeTab[i + 1];
xmlChar* pStartX = xmlGetProp(pStart, BAD_CAST("x"));
xmlChar* pEndX = xmlGetProp(pEnd, BAD_CAST("x"));
sal_Int32 nStartX = o3tl::toInt32(reinterpret_cast<char const*>(pStartX));
sal_Int32 nEndX = o3tl::toInt32(reinterpret_cast<char const*>(pEndX));
if (nStartX != nEndX)
{
// Horizontal border.
continue;
}
xmlChar* pWidth = xmlGetProp(pStart->parent, BAD_CAST("width"));
sal_Int32 nWidth = o3tl::toInt32(reinterpret_cast<char const*>(pWidth));
aBorderWidths[nStartX] = nWidth;
}
xmlXPathFreeObject(pXmlObj);
std::vector<sal_Int32> aBorderWidthVec;
std::transform(aBorderWidths.begin(), aBorderWidths.end(), std::back_inserter(aBorderWidthVec),
[](const std::pair<sal_Int32, sal_Int32>& rPair) { return rPair.second; });
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aBorderWidthVec.size());
// Without the accompanying fix in place, this test would have failed with:
// - Expected greater than: 120
// - Actual : 60
// i.e. the left border was thin+thick, not thick+thin.
CPPUNIT_ASSERT_GREATER(aBorderWidthVec[1], aBorderWidthVec[0]);
CPPUNIT_ASSERT_GREATER(aBorderWidthVec[2], aBorderWidthVec[3]);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testDoubleBorderHorizontal)
{
// Given a table with a top and bottom double border, outer is thin, inner is thick:
createSwDoc("double-border-horizontal.docx");
SwDocShell* pShell = getSwDocShell();
// When rendering table borders:
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
// Then make sure the top border is thin+thick and the bottom border is thick+thin (from top to
// bottom):
MetafileXmlDump dumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
// Collect widths of horizontal lines.
xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
// Vertical position -> width.
std::map<sal_Int32, sal_Int32> aBorderWidths;
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 = o3tl::toInt32(reinterpret_cast<char const*>(pStartY));
sal_Int32 nEndY = o3tl::toInt32(reinterpret_cast<char const*>(pEndY));
if (nStartY != nEndY)
{
// Vertical border.
continue;
}
xmlChar* pWidth = xmlGetProp(pStart->parent, BAD_CAST("width"));
sal_Int32 nWidth = o3tl::toInt32(reinterpret_cast<char const*>(pWidth));
aBorderWidths[nStartY] = nWidth;
}
xmlXPathFreeObject(pXmlObj);
std::vector<sal_Int32> aBorderWidthVec;
std::transform(aBorderWidths.begin(), aBorderWidths.end(), std::back_inserter(aBorderWidthVec),
[](const std::pair<sal_Int32, sal_Int32>& rPair) { return rPair.second; });
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aBorderWidthVec.size());
CPPUNIT_ASSERT_GREATER(aBorderWidthVec[0], aBorderWidthVec[1]);
// Without the accompanying fix in place, this test would have failed with:
// - Expected greater than: 120
// - Actual : 60
// i.e. the bottom border was thin+thick, not thick+thin.
CPPUNIT_ASSERT_GREATER(aBorderWidthVec[3], aBorderWidthVec[2]);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testParaBorderInCellClip)
{
// Given a document which has outside-cell borders defined, which should not be visible:
createSwDoc("para-border-in-cell-clip.docx");
SwDocShell* pShell = getSwDocShell();
// When rendering those borders:
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
// Then make sure that we have clipping setup for both paragraphs inside the table cell:
MetafileXmlDump dumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 2
// - Actual : 0
// - XPath '//clipregion/polygon' number of nodes is incorrect
// i.e. there was no clipping applied, leading to unexpected left/right borders.
assertXPath(pXmlDoc, "//clipregion/polygon", 2);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testDoublePageBorder)
{
// Given a page with a top and bottom double border, outer is thick, inner is thin:
createSwDoc("double-page-border.docx");
SwDocShell* pShell = getSwDocShell();
// When rendering that document:
std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
// Then make sure the top border is thick+thin and the bottom border is thin+thick (from top to
// bottom):
MetafileXmlDump dumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
// Collect widths of horizontal lines.
xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point");
xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
// Vertical position -> width.
std::map<sal_Int32, sal_Int32> aBorderWidths;
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 = o3tl::toInt32(reinterpret_cast<char const*>(pStartY));
sal_Int32 nEndY = o3tl::toInt32(reinterpret_cast<char const*>(pEndY));
if (nStartY != nEndY)
{
// Vertical border.
continue;
}
xmlChar* pWidth = xmlGetProp(pStart->parent, BAD_CAST("width"));
sal_Int32 nWidth = o3tl::toInt32(reinterpret_cast<char const*>(pWidth));
aBorderWidths[nStartY] = nWidth;
}
xmlXPathFreeObject(pXmlObj);
std::vector<sal_Int32> aBorderWidthVec;
std::transform(aBorderWidths.begin(), aBorderWidths.end(), std::back_inserter(aBorderWidthVec),
[](const std::pair<sal_Int32, sal_Int32>& rPair) { return rPair.second; });
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aBorderWidthVec.size());
CPPUNIT_ASSERT_GREATER(aBorderWidthVec[1], aBorderWidthVec[0]);
// Without the accompanying fix in place, this test would have failed with:
// - Expected greater than: 60
// - Actual : 15
// i.e. the bottom border was thick+thin, not thin+thick.
CPPUNIT_ASSERT_GREATER(aBorderWidthVec[2], aBorderWidthVec[3]);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testNegativePageBorder)
{
// FIXME: the DPI check should be removed when either (1) the test is fixed to work with
// non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
if (!IsDefaultDPI())
return;
// Given a document with a top margin and a negative border distance:
createSwDoc();
SwDocShell* pDocShell = getSwDocShell();
SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
pWrtShell->Insert(u"test"_ustr);
uno::Reference<beans::XPropertySet> xPageStyle(
getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
xPageStyle->setPropertyValue(u"TopMargin"_ustr,
uno::Any(static_cast<sal_Int32>(501))); // 284 twips
table::BorderLine2 aBorder;
aBorder.LineWidth = 159; // 90 twips
aBorder.OuterLineWidth = 159;
xPageStyle->setPropertyValue(u"TopBorder"_ustr, uno::Any(aBorder));
sal_Int32 nTopBorderDistance = -646; // -366 twips
xPageStyle->setPropertyValue(u"TopBorderDistance"_ustr, uno::Any(nTopBorderDistance));
nTopBorderDistance = 0;
xPageStyle->getPropertyValue(u"TopBorderDistance"_ustr) >>= nTopBorderDistance;
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-646), nTopBorderDistance);
// When rendering that border:
std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
// Then make sure that the negative distance pushes the horizontal borderline down:
MetafileXmlDump aDumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 899
// - Actual : 524
// i.e. the negative border distance was rounded up to 0 in lcl_CalcBorderRect().
// Ideally this would be 284 (top of first page) + 284 (top margin) + 366 (border distance) =
// 934.
assertXPath(pXmlDoc, "//polyline[@style='solid']/point[1]", "y", u"899");
assertXPath(pXmlDoc, "//polyline[@style='solid']/point[2]", "y", u"899");
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testNegativePageBorderNoMargin)
{
// FIXME: the DPI check should be removed when either (1) the test is fixed to work with
// non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
if (!IsDefaultDPI())
return;
// Given a document with no top margin and a negative border distance:
createSwDoc();
SwDocShell* pDocShell = getSwDocShell();
SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
pWrtShell->Insert(u"test"_ustr);
uno::Reference<beans::XPropertySet> xPageStyle(
getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY);
xPageStyle->setPropertyValue(u"TopMargin"_ustr, uno::Any(static_cast<sal_Int32>(0))); // 0 twips
table::BorderLine2 aBorder;
aBorder.LineWidth = 159; // 90 twips
aBorder.OuterLineWidth = 159;
xPageStyle->setPropertyValue(u"TopBorder"_ustr, uno::Any(aBorder));
sal_Int32 nTopBorderDistance = -1147; // -650 twips
xPageStyle->setPropertyValue(u"TopBorderDistance"_ustr, uno::Any(nTopBorderDistance));
// When rendering that border:
std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
// Then make sure that the negative distance pushes the horizontal borderline down:
MetafileXmlDump aDumper;
xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 899
// - Actual : 329
// i.e. this failed differently: the lack of top margin caused a second problem.
// Ideally this would be 284 (top of first page) + 650 (border distance) =
// 934.
assertXPath(pXmlDoc, "//polyline[@style='solid']/point[1]", "y", u"899");
assertXPath(pXmlDoc, "//polyline[@style='solid']/point[2]", "y", u"899");
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testFollowTextFlowWrapInBackground)
{
// Given a document with a table, and a graphic inside that table -- anchored, wrap set to
// through and follow-text-flow set to true:
createSwDoc();
SwDoc* pDoc = getSwDoc();
pDoc->getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, true);
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
pWrtShell->InsertTable(aTableOptions, 1, 1);
pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>);
SwFormatAnchor aAnchor(RndStdIds::FLY_AT_CHAR);
aFrameSet.Put(aAnchor);
SwFormatSurround aSurround(text::WrapTextMode_THROUGH);
aFrameSet.Put(aSurround);
SwFormatFrameSize aSize(SwFrameSize::Fixed, 1000, 1000);
aFrameSet.Put(aSize);
SwFormatFollowTextFlow aFlow(true);
aFrameSet.Put(aFlow);
Graphic aGrf;
// When inserting that image:
pWrtShell->SwFEShell::Insert(OUString(), OUString(), &aGrf, &aFrameSet);
// Then make sure that the cell height grows to have space for the graphic, given that
// background=true is not specified.
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
sal_Int32 nCellHeight = getXPath(pXmlDoc, "//cell[1]/infos/bounds", "height").toInt32();
// Without the accompanying fix in place, this test would have failed with:
// - Expected greater than: 1000
// - Actual : 396
// i.e. the image was larger than the containing cell.
CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(1000), nCellHeight);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testPageRemoveFlyTable)
{
// Given a document with a ToC and several tables, one table marked with a bookmark:
createSwDoc("page-remove-fly-table.odt");
// When updating the ToC and incrementally formatting the document:
SwView* pView = getSwDocShell()->GetView();
SfxDispatcher& rDispatcher = *pView->GetViewFrame().GetDispatcher();
rDispatcher.Execute(FN_UPDATE_TOX);
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->Reformat();
// Then make sure that the 2nd table below the bookmark has no unwanted top margin:
pWrtShell->GotoMark(SwMarkName(u"test"_ustr));
pWrtShell->Down(/*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
pWrtShell->Down(/*bSelect=*/false, /*nCount=*/1, /*bBasicCall=*/false);
SwCursor* pCursor = pWrtShell->GetCursor();
SwTextNode* pTextNode = pCursor->GetPoint()->GetNode().GetTextNode();
SwFrame* pTextFrame = pTextNode->getLayoutFrame(nullptr);
SwTabFrame* pInnerTable = pTextFrame->FindTabFrame();
SwTabFrame* pOuterTable = pInnerTable->GetUpper()->FindTabFrame();
tools::Long nActual = pOuterTable->getFramePrintArea().Top();
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 0
// - Actual : 5879
// i.e. the problematic table had a large, unwanted/leftover top margin.
CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long>(0), nActual);
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testPageRemoveFlyNoTable)
{
createSwDoc("page-remove-fly-no-table.fodt");
// This never returned.
calcLayout();
}
CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testNewFollowTextFlowWrapInBackground)
{
// Given a document with a table, and a graphic inside that table -- anchored, wrap set to
// through and follow-text-flow set to true, legacy USE_FORMER_TEXT_WRAPPING is not set:
createSwDoc();
SwDoc* pDoc = getSwDoc();
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
pWrtShell->InsertTable(aTableOptions, 1, 1);
pWrtShell->MoveTable(GotoPrevTable, fnTableStart);
SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>);
SwFormatAnchor aAnchor(RndStdIds::FLY_AT_CHAR);
aFrameSet.Put(aAnchor);
SwFormatSurround aSurround(text::WrapTextMode_THROUGH);
aFrameSet.Put(aSurround);
SwFormatFrameSize aSize(SwFrameSize::Fixed, 1000, 1000);
aFrameSet.Put(aSize);
SwFormatFollowTextFlow aFlow(true);
aFrameSet.Put(aFlow);
Graphic aGrf;
// When inserting that image:
pWrtShell->SwFEShell::Insert(OUString(), OUString(), &aGrf, &aFrameSet);
// Then make sure that the cell height grows to have space for the graphic, given that
// background=true is not specified.
xmlDocUniquePtr pXmlDoc = parseLayoutDump();
sal_Int32 nCellHeight = getXPath(pXmlDoc, "//cell[1]/infos/bounds", "height").toInt32();
// Without the accompanying fix in place, this test would have failed with:
// - Expected less than: 1000
// - Actual : 1120
// i.e. the cell height was too large, the image influenced it, which is not expected.
CPPUNIT_ASSERT_LESS(static_cast<sal_Int32>(1000), nCellHeight);
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */