Change-Id: I66a422f3519dceed4f5e253a2895eb2e0d943662 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210574 Reviewed-by: Julien Nabet <serval2412@yahoo.fr> Tested-by: Jenkins
1038 lines
46 KiB
C++
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: */
|