summaryrefslogtreecommitdiff
path: root/sw/source/core/text/porlay.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/text/porlay.cxx')
-rw-r--r--sw/source/core/text/porlay.cxx757
1 files changed, 494 insertions, 263 deletions
diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx
index 3d26e990c50c..50aaa43b7745 100644
--- a/sw/source/core/text/porlay.cxx
+++ b/sw/source/core/text/porlay.cxx
@@ -33,7 +33,7 @@
#include <unicode/uchar.h>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
-#include <com/sun/star/i18n/CTLScriptType.hpp>
+#include <com/sun/star/i18n/UnicodeType.hpp>
#include <com/sun/star/i18n/WordType.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <paratr.hxx>
@@ -41,58 +41,93 @@
#include <optional>
#include <editeng/adjustitem.hxx>
#include <editeng/charhiddenitem.hxx>
+#include <editeng/escapementitem.hxx>
#include <svl/asiancfg.hxx>
#include <svl/languageoptions.hxx>
#include <tools/multisel.hxx>
#include <unotools/charclass.hxx>
#include <charfmt.hxx>
#include <docary.hxx>
+#include <fmtanchr.hxx>
#include <redline.hxx>
#include <calbck.hxx>
#include <doc.hxx>
#include <swscanner.hxx>
#include <txatbase.hxx>
-#include <calc.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentContentOperations.hxx>
-#include <IDocumentFieldsAccess.hxx>
#include <IMark.hxx>
#include <sortedobjs.hxx>
-#include <dcontact.hxx>
-
-using namespace ::com::sun::star;
-using namespace i18n::ScriptType;
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/text/XBookmarksSupplier.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <comphelper/processfactory.hxx>
+#include <docsh.hxx>
+#include <unobookmark.hxx>
+#include <unocrsrhelper.hxx>
+#include <frmatr.hxx>
+#include <vcl/kernarray.hxx>
+#include <editeng/ulspitem.hxx>
+#include <com/sun/star/rdf/Statement.hpp>
+#include <com/sun/star/rdf/URI.hpp>
+#include <com/sun/star/rdf/URIs.hpp>
+#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
+#include <com/sun/star/rdf/XLiteral.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
#include <unicode/ubidi.h>
#include <i18nutil/scripttypedetector.hxx>
#include <i18nutil/unicode.hxx>
+using namespace ::com::sun::star;
+using namespace i18n::ScriptType;
+
+/*
+ https://www.khtt.net/en/page/1821/the-big-kashida-secret
+
+ the rules of priorities that govern the addition of kashidas in Arabic text
+ made ... for ... Explorer 5.5 browser.
+
+ The kashida justification is based on a connection priority scheme that
+ decides where kashidas are put automatically.
+
+ This is how the software decides on kashida-inserting priorities:
+ 1. First it looks for characters with the highest priority in each word,
+ which means kashida-extensions will only been used in one position in each
+ word. Not more.
+ 2. The kashida will be connected to the character with the highest priority.
+ 3. If kashida connection opportunities are found with an equal level of
+ priority in one word, the kashida will be placed towards the end of the
+ word.
+
+ The priority list of characters and the positioning is as follows:
+ 1. after a kashida that is manually placed in the text by the user,
+ 2. after a Seen or Sad (initial and medial form),
+ 3. before the final form of Taa Marbutah, Haa, Dal,
+ 4. before the final form of Alef, Tah Lam, Kaf and Gaf,
+ 5. before the preceding medial Baa of Ra, Ya and Alef Maqsurah,
+ 6. before the final form of Waw, Ain, Qaf and Fa,
+ 7. before the final form of other characters that can be connected.
+*/
+
#define IS_JOINING_GROUP(c, g) ( u_getIntPropertyValue( (c), UCHAR_JOINING_GROUP ) == U_JG_##g )
#define isAinChar(c) IS_JOINING_GROUP((c), AIN)
#define isAlefChar(c) IS_JOINING_GROUP((c), ALEF)
#define isDalChar(c) IS_JOINING_GROUP((c), DAL)
-#if U_ICU_VERSION_MAJOR_NUM >= 58
#define isFehChar(c) (IS_JOINING_GROUP((c), FEH) || IS_JOINING_GROUP((c), AFRICAN_FEH))
-#else
-#define isFehChar(c) IS_JOINING_GROUP((c), FEH)
-#endif
#define isGafChar(c) IS_JOINING_GROUP((c), GAF)
#define isHehChar(c) IS_JOINING_GROUP((c), HEH)
#define isKafChar(c) IS_JOINING_GROUP((c), KAF)
#define isLamChar(c) IS_JOINING_GROUP((c), LAM)
-#if U_ICU_VERSION_MAJOR_NUM >= 58
#define isQafChar(c) (IS_JOINING_GROUP((c), QAF) || IS_JOINING_GROUP((c), AFRICAN_QAF))
-#else
-#define isQafChar(c) IS_JOINING_GROUP((c), QAF)
-#endif
#define isRehChar(c) IS_JOINING_GROUP((c), REH)
#define isTahChar(c) IS_JOINING_GROUP((c), TAH)
#define isTehMarbutaChar(c) IS_JOINING_GROUP((c), TEH_MARBUTA)
#define isWawChar(c) IS_JOINING_GROUP((c), WAW)
#define isSeenOrSadChar(c) (IS_JOINING_GROUP((c), SAD) || IS_JOINING_GROUP((c), SEEN))
-// Beh and charters that behave like Beh in medial form.
+// Beh and characters that behave like Beh in medial form.
static bool isBehChar(sal_Unicode cCh)
{
bool bRet = false;
@@ -100,9 +135,7 @@ static bool isBehChar(sal_Unicode cCh)
{
case U_JG_BEH:
case U_JG_NOON:
-#if U_ICU_VERSION_MAJOR_NUM >= 58
case U_JG_AFRICAN_NOON:
-#endif
case U_JG_NYA:
case U_JG_YEH:
case U_JG_FARSI_YEH:
@@ -117,7 +150,7 @@ static bool isBehChar(sal_Unicode cCh)
return bRet;
}
-// Yeh and charters that behave like Yeh in final form.
+// Yeh and characters that behave like Yeh in final form.
static bool isYehChar(sal_Unicode cCh)
{
bool bRet = false;
@@ -185,21 +218,18 @@ void SwLineLayout::DeleteNext()
{
if (!m_pNext)
return;
- std::vector<SwLineLayout*> aNexts;
SwLineLayout* pNext = m_pNext;
do
{
- aNexts.push_back(pNext);
SwLineLayout* pLastNext = pNext;
pNext = pNext->GetNext();
pLastNext->SetNext(nullptr);
+ delete pLastNext;
}
while (pNext);
- for (auto a : aNexts)
- delete a;
}
-void SwLineLayout::Height(const sal_uInt16 nNew, const bool bText)
+void SwLineLayout::Height(const SwTwips nNew, const bool bText)
{
SwPosSize::Height(nNew);
if (bText)
@@ -318,20 +348,25 @@ void SwLineLayout::CreateSpaceAdd( const tools::Long nInit )
SetLLSpaceAdd( nInit, 0 );
}
-// Returns true if there are only blanks in [nStt, nEnd[
+// #i3952# Returns true if there are only blanks in [nStt, nEnd[
+// Used to implement IgnoreTabsAndBlanksForLineCalculation compat flag
static bool lcl_HasOnlyBlanks(std::u16string_view rText, TextFrameIndex nStt, TextFrameIndex nEnd)
{
- bool bBlankOnly = true;
while ( nStt < nEnd )
{
- const sal_Unicode cChar = rText[ sal_Int32(nStt++) ];
- if ( ' ' != cChar && CH_FULL_BLANK != cChar && CH_SIX_PER_EM != cChar )
+ switch (rText[sal_Int32(nStt++)])
{
- bBlankOnly = false;
- break;
+ case 0x0020: // SPACE
+ case 0x2002: // EN SPACE
+ case 0x2003: // EM SPACE
+ case 0x2005: // FOUR-PER-EM SPACE
+ case 0x3000: // IDEOGRAPHIC SPACE
+ continue;
+ default:
+ return false;
}
}
- return bBlankOnly;
+ return true;
}
// Swapped out from FormatLine()
@@ -342,6 +377,10 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
sal_uInt16 nFlyAscent = 0;
sal_uInt16 nFlyHeight = 0;
sal_uInt16 nFlyDescent = 0;
+
+ // If this line has a clearing break, then this is the portion's height.
+ sal_uInt16 nBreakHeight = 0;
+
bool bOnlyPostIts = true;
SetHanging( false );
@@ -374,7 +413,7 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
}
else
{
- const sal_uInt16 nLineHeight = Height();
+ const SwTwips nLineHeight = Height();
Init( GetNextPortion() );
SwLinePortion *pPos = mpNextPortion;
SwLinePortion *pLast = this;
@@ -389,6 +428,7 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
// Null portions are eliminated. They can form if two FlyFrames
// overlap.
+ // coverity[deref_arg] - "Cut" means next "GetNextPortion" returns a different Portion
if( !pPos->Compress() )
{
// Only take over Height and Ascent if the rest of the line
@@ -400,7 +440,9 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
if( !GetAscent() )
SetAscent( pPos->GetAscent() );
}
- delete pLast->Cut( pPos );
+ SwLinePortion* pPortion = pLast->Cut( pPos );
+ rLine.ClearIfIsFirstOfBorderMerge(pPortion);
+ delete pPortion;
pPos = pLast->GetNextPortion();
continue;
}
@@ -410,7 +452,7 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
AddPrtWidth( pPos->Width() );
// #i3952#
- if ( bIgnoreBlanksAndTabsForLineHeightCalculation )
+ if (bIgnoreBlanksAndTabsForLineHeightCalculation && !rInf.GetLineStart())
{
if ( pPos->InTabGrp() || pPos->IsHolePortion() ||
( pPos->IsTextPortion() &&
@@ -437,8 +479,8 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
// We had an attribute change: Sum up/build maxima of length and mass
- sal_uInt16 nPosHeight = pPos->Height();
- sal_uInt16 nPosAscent = pPos->GetAscent();
+ SwTwips nPosHeight = pPos->Height();
+ SwTwips nPosAscent = pPos->GetAscent();
SAL_WARN_IF( nPosHeight < nPosAscent,
"sw.core", "SwLineLayout::CalcLine: bad ascent or height" );
@@ -451,10 +493,16 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
else if( !bHasFlyPortion && ( pPos->IsFlyCntPortion() || pPos->IsFlyPortion() ) )
bHasFlyPortion = true;
- // To prevent that a paragraph-end-character does not change
- // the line height through a Descent and thus causing the line
- // to reformat.
- if ( !pPos->IsBreakPortion() || !Height() )
+ // A line break portion only influences the height of the line in case it's the only
+ // portion in the line, except when it's a clearing break.
+ bool bClearingBreak = false;
+ if (pPos->IsBreakPortion())
+ {
+ auto pBreakPortion = static_cast<SwBreakPortion*>(pPos);
+ bClearingBreak = pBreakPortion->GetClear() != SwLineBreakClear::NONE;
+ nBreakHeight = nPosHeight;
+ }
+ if (!(pPos->IsBreakPortion() && !bClearingBreak) || !Height())
{
if (!pPos->IsPostItsPortion()) bOnlyPostIts = false;
@@ -567,7 +615,19 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
if( nFlyDescent > nFlyHeight - nFlyAscent )
Height( nFlyHeight + nFlyDescent, false );
else
- Height( nFlyHeight, false );
+ {
+ if (nBreakHeight > nFlyHeight)
+ {
+ // The line has no content, but it has a clearing break: then the line
+ // height is not only the intersection of the fly and line's rectangle, but
+ // also includes the clearing break's height.
+ Height(nBreakHeight, false);
+ }
+ else
+ {
+ Height(nFlyHeight, false);
+ }
+ }
}
else if( nMaxDescent > Height() - mnAscent )
Height( nMaxDescent + mnAscent, false );
@@ -591,14 +651,28 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
}
}
- // #i3952#
+ // #i3952# Whitespace does not increase line height
if ( bHasBlankPortion && bHasOnlyBlankPortions )
{
sal_uInt16 nTmpAscent = GetAscent();
sal_uInt16 nTmpHeight = Height();
rLine.GetAttrHandler().GetDefaultAscentAndHeight( rInf.GetVsh(), *rInf.GetOut(), nTmpAscent, nTmpHeight );
- SetAscent( nTmpAscent );
- Height( nTmpHeight, false );
+
+ short nEscapement = rLine.GetAttrHandler().GetFont()->GetEscapement();
+ if (GetAscent() && Height() && !nTmpAscent && !nTmpHeight
+ && (nEscapement == DFLT_ESC_AUTO_SUPER || nEscapement == DFLT_ESC_AUTO_SUB))
+ {
+ // We already had a calculated ascent + height, it would be cleared, automatic
+ // sub/superscript is set and we have no content. In this case it makes no sense to
+ // clear the old, correct ascent/height.
+ nTmpAscent = GetAscent();
+ nTmpHeight = Height();
+ }
+
+ if (nTmpAscent < GetAscent() || GetAscent() <= 0)
+ SetAscent(nTmpAscent);
+ if (nTmpHeight < Height() || Height() <= 0)
+ Height(nTmpHeight, false);
}
// Robust:
@@ -630,65 +704,70 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
SetRedline( bHasRedline );
// redlining: set crossing out for deleted anchored objects
- if ( bHasFlyPortion )
+ if ( !bHasFlyPortion )
+ return;
+
+ SwLinePortion *pPos = mpNextPortion;
+ TextFrameIndex nLineLength;
+ while ( pPos )
{
- SwLinePortion *pPos = mpNextPortion;
- TextFrameIndex nLineLength;
- while ( pPos )
+ TextFrameIndex const nPorSttIdx = rInf.GetLineStart() + nLineLength;
+ nLineLength += pPos->GetLen();
+ // anchored as characters
+ if( pPos->IsFlyCntPortion() )
{
- TextFrameIndex const nPorSttIdx = rInf.GetLineStart() + nLineLength;
- nLineLength += pPos->GetLen();
- // anchored as characters
- if( pPos->IsFlyCntPortion() )
+ bool bDeleted = false;
+ size_t nAuthor = std::string::npos;
+ if ( bHasRedline )
{
- bool bDeleted = false;
- if ( bHasRedline )
- {
- OUString sRedlineText;
- bool bHasRedlineEnd;
- enum RedlineType eRedlineEnd;
- std::pair<SwTextNode const*, sal_Int32> const flyStart(
- rInf.GetTextFrame()->MapViewToModel(nPorSttIdx));
- bool bHasFlyRedline = rLine.GetRedln()->CheckLine(flyStart.first->GetIndex(),
- flyStart.second, flyStart.first->GetIndex(), flyStart.second, sRedlineText,
- bHasRedlineEnd, eRedlineEnd, /*bFullLine=*/false);
- bDeleted = bHasFlyRedline && eRedlineEnd == RedlineType::Delete;
- }
- static_cast<SwFlyCntPortion*>(pPos)->SetDeleted(bDeleted);
+ OUString sRedlineText;
+ bool bHasRedlineEnd;
+ enum RedlineType eRedlineEnd;
+ std::pair<SwTextNode const*, sal_Int32> const flyStart(
+ rInf.GetTextFrame()->MapViewToModel(nPorSttIdx));
+ bool bHasFlyRedline = rLine.GetRedln()->CheckLine(flyStart.first->GetIndex(),
+ flyStart.second, flyStart.first->GetIndex(), flyStart.second, sRedlineText,
+ bHasRedlineEnd, eRedlineEnd, /*pAuthorAtPos=*/&nAuthor);
+ bDeleted = bHasFlyRedline && eRedlineEnd == RedlineType::Delete;
}
- // anchored to characters
- else if ( pPos->IsFlyPortion() )
+ static_cast<SwFlyCntPortion*>(pPos)->SetDeleted(bDeleted);
+ static_cast<SwFlyCntPortion*>(pPos)->SetAuthor(nAuthor);
+ }
+ // anchored to characters
+ else if ( pPos->IsFlyPortion() )
+ {
+ const IDocumentRedlineAccess& rIDRA =
+ rInf.GetTextFrame()->GetDoc().getIDocumentRedlineAccess();
+ SwSortedObjs *pObjs = rInf.GetTextFrame()->GetDrawObjs();
+ if ( pObjs && IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) )
{
- const IDocumentRedlineAccess& rIDRA =
- rInf.GetTextFrame()->GetDoc().getIDocumentRedlineAccess();
- SwSortedObjs *pObjs = rInf.GetTextFrame()->GetDrawObjs();
- if ( pObjs && IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) )
+ for ( size_t i = 0; rInf.GetTextFrame()->GetDrawObjs() && i < pObjs->size(); ++i )
{
- for ( size_t i = 0; rInf.GetTextFrame()->GetDrawObjs() && i < pObjs->size(); ++i )
+ SwAnchoredObject* pAnchoredObj = (*rInf.GetTextFrame()->GetDrawObjs())[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
{
- SwAnchoredObject* pAnchoredObj = (*rInf.GetTextFrame()->GetDrawObjs())[i];
- if ( auto pFly = dynamic_cast<SwFlyFrame *>( pAnchoredObj ) )
+ bool bDeleted = false;
+ size_t nAuthor = std::string::npos;
+ const SwFormatAnchor& rAnchor = pAnchoredObj->GetFrameFormat()->GetAnchor();
+ if ( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR )
{
- bool bDeleted = false;
- const SwFormatAnchor& rAnchor = pAnchoredObj->GetFrameFormat().GetAnchor();
- if ( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR )
+ SwPosition aAnchor = *rAnchor.GetContentAnchor();
+ SwRedlineTable::size_type n = 0;
+ const SwRangeRedline* pFnd =
+ rIDRA.GetRedlineTable().FindAtPosition( aAnchor, n );
+ if ( pFnd && RedlineType::Delete == pFnd->GetType() )
{
- SwPosition aAnchor = *rAnchor.GetContentAnchor();
- const SwPaM aPam(aAnchor, aAnchor);
- if ( rIDRA.HasRedline( aPam, RedlineType::Delete,
- /*bStartOrEndInRange=*/false) )
- {
- bDeleted = true;
- }
+ bDeleted = true;
+ nAuthor = pFnd->GetAuthor();
}
- pFly->SetDeleted(bDeleted);
}
-
+ pFly->SetDeleted(bDeleted);
+ pFly->SetAuthor(nAuthor);
}
}
}
- pPos = pPos->GetNextPortion();
}
+ pPos = pPos->GetNextPortion();
}
}
@@ -721,9 +800,8 @@ void SwLineLayout::MaxAscentDescent( SwTwips& _orAscent,
( !pTmpPortion->IsFlyCntPortion() &&
!(pTmpPortion == this && pTmpPortion->GetNextPortion() ) ) ) )
{
- SwTwips nPortionAsc = static_cast<SwTwips>(pTmpPortion->GetAscent());
- SwTwips nPortionDesc = static_cast<SwTwips>(pTmpPortion->Height()) -
- nPortionAsc;
+ SwTwips nPortionAsc = pTmpPortion->GetAscent();
+ SwTwips nPortionDesc = pTmpPortion->Height() - nPortionAsc;
const bool bFlyCmp = pTmpPortion->IsFlyCntPortion() ?
static_cast<const SwFlyCntPortion*>(pTmpPortion)->IsMax() :
@@ -745,9 +823,19 @@ void SwLineLayout::MaxAscentDescent( SwTwips& _orAscent,
}
}
+void SwLineLayout::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwLineLayout"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
void SwLineLayout::ResetFlags()
{
- m_bFormatAdj = m_bDummy = m_bEndHyph = m_bMidHyph = m_bFly
+ m_bFormatAdj = m_bDummy = m_bEndHyph = m_bMidHyph = m_bLastHyph = m_bFly
= m_bRest = m_bBlinking = m_bClipping = m_bContent = m_bRedline
= m_bRedlineEnd = m_bForcedLeftMargin = m_bHanging = false;
m_eRedlineEnd = RedlineType::None;
@@ -826,19 +914,64 @@ SwFontScript SwScriptInfo::WhichFont(sal_Int32 nIdx, OUString const& rText)
return lcl_ScriptToFont(nScript);
}
+static Color getBookmarkColor(const SwTextNode& rNode, const sw::mark::IBookmark* pBookmark)
+{
+ // search custom color in metadata, otherwise use COL_TRANSPARENT;
+ Color c = COL_TRANSPARENT;
+
+ try
+ {
+ SwDoc& rDoc = const_cast<SwDoc&>(rNode.GetDoc());
+ const rtl::Reference< SwXBookmark > xRef = SwXBookmark::CreateXBookmark(rDoc,
+ const_cast<sw::mark::IMark*>(static_cast<const sw::mark::IMark*>(pBookmark)));
+ const css::uno::Reference<css::rdf::XResource> xSubject(xRef);
+ uno::Reference<frame::XModel> xModel = rDoc.GetDocShell()->GetBaseModel();
+
+ static uno::Reference< uno::XComponentContext > xContext(
+ ::comphelper::getProcessComponentContext());
+
+ static uno::Reference< rdf::XURI > xODF_SHADING(
+ rdf::URI::createKnown(xContext, rdf::URIs::LO_EXT_SHADING), uno::UNO_SET_THROW);
+
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(
+ rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ const uno::Reference<rdf::XRepository>& xRepository =
+ xDocumentMetadataAccess->getRDFRepository();
+ const uno::Reference<container::XEnumeration> xEnum(
+ xRepository->getStatements(xSubject, xODF_SHADING, nullptr), uno::UNO_SET_THROW);
+
+ rdf::Statement stmt;
+ if ( xEnum->hasMoreElements() && (xEnum->nextElement() >>= stmt) )
+ {
+ const uno::Reference<rdf::XLiteral> xObject(stmt.Object, uno::UNO_QUERY);
+ if ( xObject.is() )
+ c = Color::STRtoRGB(xObject->getValue());
+ }
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ }
+
+ return c;
+}
+
static void InitBookmarks(
std::optional<std::vector<sw::Extent>::const_iterator> oPrevIter,
std::vector<sw::Extent>::const_iterator iter,
std::vector<sw::Extent>::const_iterator const end,
TextFrameIndex nOffset,
std::vector<std::pair<sw::mark::IBookmark const*, SwScriptInfo::MarkKind>> & rBookmarks,
- std::vector<std::pair<TextFrameIndex, SwScriptInfo::MarkKind>> & o_rBookmarks)
+ std::vector<std::tuple<TextFrameIndex, SwScriptInfo::MarkKind, Color, OUString>> & o_rBookmarks)
{
SwTextNode const*const pNode(iter->pNode);
for (auto const& it : rBookmarks)
{
assert(iter->pNode == pNode || pNode->GetIndex() < iter->pNode->GetIndex());
assert(!oPrevIter || (*oPrevIter)->pNode->GetIndex() <= pNode->GetIndex());
+
+ // search for custom bookmark boundary mark color
+ Color c = getBookmarkColor(*pNode, it.first);
+
switch (it.second)
{
case SwScriptInfo::MarkKind::Start:
@@ -854,39 +987,39 @@ static void InitBookmarks(
// assume "no" because the line break it contains isn't deleted.
SwPosition const& rStart(it.first->GetMarkStart());
SwPosition const& rEnd(it.first->GetMarkEnd());
- assert(&rStart.nNode.GetNode() == pNode);
+ assert(&rStart.GetNode() == pNode);
while (iter != end)
{
- if (&rStart.nNode.GetNode() != iter->pNode // iter moved to next node
- || rStart.nContent.GetIndex() < iter->nStart)
+ if (&rStart.GetNode() != iter->pNode // iter moved to next node
+ || rStart.GetContentIndex() < iter->nStart)
{
- if (rEnd.nNode.GetIndex() < iter->pNode->GetIndex()
- || (&rEnd.nNode.GetNode() == iter->pNode && rEnd.nContent.GetIndex() <= iter->nStart))
+ if (rEnd.GetNodeIndex() < iter->pNode->GetIndex()
+ || (&rEnd.GetNode() == iter->pNode && rEnd.GetContentIndex() <= iter->nStart))
{
break; // deleted - skip it
}
else
{
- o_rBookmarks.emplace_back(nOffset, it.second);
+ o_rBookmarks.emplace_back(nOffset, it.second, c, it.first->GetName());
break;
}
}
- else if (rStart.nContent.GetIndex() <= iter->nEnd)
+ else if (rStart.GetContentIndex() <= iter->nEnd)
{
auto const iterNext(iter + 1);
- if (rStart.nContent.GetIndex() == iter->nEnd
+ if (rStart.GetContentIndex() == iter->nEnd
&& (iterNext == end
- ? &rEnd.nNode.GetNode() == iter->pNode
- : (rEnd.nNode.GetIndex() < iterNext->pNode->GetIndex()
- || (&rEnd.nNode.GetNode() == iterNext->pNode && rEnd.nContent.GetIndex() < iterNext->nStart))))
+ ? &rEnd.GetNode() == iter->pNode
+ : (rEnd.GetNodeIndex() < iterNext->pNode->GetIndex()
+ || (&rEnd.GetNode() == iterNext->pNode && rEnd.GetContentIndex() < iterNext->nStart))))
{
break; // deleted - skip it
}
else
{
o_rBookmarks.emplace_back(
- nOffset + TextFrameIndex(rStart.nContent.GetIndex() - iter->nStart),
- it.second);
+ nOffset + TextFrameIndex(rStart.GetContentIndex() - iter->nStart),
+ it.second, c, it.first->GetName());
break;
}
}
@@ -899,13 +1032,13 @@ static void InitBookmarks(
}
if (iter == end)
{
- if (pNode->GetIndex() < rEnd.nNode.GetIndex()) // pNode is last node of merged
+ if (pNode->GetIndex() < rEnd.GetNodeIndex()) // pNode is last node of merged
{
break; // deleted - skip it
}
else
{
- o_rBookmarks.emplace_back(nOffset, it.second);
+ o_rBookmarks.emplace_back(nOffset, it.second, c, it.first->GetName());
}
}
break;
@@ -913,36 +1046,36 @@ static void InitBookmarks(
case SwScriptInfo::MarkKind::End:
{
SwPosition const& rEnd(it.first->GetMarkEnd());
- assert(&rEnd.nNode.GetNode() == pNode);
+ assert(&rEnd.GetNode() == pNode);
while (true)
{
if (iter == end
- || &rEnd.nNode.GetNode() != iter->pNode // iter moved to next node
- || rEnd.nContent.GetIndex() <= iter->nStart)
+ || &rEnd.GetNode() != iter->pNode // iter moved to next node
+ || rEnd.GetContentIndex() <= iter->nStart)
{
SwPosition const& rStart(it.first->GetMarkStart());
// oPrevIter may point to pNode or a preceding node
if (oPrevIter
- ? ((*oPrevIter)->pNode->GetIndex() < rStart.nNode.GetIndex()
- || ((*oPrevIter)->pNode == &rStart.nNode.GetNode()
- && ((iter != end && &rEnd.nNode.GetNode() == iter->pNode && rEnd.nContent.GetIndex() == iter->nStart)
- ? (*oPrevIter)->nEnd < rStart.nContent.GetIndex()
- : (*oPrevIter)->nEnd <= rStart.nContent.GetIndex())))
- : rStart.nNode == rEnd.nNode)
+ ? ((*oPrevIter)->pNode->GetIndex() < rStart.GetNodeIndex()
+ || ((*oPrevIter)->pNode == &rStart.GetNode()
+ && ((iter != end && &rEnd.GetNode() == iter->pNode && rEnd.GetContentIndex() == iter->nStart)
+ ? (*oPrevIter)->nEnd < rStart.GetContentIndex()
+ : (*oPrevIter)->nEnd <= rStart.GetContentIndex())))
+ : rStart.GetNode() == rEnd.GetNode())
{
break; // deleted - skip it
}
else
{
- o_rBookmarks.emplace_back(nOffset, it.second);
+ o_rBookmarks.emplace_back(nOffset, it.second, c, it.first->GetName());
break;
}
}
- else if (rEnd.nContent.GetIndex() <= iter->nEnd)
+ else if (rEnd.GetContentIndex() <= iter->nEnd)
{
o_rBookmarks.emplace_back(
- nOffset + TextFrameIndex(rEnd.nContent.GetIndex() - iter->nStart),
- it.second);
+ nOffset + TextFrameIndex(rEnd.GetContentIndex() - iter->nStart),
+ it.second, c, it.first->GetName());
break;
}
else
@@ -957,26 +1090,26 @@ static void InitBookmarks(
case SwScriptInfo::MarkKind::Point:
{
SwPosition const& rPos(it.first->GetMarkPos());
- assert(&rPos.nNode.GetNode() == pNode);
+ assert(&rPos.GetNode() == pNode);
while (iter != end)
{
- if (&rPos.nNode.GetNode() != iter->pNode // iter moved to next node
- || rPos.nContent.GetIndex() < iter->nStart)
+ if (&rPos.GetNode() != iter->pNode // iter moved to next node
+ || rPos.GetContentIndex() < iter->nStart)
{
break; // deleted - skip it
}
- else if (rPos.nContent.GetIndex() <= iter->nEnd)
+ else if (rPos.GetContentIndex() <= iter->nEnd)
{
- if (rPos.nContent.GetIndex() == iter->nEnd
- && rPos.nContent.GetIndex() != iter->pNode->Len())
+ if (rPos.GetContentIndex() == iter->nEnd
+ && rPos.GetContentIndex() != iter->pNode->Len())
{
break; // deleted - skip it
}
else
{
o_rBookmarks.emplace_back(
- nOffset + TextFrameIndex(rPos.nContent.GetIndex() - iter->nStart),
- it.second);
+ nOffset + TextFrameIndex(rPos.GetContentIndex() - iter->nStart),
+ it.second, c, it.first->GetName());
}
break;
}
@@ -990,6 +1123,10 @@ static void InitBookmarks(
break;
}
}
+ if (iter == end)
+ {
+ break; // remaining marks are hidden
+ }
}
}
@@ -1017,11 +1154,12 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
TextFrameIndex nOffset(0);
std::optional<std::vector<sw::Extent>::const_iterator> oPrevIter;
for (auto iter = pMerged->extents.begin(); iter != pMerged->extents.end();
- oPrevIter = iter, ++iter)
+ oPrevIter = iter)
{
if (iter->pNode == pNode)
{
nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
+ ++iter;
continue; // skip extents at end of previous node
}
pNode = iter->pNode;
@@ -1037,39 +1175,66 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
const Range& rRange = aHiddenMulti.GetRange( i );
const sal_Int32 nStart = rRange.Min();
const sal_Int32 nEnd = rRange.Max() + 1;
+ bool isStartHandled(false);
+ ::std::optional<sal_Int32> oExtend;
- while (true)
+ if (nEnd <= iter->nStart)
+ { // entirely in gap, skip this hidden range
+ continue;
+ }
+
+ do
{
- // because of the selectRedLineDeleted call, never overlaps
- // extents, must be contained inside one extent
- assert(!(iter->nStart <= nStart && nStart < iter->nEnd && iter->nEnd < nEnd));
- assert(!(nStart < iter->nStart && iter->nStart < nEnd && nEnd <= iter->nEnd));
- if (iter->nStart <= nStart && nEnd <= iter->nEnd)
+ if (!isStartHandled && nStart <= iter->nEnd)
{
- if (iter->nStart == nStart && !m_HiddenChg.empty()
+ isStartHandled = true;
+ if (nStart <= iter->nStart && !m_HiddenChg.empty()
&& m_HiddenChg.back() == nOffset)
{
// previous one went until end of extent, extend it
- m_HiddenChg.back() += TextFrameIndex(nEnd - iter->nStart);
+ oExtend.emplace(::std::min(iter->nEnd, nEnd) - ::std::max(iter->nStart, nStart));
}
- else // new one
+ else
{
- m_HiddenChg.push_back(nOffset + TextFrameIndex(nStart - iter->nStart));
- m_HiddenChg.push_back(nOffset + TextFrameIndex(nEnd - iter->nStart));
+ m_HiddenChg.push_back(nOffset + TextFrameIndex(::std::max(nStart - iter->nStart, sal_Int32(0))));
}
- break;
}
- else
+ else if (oExtend)
{
- nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
- ++iter;
- // because selectRedLineDeleted, must find it in pNode
- assert(iter != pMerged->extents.end());
- assert(iter->pNode == pNode);
+ *oExtend += ::std::min(iter->nEnd, nEnd) - iter->nStart;
+ }
+ if (nEnd <= iter->nEnd)
+ {
+ if (oExtend)
+ {
+ m_HiddenChg.back() += TextFrameIndex(*oExtend);
+ }
+ else
+ {
+ m_HiddenChg.push_back(nOffset + TextFrameIndex(::std::max(nEnd - iter->nStart, sal_Int32(0))));
+ }
+ break; // iterate to next hidden range
}
+ nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
+ ++iter;
+ }
+ while (iter != pMerged->extents.end() && iter->pNode == pNode);
+ if (iter == pMerged->extents.end() || iter->pNode != pNode)
+ {
+ if (isStartHandled)
+ { // dangling end
+ if (oExtend)
+ {
+ m_HiddenChg.back() += TextFrameIndex(*oExtend);
+ }
+ else
+ {
+ m_HiddenChg.push_back(nOffset);
+ }
+ } // else: beyond last extent in node, ignore
+ break; // skip hidden ranges beyond last extent in node
}
}
- nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
}
}
else
@@ -1081,25 +1246,38 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
for (auto const& it : bookmarks)
{
+ // don't show __RefHeading__ bookmarks, which are hidden in Navigator, too
+ // (They are inserted automatically e.g. with the ToC at the beginning of
+ // the headings)
+ if (it.first->GetName().startsWith(
+ IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()))
+ {
+ continue;
+ }
+
+ // search for custom bookmark boundary mark color
+ Color c = getBookmarkColor(rNode, it.first);
+
switch (it.second)
{
case MarkKind::Start:
- m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkStart().nContent.GetIndex()), it.second);
+ m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkStart().GetContentIndex()), it.second, c, it.first->GetName());
break;
case MarkKind::End:
- m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkEnd().nContent.GetIndex()), it.second);
+ m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkEnd().GetContentIndex()), it.second, c, it.first->GetName());
break;
case MarkKind::Point:
- m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkPos().nContent.GetIndex()), it.second);
+ m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkPos().GetContentIndex()), it.second, c, it.first->GetName());
break;
}
}
+ m_HiddenChg.reserve( aHiddenMulti.GetRangeCount() * 2 );
for (sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i)
{
const Range& rRange = aHiddenMulti.GetRange( i );
const sal_Int32 nStart = rRange.Min();
- const sal_Int32 nEnd = rRange.Max() + 1;
+ const sal_Int32 nEnd = rRange.Max() + (rText.isEmpty() ? 0 : 1);
m_HiddenChg.push_back( TextFrameIndex(nStart) );
m_HiddenChg.push_back( TextFrameIndex(nEnd) );
@@ -1274,50 +1452,28 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
if (nChg > TextFrameIndex(rText.getLength()) || nChg < TextFrameIndex(0))
nChg = TextFrameIndex(rText.getLength());
- // #i28203#
- // for 'complex' portions, we make sure that a portion does not contain more
- // than one script:
- if( i18n::ScriptType::COMPLEX == nScript )
- {
- const short nScriptType = ScriptTypeDetector::getCTLScriptType(
- rText, sal_Int32(nSearchStt) );
- TextFrameIndex nNextCTLScriptStart = nSearchStt;
- short nCurrentScriptType = nScriptType;
- while( css::i18n::CTLScriptType::CTL_UNKNOWN == nCurrentScriptType || nScriptType == nCurrentScriptType )
- {
- nNextCTLScriptStart = TextFrameIndex(
- ScriptTypeDetector::endOfCTLScriptType(
- rText, sal_Int32(nNextCTLScriptStart)));
- if (nNextCTLScriptStart >= TextFrameIndex(rText.getLength())
- || nNextCTLScriptStart >= nChg)
- break;
- nCurrentScriptType = ScriptTypeDetector::getCTLScriptType(
- rText, sal_Int32(nNextCTLScriptStart));
- }
- nChg = std::min( nChg, nNextCTLScriptStart );
- }
-
// special case for dotted circle since it can be used with complex
// before a mark, so we want it associated with the mark's script
- if (nChg < TextFrameIndex(rText.getLength()) && nChg > TextFrameIndex(0)
- && (i18n::ScriptType::WEAK ==
- g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg) - 1)))
+ // tdf#112594: another special case for NNBSP followed by a Mongolian
+ // character, since NNBSP has special uses in Mongolian (tdf#112594)
+ auto nPos = sal_Int32(nChg);
+ auto nPrevPos = nPos;
+ auto nPrevChar = rText.iterateCodePoints(&nPrevPos, -1);
+ if (nChg < TextFrameIndex(rText.getLength()) && nChg > TextFrameIndex(0) &&
+ i18n::ScriptType::WEAK == g_pBreakIt->GetBreakIter()->getScriptType(rText, nPrevPos))
{
- int8_t nType = u_charType(rText[sal_Int32(nChg)]);
- if (nType == U_NON_SPACING_MARK || nType == U_ENCLOSING_MARK ||
- nType == U_COMBINING_SPACING_MARK )
- {
- m_ScriptChanges.emplace_back(nChg-TextFrameIndex(1), nScript);
- }
- else
+ auto nChar = rText.iterateCodePoints(&nPos, 0);
+ auto nType = unicode::getUnicodeType(nChar);
+ if (nType == css::i18n::UnicodeType::NON_SPACING_MARK ||
+ nType == css::i18n::UnicodeType::ENCLOSING_MARK ||
+ nType == css::i18n::UnicodeType::COMBINING_SPACING_MARK ||
+ (nPrevChar == CHAR_NNBSP &&
+ u_getIntPropertyValue(nChar, UCHAR_SCRIPT) == USCRIPT_MONGOLIAN))
{
- m_ScriptChanges.emplace_back(nChg, nScript);
+ nPos = nPrevPos;
}
}
- else
- {
- m_ScriptChanges.emplace_back(nChg, nScript);
- }
+ m_ScriptChanges.emplace_back(TextFrameIndex(nPos), nScript);
++nCnt;
// if current script is asian, we search for compressible characters
@@ -1340,6 +1496,7 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
case 0x3008: case 0x300A: case 0x300C: case 0x300E:
case 0x3010: case 0x3014: case 0x3016: case 0x3018:
case 0x301A: case 0x301D:
+ case 0xFF08: case 0xFF3B: case 0xFF5B:
eState = SPECIAL_LEFT;
break;
// Right punctuation found
@@ -1347,9 +1504,11 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
case 0x300D: case 0x300F: case 0x3011: case 0x3015:
case 0x3017: case 0x3019: case 0x301B: case 0x301E:
case 0x301F:
+ case 0xFF09: case 0xFF3D: case 0xFF5D:
eState = SPECIAL_RIGHT;
break;
case 0x3001: case 0x3002: // Fullstop or comma
+ case 0xFF0C: case 0xFF0E: case 0xFF1A: case 0xFF1B:
eState = SPECIAL_MIDDLE ;
break;
default:
@@ -1406,7 +1565,7 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
[&rNode](sal_Int32 const nBegin, sal_uInt16 const script, bool const bNoChar)
{ return rNode.GetLang(nBegin, bNoChar ? 0 : 1, script); });
auto pGetLangOfChar(pMerged ? pGetLangOfCharM : pGetLangOfChar1);
- SwScanner aScanner( pGetLangOfChar, rText, nullptr, ModelToViewHelper(),
+ SwScanner aScanner( std::move(pGetLangOfChar), rText, nullptr, ModelToViewHelper(),
i18n::WordType::DICTIONARY_WORD,
sal_Int32(nLastKashida), sal_Int32(nChg));
@@ -1415,10 +1574,9 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
{
const OUString& rWord = aScanner.GetWord();
- sal_Int32 nIdx = 0;
+ sal_Int32 nIdx = 0, nPrevIdx = 0;
sal_Int32 nKashidaPos = -1;
- sal_Unicode cCh;
- sal_Unicode cPrevCh = 0;
+ sal_Unicode cCh, cPrevCh = 0;
int nPriorityLevel = 7; // 0..6 = level found
// 7 not found
@@ -1466,7 +1624,7 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
// check if character is connectable to previous character,
if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
{
- nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nKashidaPos = aScanner.GetBegin() + nPrevIdx;
nPriorityLevel = 2;
}
}
@@ -1487,7 +1645,7 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
// check if character is connectable to previous character,
if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
{
- nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nKashidaPos = aScanner.GetBegin() + nPrevIdx;
nPriorityLevel = 3;
}
}
@@ -1502,12 +1660,12 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
// check if next character is Reh or Yeh-like
sal_Unicode cNextCh = rWord[ nIdx + 1 ];
if ( isRehChar ( cNextCh ) || isYehChar ( cNextCh ))
- {
+ {
SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
// check if character is connectable to previous character,
if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
{
- nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nKashidaPos = aScanner.GetBegin() + nPrevIdx;
nPriorityLevel = 4;
}
}
@@ -1529,7 +1687,7 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
// check if character is connectable to previous character,
if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
{
- nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nKashidaPos = aScanner.GetBegin() + nPrevIdx;
nPriorityLevel = 5;
}
}
@@ -1545,7 +1703,7 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
// check if character is connectable to previous character,
if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
{
- nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nKashidaPos = aScanner.GetBegin() + nPrevIdx;
nPriorityLevel = 6;
}
}
@@ -1554,7 +1712,10 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
// Do not consider vowel marks when checking if a character
// can be connected to previous character.
if ( !isTransparentChar ( cCh) )
+ {
cPrevCh = cCh;
+ nPrevIdx = nIdx;
+ }
++nIdx;
} // end of current word
@@ -1776,29 +1937,47 @@ TextFrameIndex SwScriptInfo::NextBookmark(TextFrameIndex const nPos) const
{
for (auto const& it : m_Bookmarks)
{
- if (nPos < it.first)
+ if (nPos < std::get<0>(it))
{
- return it.first;
+ return std::get<0>(it);
}
}
return TextFrameIndex(COMPLETE_STRING);
}
-auto SwScriptInfo::GetBookmark(TextFrameIndex const nPos) const -> MarkKind
+std::vector<std::tuple<SwScriptInfo::MarkKind, Color, OUString>>
+ SwScriptInfo::GetBookmarks(TextFrameIndex const nPos)
{
- MarkKind ret{0};
+ std::vector<std::tuple<SwScriptInfo::MarkKind, Color, OUString>> aColors;
for (auto const& it : m_Bookmarks)
{
- if (nPos == it.first)
+ if (nPos == std::get<0>(it))
{
- ret |= it.second;
+ const OUString& sName = std::get<3>(it);
+ // filter hidden bookmarks imported from OOXML
+ // TODO import them as hidden bookmarks
+ if ( !( sName.startsWith("_Toc") || sName.startsWith("_Ref") ) )
+ aColors.push_back(std::tuple<MarkKind, Color,
+ OUString>(std::get<1>(it), std::get<2>(it), std::get<3>(it)));
}
- else if (nPos < it.first)
+ else if (nPos < std::get<0>(it))
{
break;
}
}
- return ret;
+
+ // sort bookmark boundary marks at the same position
+ // mark order: ] | [
+ // color order: [c1 [c2 [c3 ... c3] c2] c1]
+ sort(aColors.begin(), aColors.end(),
+ [](std::tuple<MarkKind, Color, OUString> const a, std::tuple<MarkKind, Color, OUString> const b) {
+ return (MarkKind::End == std::get<0>(a) && MarkKind::End != std::get<0>(b)) ||
+ (MarkKind::Point == std::get<0>(a) && MarkKind::Start == std::get<0>(b)) ||
+ // if both are end or start, order by color
+ (MarkKind::End == std::get<0>(a) && MarkKind::End == std::get<0>(b) && std::get<1>(a) < std::get<1>(b)) ||
+ (MarkKind::Start == std::get<0>(a) && MarkKind::Start == std::get<0>(b) && std::get<1>(b) < std::get<1>(a));});
+
+ return aColors;
}
// Takes a string and replaced the hidden ranges with cChar.
@@ -2019,7 +2198,7 @@ size_t SwScriptInfo::HasKana(TextFrameIndex const nStart, TextFrameIndex const n
return SAL_MAX_SIZE;
}
-tools::Long SwScriptInfo::Compress(tools::Long* pKernArray, TextFrameIndex nIdx, TextFrameIndex nLen,
+tools::Long SwScriptInfo::Compress(KernArray& rKernArray, TextFrameIndex nIdx, TextFrameIndex nLen,
const sal_uInt16 nCompress, const sal_uInt16 nFontHeight,
bool bCenter,
Point* pPoint ) const
@@ -2055,7 +2234,7 @@ tools::Long SwScriptInfo::Compress(tools::Long* pKernArray, TextFrameIndex nIdx,
return 0;
tools::Long nSub = 0;
- tools::Long nLast = nI ? pKernArray[ nI - 1 ] : 0;
+ tools::Long nLast = nI ? rKernArray[ nI - 1 ] : 0;
do
{
const CompType nType = GetCompType( nCompIdx );
@@ -2067,7 +2246,7 @@ tools::Long SwScriptInfo::Compress(tools::Long* pKernArray, TextFrameIndex nIdx,
nCompLen = nLen;
// are we allowed to compress the character?
- if ( pKernArray[ nI ] - nLast < nMinWidth )
+ if ( rKernArray[ nI ] - nLast < nMinWidth )
{
nIdx++; nI++;
}
@@ -2078,7 +2257,7 @@ tools::Long SwScriptInfo::Compress(tools::Long* pKernArray, TextFrameIndex nIdx,
SAL_WARN_IF( SwScriptInfo::NONE == nType, "sw.core", "None compression?!" );
// nLast is width of current character
- nLast -= pKernArray[ nI ];
+ nLast -= rKernArray[ nI ];
nLast *= nCompress;
tools::Long nMove = 0;
@@ -2101,10 +2280,11 @@ tools::Long SwScriptInfo::Compress(tools::Long* pKernArray, TextFrameIndex nIdx,
else
nLast /= 100000;
nSub -= nLast;
- nLast = pKernArray[ nI ];
+ nLast = rKernArray[ nI ];
if( nI && nMove )
- pKernArray[ nI - 1 ] += nMove;
- pKernArray[ nI++ ] -= nSub;
+ rKernArray.adjust(nI - 1, nMove);
+ rKernArray.adjust(nI, -nSub);
+ ++nI;
++nIdx;
}
}
@@ -2123,8 +2303,9 @@ tools::Long SwScriptInfo::Compress(tools::Long* pKernArray, TextFrameIndex nIdx,
while( nIdx < nTmpChg )
{
- nLast = pKernArray[ nI ];
- pKernArray[ nI++ ] -= nSub;
+ nLast = rKernArray[ nI ];
+ rKernArray.adjust(nI, -nSub);
+ ++nI;
++nIdx;
}
} while( nIdx < nLen );
@@ -2136,8 +2317,8 @@ tools::Long SwScriptInfo::Compress(tools::Long* pKernArray, TextFrameIndex nIdx,
// total number of kashida positions, or the number of kashida positions after some positions
// have been dropped, depending on the state of the m_KashidaInvalid set.
-sal_Int32 SwScriptInfo::KashidaJustify( tools::Long* pKernArray,
- tools::Long* pScrArray,
+sal_Int32 SwScriptInfo::KashidaJustify( KernArray* pKernArray,
+ sal_Bool* pKashidaArray,
TextFrameIndex const nStt,
TextFrameIndex const nLen,
tools::Long nSpaceAdd ) const
@@ -2193,6 +2374,11 @@ sal_Int32 SwScriptInfo::KashidaJustify( tools::Long* pKernArray,
{
TextFrameIndex nArrayPos = nIdx - nStt;
+ // mark Kashida insertion positions, code in VCL will use this
+ // array to know where to insert Kashida.
+ if (pKashidaArray)
+ pKashidaArray[sal_Int32(nArrayPos)] = true;
+
// next kashida position
++nCntKash;
while (nCntKash < nCntKashEnd && !IsKashidaValid(nCntKash))
@@ -2206,9 +2392,7 @@ sal_Int32 SwScriptInfo::KashidaJustify( tools::Long* pKernArray,
while ( nArrayPos < nArrayEnd )
{
- pKernArray[ sal_Int32(nArrayPos) ] += nKashAdd;
- if ( pScrArray )
- pScrArray[ sal_Int32(nArrayPos) ] += nKashAdd;
+ pKernArray->adjust(sal_Int32(nArrayPos), nKashAdd);
++nArrayPos;
}
nKashAdd += nSpaceAdd;
@@ -2395,13 +2579,13 @@ void SwScriptInfo::MarkKashidasInvalid(sal_Int32 const nCnt,
}
}
-TextFrameIndex SwScriptInfo::ThaiJustify( const OUString& rText, tools::Long* pKernArray,
- tools::Long* pScrArray, TextFrameIndex const nStt,
+TextFrameIndex SwScriptInfo::ThaiJustify( std::u16string_view aText, KernArray* pKernArray,
+ TextFrameIndex const nStt,
TextFrameIndex const nLen,
TextFrameIndex nNumberOfBlanks,
tools::Long nSpaceAdd )
{
- SAL_WARN_IF( nStt + nLen > TextFrameIndex(rText.getLength()), "sw.core", "String in ThaiJustify too small" );
+ SAL_WARN_IF( nStt + nLen > TextFrameIndex(aText.size()), "sw.core", "String in ThaiJustify too small" );
SwTwips nNumOfTwipsToDistribute = nSpaceAdd * sal_Int32(nNumberOfBlanks) /
SPACING_PRECISION_FACTOR;
@@ -2411,7 +2595,7 @@ TextFrameIndex SwScriptInfo::ThaiJustify( const OUString& rText, tools::Long* pK
for (sal_Int32 nI = 0; nI < sal_Int32(nLen); ++nI)
{
- const sal_Unicode cCh = rText[sal_Int32(nStt) + nI];
+ const sal_Unicode cCh = aText[sal_Int32(nStt) + nI];
// check if character is not above or below base
if ( ( 0xE34 > cCh || cCh > 0xE3A ) &&
@@ -2427,8 +2611,8 @@ TextFrameIndex SwScriptInfo::ThaiJustify( const OUString& rText, tools::Long* pK
++nCnt;
}
- if ( pKernArray ) pKernArray[ nI ] += nSpaceSum;
- if ( pScrArray ) pScrArray[ nI ] += nSpaceSum;
+ if (pKernArray)
+ pKernArray->adjust(nI, nSpaceSum);
}
return nCnt;
@@ -2485,6 +2669,40 @@ TextFrameIndex SwParaPortion::GetParLen() const
return nLen;
}
+bool SwParaPortion::HasNumberingPortion(FootnoteOrNot const eFootnote) const
+{
+ SwLinePortion const* pPortion(nullptr);
+ // the first line may contain only fly portion...
+ for (SwLineLayout const* pLine = this; pLine && !pPortion; pLine = pLine->GetNext())
+ {
+ pPortion = pLine->GetFirstPortion();
+ while (pPortion && (pPortion->InGlueGrp() || pPortion->IsKernPortion() || pPortion->IsFlyPortion()))
+ { // skip margins and fly spacers - numbering should be first then
+ pPortion = pPortion->GetNextPortion();
+ }
+ }
+ if (pPortion && pPortion->InHyphGrp())
+ { // weird special case, bullet with soft hyphen
+ pPortion = pPortion->GetNextPortion();
+ }
+ return pPortion && pPortion->InNumberGrp()
+ && (eFootnote == SwParaPortion::FootnoteToo || !pPortion->IsFootnoteNumPortion());
+}
+
+bool SwParaPortion::HasContentPortions() const
+{
+ SwLinePortion const* pPortion(nullptr);
+ for (SwLineLayout const* pLine = this; pLine && !pPortion; pLine = pLine->GetNext())
+ {
+ pPortion = pLine->GetFirstPortion();
+ while (pPortion && (pPortion->InGlueGrp() || pPortion->IsKernPortion() || pPortion->IsFlyPortion()))
+ { // skip margins and fly spacers
+ pPortion = pPortion->GetNextPortion();
+ }
+ }
+ return pPortion != nullptr;
+}
+
const SwDropPortion *SwParaPortion::FindDropPortion() const
{
const SwLineLayout *pLay = this;
@@ -2502,6 +2720,16 @@ const SwDropPortion *SwParaPortion::FindDropPortion() const
return nullptr;
}
+void SwParaPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwParaPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
void SwLineLayout::Init( SwLinePortion* pNextPortion )
{
Height( 0, false );
@@ -2558,6 +2786,32 @@ SwTwips SwTextFrame::HangingMargin() const
return nRet;
}
+SwTwips SwTextFrame::GetLowerMarginForFlyIntersect() const
+{
+ const IDocumentSettingAccess& rIDSA = GetDoc().getIDocumentSettingAccess();
+ if (!rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN))
+ {
+ // Word >= 2013 style or Writer style: lower margin is ignored when determining the text
+ // frame height.
+ return 0;
+ }
+
+ const SwAttrSet* pAttrSet = GetTextNodeForParaProps()->GetpSwAttrSet();
+ if (!pAttrSet)
+ {
+ return 0;
+ }
+
+ // If it has multiple lines, then probably it already has the needed fly portion.
+ // Limit this to empty paragraphs for now.
+ if ((GetPara() && GetPara()->GetNext()) || !GetText().isEmpty())
+ {
+ return 0;
+ }
+
+ return pAttrSet->GetULSpace().GetLower();
+}
+
void SwScriptInfo::selectHiddenTextProperty(const SwTextNode& rNode,
MultiSelection & rHiddenMulti,
std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> *const pBookmarks)
@@ -2565,9 +2819,8 @@ void SwScriptInfo::selectHiddenTextProperty(const SwTextNode& rNode,
assert((rNode.GetText().isEmpty() && rHiddenMulti.GetTotalRange().Len() == 1)
|| (rNode.GetText().getLength() == rHiddenMulti.GetTotalRange().Len()));
- const SfxPoolItem* pItem = nullptr;
- if( SfxItemState::SET == rNode.GetSwAttrSet().GetItemState( RES_CHRATR_HIDDEN, true, &pItem ) &&
- static_cast<const SvxCharHiddenItem*>(pItem)->GetValue() )
+ const SvxCharHiddenItem* pItem = rNode.GetSwAttrSet().GetItemIfSet( RES_CHRATR_HIDDEN );
+ if( pItem && pItem->GetValue() )
{
rHiddenMulti.SelectAll();
}
@@ -2593,7 +2846,7 @@ void SwScriptInfo::selectHiddenTextProperty(const SwTextNode& rNode,
}
}
- for (const SwIndex* pIndex = rNode.GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
+ for (const SwContentIndex* pIndex = rNode.GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
{
const sw::mark::IMark* pMark = pIndex->GetMark();
const sw::mark::IBookmark* pBookmark = dynamic_cast<const sw::mark::IBookmark*>(pMark);
@@ -2614,33 +2867,13 @@ void SwScriptInfo::selectHiddenTextProperty(const SwTextNode& rNode,
}
}
- bool bHide = false;
+ // condition is evaluated in DocumentFieldsManager::UpdateExpFields()
if (pBookmark && pBookmark->IsHidden())
{
- // bookmark is marked as hidden
- bHide = true;
-
- // bookmark is marked as hidden with conditions
- if (!pBookmark->GetHideCondition().isEmpty())
- {
- SwDoc& rDoc = const_cast<SwDoc&>(rNode.GetDoc());
- SwCalc aCalc(rDoc);
- rDoc.getIDocumentFieldsAccess().FieldsToCalc(aCalc, rNode.GetIndex(), SAL_MAX_INT32);
-
- SwSbxValue aValue = aCalc.Calculate(pBookmark->GetHideCondition());
- if(!aValue.IsVoidValue())
- {
- bHide = aValue.GetBool();
- }
- }
- }
-
- if (bHide)
- {
// intersect bookmark range with textnode range and add the intersection to rHiddenMulti
- const sal_Int32 nSt = pBookmark->GetMarkStart().nContent.GetIndex();
- const sal_Int32 nEnd = pBookmark->GetMarkEnd().nContent.GetIndex();
+ const sal_Int32 nSt = pBookmark->GetMarkStart().GetContentIndex();
+ const sal_Int32 nEnd = pBookmark->GetMarkEnd().GetContentIndex();
if( nEnd > nSt )
{
@@ -2666,7 +2899,7 @@ void SwScriptInfo::selectRedLineDeleted(const SwTextNode& rNode, MultiSelection
{
const SwRangeRedline* pRed = rIDRA.GetRedlineTable()[ nAct ];
- if (pRed->Start()->nNode > rNode.GetIndex())
+ if (pRed->Start()->GetNode() > rNode)
break;
if (pRed->GetType() != RedlineType::Delete)
@@ -2734,12 +2967,12 @@ TextFrameIndex SwScriptInfo::CountCJKCharacters(const OUString &rText,
return nCount;
}
-void SwScriptInfo::CJKJustify( const OUString& rText, tools::Long* pKernArray,
- tools::Long* pScrArray, TextFrameIndex const nStt,
+void SwScriptInfo::CJKJustify( const OUString& rText, KernArray& rKernArray,
+ TextFrameIndex const nStt,
TextFrameIndex const nLen, LanguageType aLang,
tools::Long nSpaceAdd, bool bIsSpaceStop )
{
- assert( pKernArray != nullptr && sal_Int32(nStt) >= 0 );
+ assert( sal_Int32(nStt) >= 0 );
if (sal_Int32(nLen) <= 0)
return;
@@ -2757,9 +2990,7 @@ void SwScriptInfo::CJKJustify( const OUString& rText, tools::Long* pKernArray,
if (nNext < sal_Int32(nStt + nLen) || !bIsSpaceStop)
nSpaceSum += nSpaceAdd;
}
- pKernArray[ nI ] += nSpaceSum;
- if ( pScrArray )
- pScrArray[ nI ] += nSpaceSum;
+ rKernArray.adjust(nI, nSpaceSum);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */