editeng/source/editeng/EditLine.cxx | 8 +- oox/source/drawingml/textbodyproperties.cxx | 20 +++++ sd/qa/unit/data/pptx/tdf165732.pptx |binary sd/qa/unit/import-tests.cxx | 29 ++++++++ svx/source/sdr/primitive2d/sdrdecompositiontools.cxx | 64 ++++++++++--------- 5 files changed, 90 insertions(+), 31 deletions(-)
New commits: commit f54fc10ea8fb4c89412239be79bf8f4b40b525f4 Author: Samuel Mehrbrodt <[email protected]> AuthorDate: Thu Feb 19 14:28:28 2026 +0100 Commit: Adolfo Jayme Barrientos <[email protected]> CommitDate: Wed Feb 25 15:13:36 2026 +0100 tdf#165732 PPTX: Fix misaligned text in small text boxes Make sure to clamp the inset (left+right) in TextBodyProperties::pushTextDistances() as it was already done for top+bottom. This fixes most alignment issues from the bug documents. Additionally, allow negative values in EditLine::SetStartPosX since the offset might be negative to have centered text. This fixes the alignment of number "1" in the fourcenter-left-right.pptx bug document which was still misaligned after the above mentioned fix. Also improve the fix made in commit c0693ef135f4654d69451fc9ca5a735931aebde2 to clip the inset on the drawinglayer level on all four sides. This also helps to correctly display the example files when they are saved to odp format (in which case the bug would reappear). In contrast to pptx, odp does not specify what should happen in this case, so clipping on import does not seem appropriate here. Change-Id: I33e5be1123becef17f84427649d1f2cdbfe2d7e7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199709 Reviewed-by: Samuel Mehrbrodt <[email protected]> Tested-by: Jenkins (cherry picked from commit 9822795072ca855dcebc518dcb211d2389ba49f9) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199785 Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/editeng/source/editeng/EditLine.cxx b/editeng/source/editeng/EditLine.cxx index 7dbd121e26e4..0b8de5b5b17a 100644 --- a/editeng/source/editeng/EditLine.cxx +++ b/editeng/source/editeng/EditLine.cxx @@ -88,10 +88,10 @@ void EditLine::SetHeight(sal_uInt16 nHeight, sal_uInt16 nTextHeight) void EditLine::SetStartPosX(sal_Int32 nStart) { - if (nStart > 0) - mnStartPosX = nStart; - else - mnStartPosX = 0; + // tdf#165732 When centered text overflows its container (text width > paper width), + // the centering offset (paperWidth - textWidth) / 2 is negative. + // So we must not clamp the start pos here. + mnStartPosX = nStart; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/drawingml/textbodyproperties.cxx b/oox/source/drawingml/textbodyproperties.cxx index 25a1f0ced330..2d88147052f3 100644 --- a/oox/source/drawingml/textbodyproperties.cxx +++ b/oox/source/drawingml/textbodyproperties.cxx @@ -128,6 +128,26 @@ void TextBodyProperties::pushTextDistances(Size const& rTextAreaSize) nOff = (nOff + 1) % aProps.size(); } + // Check if left and right are set + if (maTextDistanceValues[0] && maTextDistanceValues[2]) + { + double nWidth = rTextAreaSize.getWidth(); + + double nLeft = *maTextDistanceValues[0]; + double nRight = *maTextDistanceValues[2]; + + // Check if left + right is more than text area width. + // If yes, we need to adjust the values as defined in OOXML. + // (Overload zero width to mean don't adjust) + if (nWidth > 0 && nLeft + nRight >= nWidth) + { + double diffFactor = (nLeft + nRight - nWidth) / 2.0; + + maTextDistanceValues[0] = nLeft - diffFactor; + maTextDistanceValues[2] = nRight - diffFactor; + } + } + // Check if bottom and top are set if (maTextDistanceValues[1] && maTextDistanceValues[3]) { diff --git a/sd/qa/unit/data/pptx/tdf165732.pptx b/sd/qa/unit/data/pptx/tdf165732.pptx new file mode 100644 index 000000000000..05598e30bfb1 Binary files /dev/null and b/sd/qa/unit/data/pptx/tdf165732.pptx differ diff --git a/sd/qa/unit/import-tests.cxx b/sd/qa/unit/import-tests.cxx index f47bdc1b730c..bafca667088e 100644 --- a/sd/qa/unit/import-tests.cxx +++ b/sd/qa/unit/import-tests.cxx @@ -1969,6 +1969,35 @@ CPPUNIT_TEST_FIXTURE(SdImportTest, testTdf93830) CPPUNIT_ASSERT_EQUAL(sal_Int32(4024), nTextLeftDistance); } +CPPUNIT_TEST_FIXTURE(SdImportTest, testTdf165732) +{ + // Text insets larger than shape dimension should be clamped symmetrically. + createSdImpressDoc("pptx/tdf165732.pptx"); + uno::Reference<drawing::XDrawPage> xPage(getPage(0)); + + // Shape "2": each side reduced by 1: 200 -> 199. + uno::Reference<beans::XPropertySet> xShape0(xPage->getByIndex(0), uno::UNO_QUERY); + sal_Int32 nLeft = 0, nRight = 0; + xShape0->getPropertyValue(u"TextLeftDistance"_ustr) >>= nLeft; + xShape0->getPropertyValue(u"TextRightDistance"_ustr) >>= nRight; + CPPUNIT_ASSERT_EQUAL(sal_Int32(199), nLeft); + CPPUNIT_ASSERT_EQUAL(sal_Int32(199), nRight); + + // Shape "1": no clamping. + uno::Reference<beans::XPropertySet> xShape1(xPage->getByIndex(1), uno::UNO_QUERY); + sal_Int32 nLeft1 = 0; + xShape1->getPropertyValue(u"TextLeftDistance"_ustr) >>= nLeft1; + CPPUNIT_ASSERT_EQUAL(sal_Int32(100), nLeft1); + + // Shape "5": top and bottom reduced by 17.5: 200 -> 183 (rounded). + uno::Reference<beans::XPropertySet> xShape7(xPage->getByIndex(7), uno::UNO_QUERY); + sal_Int32 nUpper = 0, nLower = 0; + xShape7->getPropertyValue(u"TextUpperDistance"_ustr) >>= nUpper; + xShape7->getPropertyValue(u"TextLowerDistance"_ustr) >>= nLower; + CPPUNIT_ASSERT_EQUAL(sal_Int32(183), nUpper); + CPPUNIT_ASSERT_EQUAL(sal_Int32(183), nLower); +} + CPPUNIT_TEST_FIXTURE(SdImportTest, testTdf127129) { createSdImpressDoc("pptx/tdf127129.pptx"); diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx index b0237633303f..1317a8f138c9 100644 --- a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx +++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx @@ -108,34 +108,49 @@ basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText, const OutlinerParaObject& rOutlinerParaObj = rText.getOutlinerParaObject(); const bool bVerticalWriting(rOutlinerParaObj.IsEffectivelyVertical()); const double fWidthForText = bVerticalWriting ? rSnapRange.getHeight() : rSnapRange.getWidth(); + const double fHeightForText = bVerticalWriting ? rSnapRange.getWidth() : rSnapRange.getHeight(); // create a range describing the wanted text position and size (aTextAnchorRange). This // means to use the text distance values here - // If the margin is larger than the entire width of the text area, then limit the - // margin. - const double fTextLeftDistance - = std::min(static_cast<double>(rText.getTextLeftDistance()), fWidthForText); - const double nTextRightDistance - = std::min(static_cast<double>(rText.getTextRightDistance()), fWidthForText); + // If left + right margins exceed the available width, reduce each by half the excess. + double fTextLeftDistance = static_cast<double>(rText.getTextLeftDistance()); + double fTextRightDistance = static_cast<double>(rText.getTextRightDistance()); + if (fWidthForText > 0 && fTextLeftDistance + fTextRightDistance >= fWidthForText) + { + const double diffFactor = (fTextLeftDistance + fTextRightDistance - fWidthForText) / 2.0; + fTextLeftDistance -= diffFactor; + fTextRightDistance -= diffFactor; + } + // If top + bottom margins exceed the available height, reduce each by half the excess. + // Guard on fWidthForText > 0 for consistency with the horizontal check: skip for + // degenerate zero-width shapes where the zero-width expansion handles layout instead. + double fTextUpperDistance = static_cast<double>(rText.getTextUpperDistance()); + double fTextLowerDistance = static_cast<double>(rText.getTextLowerDistance()); + if (fWidthForText > 0 && fHeightForText > 0 && fTextUpperDistance + fTextLowerDistance >= fHeightForText) + { + const double diffFactor = (fTextUpperDistance + fTextLowerDistance - fHeightForText) / 2.0; + fTextUpperDistance -= diffFactor; + fTextLowerDistance -= diffFactor; + } double fDistanceForTextL, fDistanceForTextT, fDistanceForTextR, fDistanceForTextB; if (!bVerticalWriting) { fDistanceForTextL = fTextLeftDistance; - fDistanceForTextT = rText.getTextUpperDistance(); - fDistanceForTextR = nTextRightDistance; - fDistanceForTextB = rText.getTextLowerDistance(); + fDistanceForTextT = fTextUpperDistance; + fDistanceForTextR = fTextRightDistance; + fDistanceForTextB = fTextLowerDistance; } else if (rOutlinerParaObj.IsTopToBottom()) { - fDistanceForTextL = rText.getTextLowerDistance(); + fDistanceForTextL = fTextLowerDistance; fDistanceForTextT = fTextLeftDistance; - fDistanceForTextR = rText.getTextUpperDistance(); - fDistanceForTextB = nTextRightDistance; + fDistanceForTextR = fTextUpperDistance; + fDistanceForTextB = fTextRightDistance; } else { - fDistanceForTextL = rText.getTextUpperDistance(); - fDistanceForTextT = nTextRightDistance; - fDistanceForTextR = rText.getTextLowerDistance(); + fDistanceForTextL = fTextUpperDistance; + fDistanceForTextT = fTextRightDistance; + fDistanceForTextR = fTextLowerDistance; fDistanceForTextB = fTextLeftDistance; } const basegfx::B2DPoint aTopLeft(rSnapRange.getMinX() + fDistanceForTextL, @@ -143,27 +158,22 @@ basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText, const basegfx::B2DPoint aBottomRight(rSnapRange.getMaxX() - fDistanceForTextR, rSnapRange.getMaxY() - fDistanceForTextB); basegfx::B2DRange aAnchorRange; - - // tdf#165732 Margins are too large in some cases (left > right or top > bottom) - // - in this case do not expand the range - bool bInvalidMargins - = (aTopLeft.getX() >= aBottomRight.getX() || aTopLeft.getY() >= aBottomRight.getY()); - if (!bInvalidMargins) - { - aAnchorRange.expand(aTopLeft); - aAnchorRange.expand(aBottomRight); - } + aAnchorRange.expand(aTopLeft); + aAnchorRange.expand(aBottomRight); // If the shape has no width, then don't attempt to break the text into multiple // lines, not a single character would satisfy a zero width requirement. // SdrTextObj::impDecomposeBlockTextPrimitive() uses the same constant to // effectively set no limits. - if (!bVerticalWriting && aAnchorRange.getWidth() == 0) + // Also check fWidthForText == 0: a zero-width shape whose margins invert the + // anchor range gets a non-zero anchor width after B2DRange normalization, so + // the getWidth() == 0 check would not trigger without this second condition. + if (!bVerticalWriting && (fWidthForText == 0 || aAnchorRange.getWidth() == 0)) { aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX() - 1000000, aTopLeft.getY())); aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX() + 1000000, aBottomRight.getY())); } - else if (bVerticalWriting && aAnchorRange.getHeight() == 0) + else if (bVerticalWriting && (fWidthForText == 0 || aAnchorRange.getHeight() == 0)) { aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX(), aTopLeft.getY() - 1000000)); aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX(), aBottomRight.getY() + 1000000));
