summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Kaganski <mike.kaganski@collabora.com>2021-05-18 06:47:38 +0300
committerMike Kaganski <mike.kaganski@collabora.com>2021-05-20 10:29:15 +0300
commit9699d0f12bb7e3ee84a44090edcaf5a013267799 (patch)
tree5d05e09637cda711c986e7db4559b113f173fd10
parent610785d6a93b86c60aca20c07f3e872873ff6117 (diff)
Balance the text in columns on overflowprivate/mikekaganski/multicolumn
Change-Id: Ia7ad030427bebba1dded5ba364559bed16263aa4
-rw-r--r--editeng/source/editeng/impedit.cxx62
-rw-r--r--editeng/source/editeng/impedit.hxx26
-rw-r--r--editeng/source/editeng/impedit2.cxx194
-rw-r--r--editeng/source/editeng/impedit3.cxx52
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<CallbackResult(
- ParaPortion& /*rPortion*/, // Current ParaPortion
- sal_Int32 /*nPortion*/,
- EditLine* /*pLine*/, // Current line, or nullptr for paragraph start
- sal_Int32 /*nLine*/,
- const tools::Rectangle& /*rArea*/, // The area for the line (or for invisible rPortion)
- sal_Int32 /*nColumn*/ // Column number (only valid for EditLine*)
- )>;
-
+ 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<CallbackResult(const LineAreaInfo&)>;
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 <svl/asiancfg.hxx>
#include <i18nutil/unicode.hxx>
#include <tools/diagnose_ex.h>
+#include <comphelper/flagguard.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/processfactory.hxx>
#include <unotools/configmgr.hxx>
#include <unicode/ubidi.h>
#include <algorithm>
+#include <limits>
#include <memory>
#include <string_view>
#include <fstream>
@@ -546,29 +548,29 @@ bool ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView )
const sal_Int32 nMaxPos = nMinPos + mpIMEInfos->nLen - 1;
std::vector<tools::Rectangle> 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<tools::Long>::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
+ // <ITERATION 1>
+ // * 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.
+ // <ITERATION 2>
+ // * 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.
+ // <ITERATION 3>
+ // * 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<tools::Long>::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;
}
}