tdf#140158 tdf#138598 tdf#140598 sw: fix sync of AS_CHAR textboxes
Textboxes anchored "As Character" fell apart, when
typing before some characters or inserting a page break.
By fixing that, the tdf#138598 bug also have fixed which
was a regression from commit b6850bbe95418ecfde404be1696548f18d200c9b
(tdf#106153 sw compatibility: fix textboxes exceeding the page).
In addition, tdf140598 is also fixed, which was
a regression from commit c96c386c5db45dc4d5e358915caad7474e373068
(tdf#136516 add positioning to SwTextBoxHelper::syncProperty()).
Change-Id: Ifeadd8b2055ce52a019d651369ca41185de7bbe3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111338
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111796
Tested-by: Jenkins
diff --git a/sw/inc/textboxhelper.hxx b/sw/inc/textboxhelper.hxx
index 45d8a6c..2043b1f 100644
--- a/sw/inc/textboxhelper.hxx
+++ b/sw/inc/textboxhelper.hxx
@@ -16,6 +16,8 @@
#include <com/sun/star/uno/Any.h>
#include <com/sun/star/uno/Type.h>
#include <com/sun/star/text/TextContentAnchorType.hpp>
#include <svx/swframetypes.hxx>
#include "swdllapi.h"
@@ -71,6 +73,9 @@ public:
static void getProperty(SwFrameFormat const* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
css::uno::Any& rValue);
/// There are two types of enum of anchor type, so this function maps this.
static css::text::TextContentAnchorType mapAnchorType(const RndStdIds& rAnchorID);
/// Similar to syncProperty(), but used by the internal API (e.g. for UI purposes).
static void syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& rSet);
diff --git a/sw/qa/extras/uiwriter/data3/AsCharTxBxTest.docx b/sw/qa/extras/uiwriter/data3/AsCharTxBxTest.docx
new file mode 100755
index 0000000..7603b80
--- /dev/null
+++ b/sw/qa/extras/uiwriter/data3/AsCharTxBxTest.docx
Binary files differ
diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx
index e568e695..018a96a 100644
--- a/sw/qa/extras/uiwriter/uiwriter3.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter3.cxx
@@ -725,6 +725,58 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf134253)
CPPUNIT_ASSERT_EQUAL(6, getPages());
}
CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, TestAsCharTextBox)
{
// Releated tickets:
// tdf#138598 Replace vertical alignment of As_char textboxes in footer
// tdf#140158 Remove horizontal positioning of As_char textboxes, because
// the anchor moving does the same for it.
load(DATA_DIRECTORY, "AsCharTxBxTest.docx");
SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
CPPUNIT_ASSERT(pTextDoc);
// Add 3x tab to the doc
pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
Scheduler::ProcessEventsToIdle();
auto pExportDump = parseLayoutDump();
CPPUNIT_ASSERT(pExportDump);
// Check if the texbox fallen apart due to the tabs
const double nLeftSideOfShape1
= getXPath(pExportDump, "/root/page/body/txt/anchored/SwAnchoredDrawObject/bounds", "left")
.toDouble();
const double nLeftSideOfTxBx1
= getXPath(pExportDump, "/root/page/body/txt/anchored/fly/infos/bounds", "left").toDouble();
CPPUNIT_ASSERT(nLeftSideOfShape1 < nLeftSideOfTxBx1);
// Another test is for the tdf#138598: Check footer textbox
const double nLeftSideOfShape2
= getXPath(pExportDump, "/root/page[2]/footer/txt/anchored/SwAnchoredDrawObject/bounds",
"left")
.toDouble();
const double nLeftSideOfTxBx2
= getXPath(pExportDump, "/root/page[2]/footer/txt/anchored/fly/infos/bounds", "left")
.toDouble();
CPPUNIT_ASSERT(nLeftSideOfShape2 < nLeftSideOfTxBx2);
const double nTopSideOfShape2
= getXPath(pExportDump, "/root/page[2]/footer/txt/anchored/SwAnchoredDrawObject/bounds",
"top")
.toDouble();
const double nTopSideOfTxBx2
= getXPath(pExportDump, "/root/page[2]/footer/txt/anchored/fly/infos/bounds", "top")
.toDouble();
CPPUNIT_ASSERT(nTopSideOfShape2 < nTopSideOfTxBx2);
// Without the fix in place the two texboxes has been fallen apart, and asserts will broken.
}
CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf76636)
{
load(DATA_DIRECTORY, "tdf76636.doc");
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
index 4d44d1c..5257d44 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -33,6 +33,7 @@
#include <editeng/memberids.h>
#include <svx/svdoashp.hxx>
#include <svx/svdpage.hxx>
#include <svx/swframetypes.hxx>
#include <svl/itemiter.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <sal/log.hxx>
@@ -686,10 +687,39 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_u
if (aValue.get<text::TextContentAnchorType>()
== text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER)
{
xPropertySet->setPropertyValue(
UNO_NAME_ANCHOR_TYPE,
uno::makeAny(
text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER));
if (const auto aPos = pShape->GetAnchor().GetContentAnchor())
{
xPropertySet->setPropertyValue(
UNO_NAME_ANCHOR_TYPE,
uno::makeAny(text::TextContentAnchorType::
TextContentAnchorType_AT_CHARACTER));
xPropertySet->setPropertyValue(
UNO_NAME_HORI_ORIENT_RELATION,
uno::makeAny(text::RelOrientation::CHAR));
auto pAnch = pFormat->GetAnchor();
pAnch.SetAnchor(pShape->GetAnchor().GetContentAnchor());
tools::Rectangle aRect(getTextRectangle(pShape, false));
SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
aNewHOri.SetPos(aRect.getX());
SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());
aNewVOri.SetPos(aRect.getY());
pFormat->SetFormatAttr(pAnch);
// tdf#140598: Do not apply wrong rectangle position.
if (aRect.TopLeft() != Point(0, 0))
{
pFormat->SetFormatAttr(aNewHOri);
pFormat->SetFormatAttr(aNewVOri);
}
else
SAL_WARN("sw.core",
"SwTextBoxHelper::syncProperty: Repositioning failed!");
}
return;
}
else // Otherwise copy the anchor type of the shape
{
@@ -699,11 +729,55 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_u
if (aValue.get<text::TextContentAnchorType>()
== text::TextContentAnchorType::TextContentAnchorType_AT_PAGE)
{
xPropertySet->setPropertyValue(
UNO_NAME_ANCHOR_PAGE_NO,
uno::makeAny(pShape->GetAnchor().GetPageNum()));
if (pShape->GetAnchor().GetPageNum())
xPropertySet->setPropertyValue(
UNO_NAME_ANCHOR_PAGE_NO,
uno::makeAny(pShape->GetAnchor().GetPageNum()));
else
{
SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: Invalid Page Num!");
return;
}
}
// At-Content Anchors have to be synced:
if (aValue.get<text::TextContentAnchorType>()
== text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH
|| aValue.get<text::TextContentAnchorType>()
== text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER)
{
// If the shape has content...
if (auto aPos = pShape->GetAnchor().GetContentAnchor())
{
SwFormatAnchor aAnch(pFormat->GetAnchor());
// ...set it for the textframe too.
aAnch.SetAnchor(aPos);
pFormat->SetFormatAttr(aAnch);
}
else
SAL_WARN("sw.core",
"SwTextBoxHelper::syncProperty: Anchor without content!");
}
// And the repositioning:
if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
{
tools::Rectangle aRect(getTextRectangle(pShape, false));
// tdf#140598: Do not apply wrong rectangle position.
if (aRect.TopLeft() != Point(0, 0))
{
SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
aNewHOri.SetPos(aNewHOri.GetPos() + aRect.getX());
SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
aNewVOri.SetPos(aNewVOri.GetPos() + aRect.getY());
pFormat->SetFormatAttr(aNewHOri);
pFormat->SetFormatAttr(aNewVOri);
}
else
SAL_WARN("sw.core",
"SwTextBoxHelper::syncProperty: Repositioning failed!");
}
return;
}
break;
@@ -834,12 +908,41 @@ void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrame
}
}
text::TextContentAnchorType SwTextBoxHelper::mapAnchorType(const RndStdIds& rAnchorID)
{
text::TextContentAnchorType aAnchorType;
switch (rAnchorID)
{
case RndStdIds::FLY_AS_CHAR:
aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER;
break;
case RndStdIds::FLY_AT_CHAR:
aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER;
break;
case RndStdIds::FLY_AT_PARA:
aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
break;
case RndStdIds::FLY_AT_PAGE:
aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PAGE;
break;
case RndStdIds::FLY_AT_FLY:
aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_FRAME;
break;
default:
aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
SAL_WARN("sw.core", "SwTextBoxHelper::mapAnchorType: Unknown AnchorType!");
break;
}
return aAnchorType;
}
void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& rSet)
{
SwFrameFormat* pFormat = getOtherTextBoxFormat(&rShape, RES_DRAWFRMFMT);
if (!pFormat)
return;
const bool bInlineAnchored = rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
SfxItemSet aTextBoxSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange);
SfxItemIter aIter(rSet);
@@ -861,6 +964,13 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const&
{
case RES_VERT_ORIENT:
{
// The new position can be with anchor changing so sync it!
const text::TextContentAnchorType aNewAnchorType
= mapAnchorType(rShape.GetAnchor().GetAnchorId());
syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType));
if (bInlineAnchored)
return;
auto& rOrient = static_cast<const SwFormatVertOrient&>(*pItem);
SwFormatVertOrient aOrient(rOrient);
@@ -885,6 +995,8 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const&
case RES_HORI_ORIENT:
{
auto& rOrient = static_cast<const SwFormatHoriOrient&>(*pItem);
if (bInlineAnchored)
return;
SwFormatHoriOrient aOrient(rOrient);
tools::Rectangle aRect = getTextRectangle(&rShape, /*bAbsolute=*/false);
@@ -910,11 +1022,14 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const&
tools::Rectangle aRect = getTextRectangle(&rShape, /*bAbsolute=*/false);
if (!aRect.IsEmpty())
{
aVertOrient.SetPos(aVertOrient.GetPos() + aRect.getY());
aTextBoxSet.Put(aVertOrient);
if (!bInlineAnchored)
{
aVertOrient.SetPos(aVertOrient.GetPos() + aRect.getY());
aHoriOrient.SetPos(aHoriOrient.GetPos() + aRect.getX());
aHoriOrient.SetPos(aHoriOrient.GetPos() + aRect.getX());
aTextBoxSet.Put(aHoriOrient);
aTextBoxSet.Put(aVertOrient);
aTextBoxSet.Put(aHoriOrient);
}
aSize.SetWidth(aRect.getWidth());
aSize.SetHeight(aRect.getHeight());
diff --git a/sw/source/core/text/porfly.cxx b/sw/source/core/text/porfly.cxx
index 24961c0..cafa70c 100644
--- a/sw/source/core/text/porfly.cxx
+++ b/sw/source/core/text/porfly.cxx
@@ -352,20 +352,30 @@ void SwFlyCntPortion::SetBase( const SwTextFrame& rFrame, const Point &rBase,
// is relative to the print area of the anchor text frame.
tools::Rectangle aTextRectangle = SwTextBoxHelper::getTextRectangle(pShape);
SwFormatHoriOrient aHori(pTextBox->GetHoriOrient());
aHori.SetHoriOrient(css::text::HoriOrientation::NONE);
sal_Int32 nLeft = aTextRectangle.getX() - rFrame.getFrameArea().Left()
- rFrame.getFramePrintArea().Left();
aHori.SetPos(nLeft);
const auto aPos(pShape->GetAnchor().GetContentAnchor());
SwFormatVertOrient aVert(pTextBox->GetVertOrient());
aVert.SetVertOrient(css::text::VertOrientation::NONE);
sal_Int32 const nTop = aTextRectangle.getY() - rFrame.getFrameArea().Top()
- rFrame.getFramePrintArea().Top();
aVert.SetPos(nTop);
// tdf#138598 Replace vertical alignment of As_char textboxes in footer
// tdf#140158 Remove horizontal positioning of As_char textboxes, because
// the anchor moving does the same for it.
if (!aPos->nNode.GetNode().FindFooterStartNode())
{
aVert.SetVertOrient(css::text::VertOrientation::NONE);
sal_Int32 const nTop = aTextRectangle.getY() - rFrame.getFrameArea().Top()
- rFrame.getFramePrintArea().Top();
aVert.SetPos(nTop);
}
else
{
aVert.SetVertOrient(css::text::VertOrientation::NONE);
aVert.SetPos(SwTextBoxHelper::getTextRectangle(pShape, false).getY());
}
SwFormatAnchor aNewTxBxAnchor(pTextBox->GetAnchor());
aNewTxBxAnchor.SetAnchor(aPos);
pTextBox->LockModify();
pTextBox->SetFormatAttr(aHori);
pTextBox->SetFormatAttr(aNewTxBxAnchor);
pTextBox->SetFormatAttr(aVert);
pTextBox->UnlockModify();
}