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 c0693ef135
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 <samuel.mehrbrodt@collabora.com>
Tested-by: Jenkins
This commit is contained in:
@@ -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: */
|
||||
|
||||
@@ -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])
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -2017,6 +2017,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");
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user