From 9699d0f12bb7e3ee84a44090edcaf5a013267799 Mon Sep 17 00:00:00 2001 From: Mike Kaganski Date: Tue, 18 May 2021 06:47:38 +0300 Subject: Balance the text in columns on overflow Change-Id: Ia7ad030427bebba1dded5ba364559bed16263aa4 --- editeng/source/editeng/impedit.cxx | 62 ++++++------ editeng/source/editeng/impedit.hxx | 26 +++-- editeng/source/editeng/impedit2.cxx | 194 ++++++++++++++++++++++++++---------- editeng/source/editeng/impedit3.cxx | 52 ++++++---- 4 files changed, 222 insertions(+), 112 deletions(-) diff --git a/editeng/source/editeng/impedit.cxx b/editeng/source/editeng/impedit.cxx index a34a5ad37807..3797677c373a 100644 --- a/editeng/source/editeng/impedit.cxx +++ b/editeng/source/editeng/impedit.cxx @@ -521,46 +521,45 @@ void ImpEditView::DrawSelectionXOR( EditSelection aTmpSel, vcl::Region* pRegion, bool bStartHandleVisible = false; bool bEndHandleVisible = false; - auto f = [&, nStartLine = sal_Int32(0), nEndLine = sal_Int32(0)]( - ParaPortion& rPortion, sal_Int32 nPortion, EditLine* pLine, sal_Int32 nLine, - const tools::Rectangle& rArea, sal_Int32 nColumn) mutable { - if (!pLine) // Begin of ParaPortion + auto f = [&, nStartLine = sal_Int32(0), + nEndLine = sal_Int32(0)](const ImpEditEngine::LineAreaInfo& rInfo) mutable { + if (!rInfo.pLine) // Begin of ParaPortion { - if (nPortion < nStartPara) + if (rInfo.nPortion < nStartPara) return ImpEditEngine::CallbackResult::SkipThisPortion; - if (nPortion > nEndPara) + if (rInfo.nPortion > nEndPara) return ImpEditEngine::CallbackResult::Stop; - DBG_ASSERT(!rPortion.IsInvalid(), "Portion in Selection not formatted!"); - if (rPortion.IsInvalid()) + DBG_ASSERT(!rInfo.rPortion.IsInvalid(), "Portion in Selection not formatted!"); + if (rInfo.rPortion.IsInvalid()) return ImpEditEngine::CallbackResult::SkipThisPortion; - if (nPortion == nStartPara) - nStartLine = rPortion.GetLines().FindLine(aTmpSel.Min().GetIndex(), false); + if (rInfo.nPortion == nStartPara) + nStartLine = rInfo.rPortion.GetLines().FindLine(aTmpSel.Min().GetIndex(), false); else nStartLine = 0; - if (nPortion == nEndPara) - nEndLine = rPortion.GetLines().FindLine(aTmpSel.Max().GetIndex(), true); + if (rInfo.nPortion == nEndPara) + nEndLine = rInfo.rPortion.GetLines().FindLine(aTmpSel.Max().GetIndex(), true); else - nEndLine = rPortion.GetLines().Count() - 1; + nEndLine = rInfo.rPortion.GetLines().Count() - 1; } else // This is a correct ParaPortion { - if (nLine < nStartLine) + if (rInfo.nLine < nStartLine) return ImpEditEngine::CallbackResult::Continue; - if (nLine > nEndLine) + if (rInfo.nLine > nEndLine) return ImpEditEngine::CallbackResult::SkipThisPortion; bool bPartOfLine = false; - sal_Int32 nStartIndex = pLine->GetStart(); - sal_Int32 nEndIndex = pLine->GetEnd(); - if ((nPortion == nStartPara) && (nLine == nStartLine) + sal_Int32 nStartIndex = rInfo.pLine->GetStart(); + sal_Int32 nEndIndex = rInfo.pLine->GetEnd(); + if ((rInfo.nPortion == nStartPara) && (rInfo.nLine == nStartLine) && (nStartIndex != aTmpSel.Min().GetIndex())) { nStartIndex = aTmpSel.Min().GetIndex(); bPartOfLine = true; } - if ((nPortion == nEndPara) && (nLine == nEndLine) + if ((rInfo.nPortion == nEndPara) && (rInfo.nLine == nEndLine) && (nEndIndex != aTmpSel.Max().GetIndex())) { nEndIndex = aTmpSel.Max().GetIndex(); @@ -572,8 +571,8 @@ void ImpEditView::DrawSelectionXOR( EditSelection aTmpSel, vcl::Region* pRegion, nEndIndex = nStartIndex; tools::Rectangle aTmpRect(pEditEngine->pImpEditEngine->GetEditCursor( - &rPortion, pLine, nStartIndex, GetCursorFlags::NONE)); - aTmpRect.Move(0, pEditEngine->pImpEditEngine->getTopDirectionAware(rArea)); + &rInfo.rPortion, rInfo.pLine, nStartIndex, GetCursorFlags::NONE)); + aTmpRect.Move(0, pEditEngine->pImpEditEngine->getTopDirectionAware(rInfo.aArea)); // Only paint if in the visible range ... if (aTmpRect.Top() > GetVisDocBottom()) @@ -582,30 +581,32 @@ void ImpEditView::DrawSelectionXOR( EditSelection aTmpSel, vcl::Region* pRegion, if (aTmpRect.Bottom() < GetVisDocTop()) return ImpEditEngine::CallbackResult::Continue; - if ((nPortion == nStartPara) && (nLine == nStartLine)) + if ((rInfo.nPortion == nStartPara) && (rInfo.nLine == nStartLine)) bStartHandleVisible = true; - if ((nPortion == nEndPara) && (nLine == nEndLine)) + if ((rInfo.nPortion == nEndPara) && (rInfo.nLine == nEndLine)) bEndHandleVisible = true; // Now that we have Bidi, the first/last index doesn't have to be the 'most outside' position if (!bPartOfLine) { - Range aLineXPosStartEnd = pEditEngine->GetLineXPosStartEnd(&rPortion, pLine); + Range aLineXPosStartEnd + = pEditEngine->GetLineXPosStartEnd(&rInfo.rPortion, rInfo.pLine); aTmpRect.SetLeft(aLineXPosStartEnd.Min()); aTmpRect.SetRight(aLineXPosStartEnd.Max()); - aTmpRect.Move(pEditEngine->pImpEditEngine->getLeftDirectionAware(rArea), 0); - ImplDrawHighlightRect(rTarget, aTmpRect.TopLeft(), aTmpRect.BottomRight(), pPolyPoly.get()); + aTmpRect.Move(pEditEngine->pImpEditEngine->getLeftDirectionAware(rInfo.aArea), 0); + ImplDrawHighlightRect(rTarget, aTmpRect.TopLeft(), aTmpRect.BottomRight(), + pPolyPoly.get()); } else { sal_Int32 nTmpStartIndex = nStartIndex; sal_Int32 nWritingDirStart, nTmpEndIndex; const sal_Int32 nLeftOffset - = pEditEngine->pImpEditEngine->getLeftDirectionAware(rArea); + = pEditEngine->pImpEditEngine->getLeftDirectionAware(rInfo.aArea); while (nTmpStartIndex < nEndIndex) { - pEditEngine->pImpEditEngine->GetRightToLeft(nPortion, nTmpStartIndex + 1, + pEditEngine->pImpEditEngine->GetRightToLeft(rInfo.nPortion, nTmpStartIndex + 1, &nWritingDirStart, &nTmpEndIndex); if (nTmpEndIndex > nEndIndex) nTmpEndIndex = nEndIndex; @@ -613,8 +614,9 @@ void ImpEditView::DrawSelectionXOR( EditSelection aTmpSel, vcl::Region* pRegion, DBG_ASSERT(nTmpEndIndex > nTmpStartIndex, "DrawSelectionXOR, Start >= End?"); tools::Long nX1 - = pEditEngine->GetXPos(&rPortion, pLine, nTmpStartIndex, true); - tools::Long nX2 = pEditEngine->GetXPos(&rPortion, pLine, nTmpEndIndex); + = pEditEngine->GetXPos(&rInfo.rPortion, rInfo.pLine, nTmpStartIndex, true); + tools::Long nX2 + = pEditEngine->GetXPos(&rInfo.rPortion, rInfo.pLine, nTmpEndIndex); aTmpRect.SetLeft(std::min(nX1, nX2)); aTmpRect.SetRight(std::max(nX1, nX2)); diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index 639533d1c8b8..2ddf537d8e72 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -785,6 +785,8 @@ private: const ParaPortionList& GetParaPortions() const { return aParaPortionList; } ParaPortionList& GetParaPortions() { return aParaPortionList; } + tools::Long Calc1ColumnTextHeight(tools::Long* pHeightNTP); + protected: virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; @@ -1130,15 +1132,17 @@ public: SkipThisPortion, // Do not call callback until next portion Stop, // Stop iteration }; - using IterateLinesAreasFunc = std::function; - + struct LineAreaInfo + { + sal_Int32 nColumn; // Column number; when overflowing, equal to total number of columns + ParaPortion& rPortion; // Current ParaPortion + sal_Int32 nPortion; + EditLine* pLine = nullptr; // Current line, or nullptr for paragraph start + sal_Int32 nLine = 0; + tools::Rectangle aArea; // The area for the line (or for invisible rPortion) + tools::Long nHeightNeededToNotWrap; + }; + using IterateLinesAreasFunc = std::function; enum IterFlag // bitmask { none = 0, @@ -1150,7 +1154,7 @@ public: tools::Long GetColumnWidth(const Size& rPaperSize) const; Point MoveToNextLine(Point& rMovePos, tools::Long nLineHeight, sal_Int32& nColumn, - Point aOrigin) const; + Point aOrigin, tools::Long* pnHeightNeededToNotWrap = nullptr) const; tools::Long getXDirectionAware(const Point& pt) const; tools::Long getYDirectionAware(const Point& pt) const; @@ -1160,7 +1164,7 @@ public: void adjustYDirectionAware(Point& pt, tools::Long y) const; void setXDirectionAware(Point& pt, tools::Long x) const; void setYDirectionAware(Point& pt, tools::Long y) const; - bool isYOverflowDirectionAware(const Point& pt, const tools::Rectangle& rectMax) const; + tools::Long getYOverflowDirectionAware(const Point& pt, const tools::Rectangle& rectMax) const; bool isXOverflowDirectionAware(const Point& pt, const tools::Rectangle& rectMax) const; tools::Long getLeftDirectionAware(const tools::Rectangle& rect) const; tools::Long getRightDirectionAware(const tools::Rectangle& rect) const; diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx index 78b9f5de368f..49d904787ee4 100644 --- a/editeng/source/editeng/impedit2.cxx +++ b/editeng/source/editeng/impedit2.cxx @@ -59,12 +59,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -546,29 +548,29 @@ bool ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView ) const sal_Int32 nMaxPos = nMinPos + mpIMEInfos->nLen - 1; std::vector aRects(mpIMEInfos->nLen); - auto f = [&](ParaPortion& rPortion, sal_Int32 nPortion, const EditLine* pLine, - sal_Int32, const tools::Rectangle& rArea, sal_Int32) { - if (!pLine) // Start of ParaPortion + auto f = [&](const LineAreaInfo& rInfo) { + if (!rInfo.pLine) // Start of ParaPortion { - if (nPortion < nPortionPos) + if (rInfo.nPortion < nPortionPos) return CallbackResult::SkipThisPortion; - if (nPortion > nPortionPos) + if (rInfo.nPortion > nPortionPos) return CallbackResult::Stop; - assert(&rPortion == pParaPortion); + assert(&rInfo.rPortion == pParaPortion); } else // This is the needed ParaPortion { - if (pLine->GetStart() > nMaxPos) + if (rInfo.pLine->GetStart() > nMaxPos) return CallbackResult::Stop; - if (pLine->GetEnd() < nMinPos) + if (rInfo.pLine->GetEnd() < nMinPos) return CallbackResult::Continue; for (sal_Int32 n = nMinPos; n <= nMaxPos; ++n) { - if (pLine->IsIn(n)) + if (rInfo.pLine->IsIn(n)) { - tools::Rectangle aR - = GetEditCursor(pParaPortion, pLine, n, GetCursorFlags::NONE); - aR.Move(getLeftDirectionAware(rArea), getTopDirectionAware(rArea)); + tools::Rectangle aR = GetEditCursor(pParaPortion, rInfo.pLine, n, + GetCursorFlags::NONE); + aR.Move(getLeftDirectionAware(rInfo.aArea), + getTopDirectionAware(rInfo.aArea)); aRects[n - nMinPos] = pView->GetImpEditView()->GetWindowPos(aR); } } @@ -3074,22 +3076,20 @@ tools::Rectangle ImpEditEngine::PaMtoEditCursor( EditPaM aPaM, GetCursorFlags nF const EditLine* pLastLine = nullptr; tools::Rectangle aLineArea; - auto f = [&, bEOL(bool(nFlags & GetCursorFlags::EndOfLine))]( - ParaPortion& rPortion, sal_Int32, const EditLine* pLine, sal_Int32, - const tools::Rectangle& rArea, sal_Int32) { - if (!pLine) // start of ParaPortion + auto f = [&, bEOL(bool(nFlags & GetCursorFlags::EndOfLine))](const LineAreaInfo& rInfo) { + if (!rInfo.pLine) // start of ParaPortion { - ContentNode* pNode = rPortion.GetNode(); + ContentNode* pNode = rInfo.rPortion.GetNode(); OSL_ENSURE(pNode, "Invalid Node in Portion!"); if (pNode != aPaM.GetNode()) return CallbackResult::SkipThisPortion; - pPortion = &rPortion; + pPortion = &rInfo.rPortion; } else // guaranteed that this is the correct ParaPortion { - pLastLine = pLine; - aLineArea = rArea; - if ((pLine->GetStart() == nIndex) || (pLine->IsIn(nIndex, bEOL))) + pLastLine = rInfo.pLine; + aLineArea = rInfo.aArea; + if ((rInfo.pLine->GetStart() == nIndex) || (rInfo.pLine->IsIn(nIndex, bEOL))) return CallbackResult::Stop; } return CallbackResult::Continue; @@ -3125,7 +3125,8 @@ void ImpEditEngine::IterateLineAreas(const IterateLinesAreasFunc& f, IterFlag eO return; // First call to f() for ParaPortion (pLine is nullptr) - auto eResult = f(rPortion, n, nullptr, 0, {}, nColumn); + LineAreaInfo aInfo{nColumn, rPortion, n}; + auto eResult = f(aInfo); if (eResult == CallbackResult::Stop) return; bSkipThis = eResult == CallbackResult::SkipThisPortion; @@ -3143,7 +3144,8 @@ void ImpEditEngine::IterateLineAreas(const IterateLinesAreasFunc& f, IterFlag eO tools::Long nLineHeight = rLine.GetHeight(); if (nLine != nLines - 1) nLineHeight += nVertLineSpacing; - MoveToNextLine(aLineStart, nLineHeight, nColumn, aOrigin); + MoveToNextLine(aLineStart, nLineHeight, nColumn, aOrigin, + &aInfo.nHeightNeededToNotWrap); const bool bInclILS = eOptions & IterFlag::inclILS; if (bInclILS && (nLine != nLines - 1) && !aStatus.IsOutliner()) { @@ -3158,8 +3160,11 @@ void ImpEditEngine::IterateLineAreas(const IterateLinesAreasFunc& f, IterFlag eO adjustYDirectionAware(aOtherCorner, -nLineHeight); // Calls to f() for each line - eResult = f(rPortion, n, &rLine, nLine, - tools::Rectangle::Justify(aLineStart, aOtherCorner), nColumn); + aInfo.nColumn = nColumn; + aInfo.pLine = &rLine; + aInfo.nLine = nLine; + aInfo.aArea = tools::Rectangle::Justify(aLineStart, aOtherCorner); + eResult = f(aInfo); if (eResult == CallbackResult::Stop) return; bSkipThis = eResult == CallbackResult::SkipThisPortion; @@ -3191,16 +3196,15 @@ ImpEditEngine::GetPortionAndLine(Point aDocPos) const EditLine* pLastLine = nullptr; tools::Long nLineStartX = 0; - auto f = [&](ParaPortion& rPortion, sal_Int32, const EditLine* pLine, sal_Int32, - const tools::Rectangle& rArea, sal_Int32 nColumn) { - if (pLine) // Only handle lines, not ParaPortion starts + auto f = [&](const LineAreaInfo& rInfo) { + if (rInfo.pLine) // Only handle lines, not ParaPortion starts { - if (nColumn > nClickColumn) + if (rInfo.nColumn > nClickColumn) return CallbackResult::Stop; - pLastPortion = &rPortion; // Candidate paragraph - pLastLine = pLine; // Last visible line not later than click position - nLineStartX = getLeftDirectionAware(rArea); - if (nColumn == nClickColumn && getBottomDirectionAware(rArea) > aDocPos.Y()) + pLastPortion = &rInfo.rPortion; // Candidate paragraph + pLastLine = rInfo.pLine; // Last visible line not later than click position + nLineStartX = getLeftDirectionAware(rInfo.aArea); + if (rInfo.nColumn == nClickColumn && getBottomDirectionAware(rInfo.aArea) > aDocPos.Y()) return CallbackResult::Stop; // Found it } return CallbackResult::Continue; @@ -3384,33 +3388,117 @@ sal_uInt32 ImpEditEngine::GetTextHeightNTP() const return nCurTextHeightNTP; } -tools::Long ImpEditEngine::CalcTextHeight(tools::Long* pHeightNTP) +tools::Long ImpEditEngine::Calc1ColumnTextHeight(tools::Long* pHeightNTP) { - OSL_ENSURE( GetUpdateMode(), "Should not be used when Update=FALSE: CalcTextHeight" ); - tools::Long nY = 0; + tools::Long nHeight = 0; + // Pretend that we have ~infinite height to get total height + comphelper::ValueRestorationGuard aGuard(nCurTextHeight, + std::numeric_limits::max()); - auto f = [&, minY = tools::Long(0), lastCol = sal_Int32(0)]( - ParaPortion& rPortion, sal_Int32, const EditLine* pLine, sal_Int32, - const tools::Rectangle& rArea, sal_Int32 nColumn) mutable { - if (pLine) + auto f = [&](const LineAreaInfo& rInfo) { + if (rInfo.pLine) { - if (lastCol != nColumn) - minY = std::max(nY, minY); // total height can't be less than previous columns - lastCol = nColumn; - nY = std::max(getBottomDirectionAware(rArea), minY); - if (pHeightNTP) - { - if (rPortion.IsEmpty()) - - *pHeightNTP = std::max(*pHeightNTP, minY); - else - *pHeightNTP = nY; - } + nHeight = getBottomDirectionAware(rInfo.aArea) + 1; + if (pHeightNTP && !rInfo.rPortion.IsEmpty()) + *pHeightNTP = nHeight; } return CallbackResult::Continue; }; IterateLineAreas(f, IterFlag::none); - return nY; + return nHeight; +} + +tools::Long ImpEditEngine::CalcTextHeight(tools::Long* pHeightNTP) +{ + OSL_ENSURE( GetUpdateMode(), "Should not be used when Update=FALSE: CalcTextHeight" ); + + const tools::Long nMinHeight + = IsVertical() ? aMinAutoPaperSize.getWidth() : aMinAutoPaperSize.getHeight(); + + tools::Long nCurrentTextHeight = Calc1ColumnTextHeight(pHeightNTP); + if (mnColumns <= 1 || nCurrentTextHeight <= nMinHeight) + return nCurrentTextHeight; // All text fits into a single column - done! + + // The final column height can be smaller than total height divided by number of columns (taking + // into account first line offset and interline spacing, that aren't considered in positioning + // after the wrap). The wrap should only happen after the minimal height is exceeded. + tools::Long nTentativeColHeight = nMinHeight; + tools::Long nWantedIncrease = 0; + + // This does the necessary column balancing for the case when the text does not fit min height. + // When the height of column (taken from nCurTextHeight) is too small, the last column will + // owerflow, so the resulting height of the text will exceed the set column height. Increasing + // the column height step by step by the minimal value that allows one of columns to accomodate + // one line more, we finally get to the point where all the text fits. At each iteration, the + // height is only increased, so it's impossible to have infinite layout loops. The found value + // is the global minimum. + // + // E.g., given the following four line heights: + // Line 1: 10; + // Line 2: 12; + // Line 3: 10; + // Line 4: 10; + // number of columns 3, and the minimal paper height of 5, the iterations would be: + // * Tentative column height is set to 5 + // + // * Line 1 is attempted to go to column 0. Overflow is 5 => moved to column 1. + // * Line 2 is attempted to go to column 1 after Line 1; overflow is 17 => moved to column 2. + // * Line 3 is attempted to go to column 2 after Line 2; overflow is 17, stays in max column 2. + // * Line 4 goes to column 2 after Line 3. + // * Final iteration columns are: {empty}, {Line 1}, {Line 2, Line 3, Line 4} + // * Total text height is max({0, 10, 32}) == 32 > Tentative column height 5 => NEXT ITERATION + // * Minimal height increase that allows at least one column to accomodate one more line is + // min({5, 17, 17}) = 5. + // * Tentative column height is set to 5 + 5 = 10. + // + // * Line 1 goes to column 0, no overflow. + // * Line 2 is attempted to go to column 0 after Line 1; overflow is 12 => moved to column 1. + // * Line 3 is attempted to go to column 1 after Line 2; overflow is 12 => moved to column 2. + // * Line 4 is attempted to go to column 2 after Line 3; overflow is 10, stays in max column 2. + // * Final iteration columns are: {Line 1}, {Line 2}, {Line 3, Line 4} + // * Total text height is max({10, 12, 20}) == 20 > Tentative column height 10 => NEXT ITERATION + // * Minimal height increase that allows at least one column to accomodate one more line is + // min({12, 12, 10}) = 10. + // * Tentative column height is set to 10 + 10 == 20. + // + // * Line 1 goes to column 0, no overflow. + // * Line 2 is attempted to go to column 0 after Line 1; overflow is 2 => moved to column 1. + // * Line 3 is attempted to go to column 1 after Line 2; overflow is 2 => moved to column 2. + // * Line 4 is attempted to go to column 2 after Line 3; no overflow. + // * Final iteration columns are: {Line 1}, {Line 2}, {Line 3, Line 4} + // * Total text height is max({10, 12, 20}) == 20 == Tentative column height 20 => END. + do + { + nTentativeColHeight += nWantedIncrease; + nWantedIncrease = std::numeric_limits::max(); + nCurrentTextHeight = 0; + auto f = [&, minHeight = tools::Long(0), + lastCol = sal_Int32(0)](const LineAreaInfo& rInfo) mutable { + if (rInfo.pLine) + { + if (lastCol != rInfo.nColumn) + { + minHeight = std::max(nCurrentTextHeight, + minHeight); // total height can't be less than previous columns + nWantedIncrease = std::min(rInfo.nHeightNeededToNotWrap, nWantedIncrease); + } + lastCol = rInfo.nColumn; + nCurrentTextHeight = std::max(getBottomDirectionAware(rInfo.aArea) + 1, minHeight); + if (pHeightNTP) + { + if (rInfo.rPortion.IsEmpty()) + + *pHeightNTP = std::max(*pHeightNTP, minHeight); + else + *pHeightNTP = nCurrentTextHeight; + } + } + return CallbackResult::Continue; + }; + comphelper::ValueRestorationGuard aGuard(nCurTextHeight, nTentativeColHeight); + IterateLineAreas(f, IterFlag::none); + } while (nCurrentTextHeight > nTentativeColHeight && nWantedIncrease > 0); + return nCurrentTextHeight; } sal_Int32 ImpEditEngine::GetLineCount( sal_Int32 nParagraph ) const diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index 384faf41df8f..5a2a130934d0 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -3011,15 +3011,17 @@ void ImpEditEngine::setYDirectionAware(Point& pt, tools::Long y) const pt.setX(y); } -bool ImpEditEngine::isYOverflowDirectionAware(const Point& pt, const tools::Rectangle& rectMax) const +tools::Long ImpEditEngine::getYOverflowDirectionAware(const Point& pt, + const tools::Rectangle& rectMax) const { + tools::Long nRes; if (!IsVertical()) - return pt.Y() >= rectMax.Bottom(); - - if (IsTopToBottom()) - return pt.X() <= rectMax.Left(); + nRes = pt.Y() - rectMax.Bottom(); + else if (IsTopToBottom()) + nRes = rectMax.Left() - pt.X(); else - return pt.X() >= rectMax.Right(); + nRes = pt.X() - rectMax.Right(); + return std::max(nRes, tools::Long(0)); } bool ImpEditEngine::isXOverflowDirectionAware(const Point& pt, const tools::Rectangle& rectMax) const @@ -3082,7 +3084,8 @@ Point ImpEditEngine::MoveToNextLine( Point& rMovePos, // [in, out] Point that will move to the next line tools::Long nLineHeight, // [in] Y-direction move distance (direction-aware) sal_Int32& rColumn, // [in, out] current column number - Point aOrigin // [in] Origin point to calculate limits and initial Y position in a new column + Point aOrigin, // [in] Origin point to calculate limits and initial Y position in a new column + tools::Long* pnHeightNeededToNotWrap // On column wrap, returns how much more height is needed ) const { const Point aOld = rMovePos; @@ -3090,17 +3093,30 @@ Point ImpEditEngine::MoveToNextLine( // Move the point by the requested distance in Y direction adjustYDirectionAware(rMovePos, nLineHeight); // Check if the resulting position has moved beyond the limits, and more columns left. - // The limits are defined by a rectangle starting from aOrigin with size of aMinAutoPaperSize - if (isYOverflowDirectionAware(rMovePos, { aOrigin, aMinAutoPaperSize }) - && rColumn < mnColumns - 1) + // The limits are defined by a rectangle starting from aOrigin with width of aPaperSize + // and height of nCurTextHeight + Size aActPaperSize(aPaperSize); + if (IsVertical()) + aActPaperSize.setWidth(nCurTextHeight); + else + aActPaperSize.setHeight(nCurTextHeight); + tools::Long nNeeded = getYOverflowDirectionAware(rMovePos, { aOrigin, aActPaperSize }); + if (pnHeightNeededToNotWrap) + *pnHeightNeededToNotWrap = nNeeded; + if (nNeeded && rColumn < mnColumns) { ++rColumn; - // Set Y position of the point to that of aOrigin - setYDirectionAware(rMovePos, getYDirectionAware(aOrigin)); - // Move the point by the requested distance in Y direction - adjustYDirectionAware(rMovePos, nLineHeight); - // Move the point by the column+spacing distance in X direction - adjustXDirectionAware(rMovePos, GetColumnWidth(aPaperSize) + mnColumnSpacing); + // If we didn't fit into the last column, indicate that only by setting the column number + // to the total number of columns; do not adjust + if (rColumn < mnColumns) + { + // Set Y position of the point to that of aOrigin + setYDirectionAware(rMovePos, getYDirectionAware(aOrigin)); + // Move the point by the requested distance in Y direction + adjustYDirectionAware(rMovePos, nLineHeight); + // Move the point by the column+spacing distance in X direction + adjustXDirectionAware(rMovePos, GetColumnWidth(aPaperSize) + mnColumnSpacing); + } } return rMovePos - aOld; @@ -3808,7 +3824,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po } // no more visible actions? - if (isYOverflowDirectionAware(aStartPos, aClipRect)) + if (getYOverflowDirectionAware(aStartPos, aClipRect)) break; } @@ -3849,7 +3865,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po pPDFExtOutDevData->EndStructureElement(); // no more visible actions? - if (isYOverflowDirectionAware(aStartPos, aClipRect)) + if (getYOverflowDirectionAware(aStartPos, aClipRect)) break; } } -- cgit v1.2.3