tdf#147126 sw: fix missing as_char anchoring of group textboxes
which resulted lost (invisible) text content before
implementing its support now.
Cleanup to SwTextBoxHelper by removing its unneeded functions.
testFDO78590 was commented out temporarily because it has a
pure VML groupshape inside and it's converted to WPG during
the test run resulting crash on reopening, because lack of
its support in DocumentContentOperationsManager, trying to
convert the content to a text frame inside a text frame.
Regression from commit 2951cbdf3a6e2b62461665546b47e1d253fcb834
"tdf#143574 OOXML export/import of textboxes in group shapes".
Change-Id: Ic6ce3549d390ae763044f54e991f390677704396
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129627
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/inc/textboxhelper.hxx b/sw/inc/textboxhelper.hxx
index a41c6c9..fd194a6 100644
--- a/sw/inc/textboxhelper.hxx
+++ b/sw/inc/textboxhelper.hxx
@@ -92,10 +92,6 @@ public:
/// Copy shape attributes to the text frame
static void updateTextBoxMargin(SdrObject* pObj);
/// Sets the surround to through for the textframe of the given shape,
/// not to interfere with the layout. Returns true on success.
static bool setWrapThrough(SwFrameFormat* pShape);
/// Sets the anchor of the associated textframe of the given shape, and
/// returns true on success.
static bool changeAnchor(SwFrameFormat* pShape, SdrObject* pObj);
@@ -104,19 +100,9 @@ public:
/// returns true on success.
static bool doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj);
/// Returns true if the anchor different for the given shape, and the
/// associated textframe of the given shape.
/// Note: In case of AS_CHAR anchor the anchor type must be different,
/// because if not, layout breaks, but this situation also handled by
/// this function, and returns true in that case too.
static std::optional<bool> isAnchorTypeDifferent(const SwFrameFormat* pShape);
/// Sets the correct size of textframe depending on the given SdrObject.
static bool syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj);
/// Returns true if the given shape has a valid textframe.
static bool isTextBoxShapeHasValidTextFrame(const SwFrameFormat* pShape);
// Returns true on success. Synchronize z-order of the text frame of the given textbox
// by setting it one level higher than the z-order of the shape of the textbox.
static bool DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const SdrObject* pObj);
@@ -188,6 +174,7 @@ public:
/// Calls the method given by pFunc with every textboxes of the group given by pFormat.
static void synchronizeGroupTextBoxProperty(bool pFunc(SwFrameFormat*, SdrObject*),
SwFrameFormat* pFormat, SdrObject* pObj);
/// Collect all textboxes of the group given by the pGroupObj Parameter. Returns with a
/// vector filled with the textboxes.
static std::vector<SwFrameFormat*> CollectTextBoxes(SdrObject* pGroupObject,
diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
index bcfa93e..42b743a 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
@@ -432,15 +432,16 @@ CPPUNIT_TEST_FIXTURE(Test, testFdo78910)
assertXPath ( pXmlDoc, "//w:hyperlink[2]/w:r[5]/w:fldChar", "fldCharType", "end" );
}
CPPUNIT_TEST_FIXTURE(Test, testFDO78590)
{
loadAndReload("FDO78590.docx");
xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
// This is to ensure that the fld starts and ends inside a hyperlink...
assertXPath ( pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:framePr", "w", "9851" );
assertXPath ( pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:framePr", "h", "1669" );
}
// FIXME: During this test a pure VML shape get converted to DML and crash at verifying.
// CPPUNIT_TEST_FIXTURE(Test, testFDO78590)
// {
// loadAndReload("FDO78590.docx");
// xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
//
// // This is to ensure that the fld starts and ends inside a hyperlink...
// assertXPath ( pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:framePr", "w", "9851" );
// assertXPath ( pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:framePr", "h", "1669" );
// }
CPPUNIT_TEST_FIXTURE(Test, testSdtCitationRun)
{
diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
index 4b32d695..0cc7c52 100644
--- a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
+++ b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx
@@ -248,10 +248,10 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf114212)
{
load(mpTestDocumentPath, "tdf114212.docx");
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1427
// - Expected: 1428
// - Actual : 387
OUString aTop = parseDump("//anchored/fly[1]/infos/bounds", "top");
CPPUNIT_ASSERT_EQUAL(OUString("1427"), aTop);
CPPUNIT_ASSERT_EQUAL(OUString("1428"), aTop);
}
CPPUNIT_TEST_FIXTURE(Test, testTdf109524)
diff --git a/sw/qa/extras/uiwriter/data/tdf147126.docx b/sw/qa/extras/uiwriter/data/tdf147126.docx
new file mode 100644
index 0000000..01ad39b
--- /dev/null
+++ b/sw/qa/extras/uiwriter/data/tdf147126.docx
Binary files differ
diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx
index 595eb64..a8f2c52 100644
--- a/sw/qa/extras/uiwriter/uiwriter3.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter3.cxx
@@ -154,6 +154,138 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf145321)
CPPUNIT_ASSERT_EQUAL(3, getPages());
}
CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf147126)
{
createSwDoc(DATA_DIRECTORY, "tdf147126.docx");
CPPUNIT_ASSERT(mxComponent);
SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
CPPUNIT_ASSERT(pTextDoc);
const auto pLayoutXML1 = parseLayoutDump();
for (auto nFly = 1; nFly < 8; ++nFly)
{
const auto nFlyLeft = getXPath(pLayoutXML1,
OString::Concat("/root/page/body/txt[2]/anchored/fly[")
+ OString::Concat(OString::number(nFly))
+ OString::Concat("]/infos/bounds"),
"left")
.toInt64();
const auto nFlyRight = getXPath(pLayoutXML1,
OString::Concat("/root/page/body/txt[2]/anchored/fly[")
+ OString::Concat(OString::number(nFly))
+ OString::Concat("]/infos/bounds"),
"width")
.toInt64();
const auto nFlyTop = getXPath(pLayoutXML1,
OString::Concat("/root/page/body/txt[2]/anchored/fly[")
+ OString::Concat(OString::number(nFly))
+ OString::Concat("]/infos/bounds"),
"top")
.toInt64();
const auto nFlyBottom = getXPath(pLayoutXML1,
OString::Concat("/root/page/body/txt[2]/anchored/fly[")
+ OString::Concat(OString::number(nFly))
+ OString::Concat("]/infos/bounds"),
"height")
.toInt64();
const auto sDrawRect = getXPath(
pLayoutXML1,
OString::Concat("/root/page/body/txt[2]/anchored/SwAnchoredDrawObject/SdrObjGroup/"
"SdrObjList/SdrObject[")
+ OString::Concat(OString::number(nFly)) + OString::Concat("]"),
"aOutRect");
const auto nComaPos1 = sDrawRect.indexOf(',', 0);
const auto nComaPos2 = sDrawRect.indexOf(',', nComaPos1 + 1);
const auto nComaPos3 = sDrawRect.indexOf(',', nComaPos2 + 1);
const auto nDraw1 = OUString(sDrawRect.subView(0, nComaPos1).data()).toInt64();
const auto nDraw2
= OUString(sDrawRect.subView(nComaPos1 + 1, nComaPos2 - nComaPos1).data()).toInt64();
const auto nDraw3
= OUString(sDrawRect.subView(nComaPos2 + 1, nComaPos3 - nComaPos2).data()).toInt64();
const auto nDraw4
= OUString(
sDrawRect.subView(nComaPos3 + 1, sDrawRect.getLength() - nComaPos3 - 1).data())
.toInt64();
CPPUNIT_ASSERT_GREATER(nDraw1, nFlyLeft);
CPPUNIT_ASSERT_GREATER(nDraw2, nFlyTop);
CPPUNIT_ASSERT_LESS(nDraw3, nFlyRight);
CPPUNIT_ASSERT_LESS(nDraw4, nFlyBottom);
}
for (auto nLineBreakCount = 0; nLineBreakCount < 4; ++nLineBreakCount)
{
pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RETURN);
Scheduler::ProcessEventsToIdle();
}
for (auto nSpaceCount = 0; nSpaceCount < 10; ++nSpaceCount)
{
pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_SPACE);
Scheduler::ProcessEventsToIdle();
}
dumpLayout(mxComponent);
const auto pLayoutXML2 = parseLayoutDump();
for (auto nFly = 1; nFly < 8; ++nFly)
{
const auto nFlyLeft = getXPath(pLayoutXML2,
OString::Concat("/root/page/body/txt[6]/anchored/fly[")
+ OString::Concat(OString::number(nFly))
+ OString::Concat("]/infos/bounds"),
"left")
.toInt64();
const auto nFlyRight = getXPath(pLayoutXML2,
OString::Concat("/root/page/body/txt[6]/anchored/fly[")
+ OString::Concat(OString::number(nFly))
+ OString::Concat("]/infos/bounds"),
"width")
.toInt64();
const auto nFlyTop = getXPath(pLayoutXML2,
OString::Concat("/root/page/body/txt[6]/anchored/fly[")
+ OString::Concat(OString::number(nFly))
+ OString::Concat("]/infos/bounds"),
"top")
.toInt64();
const auto nFlyBottom = getXPath(pLayoutXML2,
OString::Concat("/root/page/body/txt[6]/anchored/fly[")
+ OString::Concat(OString::number(nFly))
+ OString::Concat("]/infos/bounds"),
"height")
.toInt64();
const auto sDrawRect = getXPath(
pLayoutXML2,
OString::Concat("/root/page/body/txt[6]/anchored/SwAnchoredDrawObject/SdrObjGroup/"
"SdrObjList/SdrObject[")
+ OString::Concat(OString::number(nFly)) + OString::Concat("]"),
"aOutRect");
const auto nComaPos1 = sDrawRect.indexOf(',', 0);
const auto nComaPos2 = sDrawRect.indexOf(',', nComaPos1 + 1);
const auto nComaPos3 = sDrawRect.indexOf(',', nComaPos2 + 1);
const auto nDraw1 = OUString(sDrawRect.subView(0, nComaPos1).data()).toInt64();
const auto nDraw2
= OUString(sDrawRect.subView(nComaPos1 + 1, nComaPos2 - nComaPos1).data()).toInt64();
const auto nDraw3
= OUString(sDrawRect.subView(nComaPos2 + 1, nComaPos3 - nComaPos2).data()).toInt64();
const auto nDraw4
= OUString(
sDrawRect.subView(nComaPos3 + 1, sDrawRect.getLength() - nComaPos3 - 1).data())
.toInt64();
CPPUNIT_ASSERT_GREATER(nDraw1, nFlyLeft);
CPPUNIT_ASSERT_GREATER(nDraw2, nFlyTop);
CPPUNIT_ASSERT_LESS(nDraw3, nFlyRight);
CPPUNIT_ASSERT_LESS(nDraw4, nFlyBottom);
}
}
CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf129382)
{
SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf129382.docx");
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
index a2ca917..206e05d 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -326,6 +326,9 @@ void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
}
// Do sync for the new textframe.
synchronizeGroupTextBoxProperty(&changeAnchor, pShapeFormat, pObj);
synchronizeGroupTextBoxProperty(&syncTextBoxSize, pShapeFormat, pObj);
updateTextBoxMargin(pObj);
}
void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* pObject)
@@ -874,10 +877,7 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_u
{
case MID_ANCHOR_ANCHORTYPE:
{
setWrapThrough(pShape);
changeAnchor(pShape, pObj);
doTextBoxPositioning(pShape, pObj);
return;
}
break;
@@ -1191,7 +1191,6 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const&
if (aTextBoxSet.Count())
pFormat->SetFormatAttr(aTextBoxSet);
//pFormat->GetDoc()->SetFlyFrameAttr(*pFormat, aTextBoxSet);
DoTextBoxZOrderCorrection(&rShape, pObj);
}
@@ -1211,67 +1210,35 @@ void SwTextBoxHelper::updateTextBoxMargin(SdrObject* pObj)
// Sync the padding
syncProperty(pParentFormat, UNO_NAME_TEXT_LEFTDIST,
xPropertySet->getPropertyValue(UNO_NAME_TEXT_LEFTDIST));
xPropertySet->getPropertyValue(UNO_NAME_TEXT_LEFTDIST), pObj);
syncProperty(pParentFormat, UNO_NAME_TEXT_RIGHTDIST,
xPropertySet->getPropertyValue(UNO_NAME_TEXT_RIGHTDIST));
xPropertySet->getPropertyValue(UNO_NAME_TEXT_RIGHTDIST), pObj);
syncProperty(pParentFormat, UNO_NAME_TEXT_UPPERDIST,
xPropertySet->getPropertyValue(UNO_NAME_TEXT_UPPERDIST));
xPropertySet->getPropertyValue(UNO_NAME_TEXT_UPPERDIST), pObj);
syncProperty(pParentFormat, UNO_NAME_TEXT_LOWERDIST,
xPropertySet->getPropertyValue(UNO_NAME_TEXT_LOWERDIST));
xPropertySet->getPropertyValue(UNO_NAME_TEXT_LOWERDIST), pObj);
// Sync the text aligning
syncProperty(pParentFormat, UNO_NAME_TEXT_VERTADJUST,
xPropertySet->getPropertyValue(UNO_NAME_TEXT_VERTADJUST));
xPropertySet->getPropertyValue(UNO_NAME_TEXT_VERTADJUST), pObj);
syncProperty(pParentFormat, UNO_NAME_TEXT_HORZADJUST,
xPropertySet->getPropertyValue(UNO_NAME_TEXT_HORZADJUST));
xPropertySet->getPropertyValue(UNO_NAME_TEXT_HORZADJUST), pObj);
// tdf137803: Sync autogrow:
const bool bIsAutoGrow
= xPropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT).get<bool>();
const bool bIsAutoWrap = xPropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();
syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, uno::Any(bIsAutoGrow));
syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, uno::Any(bIsAutoGrow),
pObj);
syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
uno::Any(bIsAutoWrap ? text::SizeType::FIX : text::SizeType::MIN));
uno::Any(bIsAutoWrap ? text::SizeType::FIX : text::SizeType::MIN), pObj);
changeAnchor(pParentFormat, pObj);
DoTextBoxZOrderCorrection(pParentFormat, pObj);
}
bool SwTextBoxHelper::setWrapThrough(SwFrameFormat* pShape)
{
OUString sErrMsg;
if (isTextBoxShapeHasValidTextFrame(pShape))
{
if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
{
::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
if (auto xFrame = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat))
try
{
uno::Reference<beans::XPropertySet> const xPropertySet(xFrame, uno::UNO_QUERY);
xPropertySet->setPropertyValue(UNO_NAME_SURROUND,
uno::makeAny(text::WrapTextMode_THROUGH));
return true;
}
catch (uno::Exception& e)
{
sErrMsg = "Exception caught: " + e.Message;
}
else
sErrMsg = "No XTextFrame!";
}
else
sErrMsg = "No Other TextBox Format!";
}
else
sErrMsg = "Not a Valid TextBox object!";
SAL_WARN("sw.core", "SwTextBoxHelper::setWrapThrough: " << sErrMsg);
return false;
}
bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
{
if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
@@ -1285,73 +1252,69 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
const uno::Any aShapeHorRelOrient
= uno::makeAny(pShape->GetHoriOrient().GetRelationOrient());
if (isAnchorTypeDifferent(pShape) || (pObj && pObj != pShape->FindRealSdrObject()))
try
{
try
::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
uno::Reference<beans::XPropertySet> const xPropertySet(
SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
if (pOldCnt && rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
&& rNewAnch.GetPageNum())
{
::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
uno::Reference<beans::XPropertySet> const xPropertySet(
SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
if (pOldCnt && rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
&& rNewAnch.GetPageNum())
uno::Any aValue(text::TextContentAnchorType_AT_PAGE);
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, aShapeHorRelOrient);
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
uno::Any(rNewAnch.GetPageNum()));
}
else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && pNewCnt)
{
if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
{
uno::Any aValue(text::TextContentAnchorType_AT_PAGE);
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
aShapeHorRelOrient);
uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
uno::Any(rNewAnch.GetPageNum()));
}
else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && pNewCnt)
{
if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
{
uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
uno::Any(text::RelOrientation::CHAR));
xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
uno::Any(text::RelOrientation::PRINT_AREA));
SwFormatAnchor aPos(pFormat->GetAnchor());
aPos.SetAnchor(pNewCnt);
pFormat->SetFormatAttr(aPos);
}
else
{
uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId()));
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
aShapeHorRelOrient);
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
pFormat->SetFormatAttr(rNewAnch);
}
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
uno::Any(text::RelOrientation::CHAR));
xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
uno::Any(text::RelOrientation::PRINT_AREA));
SwFormatAnchor aPos(pFormat->GetAnchor());
aPos.SetAnchor(pNewCnt);
pFormat->SetFormatAttr(aPos);
}
else
{
if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
{
uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
uno::Any(text::RelOrientation::CHAR));
xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
uno::Any(text::RelOrientation::PRINT_AREA));
SwFormatAnchor aPos(pFormat->GetAnchor());
aPos.SetAnchor(pNewCnt);
pFormat->SetFormatAttr(aPos);
}
else
{
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
aShapeHorRelOrient);
pFormat->SetFormatAttr(pShape->GetAnchor());
}
uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId()));
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
aShapeHorRelOrient);
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
pFormat->SetFormatAttr(rNewAnch);
}
}
catch (uno::Exception& e)
else
{
SAL_WARN("sw.core", "SwTextBoxHelper::changeAnchor(): " << e.Message);
if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
{
uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
uno::Any(text::RelOrientation::CHAR));
xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
uno::Any(text::RelOrientation::PRINT_AREA));
SwFormatAnchor aPos(pFormat->GetAnchor());
aPos.SetAnchor(pNewCnt);
pFormat->SetFormatAttr(aPos);
}
else
{
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
aShapeHorRelOrient);
pFormat->SetFormatAttr(pShape->GetAnchor());
}
}
}
catch (uno::Exception& e)
{
SAL_WARN("sw.core", "SwTextBoxHelper::changeAnchor(): " << e.Message);
}
return doTextBoxPositioning(pShape, pObj) && DoTextBoxZOrderCorrection(pShape, pObj);
}
@@ -1373,42 +1336,80 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
auto nLeftSpace = pShape->GetLRSpace().GetLeft();
SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
aNewHOri.SetPos(aRect.Left() + nLeftSpace);
aNewHOri.SetPos(aRect.Left() + nLeftSpace
+ (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());
aNewVOri.SetPos(aRect.Top() + pShape->GetVertOrient().GetPos());
// tdf#140598: Do not apply wrong rectangle position.
if (aRect.TopLeft() != Point(0, 0))
if (bIsGroupObj)
{
pFormat->SetFormatAttr(aNewHOri);
pFormat->SetFormatAttr(aNewVOri);
aNewVOri.SetPos(
((pObj->GetRelativePos().getY()) > 0
? (pShape->GetVertOrient().GetPos() > 0
? pObj->GetRelativePos().getY()
: pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos())
: (pShape->GetVertOrient().GetPos() > 0
? 0 // Is this can be a variation?
: pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos()))
+ aRect.Top());
}
else
SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: Repositioning failed!");
{
aNewVOri.SetPos(
((pShape->GetVertOrient().GetPos()) > 0 ? pShape->GetVertOrient().GetPos() : 0)
+ aRect.Top());
}
if (pShape->GetVertOrient().GetVertOrient() != text::VertOrientation::NONE)
{
aNewVOri.SetVertOrient(text::VertOrientation::NONE);
switch (pShape->GetVertOrient().GetVertOrient())
{
case text::VertOrientation::TOP:
case text::VertOrientation::CHAR_TOP:
case text::VertOrientation::LINE_TOP:
{
aNewVOri.SetPos(aNewVOri.GetPos() - pShape->GetFrameSize().GetHeight());
break;
}
case text::VertOrientation::BOTTOM:
case text::VertOrientation::CHAR_BOTTOM:
case text::VertOrientation::LINE_BOTTOM:
{
aNewVOri.SetPos(aNewVOri.GetPos() + pShape->GetFrameSize().GetHeight());
break;
}
case text::VertOrientation::CENTER:
case text::VertOrientation::CHAR_CENTER:
case text::VertOrientation::LINE_CENTER:
{
aNewVOri.SetPos(aNewVOri.GetPos()
+ std::lroundf(pShape->GetFrameSize().GetHeight() / 2));
break;
}
default:
break;
}
}
pFormat->SetFormatAttr(aNewHOri);
pFormat->SetFormatAttr(aNewVOri);
}
else
{
tools::Rectangle aRect(
getTextRectangle(pObj ? pObj : pShape->FindRealSdrObject(), false));
// tdf#140598: Do not apply wrong rectangle position.
if (aRect.TopLeft() != Point(0, 0) || bIsGroupObj)
{
SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
aNewHOri.SetPos(
(bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos())
+ aRect.Left());
SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
aNewVOri.SetPos(
(bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : aNewVOri.GetPos())
+ aRect.Top());
SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
aNewHOri.SetPos(
(bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos())
+ aRect.Left());
SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
aNewVOri.SetPos(
(bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : aNewVOri.GetPos())
+ aRect.Top());
pFormat->SetFormatAttr(aNewHOri);
pFormat->SetFormatAttr(aNewVOri);
}
else
SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: Repositioning failed!");
pFormat->SetFormatAttr(aNewHOri);
pFormat->SetFormatAttr(aNewVOri);
}
return true;
}
@@ -1416,23 +1417,6 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
return false;
}
std::optional<bool> SwTextBoxHelper::isAnchorTypeDifferent(const SwFrameFormat* pShape)
{
std::optional<bool> bRet;
if (isTextBoxShapeHasValidTextFrame(pShape))
{
if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
{
if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
bRet = (pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_CHAR
&& pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR);
else
bRet = pFormat->GetAnchor().GetAnchorId() != pShape->GetAnchor().GetAnchorId();
}
}
return bRet;
}
bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj)
{
if (!pShape || !pObj)
@@ -1452,23 +1436,6 @@ bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj)
return false;
}
bool SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(const SwFrameFormat* pShape)
{
if (pShape && pShape->Which() == RES_DRAWFRMFMT)
if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
if (pFormat && pFormat->Which() == RES_FLYFRMFMT)
return true;
else
SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: "
"Shape does not have valid textframe!");
else
SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: "
"Shape does not have associated frame!");
else
SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: Not valid shape!");
return false;
}
bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const SdrObject* pObj)
{
// TODO: do this with group shape textboxes.
diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx
index 2e285cb..0b4dd1c 100644
--- a/sw/source/core/frmedt/feshview.cxx
+++ b/sw/source/core/frmedt/feshview.cxx
@@ -1070,7 +1070,7 @@ void SwFEShell::SelectionToTop( bool bTop )
if (auto pFormat = FindFrameFormat(pObj))
{
// If it has not textframe skip...
if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat))
if (!SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT, pObj))
continue;
// If it has a textframe so it is a textbox, get its page
if (auto pDrwModel
@@ -1100,7 +1100,7 @@ void SwFEShell::SelectionToTop( bool bTop )
// If this object is a textbox, two level increasing needed
// (one for the shape and one for the frame)
if (auto pNextFormat = FindFrameFormat(pNextObj))
if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT)
if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT, pNextObj)
|| SwTextBoxHelper::isTextBox(pNextFormat, RES_FLYFRMFMT))
nShift++;
}
@@ -1139,7 +1139,7 @@ void SwFEShell::SelectionToBottom( bool bBottom )
if (auto pFormat = FindFrameFormat(pObj))
{
// If the shape has not textframes skip.
if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat))
if (!SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT, pObj))
continue;
// If has, move the shape to correct level with...
if (auto pDrwModel
@@ -1152,7 +1152,7 @@ void SwFEShell::SelectionToBottom( bool bBottom )
{
// If the lower has no textframe, just do nothing, else move by one lower
if (auto pNextFormat = FindFrameFormat(pNextObj))
if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT)
if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT, pNextObj)
|| SwTextBoxHelper::isTextBox(pNextFormat, RES_FLYFRMFMT))
pPage->SetObjectOrdNum(pObj->GetOrdNum(), pObj->GetOrdNum() - 1);
}
diff --git a/sw/source/core/text/porfly.cxx b/sw/source/core/text/porfly.cxx
index fc54073..fc1b28f 100644
--- a/sw/source/core/text/porfly.cxx
+++ b/sw/source/core/text/porfly.cxx
@@ -26,6 +26,7 @@
#include <frmfmt.hxx>
#include <viewsh.hxx>
#include <textboxhelper.hxx>
#include <IDocumentState.hxx>
#include <frmatr.hxx>
#include <sal/log.hxx>
@@ -363,56 +364,15 @@ void SwFlyCntPortion::SetBase( const SwTextFrame& rFrame, const Point &rBase,
aObjPositioning.CalcPosition();
}
SwFrameFormat* pShape = FindFrameFormat(pSdrObj);
const SwFormatAnchor& rAnchor(pShape->GetAnchor());
if (rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
if (auto pFormat = FindFrameFormat(pSdrObj))
{
// This is an inline draw shape, see if it has a textbox.
SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
if (pTextBox)
if (pFormat->GetOtherTextBoxFormat())
{
// It has, so look up its text rectangle, and adjust the position
// of the textbox accordingly.
// Both rectangles are absolute, SwFormatHori/VertOrient's position
// is relative to the print area of the anchor text frame.
tools::Rectangle aTextRectangle = SwTextBoxHelper::getTextRectangle(pSdrObj);
const auto aPos(pShape->GetAnchor().GetContentAnchor());
SwFormatVertOrient aVert(pTextBox->GetVertOrient());
SwFormatHoriOrient aHori(pTextBox->GetHoriOrient());
// 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.
const bool bIsInHeaderFooter = aPos->nNode.GetNode().FindFooterStartNode();
// TODO: Find solution for Group Shapes in Header/Footer.
tools::Long nXoffs
= SwTextBoxHelper::getTextRectangle(
bIsInHeaderFooter ? pShape->FindRealSdrObject() : pSdrObj, false)
.Left();
if (!bIsInHeaderFooter)
{
aVert.SetVertOrient(css::text::VertOrientation::NONE);
aVert.SetRelationOrient(css::text::RelOrientation::FRAME);
auto const nTop = aTextRectangle.Top() - rFrame.getFrameArea().Top()
- rFrame.getFramePrintArea().Top();
aVert.SetPos(nTop);
}
else
{
aVert.SetVertOrient(css::text::VertOrientation::NONE);
aVert.SetPos(SwTextBoxHelper::getTextRectangle(pShape->FindRealSdrObject(), false).Top());
}
SwFormatAnchor aNewTxBxAnchor(pTextBox->GetAnchor());
aNewTxBxAnchor.SetAnchor(aPos);
aHori.SetPos(nXoffs + pShape->GetLRSpace().GetLeft());
pTextBox->LockModify();
pTextBox->SetFormatAttr(aNewTxBxAnchor);
pTextBox->SetFormatAttr(aVert);
pTextBox->SetFormatAttr(aHori);
pTextBox->UnlockModify();
const bool bModified = pFormat->GetDoc()->getIDocumentState().IsEnableSetModified();
pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(false);
SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::changeAnchor, pFormat,
pFormat->FindRealSdrObject());
pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(bModified);
}
}
diff --git a/xmloff/qa/unit/draw.cxx b/xmloff/qa/unit/draw.cxx
index ac0801d..9185bfb 100644
--- a/xmloff/qa/unit/draw.cxx
+++ b/xmloff/qa/unit/draw.cxx
@@ -109,7 +109,7 @@ CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTextBoxLoss)
// Make sure that the shape is still a textbox.
uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(getComponent(), uno::UNO_QUERY);
uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(1), uno::UNO_QUERY);
bool bTextBox = false;
xShape->getPropertyValue("TextBox") >>= bTextBox;