summaryrefslogtreecommitdiff
path: root/sw/source/core/text
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/text')
-rw-r--r--sw/source/core/text/EnhancedPDFExportHelper.cxx1279
-rw-r--r--sw/source/core/text/SwGrammarMarkUp.cxx6
-rw-r--r--sw/source/core/text/atrhndl.hxx17
-rw-r--r--sw/source/core/text/atrstck.cxx70
-rw-r--r--sw/source/core/text/frmcrsr.cxx40
-rw-r--r--sw/source/core/text/frmform.cxx432
-rw-r--r--sw/source/core/text/frminf.cxx4
-rw-r--r--sw/source/core/text/frmpaint.cxx79
-rw-r--r--sw/source/core/text/guess.cxx297
-rw-r--r--sw/source/core/text/guess.hxx6
-rw-r--r--sw/source/core/text/inftxt.cxx649
-rw-r--r--sw/source/core/text/inftxt.hxx77
-rw-r--r--sw/source/core/text/itradj.cxx37
-rw-r--r--sw/source/core/text/itratr.cxx269
-rw-r--r--sw/source/core/text/itratr.hxx8
-rw-r--r--sw/source/core/text/itrcrsr.cxx304
-rw-r--r--sw/source/core/text/itrform2.cxx644
-rw-r--r--sw/source/core/text/itrform2.hxx4
-rw-r--r--sw/source/core/text/itrpaint.cxx137
-rw-r--r--sw/source/core/text/itrpaint.hxx10
-rw-r--r--sw/source/core/text/itrtxt.cxx54
-rw-r--r--sw/source/core/text/itrtxt.hxx21
-rw-r--r--sw/source/core/text/noteurl.cxx37
-rw-r--r--sw/source/core/text/pordrop.hxx1
-rw-r--r--sw/source/core/text/porexp.cxx77
-rw-r--r--sw/source/core/text/porexp.hxx7
-rw-r--r--sw/source/core/text/porfld.cxx231
-rw-r--r--sw/source/core/text/porfld.hxx33
-rw-r--r--sw/source/core/text/porfly.cxx92
-rw-r--r--sw/source/core/text/porfly.hxx4
-rw-r--r--sw/source/core/text/porftn.hxx2
-rw-r--r--sw/source/core/text/porglue.cxx31
-rw-r--r--sw/source/core/text/porglue.hxx7
-rw-r--r--sw/source/core/text/porhyph.hxx3
-rw-r--r--sw/source/core/text/porlay.cxx757
-rw-r--r--sw/source/core/text/porlay.hxx34
-rw-r--r--sw/source/core/text/porlin.cxx66
-rw-r--r--sw/source/core/text/porlin.hxx57
-rw-r--r--sw/source/core/text/pormulti.cxx316
-rw-r--r--sw/source/core/text/pormulti.hxx13
-rw-r--r--sw/source/core/text/porref.cxx2
-rw-r--r--sw/source/core/text/porrst.cxx342
-rw-r--r--sw/source/core/text/porrst.hxx47
-rw-r--r--sw/source/core/text/portox.cxx2
-rw-r--r--sw/source/core/text/portxt.cxx146
-rw-r--r--sw/source/core/text/portxt.hxx6
-rw-r--r--sw/source/core/text/possiz.hxx32
-rw-r--r--sw/source/core/text/redlnitr.cxx203
-rw-r--r--sw/source/core/text/redlnitr.hxx26
-rw-r--r--sw/source/core/text/txtcache.hxx3
-rw-r--r--sw/source/core/text/txtdrop.cxx44
-rw-r--r--sw/source/core/text/txtfld.cxx383
-rw-r--r--sw/source/core/text/txtfly.cxx375
-rw-r--r--sw/source/core/text/txtfrm.cxx587
-rw-r--r--sw/source/core/text/txtftn.cxx70
-rw-r--r--sw/source/core/text/txthyph.cxx11
-rw-r--r--sw/source/core/text/txtinit.cxx2
-rw-r--r--sw/source/core/text/txttab.cxx119
-rw-r--r--sw/source/core/text/widorp.cxx225
-rw-r--r--sw/source/core/text/widorp.hxx12
-rw-r--r--sw/source/core/text/wrong.cxx25
-rw-r--r--sw/source/core/text/xmldump.cxx691
62 files changed, 6530 insertions, 3035 deletions
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx
index 088f21c00b10..bbe216898f70 100644
--- a/sw/source/core/text/EnhancedPDFExportHelper.cxx
+++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx
@@ -17,11 +17,12 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
+#include <EnhancedPDFExportHelper.hxx>
+
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
-#include <EnhancedPDFExportHelper.hxx>
#include <hintids.hxx>
#include <sot/exchange.hxx>
@@ -33,6 +34,7 @@
#include <editeng/langitem.hxx>
#include <tools/urlobj.hxx>
#include <svl/languageoptions.hxx>
+#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <swatrset.hxx>
#include <frmatr.hxx>
@@ -65,36 +67,35 @@
#include <rowfrm.hxx>
#include <cellfrm.hxx>
#include <sectfrm.hxx>
+#include <ftnfrm.hxx>
#include <flyfrm.hxx>
#include <notxtfrm.hxx>
#include "porfld.hxx"
+#include "pormulti.hxx"
#include <SwStyleNameMapper.hxx>
#include "itrpaint.hxx"
#include <i18nlangtag/languagetag.hxx>
#include <IMark.hxx>
#include <printdata.hxx>
+#include <vprint.hxx>
#include <SwNodeNum.hxx>
#include <calbck.hxx>
-#include <stack>
#include <frmtool.hxx>
#include <strings.hrc>
#include <frameformats.hxx>
+#include <tblafmt.hxx>
#include <authfld.hxx>
+#include <dcontact.hxx>
#include <tools/globname.hxx>
#include <svx/svdobj.hxx>
-using namespace ::com::sun::star;
-
-// Some static data structures
-
-TableColumnsMap SwEnhancedPDFExportHelper::s_aTableColumnsMap;
-LinkIdMap SwEnhancedPDFExportHelper::s_aLinkIdMap;
-NumListIdMap SwEnhancedPDFExportHelper::s_aNumListIdMap;
-NumListBodyIdMap SwEnhancedPDFExportHelper::s_aNumListBodyIdMap;
-FrameTagIdMap SwEnhancedPDFExportHelper::s_aFrameTagIdMap;
+#include <stack>
+#include <map>
+#include <set>
+#include <optional>
-LanguageType SwEnhancedPDFExportHelper::s_eLanguageDefault = LANGUAGE_SYSTEM;
+using namespace ::com::sun::star;
#if OSL_DEBUG_LEVEL > 1
@@ -125,19 +126,58 @@ void lcl_DBGCheckStack()
#endif
+typedef std::set< tools::Long, lt_TableColumn > TableColumnsMapEntry;
+typedef std::pair< SwRect, sal_Int32 > IdMapEntry;
+typedef std::vector< IdMapEntry > LinkIdMap;
+typedef std::map< const SwTable*, TableColumnsMapEntry > TableColumnsMap;
+typedef std::map< const SwNumberTreeNode*, sal_Int32 > NumListIdMap;
+typedef std::map< const SwNumberTreeNode*, sal_Int32 > NumListBodyIdMap;
+typedef std::set<const void*> FrameTagSet;
+
+struct SwEnhancedPDFState
+{
+ TableColumnsMap m_TableColumnsMap;
+ LinkIdMap m_LinkIdMap;
+ NumListIdMap m_NumListIdMap;
+ NumListBodyIdMap m_NumListBodyIdMap;
+ FrameTagSet m_FrameTagSet;
+
+ LanguageType m_eLanguageDefault;
+
+ struct Span
+ {
+ FontLineStyle eUnderline;
+ FontLineStyle eOverline;
+ FontStrikeout eStrikeout;
+ FontEmphasisMark eFontEmphasis;
+ short nEscapement;
+ SwFontScript nScript;
+ LanguageType nLang;
+ OUString StyleName;
+ };
+
+ ::std::optional<Span> m_oCurrentSpan;
+ ::std::optional<SwTextAttr const*> m_oCurrentLink;
+
+ SwEnhancedPDFState(LanguageType const eLanguageDefault)
+ : m_eLanguageDefault(eLanguageDefault)
+ {
+ }
+};
+
namespace
{
// ODF Style Names:
-const char aTableHeadingName[] = "Table Heading";
-const char aQuotations[] = "Quotations";
-const char aCaption[] = "Caption";
-const char aHeading[] = "Heading";
-const char aQuotation[] = "Quotation";
-const char aSourceText[] = "Source Text";
+constexpr OUString aTableHeadingName = u"Table Heading"_ustr;
+constexpr OUString aQuotations = u"Quotations"_ustr;
+constexpr OUString aCaption = u"Caption"_ustr;
+constexpr OUString aHeading = u"Heading"_ustr;
+constexpr OUString aQuotation = u"Quotation"_ustr;
+constexpr OUString aSourceText = u"Source Text"_ustr;
// PDF Tag Names:
constexpr OUStringLiteral aDocumentString = u"Document";
-constexpr OUStringLiteral aDivString = u"Div";
+constexpr OUString aDivString = u"Div"_ustr;
constexpr OUStringLiteral aSectString = u"Sect";
constexpr OUStringLiteral aHString = u"H";
constexpr OUStringLiteral aH1String = u"H1";
@@ -146,11 +186,16 @@ constexpr OUStringLiteral aH3String = u"H3";
constexpr OUStringLiteral aH4String = u"H4";
constexpr OUStringLiteral aH5String = u"H5";
constexpr OUStringLiteral aH6String = u"H6";
+constexpr OUStringLiteral aH7String = u"H7";
+constexpr OUStringLiteral aH8String = u"H8";
+constexpr OUStringLiteral aH9String = u"H9";
+constexpr OUStringLiteral aH10String = u"H10";
constexpr OUStringLiteral aListString = u"L";
constexpr OUStringLiteral aListItemString = u"LI";
-constexpr OUStringLiteral aListBodyString = u"LBody";
+constexpr OUStringLiteral aListLabelString = u"Lbl";
+constexpr OUString aListBodyString = u"LBody"_ustr;
constexpr OUStringLiteral aBlockQuoteString = u"BlockQuote";
-constexpr OUStringLiteral aCaptionString = u"Caption";
+constexpr OUString aCaptionString = u"Caption"_ustr;
constexpr OUStringLiteral aIndexString = u"Index";
constexpr OUStringLiteral aTOCString = u"TOC";
constexpr OUStringLiteral aTOCIString = u"TOCI";
@@ -160,11 +205,11 @@ constexpr OUStringLiteral aTDString = u"TD";
constexpr OUStringLiteral aTHString = u"TH";
constexpr OUStringLiteral aBibEntryString = u"BibEntry";
constexpr OUStringLiteral aQuoteString = u"Quote";
-constexpr OUStringLiteral aSpanString = u"Span";
+constexpr OUString aSpanString = u"Span"_ustr;
constexpr OUStringLiteral aCodeString = u"Code";
constexpr OUStringLiteral aFigureString = u"Figure";
constexpr OUStringLiteral aFormulaString = u"Formula";
-constexpr OUStringLiteral aLinkString = u"Link";
+constexpr OUString aLinkString = u"Link"_ustr;
constexpr OUStringLiteral aNoteString = u"Note";
// returns true if first paragraph in cell frame has 'table heading' style
@@ -183,6 +228,22 @@ bool lcl_IsHeadlineCell( const SwCellFrame& rCellFrame )
bRet = sStyleName == aTableHeadingName;
}
+ // tdf#153935 wild guessing for 1st row based on table autoformat
+ if (!bRet && !rCellFrame.GetUpper()->GetPrev())
+ {
+ SwTable const*const pTable(rCellFrame.FindTabFrame()->GetTable());
+ assert(pTable);
+ OUString const& rStyleName(pTable->GetTableStyleName());
+ if (!rStyleName.isEmpty())
+ {
+ if (SwTableAutoFormat const*const pTableAF =
+ pTable->GetFrameFormat()->GetDoc()->GetTableStyles().FindAutoFormat(rStyleName))
+ {
+ bRet |= pTableAF->HasHeaderRow();
+ }
+ }
+ }
+
return bRet;
}
@@ -208,25 +269,29 @@ bool lcl_IsInNonStructEnv( const SwFrame& rFrame )
}
// Generate key from frame for reopening tags:
-void* lcl_GetKeyFromFrame( const SwFrame& rFrame )
+void const* lcl_GetKeyFromFrame( const SwFrame& rFrame )
{
- void* pKey = nullptr;
+ void const* pKey = nullptr;
if ( rFrame.IsPageFrame() )
- pKey = const_cast<void*>(static_cast<void const *>(&(static_cast<const SwPageFrame&>(rFrame).GetFormat()->getIDocumentSettingAccess())));
+ pKey = static_cast<void const *>(&(static_cast<const SwPageFrame&>(rFrame).GetFormat()->getIDocumentSettingAccess()));
else if ( rFrame.IsTextFrame() )
- pKey = const_cast<void*>(static_cast<void const *>(static_cast<const SwTextFrame&>(rFrame).GetTextNodeFirst()));
+ pKey = static_cast<void const *>(static_cast<const SwTextFrame&>(rFrame).GetTextNodeFirst());
else if ( rFrame.IsSctFrame() )
- pKey = const_cast<void*>(static_cast<void const *>(static_cast<const SwSectionFrame&>(rFrame).GetSection()));
+ pKey = static_cast<void const *>(static_cast<const SwSectionFrame&>(rFrame).GetSection());
else if ( rFrame.IsTabFrame() )
- pKey = const_cast<void*>(static_cast<void const *>(static_cast<const SwTabFrame&>(rFrame).GetTable()));
+ pKey = static_cast<void const *>(static_cast<const SwTabFrame&>(rFrame).GetTable());
else if ( rFrame.IsRowFrame() )
- pKey = const_cast<void*>(static_cast<void const *>(static_cast<const SwRowFrame&>(rFrame).GetTabLine()));
+ pKey = static_cast<void const *>(static_cast<const SwRowFrame&>(rFrame).GetTabLine());
else if ( rFrame.IsCellFrame() )
{
const SwTabFrame* pTabFrame = rFrame.FindTabFrame();
const SwTable* pTable = pTabFrame->GetTable();
- pKey = const_cast<void*>(static_cast<void const *>(& static_cast<const SwCellFrame&>(rFrame).GetTabBox()->FindStartOfRowSpan( *pTable )));
+ pKey = static_cast<void const *>(& static_cast<const SwCellFrame&>(rFrame).GetTabBox()->FindStartOfRowSpan(*pTable));
+ }
+ else if (rFrame.IsFootnoteFrame())
+ {
+ pKey = static_cast<void const*>(static_cast<SwFootnoteFrame const&>(rFrame).GetAttr());
}
return pKey;
@@ -272,7 +337,7 @@ bool lcl_TryMoveToNonHiddenField(SwEditShell& rShell, const SwTextNode& rNd, con
// 3. Check for hidden text attribute
if(rNd.IsHidden())
return false;
- if(!rShell.GotoFormatField(rField) || rShell.SelectHiddenRange())
+ if(!rShell.GotoFormatField(rField) || rShell.IsInHiddenRange(/*bSelect=*/false))
{
rShell.SwCursorShell::ClearMark();
return false;
@@ -280,6 +345,40 @@ bool lcl_TryMoveToNonHiddenField(SwEditShell& rShell, const SwTextNode& rNd, con
return true;
};
+// tdf#157816: try to check if the rectangle contains actual text
+::std::vector<SwRect> GetCursorRectsContainingText(SwCursorShell const& rShell)
+{
+ ::std::vector<SwRect> ret;
+ SwRects rects;
+ rShell.GetLayout()->CalcFrameRects(*rShell.GetCursor_(), rects, SwRootFrame::RectsMode::NoAnchoredFlys);
+ for (SwRect const& rRect : rects)
+ {
+ Point center(rRect.Center());
+ SwSpecialPos special;
+ SwCursorMoveState cms(CursorMoveState::NONE);
+ cms.m_pSpecialPos = &special;
+ cms.m_bFieldInfo = true;
+ SwPosition pos(rShell.GetDoc()->GetNodes());
+ auto const [pStart, pEnd] = rShell.GetCursor_()->StartEnd();
+ if (rShell.GetLayout()->GetModelPositionForViewPoint(&pos, center, &cms)
+ && *pStart <= pos && pos <= *pEnd)
+ {
+ SwRect charRect;
+ std::pair<Point, bool> const tmp(center, false);
+ SwContentFrame const*const pFrame(
+ pos.nNode.GetNode().GetTextNode()->getLayoutFrame(rShell.GetLayout(), &pos, &tmp));
+ if (pFrame->GetCharRect(charRect, pos, &cms, false)
+ && rRect.Overlaps(charRect))
+ {
+ ret.push_back(rRect);
+ }
+ }
+ // reset stupid static var that may have gotten set now
+ SwTextCursor::SetRightMargin(false); // WTF is this crap
+ }
+ return ret;
+}
+
} // end namespace
SwTaggedPDFHelper::SwTaggedPDFHelper( const Num_Info* pNumInfo,
@@ -336,10 +435,16 @@ SwTaggedPDFHelper::~SwTaggedPDFHelper()
#endif
}
+void const* SwDrawContact::GetPDFAnchorStructureElementKey(SdrObject const& rObj)
+{
+ SwFrame const*const pAnchorFrame(GetAnchoredObj(&rObj)->GetAnchorFrame());
+ return pAnchorFrame ? lcl_GetKeyFromFrame(*pAnchorFrame) : nullptr;
+}
+
bool SwTaggedPDFHelper::CheckReopenTag()
{
bool bRet = false;
- sal_Int32 nReopenTag = -1;
+ void const* pReopenKey(nullptr);
bool bContinue = false; // in some cases we just have to reopen a tag without early returning
if ( mpFrameInfo )
@@ -356,12 +461,13 @@ bool SwTaggedPDFHelper::CheckReopenTag()
// - rFrame is a cell frame in a follow flow row (reopen TableData tag)
if ( ( rFrame.IsPageFrame() && static_cast<const SwPageFrame&>(rFrame).GetPrev() ) ||
( rFrame.IsFlowFrame() && SwFlowFrame::CastFlowFrame(&rFrame)->IsFollow() ) ||
+ (rFrame.IsFootnoteFrame() && static_cast<SwFootnoteFrame const&>(rFrame).GetMaster()) ||
( rFrame.IsRowFrame() && rFrame.IsInFollowFlowRow() ) ||
( rFrame.IsCellFrame() && const_cast<SwFrame&>(rFrame).GetPrevCellLeaf() ) )
{
pKeyFrame = &rFrame;
}
- else if ( rFrame.IsFlyFrame() )
+ else if (rFrame.IsFlyFrame() && !mpFrameInfo->m_isLink)
{
const SwFormatAnchor& rAnchor =
static_cast<const SwFlyFrame*>(&rFrame)->GetFormat()->GetAnchor();
@@ -376,29 +482,29 @@ bool SwTaggedPDFHelper::CheckReopenTag()
if ( pKeyFrame )
{
- void* pKey = lcl_GetKeyFromFrame( *pKeyFrame );
-
- if ( pKey )
+ void const*const pKey = lcl_GetKeyFromFrame(*pKeyFrame);
+ FrameTagSet& rFrameTagSet(mpPDFExtOutDevData->GetSwPDFState()->m_FrameTagSet);
+ if (rFrameTagSet.find(pKey) != rFrameTagSet.end()
+ || rFrame.IsFlyFrame()) // for hell layer flys
{
- FrameTagIdMap& rFrameTagIdMap = SwEnhancedPDFExportHelper::GetFrameTagIdMap();
- const FrameTagIdMap::const_iterator aIter = rFrameTagIdMap.find( pKey );
- if ( aIter != rFrameTagIdMap.end() )
- nReopenTag = (*aIter).second;
+ pReopenKey = pKey;
}
}
}
- if ( -1 != nReopenTag )
+ if (pReopenKey)
{
+ // note: it would be possible to get rid of the SetCurrentStructureElement()
+ // - which is quite ugly - for most cases by recreating the parents until the
+ // current ancestor, but there are special cases cell frame rowspan > 1 follow
+ // and footnote frame follow where the parent of the follow is different from
+ // the parent of the first one, and so in PDFExtOutDevData the wrong parent
+ // would be restored and used for next elements.
m_nRestoreCurrentTag = mpPDFExtOutDevData->GetCurrentStructureElement();
- const bool bSuccess = mpPDFExtOutDevData->SetCurrentStructureElement( nReopenTag );
- OSL_ENSURE( bSuccess, "Failed to reopen tag" );
-
-#if OSL_DEBUG_LEVEL > 1
- aStructStack.push_back( 99 );
-#endif
+ sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement(pReopenKey);
+ mpPDFExtOutDevData->SetCurrentStructureElement(id);
- bRet = bSuccess;
+ bRet = true;
}
return bRet && !bContinue;
@@ -408,8 +514,7 @@ void SwTaggedPDFHelper::CheckRestoreTag() const
{
if ( m_nRestoreCurrentTag != -1 )
{
- const bool bSuccess = mpPDFExtOutDevData->SetCurrentStructureElement( m_nRestoreCurrentTag );
- OSL_ENSURE( bSuccess, "Failed to restore reopened tag" );
+ mpPDFExtOutDevData->SetCurrentStructureElement( m_nRestoreCurrentTag );
#if OSL_DEBUG_LEVEL > 1
aStructStack.pop_back();
@@ -417,16 +522,62 @@ void SwTaggedPDFHelper::CheckRestoreTag() const
}
}
-void SwTaggedPDFHelper::BeginTag( vcl::PDFWriter::StructElement eType, const OUString& rString )
+void SwTaggedPDFHelper::OpenTagImpl(void const*const pKey)
+{
+ sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement(pKey);
+ mpPDFExtOutDevData->BeginStructureElement(id);
+ ++m_nEndStructureElement;
+
+#if OSL_DEBUG_LEVEL > 1
+ aStructStack.push_back( 99 );
+#endif
+}
+
+sal_Int32 SwTaggedPDFHelper::BeginTagImpl(void const*const pKey,
+ vcl::PDFWriter::StructElement const eType, const OUString& rString)
{
// write new tag
- const sal_Int32 nId = mpPDFExtOutDevData->BeginStructureElement( eType, rString );
+ const sal_Int32 nId = mpPDFExtOutDevData->EnsureStructureElement(pKey);
+ mpPDFExtOutDevData->InitStructureElement(nId, eType, rString);
+ mpPDFExtOutDevData->BeginStructureElement(nId);
++m_nEndStructureElement;
#if OSL_DEBUG_LEVEL > 1
- aStructStack.push_back( static_cast<sal_uInt16>(eType) );
+ aStructStack.push_back( o3tl::narrowing<sal_uInt16>(eType) );
#endif
+ return nId;
+}
+
+void SwTaggedPDFHelper::BeginTag( vcl::PDFWriter::StructElement eType, const OUString& rString )
+{
+ void const* pKey(nullptr);
+
+ if (mpFrameInfo)
+ {
+ const SwFrame& rFrame = mpFrameInfo->mrFrame;
+
+ if ( ( rFrame.IsPageFrame() && !static_cast<const SwPageFrame&>(rFrame).GetPrev() ) ||
+ ( rFrame.IsFlowFrame() && !SwFlowFrame::CastFlowFrame(&rFrame)->IsFollow() && SwFlowFrame::CastFlowFrame(&rFrame)->HasFollow() ) ||
+ rFrame.IsSctFrame() || // all of them, so that opening parent sections works
+ ( rFrame.IsTextFrame() && rFrame.GetDrawObjs() ) ||
+ (rFrame.IsFootnoteFrame() && static_cast<SwFootnoteFrame const&>(rFrame).GetFollow()) ||
+ ( rFrame.IsRowFrame() && rFrame.IsInSplitTableRow() ) ||
+ ( rFrame.IsCellFrame() && const_cast<SwFrame&>(rFrame).GetNextCellLeaf() ) )
+ {
+ pKey = lcl_GetKeyFromFrame(rFrame);
+
+ if (pKey)
+ {
+ FrameTagSet& rFrameTagSet(mpPDFExtOutDevData->GetSwPDFState()->m_FrameTagSet);
+ assert(rFrameTagSet.find(pKey) == rFrameTagSet.end());
+ rFrameTagSet.emplace(pKey);
+ }
+ }
+ }
+
+ sal_Int32 const nId = BeginTagImpl(pKey, eType, rString);
+
// Store the id of the current structure element if
// - it is a list structure element
// - it is a list body element with children
@@ -437,40 +588,21 @@ void SwTaggedPDFHelper::BeginTag( vcl::PDFWriter::StructElement eType, const OUS
if ( mpNumInfo )
{
- const SwTextFrame& rTextFrame = static_cast<const SwTextFrame&>(mpNumInfo->mrFrame);
+ const SwTextFrame& rTextFrame = mpNumInfo->mrFrame;
SwTextNode const*const pTextNd = rTextFrame.GetTextNodeForParaProps();
const SwNodeNum* pNodeNum = pTextNd->GetNum(rTextFrame.getRootFrame());
if ( vcl::PDFWriter::List == eType )
{
- NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap();
+ NumListIdMap& rNumListIdMap(mpPDFExtOutDevData->GetSwPDFState()->m_NumListIdMap);
rNumListIdMap[ pNodeNum ] = nId;
}
else if ( vcl::PDFWriter::LIBody == eType )
{
- NumListBodyIdMap& rNumListBodyIdMap = SwEnhancedPDFExportHelper::GetNumListBodyIdMap();
+ NumListBodyIdMap& rNumListBodyIdMap(mpPDFExtOutDevData->GetSwPDFState()->m_NumListBodyIdMap);
rNumListBodyIdMap[ pNodeNum ] = nId;
}
}
- else if ( mpFrameInfo )
- {
- const SwFrame& rFrame = mpFrameInfo->mrFrame;
-
- if ( ( rFrame.IsPageFrame() && !static_cast<const SwPageFrame&>(rFrame).GetPrev() ) ||
- ( rFrame.IsFlowFrame() && !SwFlowFrame::CastFlowFrame(&rFrame)->IsFollow() && SwFlowFrame::CastFlowFrame(&rFrame)->HasFollow() ) ||
- ( rFrame.IsTextFrame() && rFrame.GetDrawObjs() ) ||
- ( rFrame.IsRowFrame() && rFrame.IsInSplitTableRow() ) ||
- ( rFrame.IsCellFrame() && const_cast<SwFrame&>(rFrame).GetNextCellLeaf() ) )
- {
- const void* pKey = lcl_GetKeyFromFrame( rFrame );
-
- if ( pKey )
- {
- FrameTagIdMap& rFrameTagIdMap = SwEnhancedPDFExportHelper::GetFrameTagIdMap();
- rFrameTagIdMap[ pKey ] = nId;
- }
- }
- }
SetAttributes( eType );
}
@@ -484,6 +616,23 @@ void SwTaggedPDFHelper::EndTag()
#endif
}
+namespace {
+
+ // link the link annotation to the link structured element
+ void LinkLinkLink(vcl::PDFExtOutDevData & rPDFExtOutDevData, SwRect const& rRect)
+ {
+ const LinkIdMap& rLinkIdMap(rPDFExtOutDevData.GetSwPDFState()->m_LinkIdMap);
+ const Point aCenter = rRect.Center();
+ auto aIter = std::find_if(rLinkIdMap.begin(), rLinkIdMap.end(),
+ [&aCenter](const IdMapEntry& rEntry) { return rEntry.first.Contains(aCenter); });
+ if (aIter != rLinkIdMap.end())
+ {
+ sal_Int32 nLinkId = (*aIter).second;
+ rPDFExtOutDevData.SetStructureAttributeNumerical(vcl::PDFWriter::LinkAnnotation, nLinkId);
+ }
+ }
+}
+
// Sets the attributes according to the structure type.
void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
{
@@ -510,6 +659,7 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
bool bHeight = false;
bool bBox = false;
bool bRowSpan = false;
+ bool bAltText = false;
// Check which attributes to set:
@@ -519,6 +669,10 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
bWritingMode = true;
break;
+ case vcl::PDFWriter::Note:
+ bPlacement = true;
+ break;
+
case vcl::PDFWriter::Table :
bPlacement =
bWritingMode =
@@ -537,6 +691,8 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
break;
case vcl::PDFWriter::TableHeader :
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Scope, vcl::PDFWriter::Column);
+ [[fallthrough]];
case vcl::PDFWriter::TableData :
bPlacement =
bWritingMode =
@@ -545,6 +701,12 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
bRowSpan = true;
break;
+ case vcl::PDFWriter::Caption:
+ if (pFrame->IsSctFrame())
+ {
+ break;
+ }
+ [[fallthrough]];
case vcl::PDFWriter::H1 :
case vcl::PDFWriter::H2 :
case vcl::PDFWriter::H3 :
@@ -553,7 +715,6 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
case vcl::PDFWriter::H6 :
case vcl::PDFWriter::Paragraph :
case vcl::PDFWriter::Heading :
- case vcl::PDFWriter::Caption :
case vcl::PDFWriter::BlockQuote :
bPlacement =
@@ -568,11 +729,33 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
case vcl::PDFWriter::Formula :
case vcl::PDFWriter::Figure :
+ bAltText =
bPlacement =
bWidth =
bHeight =
bBox = true;
break;
+
+ case vcl::PDFWriter::Division:
+ if (pFrame->IsFlyFrame()) // this can be something else too
+ {
+ bAltText = true;
+ bBox = true;
+ }
+ break;
+
+ case vcl::PDFWriter::NonStructElement:
+ if (pFrame->IsHeaderFrame() || pFrame->IsFooterFrame())
+ {
+ // ISO 14289-1:2014, Clause: 7.8
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Type, vcl::PDFWriter::Pagination);
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Subtype,
+ pFrame->IsHeaderFrame()
+ ? vcl::PDFWriter::Header
+ : vcl::PDFWriter::Footer);
+ }
+ break;
+
default :
break;
}
@@ -632,9 +815,9 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
if ( bTextIndent )
{
OSL_ENSURE( pFrame->IsTextFrame(), "Frame type <-> tag attribute mismatch" );
- const SvxLRSpaceItem &rSpace =
- static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace();
- nVal = rSpace.GetTextFirstLineOffset();
+ const SvxFirstLineIndentItem& rFirstLine(
+ static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent());
+ nVal = rFirstLine.GetTextFirstLineOffset();
if ( 0 != nVal )
mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::TextIndent, nVal );
}
@@ -658,9 +841,25 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
}
}
- // Formally here bAlternateText was triggered for PDF export, but this
- // was moved for more general use to primitives and usage in
- // VclMetafileProcessor2D (see processGraphicPrimitive2D).
+ // ISO 14289-1:2014, Clause: 7.3
+ // ISO 14289-1:2014, Clause: 7.7
+ // For images (but not embedded objects), an ObjectInfoPrimitive2D is
+ // created, but it's not evaluated by VclMetafileProcessor2D any more;
+ // that would require producing StructureTagPrimitive2D here but that
+ // looks impossible so instead duplicate the code that sets the Alt
+ // text here again.
+ if (bAltText)
+ {
+ SwFlyFrameFormat const& rFly(*static_cast<SwFlyFrame const*>(pFrame)->GetFormat());
+ OUString const sep(
+ (rFly.GetObjTitle().isEmpty() || rFly.GetObjDescription().isEmpty())
+ ? OUString() : OUString(" - "));
+ OUString const altText(rFly.GetObjTitle() + sep + rFly.GetObjDescription());
+ if (!altText.isEmpty())
+ {
+ mpPDFExtOutDevData->SetAlternateText(altText);
+ }
+ }
if ( bWidth )
{
@@ -688,9 +887,9 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
if ( bRowSpan )
{
- const SwCellFrame* pThisCell = dynamic_cast<const SwCellFrame*>(pFrame);
- if ( pThisCell )
+ if ( pFrame->IsCellFrame() )
{
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(pFrame);
nVal = pThisCell->GetTabBox()->getRowSpan();
if ( nVal > 1 )
mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::RowSpan, nVal );
@@ -701,7 +900,7 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
SwRectFnSet fnRectX(pTabFrame);
- const TableColumnsMapEntry& rCols = SwEnhancedPDFExportHelper::GetTableColumnsMap()[ pTable ];
+ const TableColumnsMapEntry& rCols(mpPDFExtOutDevData->GetSwPDFState()->m_TableColumnsMap[pTable]);
const tools::Long nLeft = fnRectX.GetLeft(pThisCell->getFrameArea());
const tools::Long nRight = fnRectX.GetRight(pThisCell->getFrameArea());
@@ -717,6 +916,12 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
}
}
}
+
+ if (mpFrameInfo->m_isLink)
+ {
+ SwRect const aRect(mpFrameInfo->mrFrame.getFrameArea());
+ LinkLinkLink(*mpPDFExtOutDevData, aRect);
+ }
}
/*
@@ -758,6 +963,58 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
bLanguage = true;
break;
+ case vcl::PDFWriter::BibEntry :
+ bTextDecorationType =
+ bBaselineShift =
+ bLinkAttribute =
+ bLanguage = true;
+ break;
+
+ case vcl::PDFWriter::RT:
+ {
+ SwRubyPortion const*const pRuby(static_cast<SwRubyPortion const*>(pPor));
+ vcl::PDFWriter::StructAttributeValue nAlign = {};
+ switch (pRuby->GetAdjustment())
+ {
+ case text::RubyAdjust_LEFT:
+ nAlign = vcl::PDFWriter::RStart;
+ break;
+ case text::RubyAdjust_CENTER:
+ nAlign = vcl::PDFWriter::RCenter;
+ break;
+ case text::RubyAdjust_RIGHT:
+ nAlign = vcl::PDFWriter::REnd;
+ break;
+ case text::RubyAdjust_BLOCK:
+ nAlign = vcl::PDFWriter::RJustify;
+ break;
+ case text::RubyAdjust_INDENT_BLOCK:
+ nAlign = vcl::PDFWriter::RDistribute;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ ::std::optional<vcl::PDFWriter::StructAttributeValue> oPos;
+ switch (pRuby->GetRubyPosition())
+ {
+ case RubyPosition::ABOVE:
+ oPos = vcl::PDFWriter::RBefore;
+ break;
+ case RubyPosition::BELOW:
+ oPos = vcl::PDFWriter::RAfter;
+ break;
+ case RubyPosition::RIGHT:
+ break; // no such thing???
+ }
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::RubyAlign, nAlign);
+ if (oPos)
+ {
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::RubyPosition, *oPos);
+ }
+ }
+ break;
+
default:
break;
}
@@ -802,7 +1059,7 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
{
const LanguageType nCurrentLanguage = rInf.GetFont()->GetLanguage();
- const LanguageType nDefaultLang = SwEnhancedPDFExportHelper::GetDefaultLanguage();
+ const LanguageType nDefaultLang(mpPDFExtOutDevData->GetSwPDFState()->m_eLanguageDefault);
if ( nDefaultLang != nCurrentLanguage )
mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::Language, static_cast<sal_uInt16>(nCurrentLanguage) );
@@ -810,18 +1067,55 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
if ( bLinkAttribute )
{
- const LinkIdMap& rLinkIdMap = SwEnhancedPDFExportHelper::GetLinkIdMap();
SwRect aPorRect;
rInf.CalcRect( *pPor, &aPorRect );
- const Point aPorCenter = aPorRect.Center();
- auto aIter = std::find_if(rLinkIdMap.begin(), rLinkIdMap.end(),
- [&aPorCenter](const IdMapEntry& rEntry) { return rEntry.first.IsInside(aPorCenter); });
- if (aIter != rLinkIdMap.end())
+ LinkLinkLink(*mpPDFExtOutDevData, aPorRect);
+ }
+ }
+ else if (mpNumInfo && eType == vcl::PDFWriter::List)
+ {
+ SwTextFrame const& rFrame(mpNumInfo->mrFrame);
+ SwTextNode const& rNode(*rFrame.GetTextNodeForParaProps());
+ SwNumRule const*const pNumRule = rNode.GetNumRule();
+ assert(pNumRule); // was required for List
+
+ auto ToPDFListNumbering = [](SvxNumberFormat const& rFormat) {
+ switch (rFormat.GetNumberingType())
{
- sal_Int32 nLinkId = (*aIter).second;
- mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::LinkAnnotation, nLinkId );
+ case css::style::NumberingType::CHARS_UPPER_LETTER:
+ return vcl::PDFWriter::UpperAlpha;
+ case css::style::NumberingType::CHARS_LOWER_LETTER:
+ return vcl::PDFWriter::LowerAlpha;
+ case css::style::NumberingType::ROMAN_UPPER:
+ return vcl::PDFWriter::UpperRoman;
+ case css::style::NumberingType::ROMAN_LOWER:
+ return vcl::PDFWriter::LowerRoman;
+ case css::style::NumberingType::ARABIC:
+ return vcl::PDFWriter::Decimal;
+ case css::style::NumberingType::CHAR_SPECIAL:
+ switch (rFormat.GetBulletChar())
+ {
+ case u'\u2022': case u'\uE12C': case u'\uE01E': case u'\uE437':
+ return vcl::PDFWriter::Disc;
+ case u'\u2218': case u'\u25CB': case u'\u25E6':
+ return vcl::PDFWriter::Circle;
+ case u'\u25A0': case u'\u25AA': case u'\uE00A':
+ return vcl::PDFWriter::Square;
+ default:
+ return vcl::PDFWriter::NONE;
+ }
+ default: // the other 50 types
+ return vcl::PDFWriter::NONE;
}
- }
+ };
+
+ // Note: for every level, BeginNumberedListStructureElements() produces
+ // a separate List element, so even though in PDF this is limited to
+ // the whole List we can just export the current level here.
+ vcl::PDFWriter::StructAttributeValue const value(
+ ToPDFListNumbering(pNumRule->Get(rNode.GetActualListLevel())));
+ // ISO 14289-1:2014, Clause: 7.6
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::ListNumbering, value);
}
}
@@ -832,14 +1126,23 @@ void SwTaggedPDFHelper::BeginNumberedListStructureElements()
return;
const SwFrame& rFrame = mpNumInfo->mrFrame;
- OSL_ENSURE( rFrame.IsTextFrame(), "numbered only for text frames" );
+ assert(rFrame.IsTextFrame());
const SwTextFrame& rTextFrame = static_cast<const SwTextFrame&>(rFrame);
// Lowers of NonStructureElements should not be considered:
-
- if ( lcl_IsInNonStructEnv( rTextFrame ) || rTextFrame.IsFollow() )
+ if (lcl_IsInNonStructEnv(rTextFrame))
return;
+ // do it for the first one in the follow chain that has content
+ for (SwFlowFrame const* pPrecede = rTextFrame.GetPrecede(); pPrecede; pPrecede = pPrecede->GetPrecede())
+ {
+ SwTextFrame const*const pText(static_cast<SwTextFrame const*>(pPrecede));
+ if (!pText->HasPara() || pText->GetPara()->HasContentPortions())
+ {
+ return;
+ }
+ }
+
const SwTextNode *const pTextNd = rTextFrame.GetTextNodeForParaProps();
const SwNumRule* pNumRule = pTextNd->GetNumRule();
const SwNodeNum* pNodeNum = pTextNd->GetNum(rTextFrame.getRootFrame());
@@ -871,7 +1174,7 @@ void SwTaggedPDFHelper::BeginNumberedListStructureElements()
if ( bNewSubListStart || bNoLabel )
{
// Fine, we try to reopen the appropriate list body
- NumListBodyIdMap& rNumListBodyIdMap = SwEnhancedPDFExportHelper::GetNumListBodyIdMap();
+ NumListBodyIdMap& rNumListBodyIdMap(mpPDFExtOutDevData->GetSwPDFState()->m_NumListBodyIdMap);
if ( bNewSubListStart )
{
@@ -911,7 +1214,7 @@ void SwTaggedPDFHelper::BeginNumberedListStructureElements()
{
// any other than the first node in a list level has to reopen the current
// list. The current list is associated in a map with the first child of the list:
- NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap();
+ NumListIdMap& rNumListIdMap(mpPDFExtOutDevData->GetSwPDFState()->m_NumListIdMap);
// Search backwards and check if any of the previous nodes has a list associated with it:
const SwNumberTreeNode* pPrevious = pNodeNum->GetPred(true);
@@ -942,9 +1245,9 @@ void SwTaggedPDFHelper::BeginNumberedListStructureElements()
else
{
// clear list maps in case a list has been interrupted
- NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap();
+ NumListIdMap& rNumListIdMap(mpPDFExtOutDevData->GetSwPDFState()->m_NumListIdMap);
rNumListIdMap.clear();
- NumListBodyIdMap& rNumListBodyIdMap = SwEnhancedPDFExportHelper::GetNumListBodyIdMap();
+ NumListBodyIdMap& rNumListBodyIdMap(mpPDFExtOutDevData->GetSwPDFState()->m_NumListBodyIdMap);
rNumListBodyIdMap.clear();
}
@@ -958,7 +1261,12 @@ void SwTaggedPDFHelper::BeginNumberedListStructureElements()
if ( bNewItemTag )
{
BeginTag( vcl::PDFWriter::ListItem, aListItemString );
- BeginTag( vcl::PDFWriter::LIBody, aListBodyString );
+ assert(rTextFrame.GetPara());
+ // check whether to open LBody now or delay until after Lbl
+ if (!rTextFrame.GetPara()->HasNumberingPortion(SwParaPortion::OnlyNumbering))
+ {
+ BeginTag(vcl::PDFWriter::LIBody, aListBodyString);
+ }
}
}
@@ -968,7 +1276,7 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
// Lowers of NonStructureElements should not be considered:
- if ( lcl_IsInNonStructEnv( *pFrame ) )
+ if (lcl_IsInNonStructEnv(*pFrame) && !pFrame->IsFlyFrame())
return;
// Check if we have to reopen an existing structure element.
@@ -1026,7 +1334,35 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
{
const SwSection* pSection =
static_cast<const SwSectionFrame*>(pFrame)->GetSection();
- if ( SectionType::ToxContent == pSection->GetType() )
+
+ // open all parent sections, so that the SEs of sections
+ // are nested in the same way as their SwSectionNodes
+ std::vector<SwSection const*> parents;
+ for (SwSection const* pParent = pSection->GetParent();
+ pParent != nullptr; pParent = pParent->GetParent())
+ {
+ parents.push_back(pParent);
+ }
+ for (auto it = parents.rbegin(); it != parents.rend(); ++it)
+ {
+ // key is the SwSection - see lcl_GetKeyFromFrame()
+ OpenTagImpl(*it);
+ }
+
+ FrameTagSet& rFrameTagSet(mpPDFExtOutDevData->GetSwPDFState()->m_FrameTagSet);
+ if (rFrameTagSet.find(pSection) != rFrameTagSet.end())
+ {
+ // special case: section may have *multiple* master frames,
+ // when it is interrupted by nested section - reopen!
+ OpenTagImpl(pSection);
+ break;
+ }
+ else if (SectionType::ToxHeader == pSection->GetType())
+ {
+ nPDFType = vcl::PDFWriter::Caption;
+ aPDFType = aCaptionString;
+ }
+ else if (SectionType::ToxContent == pSection->GetType())
{
const SwTOXBase* pTOXBase = pSection->GetTOXBase();
if ( pTOXBase )
@@ -1057,8 +1393,18 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
case SwFrameType::Txt :
{
- const SwTextNode* pTextNd =
- static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps();
+ SwTextFrame const& rTextFrame(*static_cast<const SwTextFrame*>(pFrame));
+ const SwTextNode *const pTextNd(rTextFrame.GetTextNodeForParaProps());
+
+ // lazy open LBody after Lbl
+ if (!pTextNd->IsOutline()
+ && rTextFrame.GetPara()->HasNumberingPortion(SwParaPortion::OnlyNumbering))
+ {
+ sal_Int32 const nId = BeginTagImpl(nullptr, vcl::PDFWriter::LIBody, aListBodyString);
+ SwNodeNum const*const pNodeNum(pTextNd->GetNum(rTextFrame.getRootFrame()));
+ NumListBodyIdMap& rNumListBodyIdMap(mpPDFExtOutDevData->GetSwPDFState()->m_NumListBodyIdMap);
+ rNumListBodyIdMap[ pNodeNum ] = nId;
+ }
const SwFormat* pTextFormat = pTextNd->GetFormatColl();
const SwFormat* pParentTextFormat = pTextFormat ? pTextFormat->DerivedFrom() : nullptr;
@@ -1111,13 +1457,11 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
// Heading: H1 - H6
- if (pTextNd->IsOutline()
+ if (int nRealLevel = pTextNd->GetAttrOutlineLevel() - 1;
+ nRealLevel >= 0
+ && !pTextNd->IsInRedlines()
&& sw::IsParaPropsNode(*pFrame->getRootFrame(), *pTextNd))
{
- int nRealLevel = pTextNd->GetAttrOutlineLevel()-1;
- nRealLevel = std::min(nRealLevel, 5);
-
- nPDFType = static_cast<sal_uInt16>(vcl::PDFWriter::H1 + nRealLevel);
switch(nRealLevel)
{
case 0 :
@@ -1135,10 +1479,31 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
case 4 :
aPDFType = aH5String;
break;
- default:
+ case 5:
aPDFType = aH6String;
break;
+ case 6:
+ aPDFType = aH7String;
+ break;
+ case 7:
+ aPDFType = aH8String;
+ break;
+ case 8:
+ aPDFType = aH9String;
+ break;
+ case 9:
+ aPDFType = aH10String;
+ break;
+ default:
+ assert(false);
+ break;
}
+
+ // PDF/UA allows unlimited headings, but PDF only up to H6
+ // ... and apparently the extra H7.. must be declared in
+ // RoleMap, or veraPDF complains.
+ nRealLevel = std::min(nRealLevel, 5);
+ nPDFType = o3tl::narrowing<sal_uInt16>(vcl::PDFWriter::H1 + nRealLevel);
}
// Section: TOCI
@@ -1154,7 +1519,7 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
if ( pTOXBase && TOX_INDEX != pTOXBase->GetType() )
{
// Special case: Open additional TOCI tag:
- BeginTag( vcl::PDFWriter::TOCI, aTOCIString );
+ BeginTagImpl(nullptr, vcl::PDFWriter::TOCI, aTOCIString);
}
}
}
@@ -1173,7 +1538,7 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pFrame);
const SwTable* pTable = pTabFrame->GetTable();
- TableColumnsMap& rTableColumnsMap = SwEnhancedPDFExportHelper::GetTableColumnsMap();
+ TableColumnsMap& rTableColumnsMap(mpPDFExtOutDevData->GetSwPDFState()->m_TableColumnsMap);
const TableColumnsMap::const_iterator aIter = rTableColumnsMap.find( pTable );
if ( aIter == rTableColumnsMap.end() )
@@ -1255,9 +1620,20 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
// FlyFrame: Figure, Formula, Control
// fly in content or fly at page
+ if (mpFrameInfo->m_isLink)
+ { // tdf#154939 additional inner link element for flys
+ nPDFType = vcl::PDFWriter::Link;
+ aPDFType = aLinkString;
+ }
+ else
{
const SwFlyFrame* pFly = static_cast<const SwFlyFrame*>(pFrame);
- if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
+ if (pFly->GetAnchorFrame()->FindFooterOrHeader() != nullptr
+ || pFly->GetFrameFormat()->GetAttrSet().Get(RES_DECORATIVE).GetValue())
+ {
+ nPDFType = vcl::PDFWriter::NonStructElement;
+ }
+ else if (pFly->Lower() && pFly->Lower()->IsNoTextFrame())
{
bool bFormula = false;
@@ -1302,6 +1678,20 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
void SwTaggedPDFHelper::EndStructureElements()
{
+ if (mpFrameInfo != nullptr)
+ {
+ if (mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan)
+ { // close span at end of paragraph
+ mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan.reset();
+ ++m_nEndStructureElement;
+ }
+ if (mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink)
+ { // close link at end of paragraph
+ mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink.reset();
+ ++m_nEndStructureElement;
+ }
+ }
+
while ( m_nEndStructureElement > 0 )
{
EndTag();
@@ -1311,6 +1701,103 @@ void SwTaggedPDFHelper::EndStructureElements()
CheckRestoreTag();
}
+void SwTaggedPDFHelper::EndCurrentLink(OutputDevice const& rOut)
+{
+ vcl::PDFExtOutDevData *const pPDFExtOutDevData(
+ dynamic_cast<vcl::PDFExtOutDevData *>(rOut.GetExtOutDevData()));
+ if (pPDFExtOutDevData && pPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink)
+ {
+ pPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink.reset();
+ pPDFExtOutDevData->EndStructureElement();
+#if OSL_DEBUG_LEVEL > 1
+ aStructStack.pop_back();
+#endif
+ }
+}
+
+void SwTaggedPDFHelper::EndCurrentAll()
+{
+ if (mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan)
+ {
+ mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan.reset();
+ }
+ if (mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink)
+ {
+ mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink.reset();
+ }
+}
+
+void SwTaggedPDFHelper::EndCurrentSpan()
+{
+ mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan.reset();
+ EndTag(); // close span
+}
+
+void SwTaggedPDFHelper::CreateCurrentSpan(
+ SwTextPaintInfo const& rInf, OUString const& rStyleName)
+{
+ assert(!mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan);
+ mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan.emplace(
+ SwEnhancedPDFState::Span{
+ rInf.GetFont()->GetUnderline(),
+ rInf.GetFont()->GetOverline(),
+ rInf.GetFont()->GetStrikeout(),
+ rInf.GetFont()->GetEmphasisMark(),
+ rInf.GetFont()->GetEscapement(),
+ rInf.GetFont()->GetActual(),
+ rInf.GetFont()->GetLanguage(),
+ rStyleName});
+ // leave it open to let next portion decide to merge or close
+ --m_nEndStructureElement;
+}
+
+bool SwTaggedPDFHelper::CheckContinueSpan(
+ SwTextPaintInfo const& rInf, std::u16string_view const rStyleName,
+ SwTextAttr const*const pInetFormatAttr)
+{
+ // for now, don't create span inside of link - this should be very rare
+ // situation and it looks complicated to implement.
+ assert(!mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan
+ || !mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink);
+ if (mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink)
+ {
+ if (pInetFormatAttr && pInetFormatAttr == *mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink)
+ {
+ return true;
+ }
+ else
+ {
+ mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink.reset();
+ EndTag();
+ return false;
+ }
+ }
+ if (mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan && pInetFormatAttr)
+ {
+ EndCurrentSpan();
+ return false;
+ }
+
+ if (!mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan)
+ return false;
+
+ SwEnhancedPDFState::Span const& rCurrent(*mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentSpan);
+
+ bool const ret(rCurrent.eUnderline == rInf.GetFont()->GetUnderline()
+ && rCurrent.eOverline == rInf.GetFont()->GetOverline()
+ && rCurrent.eStrikeout == rInf.GetFont()->GetStrikeout()
+ && rCurrent.eFontEmphasis == rInf.GetFont()->GetEmphasisMark()
+ && rCurrent.nEscapement == rInf.GetFont()->GetEscapement()
+ && rCurrent.nScript == rInf.GetFont()->GetActual()
+ && rCurrent.nLang == rInf.GetFont()->GetLanguage()
+ && rCurrent.StyleName == rStyleName);
+ if (!ret)
+ {
+ EndCurrentSpan();
+ }
+ return ret;
+}
+
void SwTaggedPDFHelper::BeginInlineStructureElements()
{
const SwLinePortion* pPor = &mpPorInfo->mrPor;
@@ -1322,6 +1809,26 @@ void SwTaggedPDFHelper::BeginInlineStructureElements()
if ( lcl_IsInNonStructEnv( *pFrame ) )
return;
+ std::pair<SwTextNode const*, sal_Int32> const pos(
+ pFrame->MapViewToModel(rInf.GetIdx()));
+ SwTextAttr const*const pInetFormatAttr =
+ pos.first->GetTextAttrAt(pos.second, RES_TXTATR_INETFMT);
+
+ OUString sStyleName;
+ if (!pInetFormatAttr)
+ {
+ std::vector<SwTextAttr *> const charAttrs(
+ pos.first->GetTextAttrsAt(pos.second, RES_TXTATR_CHARFMT));
+ // TODO: handle more than 1 char style?
+ const SwCharFormat* pCharFormat = (charAttrs.size())
+ ? (*charAttrs.begin())->GetCharFormat().GetCharFormat() : nullptr;
+ if (pCharFormat)
+ SwStyleNameMapper::FillProgName( pCharFormat->GetName(), sStyleName, SwGetPoolIdFromName::TxtColl );
+ }
+
+ // note: ILSE may be nested, so only end the span if needed to start new one
+ bool const isContinueSpan(CheckContinueSpan(rInf, sStyleName, pInetFormatAttr));
+
sal_uInt16 nPDFType = USHRT_MAX;
OUString aPDFType;
@@ -1336,49 +1843,58 @@ void SwTaggedPDFHelper::BeginInlineStructureElements()
aPDFType = aSpanString;
break;
+ case PortionType::Fly:
+ // if a link is split by a fly overlap, then there will be multiple
+ // annotations for the link, and hence there must be multiple SEs,
+ // so every annotation has its own SE.
+ if (mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink)
+ {
+ mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink.reset();
+ EndTag();
+ }
+ break;
+
case PortionType::Lay :
case PortionType::Text :
case PortionType::Para :
{
- std::pair<SwTextNode const*, sal_Int32> const pos(
- pFrame->MapViewToModel(rInf.GetIdx()));
- SwTextAttr const*const pInetFormatAttr =
- pos.first->GetTextAttrAt(pos.second, RES_TXTATR_INETFMT);
-
- OUString sStyleName;
- if ( !pInetFormatAttr )
- {
- std::vector<SwTextAttr *> const charAttrs(
- pos.first->GetTextAttrsAt(pos.second, RES_TXTATR_CHARFMT));
- // TODO: handle more than 1 char style?
- const SwCharFormat* pCharFormat = (charAttrs.size())
- ? (*charAttrs.begin())->GetCharFormat().GetCharFormat() : nullptr;
- if ( pCharFormat )
- SwStyleNameMapper::FillProgName( pCharFormat->GetName(), sStyleName, SwGetPoolIdFromName::TxtColl );
- }
-
// Check for Link:
if( pInetFormatAttr )
{
- nPDFType = vcl::PDFWriter::Link;
- aPDFType = aLinkString;
+ if (!isContinueSpan)
+ {
+ nPDFType = vcl::PDFWriter::Link;
+ aPDFType = aLinkString;
+ assert(!mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink);
+ mpPDFExtOutDevData->GetSwPDFState()->m_oCurrentLink.emplace(pInetFormatAttr);
+ // leave it open to let next portion decide to merge or close
+ --m_nEndStructureElement;
+ }
}
// Check for Quote/Code character style:
else if (sStyleName == aQuotation)
{
- nPDFType = vcl::PDFWriter::Quote;
- aPDFType = aQuoteString;
+ if (!isContinueSpan)
+ {
+ nPDFType = vcl::PDFWriter::Quote;
+ aPDFType = aQuoteString;
+ CreateCurrentSpan(rInf, sStyleName);
+ }
}
else if (sStyleName == aSourceText)
{
- nPDFType = vcl::PDFWriter::Code;
- aPDFType = aCodeString;
+ if (!isContinueSpan)
+ {
+ nPDFType = vcl::PDFWriter::Code;
+ aPDFType = aCodeString;
+ CreateCurrentSpan(rInf, sStyleName);
+ }
}
- else
+ else if (!isContinueSpan)
{
const LanguageType nCurrentLanguage = rInf.GetFont()->GetLanguage();
const SwFontScript nFont = rInf.GetFont()->GetActual();
- const LanguageType nDefaultLang = SwEnhancedPDFExportHelper::GetDefaultLanguage();
+ const LanguageType nDefaultLang(mpPDFExtOutDevData->GetSwPDFState()->m_eLanguageDefault);
if ( LINESTYLE_NONE != rInf.GetFont()->GetUnderline() ||
LINESTYLE_NONE != rInf.GetFont()->GetOverline() ||
@@ -1394,6 +1910,7 @@ void SwTaggedPDFHelper::BeginInlineStructureElements()
aPDFType = sStyleName;
else
aPDFType = aSpanString;
+ CreateCurrentSpan(rInf, sStyleName);
}
}
}
@@ -1428,7 +1945,73 @@ void SwTaggedPDFHelper::BeginInlineStructureElements()
}
break;
- case PortionType::Table :
+ case PortionType::Multi:
+ {
+ SwMultiPortion const*const pMulti(static_cast<SwMultiPortion const*>(pPor));
+ if (pMulti->IsRuby())
+ {
+ EndCurrentAll();
+ switch (mpPorInfo->m_Mode)
+ {
+ case 0:
+ nPDFType = vcl::PDFWriter::Ruby;
+ aPDFType = "Ruby";
+ break;
+ case 1:
+ nPDFType = vcl::PDFWriter::RT;
+ aPDFType = "RT";
+ break;
+ case 2:
+ nPDFType = vcl::PDFWriter::RB;
+ aPDFType = "RB";
+ break;
+ }
+ }
+ else if (pMulti->IsDouble())
+ {
+ EndCurrentAll();
+ switch (mpPorInfo->m_Mode)
+ {
+ case 0:
+ nPDFType = vcl::PDFWriter::Warichu;
+ aPDFType = "Warichu";
+ break;
+ case 1:
+ nPDFType = vcl::PDFWriter::WP;
+ aPDFType = "WP";
+ break;
+ case 2:
+ nPDFType = vcl::PDFWriter::WT;
+ aPDFType = "WT";
+ break;
+ }
+ }
+ }
+ break;
+
+
+ // for FootnoteNum, is called twice: outer generates Lbl, inner Link
+ case PortionType::FootnoteNum:
+ assert(!isContinueSpan); // is at start
+ if (mpPorInfo->m_Mode == 0)
+ { // tdf#152218 link both directions
+ nPDFType = vcl::PDFWriter::Link;
+ aPDFType = aLinkString;
+ break;
+ }
+ [[fallthrough]];
+ case PortionType::Number:
+ case PortionType::Bullet:
+ case PortionType::GrfNum:
+ assert(!isContinueSpan); // is at start
+ if (mpPorInfo->m_Mode == 1)
+ { // only works for multiple lines via wrapper from PaintSwFrame
+ nPDFType = vcl::PDFWriter::LILabel;
+ aPDFType = aListLabelString;
+ }
+ break;
+
+ case PortionType::Tab :
case PortionType::TabRight :
case PortionType::TabCenter :
case PortionType::TabDecimal :
@@ -1481,27 +2064,22 @@ SwEnhancedPDFExportHelper::SwEnhancedPDFExportHelper( SwEditShell& rSh,
}
}
- s_aTableColumnsMap.clear();
- s_aLinkIdMap.clear();
- s_aNumListIdMap.clear();
- s_aNumListBodyIdMap.clear();
- s_aFrameTagIdMap.clear();
-
#if OSL_DEBUG_LEVEL > 1
aStructStack.clear();
#endif
const sal_Int16 nScript = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
- sal_uInt16 nLangRes = RES_CHRATR_LANGUAGE;
+ TypedWhichId<SvxLanguageItem> nLangRes = RES_CHRATR_LANGUAGE;
if ( i18n::ScriptType::ASIAN == nScript )
nLangRes = RES_CHRATR_CJK_LANGUAGE;
else if ( i18n::ScriptType::COMPLEX == nScript )
nLangRes = RES_CHRATR_CTL_LANGUAGE;
- s_eLanguageDefault = static_cast<const SvxLanguageItem*>(&mrSh.GetDoc()->GetDefault( nLangRes ))->GetLanguage();
+ const SvxLanguageItem& rLangItem = mrSh.GetDoc()->GetDefault( nLangRes );
+ auto const eLanguageDefault = rLangItem.GetLanguage();
- EnhancedPDFExport();
+ EnhancedPDFExport(eLanguageDefault);
}
SwEnhancedPDFExportHelper::~SwEnhancedPDFExportHelper()
@@ -1511,9 +2089,10 @@ SwEnhancedPDFExportHelper::~SwEnhancedPDFExportHelper()
tools::Rectangle SwEnhancedPDFExportHelper::SwRectToPDFRect(const SwPageFrame* pCurrPage,
const tools::Rectangle& rRectangle) const
{
- SwPostItMode nPostItMode = mrPrintData.GetPrintPostIts();
- if (nPostItMode != SwPostItMode::InMargins)
+ if (!::sw::IsShrinkPageForPostIts(mrSh, mrPrintData)) // tdf#148729
+ {
return rRectangle;
+ }
//the page has been scaled by 75% and vertically centered, so adjust these
//rectangles equivalently
tools::Rectangle aRect(rRectangle);
@@ -1531,7 +2110,7 @@ tools::Rectangle SwEnhancedPDFExportHelper::SwRectToPDFRect(const SwPageFrame* p
return aRect;
}
-void SwEnhancedPDFExportHelper::EnhancedPDFExport()
+void SwEnhancedPDFExportHelper::EnhancedPDFExport(LanguageType const eLanguageDefault)
{
vcl::PDFExtOutDevData* pPDFExtOutDevData =
dynamic_cast< vcl::PDFExtOutDevData*>( mrOut.GetExtOutDevData() );
@@ -1541,12 +2120,12 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// set the document locale
- css::lang::Locale aDocLocale( LanguageTag( SwEnhancedPDFExportHelper::GetDefaultLanguage() ).getLocale() );
+ lang::Locale const aDocLocale( LanguageTag(eLanguageDefault).getLocale() );
pPDFExtOutDevData->SetDocumentLocale( aDocLocale );
// Prepare the output device:
- mrOut.Push( PushFlags::MAPMODE );
+ mrOut.Push( vcl::PushFlags::MAPMODE );
MapMode aMapMode( mrOut.GetMapMode() );
aMapMode.SetMapUnit( MapUnit::MapTwip );
mrOut.SetMapMode( aMapMode );
@@ -1561,6 +2140,8 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
if ( !mbEditEngineOnly )
{
+ assert(pPDFExtOutDevData->GetSwPDFState() == nullptr);
+ pPDFExtOutDevData->SetSwPDFState(new SwEnhancedPDFState(eLanguageDefault));
// POSTITS
@@ -1623,10 +2204,10 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// 3. Check for hidden text attribute
if ( !pTNd->IsHidden() &&
mrSh.GotoINetAttr( p->rINetAttr ) &&
- !mrSh.SelectHiddenRange() )
+ !mrSh.IsInHiddenRange(/*bSelect=*/false) )
{
// Select the hyperlink:
- mrSh.SwCursorShell::Right( 1, CRSR_SKIP_CHARS );
+ mrSh.SwCursorShell::Right( 1, SwCursorSkipMode::Chars );
if ( mrSh.SwCursorShell::SelectTextAttr( RES_TXTATR_INETFMT, true ) )
{
// First, we create the destination, because there may be more
@@ -1635,24 +2216,24 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
p->rINetAttr.GetINetFormat().GetValue(),
INetURLObject::DecodeMechanism::Unambiguous ) );
- // We have to distinguish between intern and real URLs
- const bool bIntern = '#' == aURL[0];
+ // We have to distinguish between internal and real URLs
+ const bool bInternal = '#' == aURL[0];
// GetCursor_() is a SwShellCursor, which is derived from
// SwSelPaintRects, therefore the rectangles of the current
// selection can be easily obtained:
// Note: We make a copy of the rectangles, because they may
// be deleted again in JumpToSwMark.
- SwRects aTmp;
- aTmp.insert( aTmp.begin(), mrSh.SwCursorShell::GetCursor_()->begin(), mrSh.SwCursorShell::GetCursor_()->end() );
+ SwRects const aTmp(GetCursorRectsContainingText(mrSh));
OSL_ENSURE( !aTmp.empty(), "Enhanced pdf export - rectangles are missing" );
+ OUString const altText(mrSh.GetSelText());
const SwPageFrame* pSelectionPage =
static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
// Create the destination for internal links:
sal_Int32 nDestId = -1;
- if ( bIntern )
+ if ( bInternal )
{
aURL = aURL.copy( 1 );
mrSh.SwCursorShell::ClearMark();
@@ -1678,11 +2259,10 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
}
}
- if ( !bIntern || -1 != nDestId )
+ if ( !bInternal || -1 != nDestId )
{
// #i44368# Links in Header/Footer
- const SwPosition aPos( *pTNd );
- const bool bHeaderFooter = pDoc->IsInHeaderFooter( aPos.nNode );
+ const bool bHeaderFooter = pDoc->IsInHeaderFooter( *pTNd );
// Create links for all selected rectangles:
const size_t nNumOfRects = aTmp.size();
@@ -1699,21 +2279,21 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// Link Export
tools::Rectangle aRect(SwRectToPDFRect(pSelectionPage, rLinkRect.SVRect()));
const sal_Int32 nLinkId =
- pPDFExtOutDevData->CreateLink(aRect, aLinkPageNum);
+ pPDFExtOutDevData->CreateLink(aRect, altText, aLinkPageNum);
// Store link info for tagged pdf output:
const IdMapEntry aLinkEntry( rLinkRect, nLinkId );
- s_aLinkIdMap.push_back( aLinkEntry );
+ pPDFExtOutDevData->GetSwPDFState()->m_LinkIdMap.push_back(aLinkEntry);
// Connect Link and Destination:
- if ( bIntern )
+ if ( bInternal )
pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId );
else
pPDFExtOutDevData->SetLinkURL( nLinkId, aURL );
// #i44368# Links in Header/Footer
if ( bHeaderFooter )
- MakeHeaderFooterLinks( *pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, aURL, bIntern );
+ MakeHeaderFooterLinks(*pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, aURL, bInternal, altText);
}
}
}
@@ -1724,25 +2304,24 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// HYPERLINKS (Graphics, Frames, OLEs )
- SwFrameFormats* pTable = pDoc->GetSpzFrameFormats();
- const size_t nSpzFrameFormatsCount = pTable->size();
- for( size_t n = 0; n < nSpzFrameFormatsCount; ++n )
+ for(sw::SpzFrameFormat* pFrameFormat: *pDoc->GetSpzFrameFormats())
{
- SwFrameFormat* pFrameFormat = (*pTable)[n];
- const SfxPoolItem* pItem;
+ const SwFormatURL* pItem;
if ( RES_DRAWFRMFMT != pFrameFormat->Which() &&
GetFrameOfModify(mrSh.GetLayout(), *pFrameFormat, SwFrameType::Fly) &&
- SfxItemState::SET == pFrameFormat->GetAttrSet().GetItemState( RES_URL, true, &pItem ) )
+ (pItem = pFrameFormat->GetAttrSet().GetItemIfSet( RES_URL )) )
{
const SwPageFrame* pCurrPage =
static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
- OUString aURL( static_cast<const SwFormatURL*>(pItem)->GetURL() );
- const bool bIntern = !aURL.isEmpty() && '#' == aURL[0];
+ OUString aURL( pItem->GetURL() );
+ if (aURL.isEmpty())
+ continue;
+ const bool bInternal = '#' == aURL[0];
// Create the destination for internal links:
sal_Int32 nDestId = -1;
- if ( bIntern )
+ if ( bInternal )
{
aURL = aURL.copy( 1 );
mrSh.SwCursorShell::ClearMark();
@@ -1767,11 +2346,11 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
}
}
- if ( !bIntern || -1 != nDestId )
+ if ( !bInternal || -1 != nDestId )
{
Point aNullPt;
const SwRect aLinkRect = pFrameFormat->FindLayoutRect( false, &aNullPt );
-
+ OUString const formatName(pFrameFormat->GetName());
// Link PageNums
std::vector<sal_Int32> aLinkPageNums = CalcOutputPageNums( aLinkRect );
@@ -1780,10 +2359,14 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
{
tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, aLinkRect.SVRect()));
const sal_Int32 nLinkId =
- pPDFExtOutDevData->CreateLink(aRect, aLinkPageNum);
+ pPDFExtOutDevData->CreateLink(aRect, formatName, aLinkPageNum);
+
+ // Store link info for tagged pdf output:
+ const IdMapEntry aLinkEntry(aLinkRect, nLinkId);
+ pPDFExtOutDevData->GetSwPDFState()->m_LinkIdMap.push_back(aLinkEntry);
// Connect Link and Destination:
- if ( bIntern )
+ if ( bInternal )
pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId );
else
pPDFExtOutDevData->SetLinkURL( nLinkId, aURL );
@@ -1792,12 +2375,12 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
const SwFormatAnchor &rAnch = pFrameFormat->GetAnchor();
if (RndStdIds::FLY_AT_PAGE != rAnch.GetAnchorId())
{
- const SwPosition* pPosition = rAnch.GetContentAnchor();
- if ( pPosition && pDoc->IsInHeaderFooter( pPosition->nNode ) )
+ const SwNode* pAnchorNode = rAnch.GetAnchorNode();
+ if ( pAnchorNode && pDoc->IsInHeaderFooter( *pAnchorNode ) )
{
- const SwTextNode* pTNd = pPosition->nNode.GetNode().GetTextNode();
+ const SwTextNode* pTNd = pAnchorNode->GetTextNode();
if ( pTNd )
- MakeHeaderFooterLinks( *pPDFExtOutDevData, *pTNd, aLinkRect, nDestId, aURL, bIntern );
+ MakeHeaderFooterLinks(*pPDFExtOutDevData, *pTNd, aLinkRect, nDestId, aURL, bInternal, formatName);
}
}
}
@@ -1808,7 +2391,7 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// Turn media shapes into Screen annotations.
if (SdrObject* pObject = pFrameFormat->FindRealSdrObject())
{
- SwRect aSnapRect = pObject->GetSnapRect();
+ SwRect aSnapRect(pObject->GetSnapRect());
std::vector<sal_Int32> aScreenPageNums = CalcOutputPageNums(aSnapRect);
if (aScreenPageNums.empty())
continue;
@@ -1817,15 +2400,26 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
if (xShape->getShapeType() == "com.sun.star.drawing.MediaShape")
{
uno::Reference<beans::XPropertySet> xShapePropSet(xShape, uno::UNO_QUERY);
+ OUString title;
+ xShapePropSet->getPropertyValue("Title") >>= title;
+ OUString description;
+ xShapePropSet->getPropertyValue("Description") >>= description;
+ OUString const altText(title.isEmpty()
+ ? description
+ : description.isEmpty()
+ ? title
+ : OUString::Concat(title) + OUString::Concat("\n") + OUString::Concat(description));
+
OUString aMediaURL;
xShapePropSet->getPropertyValue("MediaURL") >>= aMediaURL;
if (!aMediaURL.isEmpty())
{
+ OUString const mimeType(xShapePropSet->getPropertyValue("MediaMimeType").get<OUString>());
const SwPageFrame* pCurrPage = mrSh.GetLayout()->GetPageAtPos(aSnapRect.Center());
tools::Rectangle aPDFRect(SwRectToPDFRect(pCurrPage, aSnapRect.SVRect()));
for (sal_Int32 nScreenPageNum : aScreenPageNums)
{
- sal_Int32 nScreenId = pPDFExtOutDevData->CreateScreen(aPDFRect, nScreenPageNum);
+ sal_Int32 nScreenId = pPDFExtOutDevData->CreateScreen(aPDFRect, altText, mimeType, nScreenPageNum, pObject);
if (aMediaURL.startsWith("vnd.sun.star.Package:"))
{
// Embedded media.
@@ -1858,11 +2452,10 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
continue;
// Select the field:
mrSh.SwCursorShell::SetMark();
- mrSh.SwCursorShell::Right( 1, CRSR_SKIP_CHARS );
+ mrSh.SwCursorShell::Right( 1, SwCursorSkipMode::Chars );
// Link Rectangles
- SwRects aTmp;
- aTmp.insert( aTmp.begin(), mrSh.SwCursorShell::GetCursor_()->begin(), mrSh.SwCursorShell::GetCursor_()->end() );
+ SwRects const aTmp(GetCursorRectsContainingText(mrSh));
OSL_ENSURE( !aTmp.empty(), "Enhanced pdf export - rectangles are missing" );
mrSh.SwCursorShell::ClearMark();
@@ -1870,7 +2463,7 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// Destination Rectangle
const SwGetRefField* pField = static_cast<SwGetRefField*>(pFormatField->GetField());
const OUString& rRefName = pField->GetSetRefName();
- mrSh.GotoRefMark( rRefName, pField->GetSubType(), pField->GetSeqNo() );
+ mrSh.GotoRefMark( rRefName, pField->GetSubType(), pField->GetSeqNo(), pField->GetFlags() );
const SwRect& rDestRect = mrSh.GetCharRect();
const SwPageFrame* pCurrPage = static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
@@ -1885,8 +2478,8 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
// #i44368# Links in Header/Footer
- const SwPosition aPos( *pTNd );
- const bool bHeaderFooter = pDoc->IsInHeaderFooter( aPos.nNode );
+ const bool bHeaderFooter = pDoc->IsInHeaderFooter( *pTNd );
+ OUString const content(pField->ExpandField(true, mrSh.GetLayout()));
// Create links for all selected rectangles:
const size_t nNumOfRects = aTmp.size();
@@ -1903,11 +2496,11 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// Link Export
aRect = SwRectToPDFRect(pCurrPage, rLinkRect.SVRect());
const sal_Int32 nLinkId =
- pPDFExtOutDevData->CreateLink(aRect, aLinkPageNum);
+ pPDFExtOutDevData->CreateLink(aRect, content, aLinkPageNum);
// Store link info for tagged pdf output:
const IdMapEntry aLinkEntry( rLinkRect, nLinkId );
- s_aLinkIdMap.push_back( aLinkEntry );
+ pPDFExtOutDevData->GetSwPDFState()->m_LinkIdMap.push_back(aLinkEntry);
// Connect Link and Destination:
pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId );
@@ -1915,7 +2508,7 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// #i44368# Links in Header/Footer
if ( bHeaderFooter )
{
- MakeHeaderFooterLinks( *pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, "", true );
+ MakeHeaderFooterLinks(*pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, "", true, content);
}
}
}
@@ -1935,12 +2528,11 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
const SwTextFootnote* pTextFootnote = pDoc->GetFootnoteIdxs()[ nIdx ];
SwTextNode& rTNd = const_cast<SwTextNode&>(pTextFootnote->GetTextNode());
- mrSh.GetCursor_()->GetPoint()->nNode = rTNd;
- mrSh.GetCursor_()->GetPoint()->nContent.Assign( &rTNd, pTextFootnote->GetStart() );
+ mrSh.GetCursor_()->GetPoint()->Assign(rTNd, pTextFootnote->GetStart());
// 1. Check if the whole paragraph is hidden
// 2. Check for hidden text attribute
- if (rTNd.GetTextNode()->IsHidden() || mrSh.SelectHiddenRange()
+ if (rTNd.GetTextNode()->IsHidden() || mrSh.IsInHiddenRange(/*bSelect=*/false)
|| (mrSh.GetLayout()->IsHideRedlines()
&& sw::IsFootnoteDeleted(pDoc->getIDocumentRedlineAccess(), *pTextFootnote)))
{
@@ -1951,7 +2543,7 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// Select the footnote:
mrSh.SwCursorShell::SetMark();
- mrSh.SwCursorShell::Right( 1, CRSR_SKIP_CHARS );
+ mrSh.SwCursorShell::Right( 1, SwCursorSkipMode::Chars );
// Link Rectangle
SwRects aTmp;
@@ -1969,38 +2561,60 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// Goto footnote text:
if ( mrSh.GotoFootnoteText() )
{
- // Link PageNums
- std::vector<sal_Int32> aLinkPageNums = CalcOutputPageNums( aLinkRect );
-
// Destination Rectangle
const SwRect& rDestRect = mrSh.GetCharRect();
-
- const SwPageFrame* pCurrPage =
- static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
-
- // Destination PageNum
const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect );
-
- for (sal_Int32 aLinkPageNum : aLinkPageNums)
+ if ( -1 != nDestPageNum )
{
- // Link Export
- tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, aLinkRect.SVRect()));
- const sal_Int32 nLinkId =
- pPDFExtOutDevData->CreateLink(aRect, aLinkPageNum);
+ const SwPageFrame* pCurrPage = static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+ // Destination PageNum
+ tools::Rectangle aRect = SwRectToPDFRect(pCurrPage, rDestRect.SVRect());
+ // Back link rectangle calculation
+ const SwPageFrame* fnBodyPage = pCurrPage->getRootFrame()->GetPageByPageNum(nDestPageNum+1);
+ SwRect fnSymbolRect;
+ if (fnBodyPage->IsVertical()){
+ tools::Long fnSymbolTop = fnBodyPage->GetTopMargin() + fnBodyPage->getFrameArea().Top();
+ tools::Long symbolHeight = rDestRect.Top() - fnSymbolTop;
+ fnSymbolRect = SwRect(rDestRect.Pos().X(),fnSymbolTop,rDestRect.Width(),symbolHeight);
+ } else {
+ if (fnBodyPage->IsRightToLeft()){
+ tools::Long fnSymbolRight = fnBodyPage->getFrameArea().Right() - fnBodyPage->GetRightMargin();
+ tools::Long symbolWidth = fnSymbolRight - rDestRect.Right();
+ fnSymbolRect = SwRect(rDestRect.Pos().X(),rDestRect.Pos().Y(),symbolWidth,rDestRect.Height());
+ } else {
+ tools::Long fnSymbolLeft = fnBodyPage->GetLeftMargin() + fnBodyPage->getFrameArea().Left();
+ tools::Long symbolWidth = rDestRect.Left() - fnSymbolLeft;
+ fnSymbolRect = SwRect(fnSymbolLeft,rDestRect.Pos().Y(),symbolWidth,rDestRect.Height());
+ }
+ }
+ tools::Rectangle aFootnoteSymbolRect = SwRectToPDFRect(pCurrPage, fnSymbolRect.SVRect());
+
+ OUString const numStrSymbol(pTextFootnote->GetFootnote().GetViewNumStr(*pDoc, mrSh.GetLayout(), true));
+ OUString const numStrRef(pTextFootnote->GetFootnote().GetViewNumStr(*pDoc, mrSh.GetLayout(), false));
+ // Export back link
+ const sal_Int32 nBackLinkId = pPDFExtOutDevData->CreateLink(aFootnoteSymbolRect, numStrSymbol, nDestPageNum);
+ // Destination Export
+ const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
+ mrSh.GotoFootnoteAnchor();
+ // Link PageNums
+ sal_Int32 aLinkPageNum = CalcOutputPageNum( aLinkRect );
+ pCurrPage = static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+ // Link Export
+ aRect = SwRectToPDFRect(pCurrPage, aLinkRect.SVRect());
+ const sal_Int32 nLinkId = pPDFExtOutDevData->CreateLink(aRect, numStrRef, aLinkPageNum);
+ // Back link destination Export
+ const sal_Int32 nBackDestId = pPDFExtOutDevData->CreateDest(aRect, aLinkPageNum);
// Store link info for tagged pdf output:
const IdMapEntry aLinkEntry( aLinkRect, nLinkId );
- s_aLinkIdMap.push_back( aLinkEntry );
-
- if ( -1 != nDestPageNum )
- {
- aRect = SwRectToPDFRect(pCurrPage, rDestRect.SVRect());
- // Destination Export
- const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest(rDestRect.SVRect(), nDestPageNum);
-
- // Connect Link and Destination:
- pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId );
- }
+ pPDFExtOutDevData->GetSwPDFState()->m_LinkIdMap.push_back(aLinkEntry);
+
+ // Store backlink info for tagged pdf output:
+ const IdMapEntry aBackLinkEntry( aFootnoteSymbolRect, nBackLinkId );
+ pPDFExtOutDevData->GetSwPDFState()->m_LinkIdMap.push_back(aBackLinkEntry);
+ // Connect Links and Destinations:
+ pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId );
+ pPDFExtOutDevData->SetLinkDest( nBackLinkId, nBackDestId );
}
}
}
@@ -2121,8 +2735,8 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
for ( const auto& rBookmark : rBookmarks )
{
OUString aBookmarkName( rBookmark.aBookmark );
- const bool bIntern = '#' == aBookmarkName[0];
- if ( bIntern )
+ const bool bInternal = '#' == aBookmarkName[0];
+ if ( bInternal )
{
aBookmarkName = aBookmarkName.copy( 1 );
JumpToSwMark( &mrSh, aBookmarkName );
@@ -2157,6 +2771,9 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
pPDFExtOutDevData->SetLinkURL( rBookmark.nLinkId, aBookmarkName );
}
rBookmarks.clear();
+ assert(pPDFExtOutDevData->GetSwPDFState());
+ delete pPDFExtOutDevData->GetSwPDFState();
+ pPDFExtOutDevData->SetSwPDFState(nullptr);
}
// Restore view, cursor, and outdev:
@@ -2173,6 +2790,53 @@ void SwEnhancedPDFExportHelper::ExportAuthorityEntryLinks()
return;
}
+ // Create PDF destinations for bibliography table entries
+ std::vector<std::tuple<const SwTOXBase*, const OUString*, sal_Int32>> vDestinations;
+ // string is the row node text, sal_Int32 is number of the destination
+ // Note: This way of iterating doesn't seem to take into account TOXes
+ // that are in a frame, probably in some other cases too
+ {
+ mrSh.GotoPage(1);
+ while (mrSh.GotoNextTOXBase())
+ {
+ const SwTOXBase* pIteratedTOX = nullptr;
+ while ((pIteratedTOX = mrSh.GetCurTOX()) != nullptr
+ && pIteratedTOX->GetType() == TOX_AUTHORITIES)
+ {
+ if (const SwNode& rCurrentNode = mrSh.GetCursor()->GetPoint()->GetNode();
+ rCurrentNode.GetNodeType() == SwNodeType::Text)
+ {
+ if (mrSh.GetCursor()->GetPoint()->GetNode().FindSectionNode()->GetSection().GetType()
+ == SectionType::ToxContent) // this checks it's not a heading
+ {
+ // Destination Rectangle
+ const SwRect& rDestRect = mrSh.GetCharRect();
+
+ const SwPageFrame* pCurrPage =
+ static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+
+ // Destination PageNum
+ const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect );
+
+ // Destination Export
+ if ( -1 != nDestPageNum )
+ {
+ tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rDestRect.SVRect()));
+ const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
+ const OUString* vNodeText = &static_cast<const SwTextNode*>(&rCurrentNode)->GetText();
+ vDestinations.emplace_back(pIteratedTOX, vNodeText, nDestId);
+ }
+ }
+ }
+ if (!mrSh.MovePara(GoNextPara, fnParaStart))
+ { // Cursor is stuck in the TOX due to document ending immediately afterwards
+ break;
+ }
+ }
+ }
+ }
+
+ // Generate links to matching entries in the bibliography tables
std::vector<SwFormatField*> aFields;
SwFieldType* pType = mrSh.GetFieldType(SwFieldIds::TableOfAuthorities, OUString());
if (!pType)
@@ -2191,36 +2855,96 @@ void SwEnhancedPDFExportHelper::ExportAuthorityEntryLinks()
const auto& rAuthorityField
= *static_cast<const SwAuthorityField*>(pFormatField->GetField());
- if (!rAuthorityField.HasURL())
- {
- continue;
- }
- const OUString& rURL = rAuthorityField.GetAuthEntry()->GetAuthorField(AUTH_FIELD_URL);
- const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
- if (!lcl_TryMoveToNonHiddenField(mrSh, rTextNode, *pFormatField))
+ if (auto targetType = rAuthorityField.GetTargetType();
+ targetType == SwAuthorityField::TargetType::UseDisplayURL
+ || targetType == SwAuthorityField::TargetType::UseTargetURL)
{
- continue;
- }
+ // Since the target type specifies to use an URL, link to it
+ const OUString& rURL = rAuthorityField.GetAbsoluteURL();
+ if (rURL.getLength() == 0)
+ {
+ continue;
+ }
- // Select the field.
- mrSh.SwCursorShell::SetMark();
- mrSh.SwCursorShell::Right(1, CRSR_SKIP_CHARS);
+ const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
+ if (!lcl_TryMoveToNonHiddenField(mrSh, rTextNode, *pFormatField))
+ {
+ continue;
+ }
- // Create the links.
- for (const auto& rLinkRect : *mrSh.SwCursorShell::GetCursor_())
- {
- for (const auto& rLinkPageNum : CalcOutputPageNums(rLinkRect))
+ OUString const content(rAuthorityField.ExpandField(true, mrSh.GetLayout()));
+
+ // Select the field.
+ mrSh.SwCursorShell::SetMark();
+ mrSh.SwCursorShell::Right(1, SwCursorSkipMode::Chars);
+
+ // Create the links.
+ SwRects const rects(GetCursorRectsContainingText(mrSh));
+ for (const auto& rLinkRect : rects)
{
- tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, rLinkRect.SVRect()));
- sal_Int32 nLinkId = pPDFExtOutDevData->CreateLink(aRect, rLinkPageNum);
- IdMapEntry aLinkEntry(rLinkRect, nLinkId);
- s_aLinkIdMap.push_back(aLinkEntry);
- pPDFExtOutDevData->SetLinkURL(nLinkId, rURL);
+ for (const auto& rLinkPageNum : CalcOutputPageNums(rLinkRect))
+ {
+ tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, rLinkRect.SVRect()));
+ sal_Int32 nLinkId = pPDFExtOutDevData->CreateLink(aRect, content, rLinkPageNum);
+ IdMapEntry aLinkEntry(rLinkRect, nLinkId);
+ pPDFExtOutDevData->GetSwPDFState()->m_LinkIdMap.push_back(aLinkEntry);
+ pPDFExtOutDevData->SetLinkURL(nLinkId, rURL);
+ }
}
+ mrSh.SwCursorShell::ClearMark();
}
+ else if (targetType == SwAuthorityField::TargetType::BibliographyTableRow)
+ {
+ // As the target type specifies, try linking to a bibliography table row
+ sal_Int32 nDestId = -1;
+
+ std::unordered_map<const SwTOXBase*, OUString> vFormattedFieldStrings;
+ for (const auto& rDestinationTuple : vDestinations)
+ {
+ if (vFormattedFieldStrings.find(std::get<0>(rDestinationTuple))
+ == vFormattedFieldStrings.end())
+ vFormattedFieldStrings.emplace(std::get<0>(rDestinationTuple),
+ rAuthorityField.GetAuthority(mrSh.GetLayout(),
+ &std::get<0>(rDestinationTuple)->GetTOXForm()));
+
+ if (vFormattedFieldStrings.at(std::get<0>(rDestinationTuple)) == *std::get<1>(rDestinationTuple))
+ {
+ nDestId = std::get<2>(rDestinationTuple);
+ break;
+ }
+ }
+
+ if (nDestId == -1)
+ continue;
- mrSh.SwCursorShell::ClearMark();
+ const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
+ if (!lcl_TryMoveToNonHiddenField(mrSh, rTextNode, *pFormatField))
+ {
+ continue;
+ }
+
+ OUString const content(rAuthorityField.ExpandField(true, mrSh.GetLayout()));
+
+ // Select the field.
+ mrSh.SwCursorShell::SetMark();
+ mrSh.SwCursorShell::Right(1, SwCursorSkipMode::Chars);
+
+ // Create the links.
+ SwRects const rects(GetCursorRectsContainingText(mrSh));
+ for (const auto& rLinkRect : rects)
+ {
+ for (const auto& rLinkPageNum : CalcOutputPageNums(rLinkRect))
+ {
+ tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, rLinkRect.SVRect()));
+ sal_Int32 nLinkId = pPDFExtOutDevData->CreateLink(aRect, content, rLinkPageNum);
+ IdMapEntry aLinkEntry(rLinkRect, nLinkId);
+ pPDFExtOutDevData->GetSwPDFState()->m_LinkIdMap.push_back(aLinkEntry);
+ pPDFExtOutDevData->SetLinkDest(nLinkId, nDestId);
+ }
+ }
+ mrSh.SwCursorShell::ClearMark();
+ }
}
}
@@ -2297,7 +3021,8 @@ void SwEnhancedPDFExportHelper::MakeHeaderFooterLinks( vcl::PDFExtOutDevData& rP
const SwRect& rLinkRect,
sal_Int32 nDestId,
const OUString& rURL,
- bool bIntern ) const
+ bool bInternal,
+ OUString const& rContent) const
{
// We assume, that the primary link has just been exported. Therefore
// the offset of the link rectangle calculates as follows:
@@ -2305,35 +3030,35 @@ void SwEnhancedPDFExportHelper::MakeHeaderFooterLinks( vcl::PDFExtOutDevData& rP
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rTNd);
for ( SwTextFrame* pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
+ {
+ // Add offset to current page:
+ const SwPageFrame* pPageFrame = pTmpFrame->FindPageFrame();
+ SwRect aHFLinkRect( rLinkRect );
+ aHFLinkRect.Pos() = pPageFrame->getFrameArea().Pos() + aOffset;
+
+ // #i97135# the gcc_x64 optimizer gets aHFLinkRect != rLinkRect wrong
+ // fool it by comparing the position only (the width and height are the
+ // same anyway)
+ if ( aHFLinkRect.Pos() != rLinkRect.Pos() )
{
- // Add offset to current page:
- const SwPageFrame* pPageFrame = pTmpFrame->FindPageFrame();
- SwRect aHFLinkRect( rLinkRect );
- aHFLinkRect.Pos() = pPageFrame->getFrameArea().Pos() + aOffset;
-
- // #i97135# the gcc_x64 optimizer gets aHFLinkRect != rLinkRect wrong
- // fool it by comparing the position only (the width and height are the
- // same anyway)
- if ( aHFLinkRect.Pos() != rLinkRect.Pos() )
- {
- // Link PageNums
- std::vector<sal_Int32> aHFLinkPageNums = CalcOutputPageNums( aHFLinkRect );
-
- for (sal_Int32 aHFLinkPageNum : aHFLinkPageNums)
- {
- // Link Export
- tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, aHFLinkRect.SVRect()));
- const sal_Int32 nHFLinkId =
- rPDFExtOutDevData.CreateLink(aRect, aHFLinkPageNum);
+ // Link PageNums
+ std::vector<sal_Int32> aHFLinkPageNums = CalcOutputPageNums( aHFLinkRect );
- // Connect Link and Destination:
- if ( bIntern )
- rPDFExtOutDevData.SetLinkDest( nHFLinkId, nDestId );
- else
- rPDFExtOutDevData.SetLinkURL( nHFLinkId, rURL );
- }
+ for (sal_Int32 aHFLinkPageNum : aHFLinkPageNums)
+ {
+ // Link Export
+ tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, aHFLinkRect.SVRect()));
+ const sal_Int32 nHFLinkId =
+ rPDFExtOutDevData.CreateLink(aRect, rContent, aHFLinkPageNum);
+
+ // Connect Link and Destination:
+ if ( bInternal )
+ rPDFExtOutDevData.SetLinkDest( nHFLinkId, nDestId );
+ else
+ rPDFExtOutDevData.SetLinkURL( nHFLinkId, rURL );
}
}
+ }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/SwGrammarMarkUp.cxx b/sw/source/core/text/SwGrammarMarkUp.cxx
index 3a007ce055d2..53fd4b13c013 100644
--- a/sw/source/core/text/SwGrammarMarkUp.cxx
+++ b/sw/source/core/text/SwGrammarMarkUp.cxx
@@ -54,9 +54,9 @@ void SwGrammarMarkUp::MoveGrammar( sal_Int32 nPos, sal_Int32 nDiff )
}
}
-SwGrammarMarkUp* SwGrammarMarkUp::SplitGrammarList( sal_Int32 nSplitPos )
+std::unique_ptr<SwGrammarMarkUp> SwGrammarMarkUp::SplitGrammarList( sal_Int32 nSplitPos )
{
- SwGrammarMarkUp* pNew = static_cast<SwGrammarMarkUp*>(SplitList( nSplitPos ));
+ std::unique_ptr<SwGrammarMarkUp> pNew( static_cast<SwGrammarMarkUp*>(SplitList( nSplitPos ).release()) );
if( maSentence.empty() )
return pNew;
auto pIter = std::find_if(maSentence.begin(), maSentence.end(),
@@ -64,7 +64,7 @@ SwGrammarMarkUp* SwGrammarMarkUp::SplitGrammarList( sal_Int32 nSplitPos )
if( pIter != maSentence.begin() )
{
if( !pNew ) {
- pNew = new SwGrammarMarkUp();
+ pNew.reset(new SwGrammarMarkUp());
pNew->SetInvalid( 0, COMPLETE_STRING );
}
pNew->maSentence.insert( pNew->maSentence.begin(), maSentence.begin(), pIter );
diff --git a/sw/source/core/text/atrhndl.hxx b/sw/source/core/text/atrhndl.hxx
index 1033242492c3..efe1ae954958 100644
--- a/sw/source/core/text/atrhndl.hxx
+++ b/sw/source/core/text/atrhndl.hxx
@@ -18,9 +18,8 @@
*/
#pragma once
-#define NUM_ATTRIBUTE_STACKS 44
+#define NUM_ATTRIBUTE_STACKS 45
-#include <memory>
#include <vector>
#include <swfntcch.hxx>
@@ -45,7 +44,9 @@ private:
// This is the base font for the paragraph. It is stored in order to have
// a template, if we have to restart the attribute evaluation
- std::unique_ptr<SwFont> m_pFnt;
+ std::optional<SwFont> m_oFnt;
+
+ int m_nINETFMT = 0; // for font's SetURL
bool m_bVertLayout;
bool m_bVertLayoutLRBT;
@@ -104,15 +105,15 @@ public:
inline void SwAttrHandler::ResetFont( SwFont& rFnt ) const
{
- OSL_ENSURE(m_pFnt, "ResetFont without a font");
- if (m_pFnt)
- rFnt = *m_pFnt;
+ OSL_ENSURE(m_oFnt, "ResetFont without a font");
+ if (m_oFnt)
+ rFnt = *m_oFnt;
};
inline const SwFont* SwAttrHandler::GetFont() const
{
- return m_pFnt.get();
+ return m_oFnt ? &*m_oFnt : nullptr;
};
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ \ No newline at end of file
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/atrstck.cxx b/sw/source/core/text/atrstck.cxx
index 4cc7b501094f..82a3da7fbf17 100644
--- a/sw/source/core/text/atrstck.cxx
+++ b/sw/source/core/text/atrstck.cxx
@@ -123,6 +123,7 @@ const sal_uInt8 StackPos[ RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN + 1 ] =
42, // RES_TXTATR_CJK_RUBY, // 53
0, // RES_TXTATR_UNKNOWN_CONTAINER, // 54
43, // RES_TXTATR_INPUTFIELD // 55
+ 44, // RES_TXTATR_CONTENTCONTROL // 56
};
namespace CharFormat
@@ -220,9 +221,8 @@ static bool lcl_ChgHyperLinkColor( const SwTextAttr& rAttr,
// take color from character format 'unvisited link'
rINetAttr.SetVisited(false);
const SwCharFormat* pTmpFormat = rINetAttr.GetCharFormat();
- const SfxPoolItem* pItem;
- if (SfxItemState::SET == pTmpFormat->GetItemState(RES_CHRATR_COLOR, true, &pItem))
- *pColor = pItem->StaticWhichCast(RES_CHRATR_COLOR).GetValue();
+ if (const SvxColorItem* pItem = pTmpFormat->GetItemIfSet(RES_CHRATR_COLOR))
+ *pColor = pItem->GetValue();
rINetAttr.SetVisited(true);
}
return true;
@@ -237,8 +237,8 @@ static bool lcl_ChgHyperLinkColor( const SwTextAttr& rAttr,
if ( pShell->GetWin() &&
(
- (rINetAttr.IsVisited() && SwViewOption::IsVisitedLinks()) ||
- (!rINetAttr.IsVisited() && SwViewOption::IsLinks())
+ (rINetAttr.IsVisited() && pShell->GetViewOptions()->IsVisitedLinks()) ||
+ (!rINetAttr.IsVisited() && pShell->GetViewOptions()->IsLinks())
)
)
{
@@ -247,12 +247,12 @@ static bool lcl_ChgHyperLinkColor( const SwTextAttr& rAttr,
if (rINetAttr.IsVisited())
{
// take color from view option 'visited link color'
- *pColor = SwViewOption::GetVisitedLinksColor();
+ *pColor = pShell->GetViewOptions()->GetVisitedLinksColor();
}
else
{
// take color from view option 'unvisited link color'
- *pColor = SwViewOption::GetLinksColor();
+ *pColor = pShell->GetViewOptions()->GetLinksColor();
}
}
return true;
@@ -322,14 +322,10 @@ void SwAttrHandler::Init( const SfxPoolItem** pPoolItem, const SwAttrSet* pAS,
// SwTextFrame::FormatOnceMore situation or (since sw_redlinehide)
// from SwAttrIter::Seek(); in the latter case SwTextSizeInfo::m_pFnt
// is an alias of m_pFnt so it must not be deleted!
- if (m_pFnt)
- {
- *m_pFnt = rFnt;
- }
+ if (m_oFnt)
+ *m_oFnt = rFnt;
else
- {
- m_pFnt.reset(new SwFont(rFnt));
- }
+ m_oFnt.emplace(rFnt);
}
void SwAttrHandler::Reset( )
@@ -371,6 +367,13 @@ void SwAttrHandler::PushAndChg( const SwTextAttr& rAttr, SwFont& rFnt )
}
}
}
+
+ if (rAttr.Which() == RES_TXTATR_INETFMT)
+ {
+ if (m_nINETFMT == 0)
+ rFnt.SetURL(true);
+ ++m_nINETFMT;
+ }
}
// this is the usual case, we have a basic attribute, push it onto the
// stack and change the font
@@ -437,6 +440,14 @@ void SwAttrHandler::PopAndChg( const SwTextAttr& rAttr, SwFont& rFnt )
const SfxItemSet* pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
if ( !pSet ) return;
+ if (rAttr.Which() == RES_TXTATR_INETFMT)
+ {
+ assert(m_nINETFMT > 0);
+ --m_nINETFMT;
+ if (m_nINETFMT == 0)
+ rFnt.SetURL(false);
+ }
+
for ( sal_uInt16 i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++)
{
const SfxPoolItem* pItem;
@@ -476,8 +487,7 @@ void SwAttrHandler::Pop( const SwTextAttr& rAttr )
void SwAttrHandler::ActivateTop( SwFont& rFnt, const sal_uInt16 nAttr )
{
- OSL_ENSURE( nAttr < RES_TXTATR_WITHEND_END,
- "I cannot activate this attribute, nWhich >= RES_TXTATR_WITHEND_END" );
+ assert(nAttr < RES_TXTATR_WITHEND_END);
const sal_uInt16 nStackPos = StackPos[ nAttr ];
const SwTextAttr* pTopAt = GetTop(nStackPos);
@@ -521,6 +531,10 @@ void SwAttrHandler::ActivateTop( SwFont& rFnt, const sal_uInt16 nAttr )
{
rFnt.GetMeta()--;
}
+ else if (nAttr == RES_TXTATR_CONTENTCONTROL)
+ {
+ rFnt.GetContentControl()--;
+ }
else if ( RES_TXTATR_CJK_RUBY == nAttr )
{
// ruby stack has no more attributes
@@ -531,7 +545,7 @@ void SwAttrHandler::ActivateTop( SwFont& rFnt, const sal_uInt16 nAttr )
if ( pTwoLineAttr )
{
- const auto& rTwoLineItem = CharFormat::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES )->StaticWhichCast(RES_CHRATR_TWO_LINES);
+ const auto& rTwoLineItem = *CharFormat::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES );
bTwoLineAct = rTwoLineItem.GetValue();
}
else
@@ -546,7 +560,7 @@ void SwAttrHandler::ActivateTop( SwFont& rFnt, const sal_uInt16 nAttr )
if ( pRotateAttr )
{
- const auto& rRotateItem = CharFormat::GetItem( *pRotateAttr, RES_CHRATR_ROTATE )->StaticWhichCast(RES_CHRATR_ROTATE);
+ const auto& rRotateItem = *CharFormat::GetItem( *pRotateAttr, RES_CHRATR_ROTATE );
rFnt.SetVertical( rRotateItem.GetValue(), m_bVertLayout );
}
else
@@ -751,7 +765,7 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush )
if ( pTwoLineAttr )
{
- const auto& rTwoLineItem = CharFormat::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES )->StaticWhichCast(RES_CHRATR_TWO_LINES);
+ const auto& rTwoLineItem = *CharFormat::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES );
bTwoLineAct = rTwoLineItem.GetValue();
}
else
@@ -786,7 +800,7 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush )
if ( pRotateAttr )
{
- const auto& rRotateItem = CharFormat::GetItem( *pRotateAttr, RES_CHRATR_ROTATE )->StaticWhichCast(RES_CHRATR_ROTATE);
+ const auto& rRotateItem = *CharFormat::GetItem( *pRotateAttr, RES_CHRATR_ROTATE );
rFnt.SetVertical( rRotateItem.GetValue(), m_bVertLayout );
}
else
@@ -815,6 +829,16 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush )
else
rFnt.GetMeta()--;
break;
+ case RES_TXTATR_CONTENTCONTROL:
+ if (bPush)
+ {
+ rFnt.GetContentControl()++;
+ }
+ else
+ {
+ rFnt.GetContentControl()--;
+ }
+ break;
case RES_TXTATR_INPUTFIELD :
if ( bPush )
rFnt.GetInputField()++;
@@ -828,11 +852,11 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush )
void SwAttrHandler::GetDefaultAscentAndHeight( SwViewShell const * pShell, OutputDevice const & rOut,
sal_uInt16& nAscent, sal_uInt16& nHeight ) const
{
- OSL_ENSURE(m_pFnt, "No font available for GetDefaultAscentAndHeight");
+ OSL_ENSURE(m_oFnt, "No font available for GetDefaultAscentAndHeight");
- if (m_pFnt)
+ if (m_oFnt)
{
- SwFont aFont( *m_pFnt );
+ SwFont aFont( *m_oFnt );
nHeight = aFont.GetHeight( pShell, rOut );
nAscent = aFont.GetAscent( pShell, rOut );
}
diff --git a/sw/source/core/text/frmcrsr.cxx b/sw/source/core/text/frmcrsr.cxx
index a4cab82d92b1..94fcdf453cfa 100644
--- a/sw/source/core/text/frmcrsr.cxx
+++ b/sw/source/core/text/frmcrsr.cxx
@@ -180,7 +180,7 @@ bool SwTextFrame::GetCharRect( SwRect& rOrig, const SwPosition &rPos,
{
OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::GetCharRect with swapped frame" );
- if( IsLocked() || IsHiddenNow() )
+ if (IsLocked())
return false;
// Find the right frame first. We need to keep in mind that:
@@ -629,7 +629,7 @@ bool SwTextFrame::GetModelPositionForViewPoint_(SwPosition* pPos, const Point& r
}
}
bool bChgFillData = false;
- if( pFillData && FindPageFrame()->getFrameArea().IsInside( aOldPoint ) )
+ if( pFillData && FindPageFrame()->getFrameArea().Contains( aOldPoint ) )
{
FillCursorPos( *pFillData );
bChgFillData = true;
@@ -671,7 +671,7 @@ bool SwTextFrame::GetModelPositionForViewPoint(SwPosition* pPos, Point& rPoint,
bool SwTextFrame::LeftMargin(SwPaM *pPam) const
{
- assert(GetMergedPara() || &pPam->GetNode() == static_cast<SwContentNode const*>(GetDep()));
+ assert(GetMergedPara() || &pPam->GetPointNode() == static_cast<SwContentNode const*>(GetDep()));
SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(),
SwTextCursor::IsRightMargin() );
@@ -705,7 +705,7 @@ bool SwTextFrame::LeftMargin(SwPaM *pPam) const
bool SwTextFrame::RightMargin(SwPaM *pPam, bool bAPI) const
{
- assert(GetMergedPara() || &pPam->GetNode() == static_cast<SwContentNode const*>(GetDep()));
+ assert(GetMergedPara() || &pPam->GetPointNode() == static_cast<SwContentNode const*>(GetDep()));
SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(),
SwTextCursor::IsRightMargin() );
@@ -764,8 +764,8 @@ bool SwTextFrame::UnitUp_( SwPaM *pPam, const SwTwips nOffset,
SwSetToRightMargin aSet;
if( IsInTab() &&
- pPam->GetNode().StartOfSectionNode() !=
- pPam->GetNode( false ).StartOfSectionNode() )
+ pPam->GetPointNode().StartOfSectionNode() !=
+ pPam->GetMarkNode().StartOfSectionNode() )
{
// If the PaM is located within different boxes, we have a table selection,
// which is handled by the base class.
@@ -831,13 +831,13 @@ bool SwTextFrame::UnitUp_( SwPaM *pPam, const SwTwips nOffset,
// See comment in SwTextFrame::GetModelPositionForViewPoint()
#if OSL_DEBUG_LEVEL > 0
- const sal_uLong nOldNode = pPam->GetPoint()->nNode.GetIndex();
+ const SwNodeOffset nOldNode = pPam->GetPoint()->GetNodeIndex();
#endif
// The node should not be changed
TextFrameIndex nTmpOfst = aLine.GetModelPositionForViewPoint(pPam->GetPoint(),
aCharBox.Pos(), false );
#if OSL_DEBUG_LEVEL > 0
- OSL_ENSURE( nOldNode == pPam->GetPoint()->nNode.GetIndex(),
+ OSL_ENSURE( nOldNode == pPam->GetPoint()->GetNodeIndex(),
"SwTextFrame::UnitUp: illegal node change" );
#endif
@@ -1151,8 +1151,8 @@ bool SwTextFrame::UnitDown_(SwPaM *pPam, const SwTwips nOffset,
{
if ( IsInTab() &&
- pPam->GetNode().StartOfSectionNode() !=
- pPam->GetNode( false ).StartOfSectionNode() )
+ pPam->GetPointNode().StartOfSectionNode() !=
+ pPam->GetMarkNode().StartOfSectionNode() )
{
// If the PaM is located within different boxes, we have a table selection,
// which is handled by the base class.
@@ -1192,7 +1192,7 @@ bool SwTextFrame::UnitDown_(SwPaM *pPam, const SwTwips nOffset,
aCharBox.Width( aCharBox.SSize().Width() / 2 );
#if OSL_DEBUG_LEVEL > 0
// See comment in SwTextFrame::GetModelPositionForViewPoint()
- const sal_uLong nOldNode = pPam->GetPoint()->nNode.GetIndex();
+ const SwNodeOffset nOldNode = pPam->GetPoint()->GetNodeIndex();
#endif
if ( pNextLine && ! bFirstOfDouble )
aLine.NextLine();
@@ -1200,7 +1200,7 @@ bool SwTextFrame::UnitDown_(SwPaM *pPam, const SwTwips nOffset,
TextFrameIndex nTmpOfst = aLine.GetModelPositionForViewPoint( pPam->GetPoint(),
aCharBox.Pos(), false );
#if OSL_DEBUG_LEVEL > 0
- OSL_ENSURE( nOldNode == pPam->GetPoint()->nNode.GetIndex(),
+ OSL_ENSURE( nOldNode == pPam->GetPoint()->GetNodeIndex(),
"SwTextFrame::UnitDown: illegal node change" );
#endif
@@ -1319,7 +1319,7 @@ void SwTextFrame::FillCursorPos( SwFillData& rFill ) const
nNextCol = 0;
}
else
- ++nNextCol; // Empty columns require column brakes
+ ++nNextCol; // Empty columns require column breaks
}
if( pTmp != GetUpper()->GetUpper() ) // Did we end up in another column?
{
@@ -1418,7 +1418,7 @@ void SwTextFrame::FillCursorPos( SwFillData& rFill ) const
if( nDiff > 0 )
{
nDiff /= nDist;
- rFill.Fill().nParaCnt = static_cast<sal_uInt16>(nDiff + 1);
+ rFill.Fill().nParaCnt = o3tl::narrowing<sal_uInt16>(nDiff + 1);
rFill.nLineWidth = 0;
rFill.bInner = false;
rFill.bEmpty = true;
@@ -1431,16 +1431,18 @@ void SwTextFrame::FillCursorPos( SwFillData& rFill ) const
else
{
const SvxTabStopItem &rRuler = pSet->GetTabStops();
- const SvxLRSpaceItem &rLRSpace = pSet->GetLRSpace();
+ const SvxFirstLineIndentItem& rFirstLine(pSet->GetFirstLineIndent());
+ const SvxTextLeftMarginItem& rTextLeftMargin(pSet->GetTextLeftMargin());
+ const SvxRightMarginItem& rRightMargin(pSet->GetRightMargin());
SwRect &rRect = rFill.Fill().aCursor;
rRect.Top( rFill.Bottom() + (nDiff+1) * nDist - nLineHeight );
if( nFirst && nDiff > -1 )
rRect.Top( rRect.Top() + nFirst );
rRect.Height( nLineHeight );
- SwTwips nLeft = rFill.Left() + rLRSpace.GetLeft() +
+ SwTwips nLeft = rFill.Left() + rTextLeftMargin.GetLeft(rFirstLine) +
GetTextNodeForParaProps()->GetLeftMarginWithNum();
- SwTwips nRight = rFill.Right() - rLRSpace.GetRight();
+ SwTwips nRight = rFill.Right() - rRightMargin.GetRight();
SwTwips nCenter = ( nLeft + nRight ) / 2;
rRect.Left( nLeft );
if( SwFillMode::Margin == rFill.Mode() )
@@ -1510,7 +1512,7 @@ void SwTextFrame::FillCursorPos( SwFillData& rFill ) const
}
else if( rFill.X() > nLeft )
{
- SwTwips nTextLeft = rFill.Left() + rLRSpace.GetTextLeft() +
+ SwTwips nTextLeft = rFill.Left() + rTextLeftMargin.GetTextLeft() +
GetTextNodeForParaProps()->GetLeftMarginWithNum(true);
rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTextLeft;
SwTwips nLeftTab;
@@ -1536,7 +1538,7 @@ void SwTextFrame::FillCursorPos( SwFillData& rFill ) const
else
{
const SvxTabStopItem& rTab =
- pSet->GetPool()->GetDefaultItem( RES_PARATR_TABSTOP );
+ pSet->GetPool()->GetUserOrPoolDefaultItem( RES_PARATR_TABSTOP );
const SwTwips nDefTabDist = rTab[0].GetTabPos();
nRightTab = nLeftTab - nTextLeft;
nRightTab /= nDefTabDist;
diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx
index 0304661e8438..4e2caf27b276 100644
--- a/sw/source/core/text/frmform.cxx
+++ b/sw/source/core/text/frmform.cxx
@@ -17,6 +17,8 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
+#include <config_wasm_strip.h>
+
#include <sal/config.h>
#include <sal/log.hxx>
@@ -47,6 +49,9 @@
#include <editeng/tstpitem.hxx>
#include <redline.hxx>
#include <comphelper/lok.hxx>
+#include <flyfrms.hxx>
+#include <frmtool.hxx>
+#include <layouter.hxx>
// Tolerance in formatting and text output
#define SLOPPY_TWIPS 5
@@ -212,7 +217,7 @@ bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
if( pPara )
{
pPara->GetReformat() = SwCharRange();
- pPara->GetDelta() = 0;
+ pPara->SetDelta(0);
}
}
@@ -310,7 +315,7 @@ bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
if( pPara )
{
pPara->GetReformat() = SwCharRange();
- pPara->GetDelta() = 0;
+ pPara->SetDelta(0);
}
}
@@ -323,7 +328,7 @@ bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
const tools::Long nRemaining =
- aRectFnSet.BottomDist( GetUpper()->getFrameArea(), nOldBottom );
- if ( nRemaining > 0 && !GetUpper()->IsSctFrame() &&
+ if ( nRemaining > 0 &&
nRemaining != ( aRectFnSet.IsVert() ?
nMyPos - getFrameArea().Right() :
getFrameArea().Top() - nMyPos ) )
@@ -337,7 +342,47 @@ bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
void SwTextFrame::MakePos()
{
+ Point aOldPos = getFrameArea().Pos();
SwFrame::MakePos();
+
+ // Recalc split flys if our position changed.
+ if (aOldPos != getFrameArea().Pos())
+ {
+ // Find the master frame.
+ const SwTextFrame* pMaster = this;
+ while (pMaster->IsFollow())
+ {
+ pMaster = pMaster->FindMaster();
+ }
+ // Find which flys are effectively anchored to this frame.
+ for (const auto& pFly : pMaster->GetSplitFlyDrawObjs())
+ {
+ SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
+ if (pFlyAnchor != this)
+ {
+ continue;
+ }
+ // Possibly this fly was positioned relative to us, invalidate its position now that our
+ // position is changed.
+ SwPageFrame* pPageFrame = pFly->FindPageFrame();
+ bool bFlyNeedsPositioning = false;
+ bool bFlyPageMismatch = false;
+ if (pPageFrame)
+ {
+ // Was the position just adjusted to be inside the page frame?
+ bFlyNeedsPositioning = pFly->getFrameArea().Pos() == pPageFrame->getFrameArea().Pos();
+ // Is the fly on a page different than the anchor frame?
+ bFlyPageMismatch = pPageFrame != FindPageFrame();
+ }
+ if (bFlyNeedsPositioning || bFlyPageMismatch)
+ {
+ // Not really positioned, unlock the position once to allow a recalc.
+ pFly->UnlockPosition();
+ }
+ pFly->InvalidatePos();
+ }
+ }
+
// Inform LOK clients about change in position of redlines (if any)
if(!comphelper::LibreOfficeKit::isActive())
return;
@@ -347,14 +392,14 @@ void SwTextFrame::MakePos()
for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
{
SwRangeRedline* pRedln = rTable[nRedlnPos];
- if (pTextNode->GetIndex() == pRedln->GetPoint()->nNode.GetNode().GetIndex())
+ if (pTextNode->GetIndex() == pRedln->GetPoint()->GetNode().GetIndex())
{
pRedln->MaybeNotifyRedlinePositionModification(getFrameArea().Top());
if (GetMergedPara()
&& pRedln->GetType() == RedlineType::Delete
- && pRedln->GetPoint()->nNode != pRedln->GetMark()->nNode)
+ && pRedln->GetPoint()->GetNode() != pRedln->GetMark()->GetNode())
{
- pTextNode = pRedln->End()->nNode.GetNode().GetTextNode();
+ pTextNode = pRedln->End()->GetNode().GetTextNode();
}
}
}
@@ -456,7 +501,7 @@ void SwTextFrame::AdjustFrame( const SwTwips nChgHght, bool bHasToFit )
else
nRstHeight = getFrameArea().Left() + getFrameArea().Width() -
( GetUpper()->getFrameArea().Left() + GetUpper()->getFramePrintArea().Left() );
- }
+ }
else
nRstHeight = GetUpper()->getFrameArea().Top()
+ GetUpper()->getFramePrintArea().Top()
@@ -517,9 +562,6 @@ void SwTextFrame::AdjustFrame( const SwTwips nChgHght, bool bHasToFit )
css::uno::Sequence< css::style::TabStop > SwTextFrame::GetTabStopInfo( SwTwips CurrentPos )
{
- css::uno::Sequence< css::style::TabStop > tabs(1);
- css::style::TabStop ts;
-
SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
SwTextFormatter aLine( this, &aInf );
SwTextCursor TextCursor( this, &aInf );
@@ -533,10 +575,11 @@ css::uno::Sequence< css::style::TabStop > SwTextFrame::GetTabStopInfo( SwTwips C
if( !pTS )
{
- return css::uno::Sequence< css::style::TabStop >();
+ return {};
}
// copy tab stop information into a Sequence, which only contains one element.
+ css::style::TabStop ts;
ts.Position = pTS->GetTabPos();
ts.DecimalChar = pTS->GetDecimal();
ts.FillChar = pTS->GetFill();
@@ -550,8 +593,7 @@ css::uno::Sequence< css::style::TabStop > SwTextFrame::GetTabStopInfo( SwTwips C
default: break; // prevent warning
}
- tabs[0] = ts;
- return tabs;
+ return { ts };
}
// AdjustFollow expects the following situation:
@@ -568,17 +610,27 @@ void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
// We got the rest of the text mass: Delete all Follows
// DummyPortions() are a special case.
// Special cases are controlled by parameter <nMode>.
- if( HasFollow() && !(nMode & 1) && nOffset == nEnd )
+ bool bDontJoin = nMode & 1;
+ if( HasFollow() && !bDontJoin && nOffset == nEnd )
{
while( GetFollow() )
{
if( GetFollow()->IsLocked() )
{
- OSL_FAIL( "+SwTextFrame::JoinFrame: Follow is locked." );
+ // this can happen when follow calls pMaster->GetFormatted()
+ SAL_INFO("sw.core", "+SwTextFrame::JoinFrame: Follow is locked." );
return;
}
if (GetFollow()->IsDeleteForbidden())
return;
+
+ if (HasNonLastSplitFlyDrawObj())
+ {
+ // If a fly frame is anchored to us that has a follow, then don't join the anchor.
+ // First those fly frames have to be joined.
+ return;
+ }
+
JoinFrame();
}
@@ -591,20 +643,46 @@ void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
const TextFrameIndex nNewOfst = (IsInFootnote() && (!GetIndNext() || HasFollow()))
? rLine.FormatQuoVadis(nOffset) : nOffset;
- if( !(nMode & 1) )
+ bool bHasNonLastSplitFlyDrawObj = false;
+ if (GetFollow() && GetOffset() == GetFollow()->GetOffset())
+ {
+ bHasNonLastSplitFlyDrawObj = HasNonLastSplitFlyDrawObj();
+ }
+
+ if( !bDontJoin )
{
// We steal text mass from our Follows
// It can happen that we have to join some of them
while( GetFollow() && GetFollow()->GetFollow() &&
nNewOfst >= GetFollow()->GetFollow()->GetOffset() )
{
+ if (bHasNonLastSplitFlyDrawObj)
+ {
+ // A non-last split fly is anchored to us, don't move content from the last frame to
+ // this one and don't join.
+ return;
+ }
+
JoinFrame();
}
}
+ if (IsEmptyMasterWithSplitFly())
+ {
+ // A split fly is anchored to us, don't move content from the follow frame to this one.
+ return;
+ }
+
// The Offset moved
if( GetFollow() )
{
+ if (!bDontJoin && bHasNonLastSplitFlyDrawObj)
+ {
+ // A non-last split fly is anchored to us, our follow is the last one in the text frame
+ // chain. No move of text from that follow to this text frame.
+ return;
+ }
+
if ( nMode )
GetFollow()->ManipOfst(TextFrameIndex(0));
@@ -666,16 +744,20 @@ SwContentFrame *SwTextFrame::JoinFrame()
// Relation CONTENT_FLOWS_FROM for current next paragraph will change
// and relation CONTENT_FLOWS_TO for current previous paragraph, which
// is <this>, will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
{
SwViewShell* pViewShell( pFoll->getRootFrame()->GetCurrShell() );
if ( pViewShell && pViewShell->GetLayout() &&
pViewShell->GetLayout()->IsAnyShellAccessible() )
{
+ auto pNext = pFoll->FindNextCnt( true );
pViewShell->InvalidateAccessibleParaFlowRelation(
- dynamic_cast<SwTextFrame*>(pFoll->FindNextCnt( true )),
+ pNext ? pNext->DynCastTextFrame() : nullptr,
this );
}
}
+#endif
+
pFoll->Cut();
SetFollow(pNxt);
SwFrame::DestroyFrame(pFoll);
@@ -700,16 +782,19 @@ void SwTextFrame::SplitFrame(TextFrameIndex const nTextPos)
// Relation CONTENT_FLOWS_FROM for current next paragraph will change
// and relation CONTENT_FLOWS_TO for current previous paragraph, which
// is <this>, will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
{
SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
if ( pViewShell && pViewShell->GetLayout() &&
pViewShell->GetLayout()->IsAnyShellAccessible() )
{
+ auto pNext = pNew->FindNextCnt( true );
pViewShell->InvalidateAccessibleParaFlowRelation(
- dynamic_cast<SwTextFrame*>(pNew->FindNextCnt( true )),
+ pNext ? pNext->DynCastTextFrame() : nullptr,
this );
}
}
+#endif
// If footnotes end up in pNew bz our actions, we need
// to re-register them
@@ -758,7 +843,7 @@ void SwTextFrame::SplitFrame(TextFrameIndex const nTextPos)
void SwTextFrame::SetOffset_(TextFrameIndex const nNewOfst)
{
- // We do not need to invalidate out Follow.
+ // We do not need to invalidate our Follow.
// We are a Follow, get formatted right away and call
// SetOffset() from there
mnOffset = nNewOfst;
@@ -768,7 +853,7 @@ void SwTextFrame::SetOffset_(TextFrameIndex const nNewOfst)
SwCharRange &rReformat = pPara->GetReformat();
rReformat.Start() = TextFrameIndex(0);
rReformat.Len() = TextFrameIndex(GetText().getLength());
- pPara->GetDelta() = sal_Int32(rReformat.Len());
+ pPara->SetDelta(sal_Int32(rReformat.Len()));
}
InvalidateSize();
}
@@ -987,7 +1072,7 @@ bool SwTextFrame::CalcPreps()
return bRet;
}
-// We rewire the footnotes and the character bound objects
+// Move the as-character objects - footnotes must be moved by RemoveFootnote!
void SwTextFrame::ChangeOffset( SwTextFrame* pFrame, TextFrameIndex nNew )
{
if( pFrame->GetOffset() < nNew )
@@ -996,6 +1081,54 @@ void SwTextFrame::ChangeOffset( SwTextFrame* pFrame, TextFrameIndex nNew )
MoveFlyInCnt( pFrame, nNew, TextFrameIndex(COMPLETE_STRING) );
}
+static bool isFirstVisibleFrameInBody(const SwTextFrame* pFrame)
+{
+ const SwFrame* pBodyFrame = pFrame->FindBodyFrame();
+ if (!pBodyFrame)
+ return false;
+ for (const SwFrame* pCur = pFrame;;)
+ {
+ for (const SwFrame* pPrev = pCur->GetPrev(); pPrev; pPrev = pPrev->GetPrev())
+ if (!pPrev->IsHiddenNow())
+ return false;
+ pCur = pCur->GetUpper();
+ assert(pCur); // We found pBodyFrame, right?
+ if (pCur->IsBodyFrame())
+ return true;
+ }
+}
+
+static bool hasFly(const SwTextFrame* pFrame)
+{
+ if (auto pDrawObjs = pFrame->GetDrawObjs(); pDrawObjs && pDrawObjs->size())
+ {
+ auto anchorId = (*pDrawObjs)[0]->GetFrameFormat()->GetAnchor().GetAnchorId();
+ if (anchorId == RndStdIds::FLY_AT_PARA || anchorId == RndStdIds::FLY_AT_CHAR)
+ return true;
+ }
+ return false;
+}
+
+static bool hasAtPageFly(const SwFrame* pFrame)
+{
+ auto pPageFrame = pFrame->FindPageFrame();
+ if (!pPageFrame)
+ return false;
+ auto pPageDrawObjs = pPageFrame->GetDrawObjs();
+ if (pPageDrawObjs)
+ {
+ for (const auto pObject : *pPageDrawObjs)
+ if (pObject->GetFrameFormat()->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ return true;
+ }
+ return false;
+}
+
+static bool isReallyEmptyMaster(const SwTextFrame* pFrame)
+{
+ return pFrame->IsEmptyMaster() && (!pFrame->GetDrawObjs() || !pFrame->GetDrawObjs()->size());
+}
+
void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
WidowsAndOrphans &rFrameBreak,
TextFrameIndex const nStrLen,
@@ -1021,20 +1154,57 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
!rFrameBreak.IsInside( rLine ) )
: rFrameBreak.IsBreakNow( rLine ) ) ) )
? 1 : 0;
+
+ SwTextFormatInfo& rInf = rLine.GetInfo();
+ bool bEmptyWithSplitFly = false;
+ if (nNew == 0 && !nStrLen && !rInf.GetTextFly().IsOn() && IsEmptyWithSplitFly())
+ {
+ // Empty paragraph, so IsBreakNow() is not called, but we should split the fly portion and
+ // the paragraph marker.
+ nNew = 1;
+ bEmptyWithSplitFly = true;
+ }
+
+ const SwFrame *pBodyFrame = FindBodyFrame();
+
// i#84870
// no split of text frame, which only contains an as-character anchored object
- bool bOnlyContainsAsCharAnchoredObj =
+ bool bLoneAsCharAnchoredObj =
+ pBodyFrame &&
!IsFollow() && nStrLen == TextFrameIndex(1) &&
GetDrawObjs() && GetDrawObjs()->size() == 1 &&
- (*GetDrawObjs())[0]->GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
-
- // Still try split text frame if we have columns.
- if (FindColFrame())
- bOnlyContainsAsCharAnchoredObj = false;
+ (*GetDrawObjs())[0]->GetFrameFormat()->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
- if ( nNew && bOnlyContainsAsCharAnchoredObj )
+ if (bLoneAsCharAnchoredObj)
{
- nNew = 0;
+ // Still try split text frame if we have columns.
+ if (FindColFrame())
+ bLoneAsCharAnchoredObj = false;
+ // tdf#160526: only no split if there is no preceding frames on same page
+ else if (!isFirstVisibleFrameInBody(this))
+ bLoneAsCharAnchoredObj = false;
+ else
+ nNew = 0;
+ }
+ else if (nNew)
+ {
+ if (IsFollow())
+ {
+ // tdf#160549: do not split the frame at the very beginning again, if its master was empty
+ auto precede = static_cast<SwTextFrame*>(GetPrecede());
+ assert(precede);
+ auto precedeText = precede->DynCastTextFrame();
+ assert(precedeText);
+ if (isReallyEmptyMaster(precedeText))
+ nNew = 0;
+ }
+ else if (!bEmptyWithSplitFly)
+ {
+ // Do not split immediately in the beginning of page (unless there is an at-para or
+ // at-char or at-page fly, which pushes the rest down)
+ if (isFirstVisibleFrameInBody(this) && !hasFly(this) && pBodyFrame && !hasAtPageFly(pBodyFrame))
+ nNew = 0;
+ }
}
if ( nNew )
@@ -1042,8 +1212,6 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
SplitFrame( nEnd );
}
- const SwFrame *pBodyFrame = FindBodyFrame();
-
const tools::Long nBodyHeight = pBodyFrame ? ( IsVertical() ?
pBodyFrame->getFrameArea().Width() :
pBodyFrame->getFrameArea().Height() ) : 0;
@@ -1052,7 +1220,7 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
// are valid now
pPara->GetReformat() = SwCharRange();
bool bDelta = pPara->GetDelta() != 0;
- pPara->GetDelta() = 0;
+ pPara->SetDelta(0);
if( rLine.IsStop() )
{
@@ -1067,7 +1235,11 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
// AdjustFollow might execute JoinFrame() because of this.
// Else, nEnd is the end of the last line in the Master.
TextFrameIndex nOld = nEnd;
- nEnd = rLine.GetEnd();
+ // Make sure content from the last floating table anchor is not shifted to previous anchors.
+ if (!HasNonLastSplitFlyDrawObj())
+ {
+ nEnd = rLine.GetEnd();
+ }
if( GetFollow() )
{
if( nNew && nOld < nEnd )
@@ -1082,6 +1254,7 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
// need to create a Follow.
// We also need to do this if the whole mass of text remains in the Master,
// because a hard line break could necessitate another line (without text mass)!
+ TextFrameIndex const nOld(nEnd);
nEnd = rLine.GetEnd();
if( GetFollow() )
{
@@ -1107,18 +1280,43 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
// for the paragraph mark.
nNew |= 1;
}
+ // move footnotes if the follow is kept - if RemoveFootnote() is
+ // called in next format iteration, it will be with the *new*
+ // offset so no effect!
+ if (nNew && nOld < nEnd)
+ {
+ RemoveFootnote(nOld, nEnd - nOld);
+ }
ChangeOffset( GetFollow(), nEnd );
+
+ SwFlyAtContentFrame* pNonLastSplitFlyDrawObj = HasNonLastSplitFlyDrawObj();
+ if (pNonLastSplitFlyDrawObj && !pNonLastSplitFlyDrawObj->IsWrapOnAllPages())
+ {
+ // Make sure content from the last floating table anchor is not shifted to previous
+ // anchors, unless we're in the special "wrap on all pages" mode.
+ nEnd = TextFrameIndex(0);
+ }
+
GetFollow()->ManipOfst( nEnd );
}
else
{
+ const SwTextNode* pTextNode = GetTextNodeForParaProps();
+ bool bHasVisibleNumRule = nStrLen == TextFrameIndex(0) && pTextNode->GetNumRule();
+
+ if (!pTextNode->HasVisibleNumberingOrBullet())
+ {
+ bHasVisibleNumRule = false;
+ }
+
// Only split frame, if the frame contains
// content or contains no content, but has a numbering.
// i#84870 - No split, if text frame only contains one
// as-character anchored object.
- if ( !bOnlyContainsAsCharAnchoredObj &&
- (nStrLen > TextFrameIndex(0) ||
- (nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule()))
+ if (!bLoneAsCharAnchoredObj
+ && (bHasVisibleNumRule
+ || (nStrLen > TextFrameIndex(0)
+ && (nEnd != rLine.GetStart() || rInf.GetRest())))
)
{
SplitFrame( nEnd );
@@ -1142,7 +1340,7 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
SwTwips nChg = rLine.CalcBottomLine() - nDocPrtTop - nOldHeight;
//#i84870# - no shrink of text frame, if it only contains one as-character anchored object.
- if ( nChg < 0 && !bDelta && bOnlyContainsAsCharAnchoredObj )
+ if (nChg < 0 && !bDelta && bLoneAsCharAnchoredObj)
{
nChg = 0;
}
@@ -1169,7 +1367,7 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
}
// bPrev is set whether Reformat.Start() was called because of Prev().
-// Else, wo don't know whether we can limit the repaint or not.
+// Else, we don't know whether we can limit the repaint or not.
bool SwTextFrame::FormatLine( SwTextFormatter &rLine, const bool bPrev )
{
OSL_ENSURE( ! IsVertical() || IsSwapped(),
@@ -1177,8 +1375,8 @@ bool SwTextFrame::FormatLine( SwTextFormatter &rLine, const bool bPrev )
SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
const SwLineLayout *pOldCur = rLine.GetCurr();
const TextFrameIndex nOldLen = pOldCur->GetLen();
- const sal_uInt16 nOldAscent = pOldCur->GetAscent();
- const sal_uInt16 nOldHeight = pOldCur->Height();
+ const SwTwips nOldAscent = pOldCur->GetAscent();
+ const SwTwips nOldHeight = pOldCur->Height();
const SwTwips nOldWidth = pOldCur->Width() + pOldCur->GetHangingMargin();
const bool bOldHyph = pOldCur->IsEndHyph();
SwTwips nOldTop = 0;
@@ -1277,7 +1475,8 @@ bool SwTextFrame::FormatLine( SwTextFormatter &rLine, const bool bPrev )
}
// Calculating the good ol' nDelta
- pPara->GetDelta() -= sal_Int32(pNew->GetLen()) - sal_Int32(nOldLen);
+ const sal_Int32 nDiff = sal_Int32(pNew->GetLen()) - sal_Int32(nOldLen);
+ pPara->SetDelta(pPara->GetDelta() - nDiff);
// Stop!
if( rLine.IsStop() )
@@ -1596,9 +1795,27 @@ void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf,
// If we're finished formatting the text and we still
// have other line objects left, these are superfluous
// now because the text has gotten shorter.
+ bool bTruncLines = false;
if( rLine.GetStart() + rLine.GetLength() >= nStrLen &&
rLine.GetCurr()->GetNext() )
{
+ bTruncLines = true;
+ }
+ else if (GetMergedPara() && rLine.GetCurr()->GetNext())
+ {
+ // We can also have superfluous lines with redlining in case the current line is shorter
+ // than the text length, but the total length of lines is still more than expected.
+ // Truncate in this case as well.
+ TextFrameIndex nLen(0);
+ for (const SwLineLayout* pLine = pPara; pLine; pLine = pLine->GetNext())
+ {
+ nLen += pLine->GetLen();
+ }
+ bTruncLines = nLen > nStrLen;
+ }
+
+ if (bTruncLines)
+ {
rLine.TruncLines();
rLine.SetTruncLines( true );
}
@@ -1687,7 +1904,8 @@ void SwTextFrame::FormatOnceMore( SwTextFormatter &rLine, SwTextFormatInfo &rInf
}
}
-void SwTextFrame::Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pPara )
+void SwTextFrame::FormatImpl(vcl::RenderContext* pRenderContext, SwParaPortion *pPara,
+ std::vector<SwAnchoredObject *> & rIntersectingObjs)
{
const bool bIsEmpty = GetText().isEmpty();
@@ -1722,6 +1940,18 @@ void SwTextFrame::Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pP
if( aLine.IsOnceMore() )
FormatOnceMore( aLine, aInf );
+ if (aInf.GetTextFly().IsOn())
+ {
+ SwRect const aRect(aInf.GetTextFly().GetFrameArea());
+ for (SwAnchoredObject *const pObj : aInf.GetTextFly().GetAnchoredObjList())
+ {
+ if (!aInf.GetTextFly().AnchoredObjToRect(pObj, aRect).IsEmpty())
+ {
+ rIntersectingObjs.push_back(pObj);
+ }
+ }
+ }
+
if ( IsVertical() )
SwapWidthAndHeight();
@@ -1757,9 +1987,7 @@ void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttr
if( aRectFnSet.GetWidth(getFramePrintArea()) <= 0 )
{
// If MustFit is set, we shrink to the Upper's bottom edge if needed.
- // Else we just take a standard size of 12 Pt. (240 twip).
SwTextLineAccess aAccess( this );
- tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
if( aAccess.GetPara()->IsPrepMustFit() )
{
@@ -1768,16 +1996,8 @@ void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttr
if( nDiff > 0 )
Shrink( nDiff );
}
- else if( 240 < nFrameHeight )
- {
- Shrink( nFrameHeight - 240 );
- }
- else if( 240 > nFrameHeight )
- {
- Grow( 240 - nFrameHeight );
- }
- nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
const tools::Long nTop = aRectFnSet.GetTopMargin(*this);
if( nTop > nFrameHeight )
@@ -1793,7 +2013,14 @@ void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttr
return;
}
- const TextFrameIndex nStrLen(GetText().getLength());
+ TextFrameIndex nStrLen(GetText().getLength());
+
+ if (HasNonLastSplitFlyDrawObj())
+ {
+ // Non-last part of split fly anchor: consider this empty.
+ nStrLen = TextFrameIndex(0);
+ }
+
if ( nStrLen || !FormatEmpty() )
{
@@ -1906,7 +2133,13 @@ void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttr
}
do
{
- Format_( pRenderContext, aAccess.GetPara() );
+ ::std::vector<SwAnchoredObject *> intersectingObjs;
+ ::std::vector<SwFrame const*> nexts;
+ for (SwFrame const* pNext = GetNext(); pNext; pNext = pNext->GetNext())
+ {
+ nexts.push_back(pNext);
+ }
+ FormatImpl(pRenderContext, aAccess.GetPara(), intersectingObjs);
if( pFootnoteBoss && nFootnoteHeight )
{
const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
@@ -1914,12 +2147,79 @@ void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttr
// If we lost some footnotes, we may have more space
// for our main text, so we have to format again ...
if( nNewHeight < nFootnoteHeight )
+ {
nFootnoteHeight = nNewHeight;
- else
- break;
+ continue;
+ }
}
- else
- break;
+ if (!intersectingObjs.empty())
+ {
+ // assumption is that FormatImpl() only moves frames
+ // in the next-chain to next page
+ SwPageFrame *const pPage(FindPageFrame());
+ SwTextFrame * pLastMovedAnchor(nullptr);
+ auto lastIter(nexts.end());
+ for (SwAnchoredObject *const pObj : intersectingObjs)
+ {
+ SwFrame *const pAnchor(pObj->AnchorFrame());
+ SwPageFrame *const pAnchorPage(pAnchor->FindPageFrame());
+ if (pAnchorPage != pPage)
+ {
+ auto const iter(::std::find(nexts.begin(), nexts.end(), pAnchor));
+ if (iter != nexts.end())
+ {
+ assert(pAnchor->IsTextFrame());
+ // (can't check SwOszControl::IsInProgress()?)
+ // called in loop in FormatAnchorFrameAndItsPrevs()
+ if (static_cast<SwTextFrame const*>(pAnchor)->IsJoinLocked()
+ // called in loop in SwFrame::PrepareMake()
+ || pAnchor->IsDeleteForbidden())
+ {
+ // when called via FormatAnchorFrameAndItsPrevs():
+ // don't do anything, caller will handle it
+ pLastMovedAnchor = nullptr;
+ break;
+ }
+ assert(pPage->GetPhyPageNum() < pAnchorPage->GetPhyPageNum()); // how could it move backward?
+
+ if (!pLastMovedAnchor || iter < lastIter)
+ {
+ pLastMovedAnchor = static_cast<SwTextFrame *>(pAnchor);
+ lastIter = iter;
+ }
+ }
+ }
+ }
+ SwPageFrame const*const pPrevPage(static_cast<SwPageFrame const*>(pPage->GetPrev()));
+ if (pLastMovedAnchor)
+ {
+ for (SwAnchoredObject *const pObj : intersectingObjs)
+ {
+ if (pObj->AnchorFrame() == pLastMovedAnchor)
+ {
+ SwPageFrame *const pAnchorPage(pLastMovedAnchor->FindPageFrame());
+ SAL_INFO("sw.layout", "SwTextFrame::Format: move anchored " << pObj << " from " << pPage->GetPhyPageNum() << " to " << pAnchorPage->GetPhyPageNum());
+ pObj->RegisterAtPage(*pAnchorPage);
+ // tdf#143239 if the position remains valid, it may not be
+ // positioned again so would remain on the wrong page!
+ pObj->InvalidateObjPos();
+ ::Notify_Background(pObj->GetDrawObj(), pPage,
+ pObj->GetObjRect(), PrepareHint::FlyFrameLeave, false);
+ pObj->SetForceNotifyNewBackground(true);
+ }
+ }
+ if (GetFollow() // this frame was split
+ && (!pPrevPage // prev page is still valid
+ || (!pPrevPage->IsInvalid()
+ && (!pPrevPage->GetSortedObjs() || !pPrevPage->IsInvalidFly()))))
+ { // this seems a bit risky...
+ SwLayouter::InsertMovedFwdFrame(GetTextNodeFirst()->GetDoc(),
+ *pLastMovedAnchor, FindPageFrame()->GetPhyPageNum() + 1);
+ }
+ continue; // try again without the fly
+ }
+ }
+ break;
} while ( pFootnoteBoss );
if( bOrphan )
{
@@ -1937,6 +2237,18 @@ void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttr
{
pPre->InvalidatePos();
}
+
+ if (IsEmptyMasterWithSplitFly())
+ {
+ // A fly is anchored to us, reduce size, so we definitely still fit the current
+ // page.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetTop(aPrt, 0);
+ aRectFnSet.SetHeight(aPrt, 0);
+ }
}
}
@@ -2054,7 +2366,7 @@ bool SwTextFrame::FormatQuick( bool bForceQuickFormat )
// Delete reformat
pPara->GetReformat() = SwCharRange();
- pPara->GetDelta() = 0;
+ pPara->SetDelta(0);
return true;
}
diff --git a/sw/source/core/text/frminf.cxx b/sw/source/core/text/frminf.cxx
index 92b287d7bd16..a123691db703 100644
--- a/sw/source/core/text/frminf.cxx
+++ b/sw/source/core/text/frminf.cxx
@@ -19,8 +19,6 @@
#include <sal/config.h>
-#include <o3tl/safeint.hxx>
-
#include <frminf.hxx>
#include "itrtxt.hxx"
@@ -82,7 +80,7 @@ bool SwTextFrameInfo::IsFilled( const sal_uInt8 nPercent ) const
tools::Long nWidth = m_pFrame->getFramePrintArea().Width();
nWidth *= nPercent;
nWidth /= 100;
- return o3tl::make_unsigned(nWidth) <= pLay->Width();
+ return nWidth <= pLay->Width();
}
// Where does the text start (without whitespace)? (document global)
diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx
index 7f35ea7633b6..d6d47f7ae637 100644
--- a/sw/source/core/text/frmpaint.cxx
+++ b/sw/source/core/text/frmpaint.cxx
@@ -68,7 +68,7 @@ class SwExtraPainter
const SwLineNumberInfo &m_rLineInf;
SwTwips m_nX;
SwTwips m_nRedX;
- sal_uLong m_nLineNr;
+ sal_Int32 m_nLineNr;
sal_uInt16 m_nDivider;
bool m_bGoLeft;
bool IsClipChg() const { return m_aClip.IsChg(); }
@@ -82,9 +82,16 @@ public:
sal_Int16 eHor, bool bLnNm );
SwFont* GetFont() const { return m_pFnt.get(); }
void IncLineNr() { ++m_nLineNr; }
- bool HasNumber() const { return !( m_nLineNr % m_rLineInf.GetCountBy() ); }
+ bool HasNumber() const {
+ assert( m_rLineInf.GetCountBy() != 0 );
+ if( m_rLineInf.GetCountBy() == 0 )
+ return false;
+ return !( m_nLineNr % static_cast<sal_Int32>(m_rLineInf.GetCountBy()) );
+ }
bool HasDivider() const {
- if( !m_nDivider ) return false;
+ assert( m_rLineInf.GetDividerCountBy() != 0 );
+ if( !m_nDivider || m_rLineInf.GetDividerCountBy() == 0 )
+ return false;
return !(m_nLineNr % m_rLineInf.GetDividerCountBy());
}
@@ -170,7 +177,7 @@ SwExtraPainter::SwExtraPainter( const SwTextFrame *pFrame, SwViewShell *pVwSh,
if( text::HoriOrientation::INSIDE == eHor || text::HoriOrientation::OUTSIDE == eHor )
{
- if (!oIsRightPage)
+ if (!oIsRightPage.has_value())
oIsRightPage = pFrame->FindPageFrame()->OnRightPage();
if (*oIsRightPage)
eHor = eHor == text::HoriOrientation::INSIDE ? text::HoriOrientation::LEFT : text::HoriOrientation::RIGHT;
@@ -248,7 +255,7 @@ void SwExtraPainter::PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax,
SwRect aRct( Point( aTmpPos.X(),
aTmpPos.Y() - pTmpFnt->GetAscent( m_pSh, *m_pSh->GetOut() )
), aSize );
- if( !m_aRect.IsInside( aRct ) )
+ if( !m_aRect.Contains( aRct ) )
{
if( aRct.Intersection( m_aRect ).IsEmpty() )
bPaint = false;
@@ -280,7 +287,7 @@ void SwExtraPainter::PaintRedline( SwTwips nY, tools::Long nMax )
if( !IsClipChg() )
{
SwRect aRct( aStart, aEnd );
- if( !m_aRect.IsInside( aRct ) )
+ if( !m_aRect.Contains( aRct ) )
{
if( aRct.Intersection( m_aRect ).IsEmpty() )
return;
@@ -398,7 +405,7 @@ void SwTextFrame::PaintExtraData( const SwRect &rRect ) const
bool bNum = bLineNum && ( aExtra.HasNumber() || aExtra.HasDivider() );
if( bRedInMargin || bNum )
{
- sal_uInt16 nTmpHeight, nTmpAscent;
+ SwTwips nTmpHeight, nTmpAscent;
aLine.CalcAscentAndHeight( nTmpAscent, nTmpHeight );
if ( bRedInMargin )
{
@@ -494,6 +501,8 @@ SwRect SwTextFrame::GetPaintSwRect()
bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const
{
+ PaintParagraphStylesHighlighting();
+
SwViewShell *pSh = getRootFrame()->GetCurrShell();
if( pSh && ( pSh->GetViewOptions()->IsParagraph() || bInitFont ) )
{
@@ -532,13 +541,13 @@ bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const
SwRedlineItr aRedln(rTextNode, *pFnt, aAttrHandler, nRedlPos, SwRedlineItr::Mode::Show);
const SwRangeRedline* pRedline = rIDRA.GetRedlineTable()[nRedlPos];
// show redlining only on the inserted/deleted empty paragraph, but not on the next one
- if ( rTextNode.GetIndex() != pRedline->End()->nNode.GetIndex() )
+ if ( rTextNode.GetIndex() != pRedline->End()->GetNodeIndex() )
eRedline = pRedline->GetType();
// except if the next empty paragraph starts a new redline (e.g. deletion after insertion)
else if ( nRedlPos + 1 < rIDRA.GetRedlineTable().size() )
{
const SwRangeRedline* pNextRedline = rIDRA.GetRedlineTable()[nRedlPos + 1];
- if ( rTextNode.GetIndex() == pNextRedline->Start()->nNode.GetIndex() )
+ if ( rTextNode.GetIndex() == pNextRedline->Start()->GetNodeIndex() )
eRedline = pNextRedline->GetType();
}
}
@@ -563,11 +572,13 @@ bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const
pFnt->ChgPhysFnt( pSh, *pSh->GetOut() );
Point aPos = getFrameArea().Pos() + getFramePrintArea().Pos();
- const SvxLRSpaceItem &rSpace =
- GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace();
+ const SvxFirstLineIndentItem& rFirstLine(
+ GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent());
- if ( rSpace.GetTextFirstLineOffset() > 0 )
- aPos.AdjustX(rSpace.GetTextFirstLineOffset() );
+ if (0 < rFirstLine.GetTextFirstLineOffset())
+ {
+ aPos.AdjustX(rFirstLine.GetTextFirstLineOffset());
+ }
std::unique_ptr<SwSaveClip, o3tl::default_delete<SwSaveClip>> xClip;
if( IsUndersized() )
@@ -594,7 +605,8 @@ bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const
}
// Don't show the paragraph mark for collapsed paragraphs, when they are hidden
- if ( EmptyHeight( ) > 1 )
+ // No paragraph marker in the non-last part of a split fly anchor, either.
+ if ( EmptyHeight( ) > 1 && !HasNonLastSplitFlyDrawObj() )
{
SwDrawTextInfo aDrawInf( pSh, *pSh->GetOut(), CH_PAR, 0, 1 );
aDrawInf.SetPos( aPos );
@@ -630,19 +642,13 @@ bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const
return false;
}
-void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect) const
{
ResetRepaint();
// #i16816# tagged pdf support
SwViewShell *pSh = getRootFrame()->GetCurrShell();
- Num_Info aNumInfo( *this );
- SwTaggedPDFHelper aTaggedPDFHelperNumbering( &aNumInfo, nullptr, nullptr, rRenderContext );
-
- Frame_Info aFrameInfo( *this );
- SwTaggedPDFHelper aTaggedPDFHelperParagraph( nullptr, &aFrameInfo, nullptr, rRenderContext );
-
if( IsEmpty() && PaintEmpty( rRect, true ) )
return;
@@ -669,6 +675,31 @@ void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
}
}
+ // tdf140219-2.odt text frame with only fly portions and a follow is not
+ // actually a paragraph - delay creating all structured elements to follow.
+ bool const isPDFTaggingEnabled(!HasFollow() || GetPara()->HasContentPortions());
+ ::std::optional<SwTaggedPDFHelper> oTaggedPDFHelperNumbering;
+ if (isPDFTaggingEnabled)
+ {
+ Num_Info aNumInfo(*this);
+ oTaggedPDFHelperNumbering.emplace(&aNumInfo, nullptr, nullptr, rRenderContext);
+ }
+
+ // Lbl unfortunately must be able to contain multiple numbering portions
+ // that may be on multiple lines of text (but apparently always in the
+ // master frame), so it gets complicated.
+ ::std::optional<SwTaggedPDFHelper> oTaggedLabel;
+ // Paragraph tag - if there is a list label, opening should be delayed.
+ ::std::optional<SwTaggedPDFHelper> oTaggedParagraph;
+
+ if (isPDFTaggingEnabled
+ && (GetTextNodeForParaProps()->IsOutline()
+ || !GetPara()->HasNumberingPortion(SwParaPortion::FootnoteToo)))
+ { // no Lbl needed => open paragraph tag now
+ Frame_Info aFrameInfo(*this, false);
+ oTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, rRenderContext);
+ }
+
// We don't want to be interrupted while painting.
// Do that after thr Format()!
TextFrameLockGuard aLock(const_cast<SwTextFrame*>(this));
@@ -753,7 +784,7 @@ void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
{
do
{
- aLine.DrawTextLine( rRect, aClip, IsUndersized() );
+ aLine.DrawTextLine(rRect, aClip, IsUndersized(), oTaggedLabel, oTaggedParagraph, isPDFTaggingEnabled);
} while( aLine.Next() && aLine.Y() <= nBottom );
}
@@ -766,10 +797,14 @@ void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
rRepaint.Clear();
}
+ PaintParagraphStylesHighlighting();
+
const_cast<SwRect&>(rRect) = aOldRect;
OSL_ENSURE( ! IsSwapped(), "A frame is swapped after Paint" );
+ assert(!oTaggedLabel); // must have been closed if opened
+ assert(!isPDFTaggingEnabled || oTaggedParagraph || rRect.GetIntersection(getFrameArea()) != getFrameArea()); // must have been created during complete paint (PDF export is always complete paint)
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/guess.cxx b/sw/source/core/text/guess.cxx
index 8bf9819c4d66..c3a94187a7ea 100644
--- a/sw/source/core/text/guess.cxx
+++ b/sw/source/core/text/guess.cxx
@@ -28,9 +28,11 @@
#include <com/sun/star/i18n/WordType.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <unotools/charclass.hxx>
+#include <svl/urihelper.hxx>
#include "porfld.hxx"
#include <paratr.hxx>
#include <doc.hxx>
+#include <unotools/linguprops.hxx>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
@@ -42,13 +44,115 @@ namespace{
bool IsBlank(sal_Unicode ch) { return ch == CH_BLANK || ch == CH_FULL_BLANK || ch == CH_NB_SPACE || ch == CH_SIX_PER_EM; }
+// Used when spaces should not be counted in layout
+// Returns adjusted cut position
+TextFrameIndex AdjustCutPos(TextFrameIndex cutPos, TextFrameIndex& rBreakPos,
+ const SwTextFormatInfo& rInf)
+{
+ assert(cutPos >= rInf.GetIdx());
+ TextFrameIndex x = rBreakPos = cutPos;
+
+ // we step back until a non blank character has been found
+ // or there is only one more character left
+ while (x && x > rInf.GetIdx() + TextFrameIndex(1) && IsBlank(rInf.GetChar(--x)))
+ --rBreakPos;
+
+ while (IsBlank(rInf.GetChar(cutPos)))
+ ++cutPos;
+
+ return cutPos;
+}
+
+bool hasBlanksInLine(const SwTextFormatInfo& rInf, TextFrameIndex end)
+{
+ for (auto x = rInf.GetLineStart(); x < end; ++x)
+ if (IsBlank(rInf.GetChar(x)))
+ return true;
+ return false;
+}
+
+// Called for the last text run in a line; if it is block-adjusted, or center / right-adjusted
+// with Word compatibility option set, and it has trailing spaces, then the function sets the
+// values, and returns 'false' value that SwTextGuess::Guess should return, to create a
+// trailing SwHolePortion.
+bool maybeAdjustPositionsForBlockAdjust(TextFrameIndex& rCutPos, TextFrameIndex& rBreakPos,
+ TextFrameIndex& rBreakStart, sal_uInt16& rBreakWidth,
+ sal_uInt16& rExtraBlankWidth, sal_uInt16& rMaxSizeDiff,
+ const SwTextFormatInfo& rInf, const SwScriptInfo& rSI,
+ sal_uInt16 maxComp)
+{
+ const auto& adjObj = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
+ const SvxAdjust& adjust = adjObj.GetAdjust();
+ if (adjust == SvxAdjust::Block)
+ {
+ if (rInf.DontBlockJustify())
+ return true; // See tdf#106234
+ }
+ else
+ {
+ // tdf#104668 space chars at the end should be cut if the compatibility option is enabled
+ if (!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
+ return true;
+ // for LTR mode only
+ if (rInf.GetTextFrame()->IsRightToLeft())
+ return true;
+ }
+ if (auto ch = rInf.GetChar(rCutPos); !ch) // end of paragraph - last line
+ {
+ if (adjust == SvxAdjust::Block)
+ {
+ // Check adjustment for last line
+ switch (adjObj.GetLastBlock())
+ {
+ default:
+ return true;
+ case SvxAdjust::Center: // tdf#104668
+ if (!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
+ return true;
+ break;
+ case SvxAdjust::Block:
+ break; // OK - last line uses block-adjustment
+ }
+ }
+ }
+ else if (ch != CH_BREAK && !IsBlank(ch))
+ return true;
+
+ // tdf#57187: block-adjusted line shorter than full width, terminated by manual
+ // line break, must not use trailing spaces for adjustment
+ TextFrameIndex breakPos;
+ TextFrameIndex newCutPos = AdjustCutPos(rCutPos, breakPos, rInf);
+
+ if (auto ch = rInf.GetChar(newCutPos); ch && ch != CH_BREAK)
+ return true; // next is neither line break nor paragraph end
+ if (breakPos == newCutPos)
+ return true; // no trailing whitespace
+ if (adjust == SvxAdjust::Block && adjObj.GetOneWord() != SvxAdjust::Block
+ && !hasBlanksInLine(rInf, breakPos))
+ return true; // line can't block-adjust
+
+ // Some trailing spaces actually found, and in case of block adjustment, the text portion
+ // itself has spaces to be able to block-adjust, or single word is allowed to adjust
+ rBreakStart = rCutPos = newCutPos;
+ rBreakPos = breakPos;
+
+ rInf.GetTextSize(&rSI, rInf.GetIdx(), breakPos - rInf.GetIdx(), maxComp, rBreakWidth,
+ rMaxSizeDiff, rInf.GetCachedVclData().get());
+ rInf.GetTextSize(&rSI, breakPos, rBreakStart - breakPos, maxComp, rExtraBlankWidth,
+ rMaxSizeDiff, rInf.GetCachedVclData().get());
+
+ return false; // require SwHolePortion creation
+}
+
}
// provides information for line break calculation
// returns true if no line break has to be performed
// otherwise possible break or hyphenation position is determined
bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
- const sal_uInt16 nPorHeight )
+ const sal_uInt16 nPorHeight, sal_Int32 nSpacesInLine )
{
m_nCutPos = rInf.GetIdx();
@@ -78,33 +182,12 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
const SvxAdjust& rAdjust = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust().GetAdjust();
- // tdf#104668 space chars at the end should be cut if the compatibility option is enabled
- // for LTR mode only
- if ( !rInf.GetTextFrame()->IsRightToLeft() )
+ // allow up to 20% shrinking of the spaces
+ if ( nSpacesInLine )
{
- if (rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
- DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
- {
- if ( rAdjust == SvxAdjust::Right || rAdjust == SvxAdjust::Center )
- {
- TextFrameIndex nSpaceCnt(0);
- for (sal_Int32 i = rInf.GetText().getLength() - 1;
- sal_Int32(rInf.GetIdx()) <= i; --i)
- {
- sal_Unicode cChar = rInf.GetText()[i];
- if ( cChar != CH_BLANK && cChar != CH_FULL_BLANK && cChar != CH_SIX_PER_EM )
- break;
- ++nSpaceCnt;
- }
- TextFrameIndex nCharsCnt = nMaxLen - nSpaceCnt;
- if ( nSpaceCnt && nCharsCnt < rPor.GetLen() )
- {
- nMaxLen = nCharsCnt;
- if ( !nMaxLen )
- return true;
- }
- }
- }
+ static constexpr OUStringLiteral STR_BLANK = u" ";
+ sal_Int16 nSpaceWidth = rInf.GetTextSize(STR_BLANK).Width();
+ nLineWidth += nSpacesInLine * (nSpaceWidth/0.8 - nSpaceWidth);
}
if ( rInf.GetLen() < nMaxLen )
@@ -170,12 +253,16 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
{
// portion fits to line
m_nCutPos = rInf.GetIdx() + nMaxLen;
+ bool bRet = rPor.InFieldGrp()
+ || maybeAdjustPositionsForBlockAdjust(m_nCutPos, m_nBreakPos, m_nBreakStart,
+ m_nBreakWidth, m_nExtraBlankWidth,
+ nMaxSizeDiff, rInf, rSI, nMaxComp);
if( nItalic &&
(m_nCutPos >= TextFrameIndex(rInf.GetText().getLength()) ||
// #i48035# Needed for CalcFitToContent
// if first line ends with a manual line break
rInf.GetText()[sal_Int32(m_nCutPos)] == CH_BREAK))
- m_nBreakWidth = m_nBreakWidth + nItalic;
+ m_nBreakWidth += nItalic;
// save maximum width for later use
if ( nMaxSizeDiff )
@@ -183,7 +270,7 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
m_nBreakWidth += nLeftRightBorderSpace;
- return true;
+ return bRet;
}
}
@@ -195,8 +282,104 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
// considering an additional "-" for hyphenation
if( bHyph )
{
+ // nHyphZone is the first character not fitting in the hyphenation zone,
+ // or 0, if the whole line in the hyphenation zone,
+ // or -1, if no hyphenation zone defined (i.e. it is 0)
+ sal_Int32 nHyphZone = -1;
+ const css::beans::PropertyValues & rHyphValues = rInf.GetHyphValues();
+ assert( rHyphValues.getLength() > 5 && rHyphValues[5].Name == UPN_HYPH_ZONE );
+ // hyphenation zone (distance from the line end in twips)
+ sal_uInt16 nTextHyphenZone;
+ if ( rHyphValues[5].Value >>= nTextHyphenZone )
+ nHyphZone = nTextHyphenZone >= nLineWidth
+ ? 0
+ : sal_Int32(rInf.GetTextBreak( nLineWidth - nTextHyphenZone,
+ nMaxLen, nMaxComp, rInf.GetCachedVclData().get() ));
+
m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, nHyphPos, rInf.GetCachedVclData().get() );
+ // don't try to hyphenate in the hyphenation zone
+ if ( nHyphZone != -1 && TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
+ {
+ sal_Int32 nZonePos = sal_Int32(m_nCutPos);
+ // disable hyphenation, if there is a space within the hyphenation zone
+ // Note: for better interoperability, not fitting space character at
+ // rInf.GetIdx()[nHyphZone] always disables the hyphenation, don't need to calculate
+ // with its fitting part. Moreover, do not check double or more spaces there, they
+ // are accepted outside of the hyphenation zone, too.
+ for (; sal_Int32(rInf.GetIdx()) <= nZonePos && nHyphZone <= nZonePos; --nZonePos )
+ {
+ sal_Unicode cChar = rInf.GetText()[nZonePos];
+ if ( cChar == CH_BLANK || cChar == CH_FULL_BLANK || cChar == CH_SIX_PER_EM )
+ {
+ bHyph = false;
+ }
+ }
+ }
+
+ if (!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::HYPHENATE_URLS))
+ {
+ // check URL from preceding space - similar to what AutoFormat does
+ const CharClass& rCC = GetAppCharClass();
+ sal_Int32 begin(m_nCutPos == TextFrameIndex(COMPLETE_STRING) ? rInf.GetText().getLength() : sal_Int32(m_nCutPos));
+ sal_Int32 end(begin);
+ for (; 0 < begin; --begin)
+ {
+ sal_Unicode cChar = rInf.GetText()[begin - 1];
+ if (cChar == CH_BLANK || cChar == CH_FULL_BLANK || cChar == CH_SIX_PER_EM)
+ {
+ break;
+ }
+ }
+ for (; end < rInf.GetText().getLength(); ++end)
+ {
+ sal_Unicode cChar = rInf.GetText()[end];
+ if (cChar == CH_BLANK || cChar == CH_FULL_BLANK || cChar == CH_SIX_PER_EM)
+ {
+ break;
+ }
+ }
+ if (!URIHelper::FindFirstURLInText(rInf.GetText(), begin, end, rCC).isEmpty())
+ {
+ bHyph = false;
+ }
+ }
+
+ // search start of the last word, if needed
+ if ( bHyph )
+ {
+ // nLastWord is the space character before the last word
+ sal_Int32 nLastWord = rInf.GetText().getLength() - 1;
+ bool bHyphenationNoLastWord = false;
+ assert( rHyphValues.getLength() > 3 && rHyphValues[3].Name == UPN_HYPH_NO_LAST_WORD );
+ if ( rHyphValues[3].Value >>= bHyphenationNoLastWord )
+ {
+ // skip spaces after the last word
+ bool bCutBlank = false;
+ for (; sal_Int32(rInf.GetIdx()) <= nLastWord; --nLastWord )
+ {
+ sal_Unicode cChar = rInf.GetText()[nLastWord];
+ if ( cChar != CH_BLANK && cChar != CH_FULL_BLANK && cChar != CH_SIX_PER_EM )
+ bCutBlank = true;
+ else if ( bCutBlank )
+ break;
+ }
+ }
+
+ // don't hyphenate the last word of the paragraph
+ if ( bHyphenationNoLastWord && sal_Int32(m_nCutPos) > nLastWord &&
+ TextFrameIndex(COMPLETE_STRING) != m_nCutPos &&
+ // if the last word is multiple line long, e.g. an URL,
+ // apply this only if the space before the word is there
+ // in the actual line, i.e. start the long word in a new
+ // line, but still allows to break its last parts
+ sal_Int32(rInf.GetIdx()) < nLastWord )
+ {
+ m_nCutPos = TextFrameIndex(nLastWord);
+ }
+ }
+
if ( !nHyphPos && rInf.GetIdx() )
nHyphPos = rInf.GetIdx() - TextFrameIndex(1);
}
@@ -226,8 +409,13 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
// there likely has been a pixel rounding error in GetTextBreak
if ( m_nBreakWidth <= nLineWidth )
{
+ bool bRet = rPor.InFieldGrp()
+ || maybeAdjustPositionsForBlockAdjust(m_nCutPos, m_nBreakPos, m_nBreakStart,
+ m_nBreakWidth, m_nExtraBlankWidth,
+ nMaxSizeDiff, rInf, rSI, nMaxComp);
+
if (nItalic && (m_nBreakPos + TextFrameIndex(1)) >= TextFrameIndex(rInf.GetText().getLength()))
- m_nBreakWidth = m_nBreakWidth + nItalic;
+ m_nBreakWidth += nItalic;
// save maximum width for later use
if ( nMaxSizeDiff )
@@ -235,7 +423,7 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
m_nBreakWidth += nLeftRightBorderSpace;
- return true;
+ return bRet;
}
}
@@ -250,36 +438,11 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
TextFrameIndex nPorLen(0);
// do not call the break iterator nCutPos is a blank
- sal_Unicode cCutChar = m_nCutPos < TextFrameIndex(rInf.GetText().getLength())
- ? rInf.GetText()[sal_Int32(m_nCutPos)]
- : 0;
+ sal_Unicode cCutChar = rInf.GetChar(m_nCutPos);
if (IsBlank(cCutChar))
{
- m_nBreakPos = m_nCutPos;
- TextFrameIndex nX = m_nBreakPos;
-
- if ( rAdjust == SvxAdjust::Left )
- {
- // we step back until a non blank character has been found
- // or there is only one more character left
- while (nX && TextFrameIndex(rInf.GetText().getLength()) < m_nBreakPos &&
- IsBlank(rInf.GetChar(--nX)))
- --m_nBreakPos;
- }
- else // #i20878#
- {
- while (nX && m_nBreakPos > rInf.GetLineStart() + TextFrameIndex(1) &&
- IsBlank(rInf.GetChar(--nX)))
- --m_nBreakPos;
- }
-
- if( m_nBreakPos > rInf.GetIdx() )
- nPorLen = m_nBreakPos - rInf.GetIdx();
- while (++m_nCutPos < TextFrameIndex(rInf.GetText().getLength()) &&
- IsBlank(rInf.GetChar(m_nCutPos)))
- ; // nothing
-
- m_nBreakStart = m_nCutPos;
+ m_nCutPos = m_nBreakStart = AdjustCutPos(m_nCutPos, m_nBreakPos, rInf);
+ nPorLen = m_nBreakPos - rInf.GetIdx();
}
else
{
@@ -422,7 +585,12 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
m_nBreakStart = m_nBreakPos;
- bHyph = BreakType::HYPHENATION == aResult.breakType;
+ bHyph = BreakType::HYPHENATION == aResult.breakType &&
+ // allow hyphenation of the word only if it's not disabled by character formatting
+ LANGUAGE_NONE != rInf.GetTextFrame()->GetLangOfChar(
+ TextFrameIndex( sal_Int32(m_nBreakPos) +
+ aResult.rHyphenatedWord->getHyphenationPos() ),
+ 1, true, /*bNoneIfNoHyphenation=*/true );
if (bHyph && m_nBreakPos != TextFrameIndex(COMPLETE_STRING))
{
@@ -497,7 +665,7 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
SwPosSize aTmpSize = rInf.GetTextSize( &rSI, m_nCutPos, nHangingLen );
aTmpSize.Width(aTmpSize.Width() + nLeftRightBorderSpace);
OSL_ENSURE( !m_pHanging, "A hanging portion is hanging around" );
- m_pHanging.reset( new SwHangingPortion( aTmpSize ) );
+ m_pHanging.reset( new SwHangingPortion( std::move(aTmpSize) ) );
m_pHanging->SetLen( nHangingLen );
nPorLen = m_nCutPos - rInf.GetIdx();
}
@@ -548,6 +716,13 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
else
m_nBreakWidth = 0;
+ if (m_nBreakStart > rInf.GetIdx() + nPorLen + m_nFieldDiff)
+ {
+ rInf.GetTextSize(&rSI, rInf.GetIdx() + nPorLen,
+ m_nBreakStart - rInf.GetIdx() - nPorLen - m_nFieldDiff, nMaxComp,
+ m_nExtraBlankWidth, nMaxSizeDiff, rInf.GetCachedVclData().get());
+ }
+
if( m_pHanging )
{
m_nBreakPos = m_nCutPos;
diff --git a/sw/source/core/text/guess.hxx b/sw/source/core/text/guess.hxx
index 696a09fc8589..5a7a9ac1cfa2 100644
--- a/sw/source/core/text/guess.hxx
+++ b/sw/source/core/text/guess.hxx
@@ -38,19 +38,21 @@ class SwTextGuess
TextFrameIndex m_nFieldDiff; // absolute positions can be wrong if we
// a field in the text has been expanded
sal_uInt16 m_nBreakWidth; // width of the broken portion
+ sal_uInt16 m_nExtraBlankWidth; // width of spaces after the break
public:
SwTextGuess(): m_nCutPos(0), m_nBreakStart(0),
- m_nBreakPos(0), m_nFieldDiff(0), m_nBreakWidth(0)
+ m_nBreakPos(0), m_nFieldDiff(0), m_nBreakWidth(0), m_nExtraBlankWidth(0)
{ }
// true, if current portion still fits to current line
bool Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
- const sal_uInt16 nHeight );
+ const sal_uInt16 nHeight, sal_Int32 nSpacesInLine = 0 );
bool AlternativeSpelling( const SwTextFormatInfo &rInf, const TextFrameIndex nPos );
SwHangingPortion* GetHangingPortion() const { return m_pHanging.get(); }
SwHangingPortion* ReleaseHangingPortion() { return m_pHanging.release(); }
sal_uInt16 BreakWidth() const { return m_nBreakWidth; }
+ sal_uInt16 ExtraBlankWidth() const { return m_nExtraBlankWidth; }
TextFrameIndex CutPos() const { return m_nCutPos; }
TextFrameIndex BreakStart() const { return m_nBreakStart; }
TextFrameIndex BreakPos() const {return m_nBreakPos; }
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index bb63a36ae157..ddcca31668db 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -21,7 +21,9 @@
#include <unotools/linguprops.hxx>
#include <unotools/lingucfg.hxx>
+#include <fmtinfmt.hxx>
#include <hintids.hxx>
+#include <txatbase.hxx>
#include <svl/ctloptions.hxx>
#include <sfx2/infobar.hxx>
#include <sfx2/printer.hxx>
@@ -29,7 +31,6 @@
#include <editeng/hyphenzoneitem.hxx>
#include <editeng/hngpnctitem.hxx>
#include <editeng/scriptspaceitem.hxx>
-#include <editeng/brushitem.hxx>
#include <editeng/splwrap.hxx>
#include <editeng/pgrditem.hxx>
#include <editeng/tstpitem.hxx>
@@ -38,12 +39,12 @@
#include <SwSmartTagMgr.hxx>
#include <breakit.hxx>
#include <editeng/forbiddenruleitem.hxx>
-#include <paintfrm.hxx>
#include <swmodule.hxx>
#include <vcl/svapp.hxx>
#include <viewsh.hxx>
#include <viewopt.hxx>
#include <frmtool.hxx>
+#include <fmturl.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentDeviceAccess.hxx>
#include <IDocumentMarkAccess.hxx>
@@ -51,6 +52,7 @@
#include <rootfrm.hxx>
#include "inftxt.hxx"
#include <noteurl.hxx>
+#include "porfly.hxx"
#include "porftn.hxx"
#include "porrst.hxx"
#include "itratr.hxx"
@@ -66,6 +68,18 @@
#include <vcl/gdimtf.hxx>
#include <vcl/virdev.hxx>
#include <vcl/gradient.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <formatlinebreak.hxx>
+
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <unotextrange.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <unoprnms.hxx>
+#include <editeng/unoprnms.hxx>
+#include <unomap.hxx>
+#include <com/sun/star/awt/FontSlant.hpp>
using namespace ::com::sun::star;
using namespace ::com::sun::star::linguistic2;
@@ -99,7 +113,7 @@ SwLineInfo::~SwLineInfo()
void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet,
const SwTextNode& rTextNode )
{
- m_pRuler.reset( new SvxTabStopItem( rAttrSet.GetTabStops() ) );
+ m_oRuler.emplace( rAttrSet.GetTabStops() );
if ( rTextNode.GetListTabStopPosition( m_nListTabStopPosition ) )
{
m_bListTabStopIncluded = true;
@@ -107,15 +121,15 @@ void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet,
// insert the list tab stop into SvxTabItem instance <pRuler>
const SvxTabStop aListTabStop( m_nListTabStopPosition,
SvxTabAdjust::Left );
- m_pRuler->Insert( aListTabStop );
+ m_oRuler->Insert( aListTabStop );
// remove default tab stops, which are before the inserted list tab stop
- for ( sal_uInt16 i = 0; i < m_pRuler->Count(); i++ )
+ for ( sal_uInt16 i = 0; i < m_oRuler->Count(); i++ )
{
- if ( (*m_pRuler)[i].GetTabPos() < m_nListTabStopPosition &&
- (*m_pRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
+ if ( (*m_oRuler)[i].GetTabPos() < m_nListTabStopPosition &&
+ (*m_oRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
{
- m_pRuler->Remove(i);
+ m_oRuler->Remove(i);
continue;
}
}
@@ -124,12 +138,12 @@ void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet,
if ( !rTextNode.getIDocumentSettingAccess()->get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) )
{
// remove default tab stop at position 0
- for ( sal_uInt16 i = 0; i < m_pRuler->Count(); i++ )
+ for ( sal_uInt16 i = 0; i < m_oRuler->Count(); i++ )
{
- if ( (*m_pRuler)[i].GetTabPos() == 0 &&
- (*m_pRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
+ if ( (*m_oRuler)[i].GetTabPos() == 0 &&
+ (*m_oRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
{
- m_pRuler->Remove(i);
+ m_oRuler->Remove(i);
break;
}
}
@@ -191,6 +205,7 @@ SwTextSizeInfo::SwTextSizeInfo()
, m_pText(nullptr)
, m_nIdx(0)
, m_nLen(0)
+, m_nMeasureLen(COMPLETE_STRING)
, m_nKanaIdx(0)
, m_bOnWin (false)
, m_bNotEOL (false)
@@ -221,6 +236,7 @@ SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew )
m_pText(&rNew.GetText()),
m_nIdx(rNew.GetIdx()),
m_nLen(rNew.GetLen()),
+ m_nMeasureLen(rNew.GetMeasureLen()),
m_nKanaIdx( rNew.GetKanaIdx() ),
m_bOnWin( rNew.OnWin() ),
m_bNotEOL( rNew.NotEOL() ),
@@ -280,14 +296,14 @@ void SwTextSizeInfo::CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextF
// Set default layout mode ( LTR or RTL ).
if ( m_pFrame->IsRightToLeft() )
{
- m_pOut->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl );
- m_pRef->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl );
+ m_pOut->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl );
+ m_pRef->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl );
m_nDirection = DIR_RIGHT2LEFT;
}
else
{
- m_pOut->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong );
- m_pRef->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong );
+ m_pOut->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong );
+ m_pRef->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong );
m_nDirection = DIR_LEFT2RIGHT;
}
@@ -309,7 +325,7 @@ void SwTextSizeInfo::CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextF
m_pText = &m_pFrame->GetText();
m_nIdx = nNewIdx;
- m_nLen = TextFrameIndex(COMPLETE_STRING);
+ m_nLen = m_nMeasureLen = TextFrameIndex(COMPLETE_STRING);
m_bNotEOL = false;
m_bStopUnderflow = m_bFootnoteInside = m_bOtherThanFootnoteInside = false;
m_bMulti = m_bFirstMulti = m_bRuby = m_bHanging = m_bScriptSpace =
@@ -332,6 +348,7 @@ SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew, const OUString* pTex
m_pText(pText),
m_nIdx(nIndex),
m_nLen(COMPLETE_STRING),
+ m_nMeasureLen(COMPLETE_STRING),
m_nKanaIdx( rNew.GetKanaIdx() ),
m_bOnWin( rNew.OnWin() ),
m_bNotEOL( rNew.NotEOL() ),
@@ -407,6 +424,7 @@ SwPosSize SwTextSizeInfo::GetTextSize() const
0 ;
SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rSI, *m_pText, m_nIdx, m_nLen );
+ aDrawInf.SetMeasureLen( m_nMeasureLen );
aDrawInf.SetFrame( m_pFrame );
aDrawInf.SetFont( m_pFnt );
aDrawInf.SetSnapToGrid( SnapToGrid() );
@@ -417,7 +435,7 @@ SwPosSize SwTextSizeInfo::GetTextSize() const
void SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI, const TextFrameIndex nIndex,
const TextFrameIndex nLength, const sal_uInt16 nComp,
sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
- vcl::TextLayoutCache const*const pCache) const
+ vcl::text::TextLayoutCache const*const pCache) const
{
SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, pSI, *m_pText, nIndex, nLength,
0, false, pCache);
@@ -426,14 +444,14 @@ void SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI, const TextFrameIndex
aDrawInf.SetSnapToGrid( SnapToGrid() );
aDrawInf.SetKanaComp( nComp );
SwPosSize aSize( m_pFnt->GetTextSize_( aDrawInf ) );
- nMaxSizeDiff = static_cast<sal_uInt16>(aDrawInf.GetKanaDiff());
+ nMaxSizeDiff = o3tl::narrowing<sal_uInt16>(aDrawInf.GetKanaDiff());
nMinSize = aSize.Width();
}
TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
const TextFrameIndex nMaxLen,
const sal_uInt16 nComp,
- vcl::TextLayoutCache const*const pCache) const
+ vcl::text::TextLayoutCache const*const pCache) const
{
const SwScriptInfo& rScriptInfo =
const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
@@ -454,7 +472,7 @@ TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
const TextFrameIndex nMaxLen,
const sal_uInt16 nComp,
TextFrameIndex& rExtraCharPos,
- vcl::TextLayoutCache const*const pCache) const
+ vcl::text::TextLayoutCache const*const pCache) const
{
const SwScriptInfo& rScriptInfo =
const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
@@ -521,39 +539,6 @@ SwTextPaintInfo::SwTextPaintInfo( SwTextFrame *pFrame, const SwRect &rPaint )
CtorInitTextPaintInfo( pFrame->getRootFrame()->GetCurrShell()->GetOut(), pFrame, rPaint );
}
-/// Returns if the current background color is dark.
-static bool lcl_IsDarkBackground( const SwTextPaintInfo& rInf )
-{
- std::optional<Color> pCol = rInf.GetFont()->GetBackColor();
- if( ! pCol || COL_TRANSPARENT == *pCol )
- {
- const SvxBrushItem* pItem;
- SwRect aOrigBackRect;
- drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
-
- // Consider, that [GetBackgroundBrush(...)] can set <pCol>
- // See implementation in /core/layout/paintfrm.cxx
- // There is a background color, if there is a background brush and
- // its color is *not* "no fill"/"auto fill".
- if( rInf.GetTextFrame()->GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/false ) )
- {
- if ( !pCol )
- pCol = pItem->GetColor();
-
- // Determined color <pCol> can be <COL_TRANSPARENT>. Thus, check it.
- if ( *pCol == COL_TRANSPARENT)
- pCol.reset();
- }
- else
- pCol.reset();
- }
-
- if( !pCol )
- pCol = aGlobalRetoucheColor;
-
- return pCol->IsDark();
-}
-
namespace
{
/**
@@ -604,7 +589,7 @@ SwTransparentTextGuard::~SwTransparentTextGuard()
Gradient aVCLGradient;
sal_uInt8 nTransPercentVcl = 255 - m_rPaintInf.GetFont()->GetColor().GetAlpha();
const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
- aVCLGradient.SetStyle(GradientStyle::Linear);
+ aVCLGradient.SetStyle(css::awt::GradientStyle_LINEAR);
aVCLGradient.SetStartColor(aTransColor);
aVCLGradient.SetEndColor(aTransColor);
aVCLGradient.SetAngle(0_deg10);
@@ -653,7 +638,7 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPo
aDrawInf.SetUnderFnt( m_pUnderFnt );
const tools::Long nSpaceAdd = ( rPor.IsBlankPortion() || rPor.IsDropPortion() ||
- rPor.InNumberGrp() ) ? 0 : GetSpaceAdd();
+ rPor.InNumberGrp() ) ? 0 : GetSpaceAdd(/*bShrink=*/true);
if ( nSpaceAdd )
{
TextFrameIndex nCharCnt(0);
@@ -681,7 +666,7 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPo
// Draw text next to the left border
Point aFontPos(m_aPos);
- if( m_pFnt->GetLeftBorder() && !static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev() )
+ if( m_pFnt->GetLeftBorder() && rPor.InTextGrp() && !static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev() )
{
const sal_uInt16 nLeftBorderSpace = m_pFnt->GetLeftBorderSpace();
if ( GetTextFrame()->IsRightToLeft() )
@@ -716,7 +701,9 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPo
std::unique_ptr<SwTransparentTextGuard, o3tl::default_delete<SwTransparentTextGuard>> pTransparentText;
if (m_pFnt->GetColor() != COL_AUTO && m_pFnt->GetColor().IsTransparent())
{
- pTransparentText.reset(new SwTransparentTextGuard(rPor, *this, aDrawInf));
+ // if drawing to a backend that supports transparency for text color, then we don't need to use this
+ if (!m_bOnWin || !m_pOut->SupportsOperation(OutDevSupportType::TransparentText) || m_pOut->GetConnectMetaFile())
+ pTransparentText.reset(new SwTransparentTextGuard(rPor, *this, aDrawInf));
}
if( GetTextFly().IsOn() )
@@ -955,7 +942,7 @@ static void lcl_DrawSpecial( const SwTextPaintInfo& rTextPaintInfo, const SwLine
Point aTmpPos( nX, nY );
rNonConstTextPaintInfo.SetPos( aTmpPos );
sal_uInt16 nOldWidth = rPor.Width();
- const_cast<SwLinePortion&>(rPor).Width( static_cast<sal_uInt16>(aFontSize.Width()) );
+ const_cast<SwLinePortion&>(rPor).Width( o3tl::narrowing<sal_uInt16>(aFontSize.Width()) );
rTextPaintInfo.DrawText( aTmp, rPor );
const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
rNonConstTextPaintInfo.SetFont( const_cast<SwFont*>(pOldFnt) );
@@ -996,6 +983,13 @@ void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const
if( !OnWin() )
return;
+ SwLineBreakClear eClear = SwLineBreakClear::NONE;
+ if (rPor.IsBreakPortion())
+ {
+ const auto& rBreakPortion = static_cast<const SwBreakPortion&>(rPor);
+ eClear = rBreakPortion.GetClear();
+ }
+
sal_uInt16 nOldWidth = rPor.Width();
const_cast<SwLinePortion&>(rPor).Width( LINE_BREAK_WIDTH );
@@ -1008,7 +1002,24 @@ void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const
CHAR_LINEBREAK_RTL : CHAR_LINEBREAK;
const sal_uInt8 nOptions = 0;
- lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
+ SwRect aTextRect(aRect);
+ if (eClear == SwLineBreakClear::LEFT || eClear == SwLineBreakClear::ALL)
+ aTextRect.AddLeft(30);
+ if (eClear == SwLineBreakClear::RIGHT || eClear == SwLineBreakClear::ALL)
+ aTextRect.AddRight(-30);
+ lcl_DrawSpecial( *this, rPor, aTextRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
+
+ if (eClear != SwLineBreakClear::NONE)
+ {
+ // Paint indicator if this clear is left/right/all.
+ m_pOut->Push(vcl::PushFlags::LINECOLOR);
+ m_pOut->SetLineColor(NON_PRINTING_CHARACTER_COLOR);
+ if (eClear != SwLineBreakClear::RIGHT)
+ m_pOut->DrawLine(aRect.BottomLeft(), aRect.TopLeft());
+ if (eClear != SwLineBreakClear::LEFT)
+ m_pOut->DrawLine(aRect.BottomRight(), aRect.TopRight());
+ m_pOut->Pop();
+ }
}
const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
@@ -1089,8 +1100,7 @@ void SwTextPaintInfo::DrawPostIts( bool bScript ) const
if ( GetTextFrame()->IsVertical() )
GetTextFrame()->SwitchHorizontalToVertical( aTmpRect );
- const tools::Rectangle aRect( aTmpRect.SVRect() );
- SwViewOption::PaintPostIts( const_cast<OutputDevice*>(GetOut()), aRect, bScript );
+ GetOpt().PaintPostIts( const_cast<OutputDevice*>(GetOut()), aTmpRect, bScript );
}
@@ -1101,19 +1111,22 @@ void SwTextPaintInfo::DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool
if ( !aIntersect.HasArea() )
return;
- if (OnWin() && SwViewOption::IsFieldShadings() &&
+ if (OnWin() && GetOpt().IsFieldShadings() &&
!GetOpt().IsPagePreview())
{
OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
- pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
- pOut->SetFillColor( SwViewOption::GetFieldShadingsColor() );
+ pOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+ if( m_pFnt->GetHighlightColor() != COL_TRANSPARENT )
+ pOut->SetFillColor(m_pFnt->GetHighlightColor());
+ else
+ pOut->SetFillColor(GetOpt().GetFieldShadingsColor());
pOut->SetLineColor();
pOut->DrawRect( aIntersect.SVRect() );
pOut->Pop();
}
- const int delta=10;
+ const int delta = 25;
tools::Rectangle r(aIntersect.Left()+delta, aIntersect.Top()+delta, aIntersect.Right()-delta, aIntersect.Bottom()-delta);
- m_pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
+ m_pOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
m_pOut->SetLineColor( Color(0, 0, 0));
m_pOut->SetFillColor();
m_pOut->DrawRect( r );
@@ -1125,7 +1138,7 @@ void SwTextPaintInfo::DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool
m_pOut->Pop();
}
-void SwTextPaintInfo::DrawBackground( const SwLinePortion &rPor ) const
+void SwTextPaintInfo::DrawBackground( const SwLinePortion &rPor, const Color *pColor ) const
{
OSL_ENSURE( OnWin(), "SwTextPaintInfo::DrawBackground: printer pollution ?" );
@@ -1136,18 +1149,14 @@ void SwTextPaintInfo::DrawBackground( const SwLinePortion &rPor ) const
return;
OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
- pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
+ pOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
- // For dark background we do not want to have a filled rectangle
- if ( GetVsh() && GetVsh()->GetWin() && lcl_IsDarkBackground( *this ) )
- {
- pOut->SetLineColor( SwViewOption::GetFontColor() );
- }
+ if ( pColor )
+ pOut->SetFillColor( *pColor );
else
- {
- pOut->SetFillColor( SwViewOption::GetFieldShadingsColor() );
- pOut->SetLineColor();
- }
+ pOut->SetFillColor( GetOpt().GetFieldShadingsColor() );
+
+ pOut->SetLineColor();
DrawRect( aIntersect, true );
pOut->Pop();
@@ -1162,7 +1171,7 @@ void SwTextPaintInfo::DrawBackBrush( const SwLinePortion &rPor ) const
{
SwPosition const aPosition(m_pFrame->MapViewToModelPos(GetIdx()));
const ::sw::mark::IMark* pFieldmark =
- m_pFrame->GetDoc().getIDocumentMarkAccess()->getFieldmarkFor(aPosition);
+ m_pFrame->GetDoc().getIDocumentMarkAccess()->getInnerFieldmarkFor(aPosition);
bool bIsStartMark = (TextFrameIndex(1) == GetLen()
&& CH_TXT_ATR_FIELDSTART == GetText()[sal_Int32(GetIdx())]);
if(pFieldmark) {
@@ -1171,12 +1180,12 @@ void SwTextPaintInfo::DrawBackBrush( const SwLinePortion &rPor ) const
if(bIsStartMark)
SAL_INFO("sw.core", "Found StartMark");
if (OnWin() && (pFieldmark!=nullptr || bIsStartMark) &&
- SwViewOption::IsFieldShadings() &&
+ GetOpt().IsFieldShadings() &&
!GetOpt().IsPagePreview())
{
OutputDevice* pOutDev = const_cast<OutputDevice*>(GetOut());
- pOutDev->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
- pOutDev->SetFillColor( SwViewOption::GetFieldShadingsColor() );
+ pOutDev->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+ pOutDev->SetFillColor( GetOpt().GetFieldShadingsColor() );
pOutDev->SetLineColor( );
pOutDev->DrawRect( aIntersect.SVRect() );
pOutDev->Pop();
@@ -1208,89 +1217,7 @@ void SwTextPaintInfo::DrawBackBrush( const SwLinePortion &rPor ) const
aFillColor = *m_pFnt->GetBackColor();
}
- // tdf#104349 do not highlight portions of space chars before end of line if the compatibility option is enabled
- // for LTR mode only
- if ( !GetTextFrame()->IsRightToLeft() )
- {
- if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
- {
- bool draw = false;
- bool full = false;
- SwLinePortion *pPos = const_cast<SwLinePortion *>(&rPor);
- TextFrameIndex nIdx = GetIdx();
- TextFrameIndex nLen;
-
- do
- {
- nLen = pPos->GetLen();
- for (TextFrameIndex i = nIdx; i < (nIdx + nLen); ++i)
- {
- if (i < TextFrameIndex(GetText().getLength())
- && GetText()[sal_Int32(i)] == CH_TXTATR_NEWLINE)
- {
- if ( i >= (GetIdx() + rPor.GetLen()) )
- {
- goto drawcontinue;
- }
- }
- if (i >= TextFrameIndex(GetText().getLength())
- || GetText()[sal_Int32(i)] != CH_BLANK)
- {
- draw = true;
- if ( i >= (GetIdx() + rPor.GetLen()) )
- {
- full = true;
- goto drawcontinue;
- }
- }
- }
- nIdx += nLen;
- pPos = pPos->GetNextPortion();
- } while ( pPos );
-
- drawcontinue:
-
- if ( !draw )
- return;
-
- if ( !full )
- {
- pPos = const_cast<SwLinePortion *>(&rPor);
- nIdx = GetIdx();
-
- nLen = pPos->GetLen();
- for (TextFrameIndex i = nIdx + nLen - TextFrameIndex(1);
- i >= nIdx; --i)
- {
- if (i < TextFrameIndex(GetText().getLength())
- && GetText()[sal_Int32(i)] == CH_TXTATR_NEWLINE)
- {
- continue;
- }
- if (i >= TextFrameIndex(GetText().getLength())
- || GetText()[sal_Int32(i)] != CH_BLANK)
- {
- sal_uInt16 nOldWidth = rPor.Width();
- sal_uInt16 nNewWidth = GetTextSize(m_pOut, nullptr,
- GetText(), nIdx, (i + TextFrameIndex(1) - nIdx)).Width();
-
- const_cast<SwLinePortion&>(rPor).Width( nNewWidth );
- CalcRect( rPor, nullptr, &aIntersect, true );
- const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
-
- if ( !aIntersect.HasArea() )
- {
- return;
- }
-
- break;
- }
- }
- }
- }
- }
-
- pTmpOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
+ pTmpOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
pTmpOut->SetFillColor(aFillColor);
pTmpOut->SetLineColor();
@@ -1312,71 +1239,276 @@ void SwTextPaintInfo::DrawBorder( const SwLinePortion &rPor ) const
}
}
+namespace {
+
+bool HasValidPropertyValue(const uno::Any& rAny)
+{
+ if (bool bValue; rAny >>= bValue)
+ {
+ return true;
+ }
+ else if (OUString aValue; (rAny >>= aValue) && !(aValue.isEmpty()))
+ {
+ return true;
+ }
+ else if (awt::FontSlant eValue; rAny >>= eValue)
+ {
+ return true;
+ }
+ else if (tools::Long nValueLong; rAny >>= nValueLong)
+ {
+ return true;
+ }
+ else if (double fValue; rAny >>= fValue)
+ {
+ return true;
+ }
+ else if (short nValueShort; rAny >>= nValueShort)
+ {
+ return true;
+ }
+ else
+ return false;
+}
+}
+
+void SwTextPaintInfo::DrawCSDFHighlighting(const SwLinePortion &rPor) const
+{
+ // Don't use GetActiveView() as it does not work as expected when there are multiple open
+ // documents.
+ SwView* pView = SwTextFrame::GetView();
+ if (!pView)
+ return;
+
+ StylesHighlighterColorMap& rCharStylesColorMap = pView->GetStylesHighlighterCharColorMap();
+
+ if (rCharStylesColorMap.empty() && !pView->IsHighlightCharDF())
+ return;
+
+ SwRect aRect;
+ CalcRect(rPor, &aRect, nullptr, true);
+ if(!aRect.HasArea())
+ return;
+
+ SwTextFrame* pFrame = const_cast<SwTextFrame*>(GetTextFrame());
+ if (!pFrame)
+ return;
+
+ SwPosition aPosition(pFrame->MapViewToModelPos(GetIdx()));
+ SwPosition aMarkPosition(pFrame->MapViewToModelPos(GetIdx() + GetLen()));
+
+ rtl::Reference<SwXTextRange> xRange(
+ SwXTextRange::CreateXTextRange(pFrame->GetDoc(), aPosition, &aMarkPosition));
+
+ OUString sCurrentCharStyle;
+ xRange->getPropertyValue("CharStyleName") >>= sCurrentCharStyle;
+
+ std::optional<OUString> sCSNumberOrDF; // CS number or "df" or not used
+ std::optional<Color> aFillColor;
+
+ // check for CS formatting, if not CS formatted check for direct character formatting
+ if (!sCurrentCharStyle.isEmpty())
+ {
+ if (!rCharStylesColorMap.empty())
+ {
+ OUString sCharStyleDisplayName;
+ sCharStyleDisplayName = SwStyleNameMapper::GetUIName(sCurrentCharStyle,
+ SwGetPoolIdFromName::ChrFmt);
+ if (!sCharStyleDisplayName.isEmpty()
+ && rCharStylesColorMap.find(sCharStyleDisplayName)
+ != rCharStylesColorMap.end())
+ {
+ aFillColor = rCharStylesColorMap[sCharStyleDisplayName].first;
+ sCSNumberOrDF = OUString::number(rCharStylesColorMap[sCharStyleDisplayName].second);
+ }
+ }
+ }
+ // not character style formatted
+ else if (pView->IsHighlightCharDF())
+ {
+ const std::vector<OUString> aHiddenProperties{ UNO_NAME_RSID,
+ UNO_NAME_PARA_IS_NUMBERING_RESTART,
+ UNO_NAME_PARA_STYLE_NAME,
+ UNO_NAME_PARA_CONDITIONAL_STYLE_NAME,
+ UNO_NAME_PAGE_STYLE_NAME,
+ UNO_NAME_NUMBERING_START_VALUE,
+ UNO_NAME_NUMBERING_IS_NUMBER,
+ UNO_NAME_PARA_CONTINUEING_PREVIOUS_SUB_TREE,
+ UNO_NAME_CHAR_STYLE_NAME,
+ UNO_NAME_NUMBERING_LEVEL,
+ UNO_NAME_SORTED_TEXT_ID,
+ UNO_NAME_PARRSID,
+ UNO_NAME_CHAR_COLOR_THEME,
+ UNO_NAME_CHAR_COLOR_TINT_OR_SHADE };
+
+ SfxItemPropertySet const& rPropSet(
+ *aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE));
+ SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
+
+
+ const uno::Sequence<beans::Property> aProperties
+ = xRange->getPropertySetInfo()->getProperties();
+
+ for (const beans::Property& rProperty : aProperties)
+ {
+ const OUString& rPropName = rProperty.Name;
+
+ if (!rMap.hasPropertyByName(rPropName))
+ continue;
+
+ if (std::find(aHiddenProperties.begin(), aHiddenProperties.end(), rPropName)
+ != aHiddenProperties.end())
+ continue;
+
+ if (xRange->getPropertyState(rPropName) == beans::PropertyState_DIRECT_VALUE)
+ {
+ const uno::Any aAny = xRange->getPropertyValue(rPropName);
+ if (HasValidPropertyValue(aAny))
+ {
+ sCSNumberOrDF = SwResId(STR_CHARACTER_DIRECT_FORMATTING_TAG);
+ aFillColor = COL_LIGHTGRAY;
+ break;
+ }
+ }
+ }
+ }
+ if (sCSNumberOrDF)
+ {
+ OutputDevice* pTmpOut = const_cast<OutputDevice*>(GetOut());
+ pTmpOut->Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR
+ | vcl::PushFlags::TEXTLAYOUTMODE | vcl::PushFlags::FONT);
+
+ // draw a filled rectangle at the formatted CS or DF text
+ pTmpOut->SetFillColor(aFillColor.value());
+ pTmpOut->SetLineColor(aFillColor.value());
+ tools::Rectangle aSVRect(aRect.SVRect());
+ pTmpOut->DrawRect(aSVRect);
+
+ // calculate size and position for the CS number or "df" text and rectangle
+ tools::Long nWidth = pTmpOut->GetTextWidth(sCSNumberOrDF.value());
+ tools::Long nHeight = pTmpOut->GetTextHeight();
+ aSVRect.SetSize(Size(nWidth, nHeight));
+ aSVRect.Move(-(nWidth / 1.5), -(nHeight / 1.5));
+
+ vcl::Font aFont(pTmpOut->GetFont());
+ aFont.SetOrientation(Degree10(0));
+ pTmpOut->SetFont(aFont);
+
+ pTmpOut->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::TextOriginLeft);
+ //pTmpOut->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::BiDiStrong);
+
+ pTmpOut->SetTextFillColor(aFillColor.value());
+ pTmpOut->DrawText(aSVRect, sCSNumberOrDF.value(), DrawTextFlags::NONE);
+
+ pTmpOut->Pop();
+ }
+}
+
void SwTextPaintInfo::DrawViewOpt( const SwLinePortion &rPor,
- PortionType nWhich ) const
+ PortionType nWhich, const Color *pColor ) const
{
if( !OnWin() || IsMulti() )
return;
bool bDraw = false;
- switch( nWhich )
- {
- case PortionType::Footnote:
- case PortionType::QuoVadis:
- case PortionType::Number:
- case PortionType::Field:
- case PortionType::Hidden:
- case PortionType::Tox:
- case PortionType::Ref:
- case PortionType::Meta:
- case PortionType::ControlChar:
- if ( !GetOpt().IsPagePreview()
- && !GetOpt().IsReadonly()
- && SwViewOption::IsFieldShadings()
- && ( PortionType::Number != nWhich
- || m_pFrame->GetTextNodeForParaProps()->HasMarkedLabel())) // #i27615#
+ if ( !GetOpt().IsPagePreview()
+ && !GetOpt().IsReadonly() )
+ {
+ switch( nWhich )
{
- bDraw = PortionType::Footnote != nWhich || m_pFrame->IsFootnoteAllowed();
+ case PortionType::Tab:
+ if ( GetOpt().IsViewMetaChars() )
+ bDraw = GetOpt().IsTab();
+ break;
+ case PortionType::SoftHyphen:
+ if ( GetOpt().IsViewMetaChars() )
+ bDraw = GetOpt().IsSoftHyph();
+ break;
+ case PortionType::Blank:
+ if ( GetOpt().IsViewMetaChars() )
+ bDraw = GetOpt().IsHardBlank();
+ break;
+ case PortionType::ControlChar:
+ if ( GetOpt().IsViewMetaChars() )
+ bDraw = true;
+ break;
+ case PortionType::Bookmark:
+ // no shading
+ break;
+ case PortionType::Footnote:
+ case PortionType::QuoVadis:
+ case PortionType::Number:
+ case PortionType::Hidden:
+ case PortionType::Tox:
+ case PortionType::Ref:
+ case PortionType::Meta:
+ case PortionType::ContentControl:
+ case PortionType::Field:
+ case PortionType::InputField:
+ // input field shading also in read-only mode
+ if (GetOpt().IsFieldShadings()
+ && ( PortionType::Number != nWhich
+ || m_pFrame->GetTextNodeForParaProps()->HasMarkedLabel())) // #i27615#
+ {
+ bDraw = PortionType::Footnote != nWhich || m_pFrame->IsFootnoteAllowed();
+ }
+ break;
+ default:
+ {
+ OSL_ENSURE( false, "SwTextPaintInfo::DrawViewOpt: don't know how to draw this" );
+ break;
+ }
}
- break;
- case PortionType::Bookmark:
- // no shading
- break;
- case PortionType::InputField:
- // input field shading also in read-only mode
- if ( !GetOpt().IsPagePreview()
- && SwViewOption::IsFieldShadings() )
+ }
+
+ if ( bDraw )
+ DrawBackground( rPor, pColor );
+}
+
+void SwTextPaintInfo::NotifyURL_(const SwLinePortion& rPor) const
+{
+ assert(pNoteURL);
+
+ SwRect aIntersect;
+ CalcRect(rPor, nullptr, &aIntersect);
+
+ if (aIntersect.HasArea())
+ {
+ SwTextNode* pNd = const_cast<SwTextNode*>(GetTextFrame()->GetTextNodeFirst());
+ SwTextAttr* const pAttr = pNd->GetTextAttrAt(sal_Int32(GetIdx()), RES_TXTATR_INETFMT);
+ if (pAttr)
{
- bDraw = true;
+ const SwFormatINetFormat& rFormat = pAttr->GetINetFormat();
+ pNoteURL->InsertURLNote(rFormat.GetValue(), rFormat.GetTargetFrame(), aIntersect);
}
- break;
- case PortionType::Table:
- if ( GetOpt().IsTab() ) bDraw = true;
- break;
- case PortionType::SoftHyphen:
- if ( GetOpt().IsSoftHyph() )bDraw = true;
- break;
- case PortionType::Blank:
- if ( GetOpt().IsHardBlank())bDraw = true;
- break;
- default:
+ else if (rPor.IsFlyCntPortion())
{
- OSL_ENSURE( false, "SwTextPaintInfo::DrawViewOpt: don't know how to draw this" );
- break;
+ if (auto* pFlyContentPortion = dynamic_cast<const sw::FlyContentPortion*>(&rPor))
+ {
+ if (auto* pFlyFtame = pFlyContentPortion->GetFlyFrame())
+ {
+ if (auto* pFormat = pFlyFtame->GetFormat())
+ {
+ auto& url = pFormat->GetURL(); // TODO: url.GetMap() ?
+ pNoteURL->InsertURLNote(url.GetURL(), url.GetTargetFrameName(), aIntersect);
+ }
+ }
+ }
}
}
- if ( bDraw )
- DrawBackground( rPor );
}
static void lcl_InitHyphValues( PropertyValues &rVals,
- sal_Int16 nMinLeading, sal_Int16 nMinTrailing, bool bNoCapsHyphenation )
+ sal_Int16 nMinLeading, sal_Int16 nMinTrailing,
+ bool bNoCapsHyphenation, bool bNoLastWordHyphenation,
+ sal_Int16 nMinWordLength, sal_Int16 nTextHyphZone, bool bKeep, sal_Int16 nKeepType,
+ sal_Int16 nCompoundMinLeading )
{
sal_Int32 nLen = rVals.getLength();
if (0 == nLen) // yet to be initialized?
{
- rVals.realloc( 3 );
+ rVals.realloc( 9 );
PropertyValue *pVal = rVals.getArray();
pVal[0].Name = UPN_HYPH_MIN_LEADING;
@@ -1390,13 +1522,43 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
pVal[2].Name = UPN_HYPH_NO_CAPS;
pVal[2].Handle = UPH_HYPH_NO_CAPS;
pVal[2].Value <<= bNoCapsHyphenation;
+
+ pVal[3].Name = UPN_HYPH_NO_LAST_WORD;
+ pVal[3].Handle = UPH_HYPH_NO_LAST_WORD;
+ pVal[3].Value <<= bNoLastWordHyphenation;
+
+ pVal[4].Name = UPN_HYPH_MIN_WORD_LENGTH;
+ pVal[4].Handle = UPH_HYPH_MIN_WORD_LENGTH;
+ pVal[4].Value <<= nMinWordLength;
+
+ pVal[5].Name = UPN_HYPH_ZONE;
+ pVal[5].Handle = UPH_HYPH_ZONE;
+ pVal[5].Value <<= nTextHyphZone;
+
+ pVal[6].Name = UPN_HYPH_KEEP_TYPE;
+ pVal[6].Handle = UPH_HYPH_KEEP_TYPE;
+ pVal[6].Value <<= nKeepType;
+
+ pVal[7].Name = UPN_HYPH_COMPOUND_MIN_LEADING;
+ pVal[7].Handle = UPH_HYPH_COMPOUND_MIN_LEADING;
+ pVal[7].Value <<= nCompoundMinLeading;
+
+ pVal[8].Name = UPN_HYPH_KEEP;
+ pVal[8].Handle = UPH_HYPH_KEEP;
+ pVal[8].Value <<= bKeep;
}
- else if (3 == nLen) // already initialized once?
+ else if (9 == nLen) // already initialized once?
{
PropertyValue *pVal = rVals.getArray();
pVal[0].Value <<= nMinLeading;
pVal[1].Value <<= nMinTrailing;
pVal[2].Value <<= bNoCapsHyphenation;
+ pVal[3].Value <<= bNoLastWordHyphenation;
+ pVal[4].Value <<= nMinWordLength;
+ pVal[5].Value <<= nTextHyphZone;
+ pVal[6].Value <<= nKeepType;
+ pVal[7].Value <<= nCompoundMinLeading;
+ pVal[8].Value <<= bKeep;
}
else {
OSL_FAIL( "unexpected size of sequence" );
@@ -1405,7 +1567,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
const PropertyValues & SwTextFormatInfo::GetHyphValues() const
{
- OSL_ENSURE( 3 == m_aHyphVals.getLength(),
+ OSL_ENSURE( 9 == m_aHyphVals.getLength(),
"hyphenation values not yet initialized" );
return m_aHyphVals;
}
@@ -1423,8 +1585,16 @@ bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen )
{
const sal_Int16 nMinimalLeading = std::max(rAttr.GetMinLead(), sal_uInt8(2));
const sal_Int16 nMinimalTrailing = rAttr.GetMinTrail();
+ const sal_Int16 nMinimalWordLength = rAttr.GetMinWordLength();
const bool bNoCapsHyphenation = rAttr.IsNoCapsHyphenation();
- lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing, bNoCapsHyphenation);
+ const bool bNoLastWordHyphenation = rAttr.IsNoLastWordHyphenation();
+ const sal_Int16 nTextHyphZone = rAttr.GetTextHyphenZone();
+ const bool bKeep = rAttr.IsKeep();
+ const sal_Int16 nKeepType = rAttr.GetKeepType();
+ const sal_Int16 nCompoundMinimalLeading = std::max(rAttr.GetCompoundMinLead(), sal_uInt8(2));
+ lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing,
+ bNoCapsHyphenation, bNoLastWordHyphenation,
+ nMinimalWordLength, nTextHyphZone, bKeep, nKeepType, nCompoundMinimalLeading );
}
return bAuto;
}
@@ -1456,7 +1626,7 @@ void SwTextFormatInfo::CtorInitTextFormatInfo( OutputDevice* pRenderContext, SwT
SetLineStart(TextFrameIndex(0));
SvtCTLOptions::TextNumerals const nTextNumerals(
- SW_MOD()->GetCTLOptions().GetCTLTextNumerals());
+ SvtCTLOptions::GetCTLTextNumerals());
// cannot cache for NUMERALS_CONTEXT because we need to know the string
// for the whole paragraph now
if (nTextNumerals != SvtCTLOptions::NUMERALS_CONTEXT)
@@ -1483,7 +1653,9 @@ bool SwTextFormatInfo::IsHyphenate() const
return false;
LanguageType eTmp = GetFont()->GetLanguage();
- if( LANGUAGE_DONTKNOW == eTmp || LANGUAGE_NONE == eTmp )
+ // TODO: check for more ideographic langs w/o hyphenation as a concept
+ if ( LANGUAGE_DONTKNOW == eTmp || LANGUAGE_NONE == eTmp
+ || !MsLangId::usesHyphenation(eTmp) )
return false;
uno::Reference< XHyphenator > xHyph = ::GetHyphenator();
@@ -1501,7 +1673,7 @@ bool SwTextFormatInfo::IsHyphenate() const
pShell->AppendInfoBarWhenReady(
"hyphenationmissing", SwResId(STR_HYPH_MISSING),
SwResId(STR_HYPH_MISSING_DETAIL)
- .replaceFirst("%1", g_pBreakIt->GetLocale(eTmp).Language),
+ .replaceFirst("%1", LanguageTag::convertToBcp47( g_pBreakIt->GetLocale(eTmp))),
InfobarType::WARNING);
}
}
@@ -1616,6 +1788,37 @@ SwTextFormatInfo::SwTextFormatInfo( const SwTextFormatInfo& rInf,
SetFirstMulti( rInf.IsFirstMulti() );
}
+void SwTextFormatInfo::UpdateTabSeen(PortionType type)
+{
+ switch (type)
+ {
+ case PortionType::TabLeft:
+ m_eLastTabsSeen = TabSeen::Left;
+ break;
+ case PortionType::TabRight:
+ m_eLastTabsSeen = TabSeen::Right;
+ break;
+ case PortionType::TabCenter:
+ m_eLastTabsSeen = TabSeen::Center;
+ break;
+ case PortionType::TabDecimal:
+ m_eLastTabsSeen = TabSeen::Decimal;
+ break;
+ case PortionType::Break:
+ m_eLastTabsSeen = TabSeen::None;
+ break;
+ default:
+ break;
+ }
+}
+
+void SwTextFormatInfo::SetLast(SwLinePortion* pNewLast)
+{
+ m_pLast = pNewLast;
+ assert(pNewLast); // We never pass nullptr here. If we start, then a check is needed below.
+ UpdateTabSeen(pNewLast->GetWhichPor());
+}
+
bool SwTextFormatInfo::CheckFootnotePortion_( SwLineLayout const * pCurr )
{
const sal_uInt16 nHeight = pCurr->GetRealHeight();
@@ -1758,7 +1961,9 @@ SwTwips SwTextFormatInfo::GetLineWidth()
const bool bTabOverMargin = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
DocumentSettingId::TAB_OVER_MARGIN);
- if (!bTabOverMargin)
+ const bool bTabOverSpacing = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::TAB_OVER_SPACING);
+ if (!bTabOverMargin && !bTabOverSpacing)
return nLineWidth;
SwTabPortion* pLastTab = GetLastTab();
@@ -1789,6 +1994,12 @@ SwTwips SwTextFormatInfo::GetLineWidth()
// text frame area to the right (RR above, but not LL).
nLineWidth = nTextFrameWidth - X();
+ if (!bTabOverMargin) // thus bTabOverSpacing only
+ {
+ // right, center, decimal can back-fill all the available space - same as TabOverMargin
+ if (pLastTab->GetWhichPor() == PortionType::TabLeft)
+ nLineWidth = nTextFrameWidth - pLastTab->GetTabPos();
+ }
return nLineWidth;
}
@@ -1803,6 +2014,7 @@ SwTextSlot::SwTextSlot(
, m_pOldGrammarCheckList(nullptr)
, nIdx(0)
, nLen(0)
+ , nMeasureLen(0)
, pInf(nullptr)
{
if( rCh.isEmpty() )
@@ -1822,11 +2034,15 @@ SwTextSlot::SwTextSlot(
pInf = const_cast<SwTextSizeInfo*>(pNew);
nIdx = pInf->GetIdx();
nLen = pInf->GetLen();
+ nMeasureLen = pInf->GetMeasureLen();
pOldText = &(pInf->GetText());
m_pOldCachedVclData = pInf->GetCachedVclData();
pInf->SetText( aText );
pInf->SetIdx(TextFrameIndex(0));
pInf->SetLen(bTextLen ? TextFrameIndex(pInf->GetText().getLength()) : pPor->GetLen());
+ if (nMeasureLen != TextFrameIndex(COMPLETE_STRING))
+ pInf->SetMeasureLen(TextFrameIndex(COMPLETE_STRING));
+
pInf->SetCachedVclData(nullptr);
// ST2
@@ -1900,6 +2116,7 @@ SwTextSlot::~SwTextSlot()
pInf->SetText( *pOldText );
pInf->SetIdx( nIdx );
pInf->SetLen( nLen );
+ pInf->SetMeasureLen( nMeasureLen );
// ST2
// Restore old smart tag list
diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx
index bb245d5b031f..9c4126a7a6e9 100644
--- a/sw/source/core/text/inftxt.hxx
+++ b/sw/source/core/text/inftxt.hxx
@@ -19,6 +19,7 @@
#pragma once
#include <memory>
+#include <optional>
#include <com/sun/star/beans/PropertyValues.hpp>
#include <map>
@@ -60,7 +61,7 @@ class SwLineInfo
{
friend class SwTextIter;
- std::unique_ptr<SvxTabStopItem> m_pRuler;
+ std::optional<SvxTabStopItem> m_oRuler;
const SvxLineSpacingItem *m_pSpace;
SvxParaVertAlignItem::Align m_nVertAlign;
sal_uInt16 m_nDefTabStop;
@@ -70,12 +71,11 @@ class SwLineInfo
void CtorInitLineInfo( const SwAttrSet& rAttrSet,
const SwTextNode& rTextNode );
- SwLineInfo();
- ~SwLineInfo();
+ SW_DLLPUBLIC SwLineInfo();
+ SW_DLLPUBLIC ~SwLineInfo();
public:
// #i24363# tab stops relative to indent - returns the tab stop following nSearchPos or NULL
- const SvxTabStop *GetTabStop( const SwTwips nSearchPos,
- const SwTwips nRight ) const;
+ const SvxTabStop* GetTabStop(const SwTwips nSearchPos, SwTwips& nRight) const;
const SvxLineSpacingItem *GetLineSpacing() const { return m_pSpace; }
sal_uInt16 GetDefTabStop() const { return m_nDefTabStop; }
void SetDefTabStop( sal_uInt16 nNew ) const
@@ -145,7 +145,7 @@ protected:
// performance hack - this is only used by SwTextFormatInfo but
// because it's not even possible to dynamic_cast these things
// currently it has to be stored here
- std::shared_ptr<vcl::TextLayoutCache> m_pCachedVclData;
+ std::shared_ptr<const vcl::text::TextLayoutCache> m_pCachedVclData;
SwFont *m_pFnt;
SwUnderlineFont *m_pUnderFnt; // Font for underlining
@@ -154,6 +154,7 @@ protected:
const OUString *m_pText;
TextFrameIndex m_nIdx;
TextFrameIndex m_nLen;
+ TextFrameIndex m_nMeasureLen;
sal_uInt16 m_nKanaIdx;
bool m_bOnWin : 1;
bool m_bNotEOL : 1;
@@ -182,12 +183,12 @@ public:
SwTextSizeInfo( const SwTextSizeInfo &rInf );
SwTextSizeInfo( const SwTextSizeInfo &rInf, const OUString* pText,
TextFrameIndex nIdx = TextFrameIndex(0) );
- SwTextSizeInfo(SwTextFrame *pTextFrame, TextFrameIndex nIndex = TextFrameIndex(0));
+ SW_DLLPUBLIC SwTextSizeInfo(SwTextFrame *pTextFrame, TextFrameIndex nIndex = TextFrameIndex(0));
// GetMultiAttr returns the text attribute of the multiportion,
// if rPos is inside any multi-line part.
// rPos will set to the end of the multi-line part.
- std::unique_ptr<SwMultiCreator> GetMultiCreator(TextFrameIndex &rPos, SwMultiPortion const* pM) const;
+ std::optional<SwMultiCreator> GetMultiCreator(TextFrameIndex &rPos, SwMultiPortion const* pM) const;
bool OnWin() const { return m_bOnWin; }
void SetOnWin( const bool bNew ) { m_bOnWin = bNew; }
@@ -251,7 +252,7 @@ public:
void GetTextSize( const SwScriptInfo* pSI, TextFrameIndex nIdx,
TextFrameIndex nLen, const sal_uInt16 nComp,
sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
- vcl::TextLayoutCache const* = nullptr) const;
+ vcl::text::TextLayoutCache const* = nullptr) const;
inline SwPosSize GetTextSize(const SwScriptInfo* pSI, TextFrameIndex nIdx,
TextFrameIndex nLen) const;
inline SwPosSize GetTextSize( const OUString &rText ) const;
@@ -259,19 +260,22 @@ public:
TextFrameIndex GetTextBreak( const tools::Long nLineWidth,
const TextFrameIndex nMaxLen,
const sal_uInt16 nComp,
- vcl::TextLayoutCache const*) const;
+ vcl::text::TextLayoutCache const*) const;
TextFrameIndex GetTextBreak( const tools::Long nLineWidth,
const TextFrameIndex nMaxLen,
const sal_uInt16 nComp,
TextFrameIndex& rExtraCharPos,
- vcl::TextLayoutCache const*) const;
+ vcl::text::TextLayoutCache const*) const;
sal_uInt16 GetAscent() const;
+ sal_uInt16 GetHangingBaseline() const;
TextFrameIndex GetIdx() const { return m_nIdx; }
void SetIdx(const TextFrameIndex nNew) { m_nIdx = nNew; }
TextFrameIndex GetLen() const { return m_nLen; }
void SetLen(const TextFrameIndex nNew) { m_nLen = nNew; }
+ TextFrameIndex GetMeasureLen() const { return m_nMeasureLen; }
+ void SetMeasureLen(const TextFrameIndex nNew) { m_nMeasureLen = nNew; }
void SetText( const OUString &rNew ){ m_pText = &rNew; }
// No Bullets for the symbol font!
@@ -325,11 +329,11 @@ public:
{ return ( m_pKanaComp && m_nKanaIdx < m_pKanaComp->size() )
? (*m_pKanaComp)[m_nKanaIdx] : 0; }
- const std::shared_ptr<vcl::TextLayoutCache>& GetCachedVclData() const
+ const std::shared_ptr<const vcl::text::TextLayoutCache>& GetCachedVclData() const
{
return m_pCachedVclData;
}
- void SetCachedVclData(std::shared_ptr<vcl::TextLayoutCache> const& pCachedVclData)
+ void SetCachedVclData(std::shared_ptr<const vcl::text::TextLayoutCache> const& pCachedVclData)
{
m_pCachedVclData = pCachedVclData;
}
@@ -354,6 +358,7 @@ class SwTextPaintInfo : public SwTextSizeInfo
const bool bGrammarCheck = false );
SwTextPaintInfo &operator=(const SwTextPaintInfo&) = delete;
+ void NotifyURL_(const SwLinePortion& rPor) const;
protected:
SwTextPaintInfo()
@@ -399,8 +404,8 @@ public:
void DrawLineBreak( const SwLinePortion &rPor ) const;
void DrawRedArrow( const SwLinePortion &rPor ) const;
void DrawPostIts( bool bScript ) const;
- void DrawBackground( const SwLinePortion &rPor ) const;
- void DrawViewOpt( const SwLinePortion &rPor, PortionType nWhich ) const;
+ void DrawBackground( const SwLinePortion &rPor, const Color *pColor=nullptr ) const;
+ void DrawViewOpt( const SwLinePortion &rPor, PortionType nWhich, const Color *pColor=nullptr ) const;
void DrawBackBrush( const SwLinePortion &rPor ) const;
/**
@@ -412,6 +417,14 @@ public:
void DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool bChecked) const;
+ void DrawCSDFHighlighting(const SwLinePortion &rPor) const;
+
+ void NotifyURL(const SwLinePortion& rPor) const
+ {
+ if (URLNotify())
+ NotifyURL_(rPor);
+ }
+
/**
* Calculate the rectangular area where the portion takes place.
* @param[in] rPor portion for which the method specify the painting area
@@ -437,8 +450,10 @@ public:
void SetSpaceIdx( sal_uInt16 nNew ) { m_nSpaceIdx = nNew; }
void IncSpaceIdx() { ++m_nSpaceIdx; }
void RemoveFirstSpaceAdd() { m_pSpaceAdd->erase( m_pSpaceAdd->begin() ); }
- tools::Long GetSpaceAdd() const
- { return ( m_pSpaceAdd && m_nSpaceIdx < m_pSpaceAdd->size() )
+ tools::Long GetSpaceAdd( bool bShrink = false ) const
+ { return ( m_pSpaceAdd && m_nSpaceIdx < m_pSpaceAdd->size() &&
+ // get shrink data only if asked explicitly, otherwise zero it
+ ( bShrink || (*m_pSpaceAdd)[m_nSpaceIdx] < LONG_MAX/2 ) )
? (*m_pSpaceAdd)[m_nSpaceIdx] : 0; }
void SetpSpaceAdd( std::vector<tools::Long>* pNew ){ m_pSpaceAdd = pNew; }
@@ -509,6 +524,16 @@ class SwTextFormatInfo : public SwTextPaintInfo
sal_Unicode m_cHookChar; // For tabs in fields etc.
sal_uInt8 m_nMaxHyph; // Max. line count of followup hyphenations
+ // Used to stop justification after center/right/decimal tab stops - see tdf#tdf#106234
+ enum class TabSeen
+ {
+ None,
+ Left,
+ Center,
+ Right,
+ Decimal,
+ } m_eLastTabsSeen = TabSeen::None;
+
// Hyphenating ...
bool InitHyph( const bool bAuto = false );
bool CheckFootnotePortion_( SwLineLayout const * pCurr );
@@ -558,7 +583,7 @@ public:
void SetRoot( SwLineLayout *pNew ) { m_pRoot = pNew; }
SwLinePortion *GetLast() { return m_pLast; }
- void SetLast( SwLinePortion *pNewLast ) { m_pLast = pNewLast; }
+ void SetLast(SwLinePortion* pNewLast);
bool IsFull() const { return m_bFull; }
void SetFull( const bool bNew ) { m_bFull = bNew; }
bool IsHyphForbud() const
@@ -585,6 +610,13 @@ public:
void SetDropInit( const bool bNew ) { m_bDropInit = bNew; }
bool IsQuick() const { return m_bQuick; }
bool IsTest() const { return m_bTestFormat; }
+ // see tdf#106234
+ void UpdateTabSeen(PortionType);
+ bool DontBlockJustify() const
+ {
+ return m_eLastTabsSeen == TabSeen::Center || m_eLastTabsSeen == TabSeen::Right
+ || m_eLastTabsSeen == TabSeen::Decimal;
+ }
TextFrameIndex GetLineStart() const { return m_nLineStart; }
void SetLineStart(TextFrameIndex const nNew) { m_nLineStart = nNew; }
@@ -673,7 +705,7 @@ public:
class SwTextSlot final
{
OUString aText;
- std::shared_ptr<vcl::TextLayoutCache> m_pOldCachedVclData;
+ std::shared_ptr<const vcl::text::TextLayoutCache> m_pOldCachedVclData;
const OUString *pOldText;
sw::WrongListIterator * m_pOldSmartTagList;
sw::WrongListIterator * m_pOldGrammarCheckList;
@@ -681,6 +713,7 @@ class SwTextSlot final
std::unique_ptr<sw::WrongListIterator> m_pTempIter;
TextFrameIndex nIdx;
TextFrameIndex nLen;
+ TextFrameIndex nMeasureLen;
bool bOn;
SwTextSizeInfo *pInf;
@@ -715,6 +748,12 @@ inline sal_uInt16 SwTextSizeInfo::GetTextHeight() const
return const_cast<SwFont*>(GetFont())->GetHeight( m_pVsh, *GetOut() );
}
+inline sal_uInt16 SwTextSizeInfo::GetHangingBaseline() const
+{
+ assert(GetOut());
+ return const_cast<SwFont*>(GetFont())->GetHangingBaseline( m_pVsh, *GetOut() );
+}
+
inline SwPosSize SwTextSizeInfo::GetTextSize( const OUString &rText ) const
{
return GetTextSize(m_pOut, nullptr, rText, TextFrameIndex(0), TextFrameIndex(rText.getLength()));
diff --git a/sw/source/core/text/itradj.cxx b/sw/source/core/text/itradj.cxx
index a5944e49e357..4dcaf03df1f8 100644
--- a/sw/source/core/text/itradj.cxx
+++ b/sw/source/core/text/itradj.cxx
@@ -122,7 +122,7 @@ static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf,
// total number of kashida positions, or the number of kashida positions after some positions
// have been dropped.
// Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before.
- rKashidas = rSI.KashidaJustify ( nullptr, nullptr, rItr.GetStart(), rItr.GetLength() );
+ rKashidas = rSI.KashidaJustify(nullptr, nullptr, rItr.GetStart(), rItr.GetLength());
if (rKashidas <= 0) // nothing to do
return true;
@@ -147,7 +147,7 @@ static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf,
if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
nNext = nEnd;
- sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( nullptr, nullptr, nIdx, nNext - nIdx );
+ sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nullptr, nIdx, nNext - nIdx);
if (nKashidasInAttr > 0)
{
// Kashida glyph looks suspicious, skip Kashida justification
@@ -164,8 +164,8 @@ static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf,
}
else
{
- ComplexTextLayoutFlags nOldLayout = rInf.GetOut()->GetLayoutMode();
- rInf.GetOut()->SetLayoutMode ( nOldLayout | ComplexTextLayoutFlags::BiDiRtl );
+ vcl::text::ComplexTextLayoutFlags nOldLayout = rInf.GetOut()->GetLayoutMode();
+ rInf.GetOut()->SetLayoutMode ( nOldLayout | vcl::text::ComplexTextLayoutFlags::BiDiRtl );
nKashidasDropped = rInf.GetOut()->ValidateKashidas(
rInf.GetText(), sal_Int32(nIdx), sal_Int32(nNext - nIdx),
nKashidasInAttr,
@@ -212,7 +212,7 @@ static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwT
if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
nNext = nEnd;
- sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( nullptr, nullptr, nIdx, nNext - nIdx );
+ sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nullptr, nIdx, nNext - nIdx);
tools::Long nFontMinKashida = rInf.GetOut()->GetMinKashida();
if ( nFontMinKashida && nKashidasInAttr > 0 && SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
@@ -290,6 +290,8 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
bool bDoNotJustifyTab = false;
SwLinePortion *pPos = pCurrent->GetNextPortion();
+ // calculate real text width for shrinking
+ tools::Long nBreakWidth = 0;
while( pPos )
{
@@ -367,6 +369,11 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
if( nGluePortion )
{
tools::Long nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
+ // shrink, if portions exceed the line width
+ tools::Long nSpaceSub = ( nBreakWidth > pCurrent->Width() )
+ ? (nBreakWidth - pCurrent->Width()) * SPACING_PRECISION_FACTOR /
+ sal_Int32(nGluePortion) + LONG_MAX/2
+ : 0;
// i60594
if( rSI.CountKashida() && !bSkipKashida )
@@ -382,7 +389,7 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
}
}
- pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
+ pCurrent->SetLLSpaceAdd( nSpaceSub ? nSpaceSub : nSpaceAdd, nSpaceIdx );
pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
}
else if (IsOneBlock() && nCharCnt > TextFrameIndex(1))
@@ -399,6 +406,10 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
else
++nGluePortion;
}
+ else
+ {
+ nBreakWidth += pPos->Width();
+ }
GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
if ( pPos == pStopAt )
{
@@ -488,7 +499,7 @@ SwTwips SwTextAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
else
nCompress = 10000 - nCompress;
- ( pCurrent->GetKanaComp() )[ nKanaIdx ] = static_cast<sal_uInt16>(nCompress);
+ ( pCurrent->GetKanaComp() )[ nKanaIdx ] = o3tl::narrowing<sal_uInt16>(nCompress);
nKanaDiffSum = 0;
}
@@ -509,11 +520,11 @@ SwTwips SwTextAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
{
if ( pPos->InTextGrp() )
{
- const sal_uInt16 nMinWidth = pPos->Width();
+ const SwTwips nMinWidth = pPos->Width();
// get maximum portion width from info structure, calculated
// during text formatting
- sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
+ SwTwips nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
// check, if information is stored under other key
if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
@@ -524,7 +535,7 @@ SwTwips SwTextAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
}
else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
{
- pPos->Width( static_cast<sal_uInt16>(pPos->Width() - nDecompress) );
+ pPos->Width(pPos->Width() - nDecompress);
if ( pPos->InTabGrp() )
// set fix width to width
@@ -747,7 +758,7 @@ void SwTextAdjuster::CalcDropAdjust()
OSL_ENSURE( 1<GetDropLines() && SvxAdjust::Left!=GetAdjust() && SvxAdjust::Block!=GetAdjust(),
"CalcDropAdjust: No reason for DropAdjustment." );
- const sal_uInt16 nLineNumber = GetLineNr();
+ const sal_Int32 nLineNumber = GetLineNr();
// 1) Skip dummies
Top();
@@ -780,7 +791,7 @@ void SwTextAdjuster::CalcDropAdjust()
const auto nDropLineStart =
GetLineStart() + pLeft->Width() + pDropPor->Width();
auto nMinLeft = nDropLineStart;
- for( sal_uInt16 i = 1; i < GetDropLines(); ++i )
+ for( sal_Int32 i = 1; i < GetDropLines(); ++i )
{
if( NextLine() )
{
@@ -831,7 +842,7 @@ void SwTextAdjuster::CalcDropRepaint()
SwRepaint &rRepaint = GetInfo().GetParaPortion()->GetRepaint();
if( rRepaint.Top() > Y() )
rRepaint.Top( Y() );
- for( sal_uInt16 i = 1; i < GetDropLines(); ++i )
+ for( sal_Int32 i = 1; i < GetDropLines(); ++i )
NextLine();
const SwTwips nBottom = Y() + GetLineHeight() - 1;
if( rRepaint.Bottom() < nBottom )
diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx
index a0ae8073c27b..e498db01c323 100644
--- a/sw/source/core/text/itratr.cxx
+++ b/sw/source/core/text/itratr.cxx
@@ -57,6 +57,10 @@
#include <editeng/lrspitem.hxx>
#include <calbck.hxx>
#include <frameformats.hxx>
+#include <sortedobjs.hxx>
+#include <anchoredobject.hxx>
+#include <flyfrm.hxx>
+#include <flyfrms.hxx>
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star;
@@ -260,6 +264,7 @@ void SwAttrIter::SeekFwd(const sal_Int32 nOldPos, const sal_Int32 nNewPos)
{
SwpHints const*const pHints(m_pTextNode->GetpSwpHints());
SwTextAttr *pTextAttr;
+ const auto nHintsCount = pHints->Count();
if ( m_nStartIndex ) // If attributes have been opened at all ...
{
@@ -267,7 +272,7 @@ void SwAttrIter::SeekFwd(const sal_Int32 nOldPos, const sal_Int32 nNewPos)
// As long as we've not yet reached the end of EndArray and the
// TextAttribute ends before or at the new position ...
- while ((m_nEndIndex < pHints->Count()) &&
+ while ((m_nEndIndex < nHintsCount) &&
((pTextAttr = pHints->GetSortedByEnd(m_nEndIndex))->GetAnyEnd() <= nNewPos))
{
// Close the TextAttributes, whose StartPos were before or at
@@ -278,7 +283,7 @@ void SwAttrIter::SeekFwd(const sal_Int32 nOldPos, const sal_Int32 nNewPos)
}
else // skip the not opened ends
{
- while ((m_nEndIndex < pHints->Count()) &&
+ while ((m_nEndIndex < nHintsCount) &&
(pHints->GetSortedByEnd(m_nEndIndex)->GetAnyEnd() <= nNewPos))
{
m_nEndIndex++;
@@ -287,7 +292,7 @@ void SwAttrIter::SeekFwd(const sal_Int32 nOldPos, const sal_Int32 nNewPos)
// As long as we've not yet reached the end of EndArray and the
// TextAttribute ends before or at the new position...
- while ((m_nStartIndex < pHints->Count()) &&
+ while ((m_nStartIndex < nHintsCount) &&
((pTextAttr = pHints->Get(m_nStartIndex))->GetStart() <= nNewPos))
{
@@ -329,7 +334,6 @@ bool SwAttrIter::Seek(TextFrameIndex const nNewPos)
}
while (nPos < m_pTextNode->Len());
}
- assert(m_nChgCnt == 0); // should have reset it all? there cannot be ExtOn() inside of a Delete redline, surely?
// Unapply current para items:
// the SwAttrHandler doesn't appear to be capable of *unapplying*
// items at all; it can only apply a previously effective item.
@@ -443,7 +447,7 @@ static void InsertCharAttrs(SfxPoolItem const** pAttrs, SfxItemSet const& rItems
}
else if (nWhich == RES_TXTATR_UNKNOWN_CONTAINER)
{
- pAttrs[RES_CHRATR_END] = pItem;
+ pAttrs[RES_CHRATR_END - RES_CHRATR_BEGIN] = pItem;
}
}
}
@@ -459,8 +463,8 @@ static bool CanSkipOverRedline(
size_t nStartIndex(rStartIndex);
size_t nEndIndex(rEndIndex);
SwPosition const*const pRLEnd(rRedline.End());
- if (!pRLEnd->nNode.GetNode().IsTextNode() // if fully deleted...
- || pRLEnd->nContent == pRLEnd->nNode.GetNode().GetTextNode()->Len())
+ if (!pRLEnd->GetNode().IsTextNode() // if fully deleted...
+ || pRLEnd->GetContentIndex() == pRLEnd->GetNode().GetTextNode()->Len())
{
// shortcut: nothing follows redline
// current state is end state
@@ -470,15 +474,15 @@ static bool CanSkipOverRedline(
// can't compare the SwFont that's stored somewhere, it doesn't have compare
// operator, so try to recreate the situation with some temp arrays here
SfxPoolItem const* activeCharAttrsStart[RES_CHRATR_END - RES_CHRATR_BEGIN + 1] = { nullptr, };
- if (&rStartNode != &pRLEnd->nNode.GetNode())
+ if (rStartNode != pRLEnd->GetNode())
{ // nodes' attributes are only needed if there are different nodes
InsertCharAttrs(activeCharAttrsStart, rStartNode.GetSwAttrSet());
}
if (SwpHints const*const pStartHints = rStartNode.GetpSwpHints())
{
// check hint ends of hints that start before and end within
- sal_Int32 const nRedlineEnd(&rStartNode == &pRLEnd->nNode.GetNode()
- ? pRLEnd->nContent.GetIndex()
+ sal_Int32 const nRedlineEnd(rStartNode == pRLEnd->GetNode()
+ ? pRLEnd->GetContentIndex()
: rStartNode.Len());
for ( ; nEndIndex < pStartHints->Count(); ++nEndIndex)
{
@@ -509,6 +513,7 @@ static bool CanSkipOverRedline(
case RES_TXTATR_INETFMT:
case RES_TXTATR_CJK_RUBY:
case RES_TXTATR_INPUTFIELD:
+ case RES_TXTATR_CONTENTCONTROL:
{
if (!isTheAnswerYes) return false; // always break
}
@@ -539,10 +544,10 @@ static bool CanSkipOverRedline(
}
}
assert(nEndIndex == pStartHints->Count() ||
- pRLEnd->nContent.GetIndex() < pStartHints->GetSortedByEnd(nEndIndex)->GetAnyEnd());
+ pRLEnd->GetContentIndex() < pStartHints->GetSortedByEnd(nEndIndex)->GetAnyEnd());
}
- if (&rStartNode != &pRLEnd->nNode.GetNode())
+ if (rStartNode != pRLEnd->GetNode())
{
nStartIndex = 0;
nEndIndex = 0;
@@ -554,17 +559,17 @@ static bool CanSkipOverRedline(
// ... and the charfmt must be *nominally* the same
SfxPoolItem const* activeCharAttrsEnd[RES_CHRATR_END - RES_CHRATR_BEGIN + 1] = { nullptr, };
- if (&rStartNode != &pRLEnd->nNode.GetNode())
+ if (rStartNode != pRLEnd->GetNode())
{ // nodes' attributes are only needed if there are different nodes
InsertCharAttrs(activeCharAttrsEnd,
- pRLEnd->nNode.GetNode().GetTextNode()->GetSwAttrSet());
+ pRLEnd->GetNode().GetTextNode()->GetSwAttrSet());
}
- if (SwpHints *const pEndHints = pRLEnd->nNode.GetNode().GetTextNode()->GetpSwpHints())
+ if (SwpHints *const pEndHints = pRLEnd->GetNode().GetTextNode()->GetpSwpHints())
{
// check hint starts of hints that start within and end after
#ifndef NDEBUG
- sal_Int32 const nRedlineStart(&rStartNode == &pRLEnd->nNode.GetNode()
+ sal_Int32 const nRedlineStart(rStartNode == pRLEnd->GetNode()
? nStartRedline
: 0);
#endif
@@ -575,7 +580,7 @@ static bool CanSkipOverRedline(
// of the 1st char after the redline; should not cause problems
// with consecutive delete redlines because those are handed by
// GetNextRedln() and here we have the last end pos.
- if (pRLEnd->nContent.GetIndex() < pAttr->GetStart())
+ if (pRLEnd->GetContentIndex() < pAttr->GetStart())
{
break;
}
@@ -586,7 +591,7 @@ static bool CanSkipOverRedline(
continue;
}
assert(nRedlineStart <= pAttr->GetStart()); // we wouldn't be here otherwise?
- if (*pAttr->End() <= pRLEnd->nContent.GetIndex())
+ if (*pAttr->End() <= pRLEnd->GetContentIndex())
{
continue;
}
@@ -599,6 +604,7 @@ static bool CanSkipOverRedline(
case RES_TXTATR_INETFMT:
case RES_TXTATR_CJK_RUBY:
case RES_TXTATR_INPUTFIELD:
+ case RES_TXTATR_CONTENTCONTROL:
{
if (!isTheAnswerYes) return false;
}
@@ -629,7 +635,7 @@ static bool CanSkipOverRedline(
default: assert(false);
}
}
- if (&rStartNode != &pRLEnd->nNode.GetNode())
+ if (rStartNode != pRLEnd->GetNode())
{
// need to iterate the nEndIndex forward too so the loop in the
// caller can look for the right ends in the next iteration
@@ -638,7 +644,7 @@ static bool CanSkipOverRedline(
SwTextAttr *const pAttr(pEndHints->GetSortedByEnd(nEndIndex));
if (!pAttr->End())
continue;
- if (pRLEnd->nContent.GetIndex() < *pAttr->End())
+ if (pRLEnd->GetContentIndex() < *pAttr->End())
{
break;
}
@@ -653,9 +659,9 @@ static bool CanSkipOverRedline(
}
for (size_t i = 0; i < SAL_N_ELEMENTS(activeCharAttrsStart); ++i)
{
- // all of these are poolable
-// assert(!activeCharAttrsStart[i] || activeCharAttrsStart[i]->GetItemPool()->IsItemPoolable(*activeCharAttrsStart[i]));
- if (activeCharAttrsStart[i] != activeCharAttrsEnd[i])
+ // all of these should be shareable (but we have no SfxItemPool to check it here)
+ // assert(!activeCharAttrsStart[i] || activeCharAttrsStart[i]->GetItemPool()->Shareable(*activeCharAttrsStart[i]));
+ if (!SfxPoolItem::areSame(activeCharAttrsStart[i], activeCharAttrsEnd[i]))
{
if (!isTheAnswerYes) return false;
}
@@ -743,20 +749,20 @@ TextFrameIndex SwAttrIter::GetNextAttr() const
if (redline.second.first)
{
assert(m_pMergedPara);
- assert(redline.second.first->End()->nNode.GetIndex() <= m_pMergedPara->pLastNode->GetIndex()
- || !redline.second.first->End()->nNode.GetNode().IsTextNode());
+ assert(redline.second.first->End()->GetNodeIndex() <= m_pMergedPara->pLastNode->GetIndex()
+ || !redline.second.first->End()->GetNode().IsTextNode());
if (CanSkipOverRedline(*pTextNode, redline.first, *redline.second.first,
nStartIndex, nEndIndex, m_nPosition == redline.first))
{ // if current position is start of the redline, must skip!
nActRedline += redline.second.second;
- if (&redline.second.first->End()->nNode.GetNode() != pTextNode)
+ if (&redline.second.first->End()->GetNode() != pTextNode)
{
- pTextNode = redline.second.first->End()->nNode.GetNode().GetTextNode();
- nPosition = redline.second.first->End()->nContent.GetIndex();
+ pTextNode = redline.second.first->End()->GetNode().GetTextNode();
+ nPosition = redline.second.first->End()->GetContentIndex();
}
else
{
- nPosition = redline.second.first->End()->nContent.GetIndex();
+ nPosition = redline.second.first->End()->GetContentIndex();
}
}
else
@@ -869,7 +875,7 @@ public:
tools::Long m_nRightRest; // space not already covered by frames in the right margin
tools::Long m_nLeftDiff; // Min/Max-difference of the frame in the left margin
tools::Long m_nRightDiff; // Min/Max-difference of the frame in the right margin
- sal_uLong m_nIndex; // index of the node
+ SwNodeOffset m_nIndex; // index of the node
void Minimum( tools::Long nNew ) {
if (nNew > m_nMinWidth)
m_nMinWidth = nNew;
@@ -878,7 +884,7 @@ public:
}
-static void lcl_MinMaxNode( SwFrameFormat* pNd, SwMinMaxNodeArgs* pIn )
+static void lcl_MinMaxNode(SwFrameFormat* pNd, SwMinMaxNodeArgs& rIn)
{
const SwFormatAnchor& rFormatA = pNd->GetAnchor();
@@ -888,9 +894,9 @@ static void lcl_MinMaxNode( SwFrameFormat* pNd, SwMinMaxNodeArgs* pIn )
return;
}
- const SwPosition *pPos = rFormatA.GetContentAnchor();
- OSL_ENSURE(pPos && pIn, "Unexpected NULL arguments");
- if (!pPos || !pIn || pIn->m_nIndex != pPos->nNode.GetIndex())
+ const SwNode *pAnchorNode = rFormatA.GetAnchorNode();
+ OSL_ENSURE(pAnchorNode, "Unexpected NULL arguments");
+ if (!pAnchorNode || rIn.m_nIndex != pAnchorNode->GetIndex())
return;
tools::Long nMin, nMax;
@@ -901,7 +907,7 @@ static void lcl_MinMaxNode( SwFrameFormat* pNd, SwMinMaxNodeArgs* pIn )
// Does the frame contain a table at the start or the end?
const SwNodes& rNodes = pNd->GetDoc()->GetNodes();
const SwFormatContent& rFlyContent = pNd->GetContent();
- sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
SwTableNode* pTableNd = rNodes[nStt+1]->GetTableNode();
if( !pTableNd )
{
@@ -953,7 +959,7 @@ static void lcl_MinMaxNode( SwFrameFormat* pNd, SwMinMaxNodeArgs* pIn )
if( css::text::WrapTextMode_THROUGH == pNd->GetSurround().GetSurround() )
{
- pIn->Minimum( nMin );
+ rIn.Minimum( nMin );
return;
}
@@ -966,33 +972,33 @@ static void lcl_MinMaxNode( SwFrameFormat* pNd, SwMinMaxNodeArgs* pIn )
{
if( nDiff )
{
- pIn->m_nRightRest -= pIn->m_nRightDiff;
- pIn->m_nRightDiff = nDiff;
+ rIn.m_nRightRest -= rIn.m_nRightDiff;
+ rIn.m_nRightDiff = nDiff;
}
if( text::RelOrientation::FRAME != rOrient.GetRelationOrient() )
{
- if (pIn->m_nRightRest > 0)
- pIn->m_nRightRest = 0;
+ if (rIn.m_nRightRest > 0)
+ rIn.m_nRightRest = 0;
}
- pIn->m_nRightRest -= nMin;
+ rIn.m_nRightRest -= nMin;
break;
}
case text::HoriOrientation::LEFT:
{
if( nDiff )
{
- pIn->m_nLeftRest -= pIn->m_nLeftDiff;
- pIn->m_nLeftDiff = nDiff;
+ rIn.m_nLeftRest -= rIn.m_nLeftDiff;
+ rIn.m_nLeftDiff = nDiff;
}
- if (text::RelOrientation::FRAME != rOrient.GetRelationOrient() && pIn->m_nLeftRest < 0)
- pIn->m_nLeftRest = 0;
- pIn->m_nLeftRest -= nMin;
+ if (text::RelOrientation::FRAME != rOrient.GetRelationOrient() && rIn.m_nLeftRest < 0)
+ rIn.m_nLeftRest = 0;
+ rIn.m_nLeftRest -= nMin;
break;
}
default:
{
- pIn->m_nMaxWidth += nMax;
- pIn->Minimum( nMin );
+ rIn.m_nMaxWidth += nMax;
+ rIn.Minimum(nMin);
}
}
}
@@ -1003,13 +1009,13 @@ static void lcl_MinMaxNode( SwFrameFormat* pNd, SwMinMaxNodeArgs* pIn )
* Changing this method very likely requires changing of GetScalingOfSelectedText
* This one is called exclusively from import filters, so there is no layout.
*/
-void SwTextNode::GetMinMaxSize( sal_uLong nIndex, sal_uLong& rMin, sal_uLong &rMax,
+void SwTextNode::GetMinMaxSize( SwNodeOffset nIndex, sal_uLong& rMin, sal_uLong &rMax,
sal_uLong& rAbsMin ) const
{
SwViewShell const * pSh = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
OutputDevice* pOut = nullptr;
if( pSh )
- pOut = pSh->GetWin();
+ pOut = pSh->GetWin()->GetOutDev();
if( !pOut )
pOut = Application::GetDefaultDevice();
@@ -1020,8 +1026,9 @@ void SwTextNode::GetMinMaxSize( sal_uLong nIndex, sal_uLong& rMin, sal_uLong &rM
rMax = 0;
rAbsMin = 0;
- const SvxLRSpaceItem &rSpace = GetSwAttrSet().GetLRSpace();
- tools::Long nLROffset = rSpace.GetTextLeft() + GetLeftMarginWithNum( true );
+ SvxTextLeftMarginItem const& rTextLeftMargin(GetSwAttrSet().GetTextLeftMargin());
+ SvxRightMarginItem const& rRightMargin(GetSwAttrSet().GetRightMargin());
+ tools::Long nLROffset = rTextLeftMargin.GetTextLeft() + GetLeftMarginWithNum( true );
short nFLOffs;
// For enumerations a negative first line indentation is probably filled already
if( !GetFirstLineOfsWithNum( nFLOffs ) || nFLOffs > nLROffset )
@@ -1031,17 +1038,17 @@ void SwTextNode::GetMinMaxSize( sal_uLong nIndex, sal_uLong& rMin, sal_uLong &rM
aNodeArgs.m_nMinWidth = 0;
aNodeArgs.m_nMaxWidth = 0;
aNodeArgs.m_nLeftRest = nLROffset;
- aNodeArgs.m_nRightRest = rSpace.GetRight();
+ aNodeArgs.m_nRightRest = rRightMargin.GetRight();
aNodeArgs.m_nLeftDiff = 0;
aNodeArgs.m_nRightDiff = 0;
if( nIndex )
{
- SwFrameFormats* pTmp = const_cast<SwFrameFormats*>(GetDoc().GetSpzFrameFormats());
- if( pTmp )
+ sw::SpzFrameFormats* pSpzs = const_cast<sw::SpzFrameFormats*>(GetDoc().GetSpzFrameFormats());
+ if(pSpzs)
{
aNodeArgs.m_nIndex = nIndex;
- for( SwFrameFormat *pFormat : *pTmp )
- lcl_MinMaxNode( pFormat, &aNodeArgs );
+ for(auto pFormat: *pSpzs)
+ lcl_MinMaxNode(pFormat, aNodeArgs);
}
}
if (aNodeArgs.m_nLeftRest < 0)
@@ -1051,7 +1058,7 @@ void SwTextNode::GetMinMaxSize( sal_uLong nIndex, sal_uLong& rMin, sal_uLong &rM
aNodeArgs.m_nMaxWidth -= aNodeArgs.m_nLeftRest;
if (aNodeArgs.m_nRightRest < 0)
- aNodeArgs.Minimum(rSpace.GetRight() - aNodeArgs.m_nRightRest);
+ aNodeArgs.Minimum(rRightMargin.GetRight() - aNodeArgs.m_nRightRest);
aNodeArgs.m_nRightRest -= aNodeArgs.m_nRightDiff;
if (aNodeArgs.m_nRightRest < 0)
aNodeArgs.m_nMaxWidth -= aNodeArgs.m_nRightRest;
@@ -1224,7 +1231,7 @@ void SwTextNode::GetMinMaxSize( sal_uLong nIndex, sal_uLong& rMin, sal_uLong &rM
if (static_cast<tools::Long>(rMax) < aArg.m_nRowWidth)
rMax = aArg.m_nRowWidth;
- nLROffset += rSpace.GetRight();
+ nLROffset += rRightMargin.GetRight();
rAbsMin += nLROffset;
rAbsMin += nAdd;
@@ -1301,7 +1308,7 @@ sal_uInt16 SwTextFrame::GetScalingOfSelectedText(
// scaling value 100 and priority flag on top of the scaling stack
SwAttrHandler& rAH = aIter.GetAttrHandler();
SvxCharScaleWidthItem aItem(100, RES_CHRATR_SCALEW);
- SwTextAttrEnd aAttr( aItem, 0, COMPLETE_STRING );
+ SwTextAttrEnd aAttr( SfxPoolItemHolder(getRootFrame()->GetCurrShell()->GetAttrPool(), &aItem), 0, COMPLETE_STRING );
aAttr.SetPriorityAttr( true );
rAH.PushAndChg( aAttr, *(aIter.GetFnt()) );
@@ -1432,7 +1439,7 @@ sal_uInt16 SwTextFrame::GetScalingOfSelectedText(
SwTextIter aLine(this, &aInf);
aLine.CharToLine( nStart );
pOut->SetMapMode( aOldMap );
- return static_cast<sal_uInt16>( nWidth ?
+ return o3tl::narrowing<sal_uInt16>( nWidth ?
( ( 100 * aLine.GetCurr()->Height() ) / nWidth ) : 0 );
}
// no frame or no paragraph, we take the height of the character
@@ -1442,7 +1449,142 @@ sal_uInt16 SwTextFrame::GetScalingOfSelectedText(
pOut->SetMapMode( aOldMap );
SwDrawTextInfo aDrawInf(pSh, *pOut, GetText(), sal_Int32(nStart), 1);
- return static_cast<sal_uInt16>( nWidth ? ((100 * aIter.GetFnt()->GetTextSize_( aDrawInf ).Height()) / nWidth ) : 0 );
+ return o3tl::narrowing<sal_uInt16>( nWidth ? ((100 * aIter.GetFnt()->GetTextSize_( aDrawInf ).Height()) / nWidth ) : 0 );
+}
+
+std::vector<SwFlyAtContentFrame*> SwTextFrame::GetSplitFlyDrawObjs() const
+{
+ std::vector<SwFlyAtContentFrame*> aObjs;
+ const SwSortedObjs* pSortedObjs = GetDrawObjs();
+ if (!pSortedObjs)
+ {
+ return aObjs;
+ }
+
+ for (const auto& pSortedObj : *pSortedObjs)
+ {
+ SwFlyFrame* pFlyFrame = pSortedObj->DynCastFlyFrame();
+ if (!pFlyFrame)
+ {
+ continue;
+ }
+
+ if (!pFlyFrame->IsFlySplitAllowed())
+ {
+ continue;
+ }
+
+ aObjs.push_back(static_cast<SwFlyAtContentFrame*>(pFlyFrame));
+ }
+
+ return aObjs;
+}
+
+SwFlyAtContentFrame* SwTextFrame::HasNonLastSplitFlyDrawObj() const
+{
+ const SwTextFrame* pFollow = GetFollow();
+ if (!pFollow)
+ {
+ return nullptr;
+ }
+
+ if (mnOffset != pFollow->GetOffset())
+ {
+ return nullptr;
+ }
+
+ // At this point we know what we're part of a chain that is an anchor for split fly frames, but
+ // we're not the last one. See if we have a matching fly.
+
+ // Look up the master of the anchor.
+ const SwTextFrame* pAnchor = this;
+ while (pAnchor->IsFollow())
+ {
+ pAnchor = pAnchor->FindMaster();
+ }
+ for (const auto& pFly : pAnchor->GetSplitFlyDrawObjs())
+ {
+ // Nominally all flys are anchored in the master; see if this fly is effectively anchored in
+ // us.
+ SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
+ if (pFlyAnchor != this)
+ {
+ continue;
+ }
+ if (pFly->GetFollow())
+ {
+ return pFly;
+ }
+ }
+
+ return nullptr;
+}
+
+bool SwTextFrame::IsEmptyMasterWithSplitFly() const
+{
+ if (!IsEmptyMaster())
+ {
+ return false;
+ }
+
+ if (!m_pDrawObjs || m_pDrawObjs->size() != 1)
+ {
+ return false;
+ }
+
+ SwFlyFrame* pFlyFrame = (*m_pDrawObjs)[0]->DynCastFlyFrame();
+ if (!pFlyFrame || !pFlyFrame->IsFlySplitAllowed())
+ {
+ return false;
+ }
+
+ if (mnOffset != GetFollow()->GetOffset())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool SwTextFrame::IsEmptyWithSplitFly() const
+{
+ if (IsFollow())
+ {
+ return false;
+ }
+
+ if (GetTextNodeFirst()->GetSwAttrSet().HasItem(RES_PAGEDESC))
+ {
+ return false;
+ }
+
+ if (getFrameArea().Bottom() <= GetUpper()->getFramePrintArea().Bottom())
+ {
+ return false;
+ }
+
+ // This is a master that doesn't fit the current parent.
+ if (!m_pDrawObjs || m_pDrawObjs->size() != 1)
+ {
+ return false;
+ }
+
+ SwFlyFrame* pFlyFrame = (*m_pDrawObjs)[0]->DynCastFlyFrame();
+ if (!pFlyFrame || !pFlyFrame->IsFlySplitAllowed())
+ {
+ return false;
+ }
+
+ // It has a split fly anchored to it.
+ if (pFlyFrame->GetFrameFormat()->GetVertOrient().GetPos() >= 0)
+ {
+ return false;
+ }
+
+ // Negative vertical offset means that visually it already may have a first line.
+ // Consider that, we may need to split the frame, so the fly frame is on one page and the empty
+ // paragraph's frame is on a next page.
+ return true;
}
SwTwips SwTextNode::GetWidthOfLeadingTabs() const
@@ -1463,8 +1605,7 @@ SwTwips SwTextNode::GetWidthOfLeadingTabs() const
if ( nIdx > 0 )
{
- SwPosition aPos( *this );
- aPos.nContent += nIdx;
+ SwPosition aPos( *this, nIdx );
// Find the non-follow text frame:
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
diff --git a/sw/source/core/text/itratr.hxx b/sw/source/core/text/itratr.hxx
index e4dedc22cda1..4ad2c6d04c5f 100644
--- a/sw/source/core/text/itratr.hxx
+++ b/sw/source/core/text/itratr.hxx
@@ -29,7 +29,7 @@ class SwRedlineItr;
class SwViewShell;
class SwTextFrame;
-class SwAttrIter
+class SAL_DLLPUBLIC_RTTI SwAttrIter
{
friend class SwFontSave;
protected:
@@ -61,14 +61,14 @@ private:
void SetFnt( SwFont* pNew ) { m_pFont = pNew; }
void InitFontAndAttrHandler(
SwTextNode const& rPropsNode, SwTextNode const& rTextNode,
- OUString const& rText, bool const* pbVertLayout,
+ std::u16string_view aText, bool const* pbVertLayout,
bool const* pbVertLayoutLRBT);
protected:
void Chg( SwTextAttr const *pHt );
void Rst( SwTextAttr const *pHt );
void CtorInitAttrIter(SwTextNode& rTextNode, SwScriptInfo& rScrInf, SwTextFrame const* pFrame = nullptr);
- explicit SwAttrIter(SwTextNode const * pTextNode);
+ SW_DLLPUBLIC explicit SwAttrIter(SwTextNode const * pTextNode);
public:
/// All subclasses of this always have a SwTextFrame passed to the
@@ -76,7 +76,7 @@ public:
/// SwTextFrame in certain special cases via this ctor here
SwAttrIter(SwTextNode& rTextNode, SwScriptInfo& rScrInf, SwTextFrame const*const pFrame = nullptr);
- virtual ~SwAttrIter();
+ SW_DLLPUBLIC virtual ~SwAttrIter();
SwRedlineItr *GetRedln() { return m_pRedline.get(); }
// The parameter returns the position of the next change before or at the
diff --git a/sw/source/core/text/itrcrsr.cxx b/sw/source/core/text/itrcrsr.cxx
index f7c6380cb3bb..ea399a66f5d9 100644
--- a/sw/source/core/text/itrcrsr.cxx
+++ b/sw/source/core/text/itrcrsr.cxx
@@ -82,7 +82,7 @@ static void lcl_GetCharRectInsideField( SwTextSizeInfo& rInf, SwRect& rOrig,
if ( ! pPor->GetNextPortion() || nFieldIdx + nFieldLen > nCharOfst )
break;
- nFieldIdx = nFieldIdx + nFieldLen;
+ nFieldIdx += nFieldLen;
rOrig.Pos().AdjustX(pPor->Width() );
pPor = pPor->GetNextPortion();
@@ -142,7 +142,7 @@ namespace {
nListLevel = MAXLEVEL - 1;
const SwNumFormat& rNumFormat =
- rTextNode.GetNumRule()->Get( static_cast<sal_uInt16>(nListLevel) );
+ rTextNode.GetNumRule()->Get( o3tl::narrowing<sal_uInt16>(nListLevel) );
if ( rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
{
bRet = true;
@@ -161,12 +161,13 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
GetInfo().SetFont( GetFnt() );
const SwTextNode *const pNode = m_pFrame->GetTextNodeForParaProps();
- const SvxLRSpaceItem &rSpace = pNode->GetSwAttrSet().GetLRSpace();
+ SvxFirstLineIndentItem const& rFirstLine(pNode->GetSwAttrSet().GetFirstLineIndent());
+ SvxTextLeftMarginItem const& rTextLeftMargin(pNode->GetSwAttrSet().GetTextLeftMargin());
// #i95907#
// #i111284#
const SwTextNode *pTextNode = m_pFrame->GetTextNodeForParaProps();
const bool bLabelAlignmentActive = IsLabelAlignmentActive( *pTextNode );
- const bool bListLevelIndentsApplicable = pTextNode->AreListLevelIndentsApplicable();
+ const bool bListLevelIndentsApplicable = pTextNode->AreListLevelIndentsApplicable() != ::sw::ListLevelIndents::No;
const bool bListLevelIndentsApplicableAndLabelAlignmentActive = bListLevelIndentsApplicable && bLabelAlignmentActive;
// Carefully adjust the text formatting ranges.
@@ -191,9 +192,7 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
// #i95907#
// #i111284#
// rSpace.GetLeft() + rSpace.GetTextLeft();
- ( bListLevelIndentsApplicableAndLabelAlignmentActive
- ? 0
- : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
+ (rTextLeftMargin.GetLeft(rFirstLine) - rTextLeftMargin.GetTextLeft());
}
else
{
@@ -209,14 +208,12 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
pNode->GetLeftMarginWithNum() -
// #i95907#
// #i111284#
- ( bListLevelIndentsApplicableAndLabelAlignmentActive
- ? 0
- : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
+ (rTextLeftMargin.GetLeft(rFirstLine) - rTextLeftMargin.GetTextLeft());
}
else
{
mnLeft = m_pFrame->getFrameArea().Left() +
- std::max( tools::Long( rSpace.GetTextLeft() + nLMWithNum ),
+ std::max(tools::Long(rTextLeftMargin.GetTextLeft() + nLMWithNum),
m_pFrame->getFramePrintArea().Left() );
}
}
@@ -228,7 +225,8 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
// paras inside cells inside new documents:
( pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ||
!m_pFrame->IsInTab() ||
- ( !nLMWithNum && (!bLabelAlignmentActive || bListLevelIndentsApplicable) ) ) )
+ (bListLevelIndentsApplicable && nLMWithNum == rTextLeftMargin.GetTextLeft())
+ || (!bLabelAlignmentActive && nLMWithNum == 0)))
{
mnLeft = m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left();
if( mnLeft >= mnRight ) // e.g. with large paragraph indentations in slim table columns
@@ -242,7 +240,7 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
short nFLOfst = 0;
tools::Long nFirstLineOfs = 0;
if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
- rSpace.IsAutoFirst() )
+ rFirstLine.IsAutoFirst())
{
nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
LanguageType const aLang = m_pFrame->GetLangOfChar(
@@ -250,49 +248,54 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
if (aLang != LANGUAGE_KOREAN && aLang != LANGUAGE_JAPANESE)
nFirstLineOfs<<=1;
- const SvxLineSpacingItem *pSpace = m_aLineInf.GetLineSpacing();
- if( pSpace )
+ // tdf#129448: Auto first-line indent should not be effected by line space.
+ // Below is for compatibility with old documents.
+ if (!pNode->getIDocumentSettingAccess()->get(DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE))
{
- switch( pSpace->GetLineSpaceRule() )
+ const SvxLineSpacingItem *pSpace = m_aLineInf.GetLineSpacing();
+ if( pSpace )
{
- case SvxLineSpaceRule::Auto:
- break;
- case SvxLineSpaceRule::Min:
+ switch( pSpace->GetLineSpaceRule() )
{
- if( nFirstLineOfs < pSpace->GetLineHeight() )
- nFirstLineOfs = pSpace->GetLineHeight();
+ case SvxLineSpaceRule::Auto:
break;
- }
- case SvxLineSpaceRule::Fix:
- nFirstLineOfs = pSpace->GetLineHeight();
- break;
- default: OSL_FAIL( ": unknown LineSpaceRule" );
- }
- switch( pSpace->GetInterLineSpaceRule() )
- {
- case SvxInterLineSpaceRule::Off:
- break;
- case SvxInterLineSpaceRule::Prop:
- {
- tools::Long nTmp = pSpace->GetPropLineSpace();
- // 50% is the minimum, at 0% we switch to
- // the default value 100%...
- if( nTmp < 50 )
- nTmp = nTmp ? 50 : 100;
-
- nTmp *= nFirstLineOfs;
- nTmp /= 100;
- if( !nTmp )
- ++nTmp;
- nFirstLineOfs = nTmp;
+ case SvxLineSpaceRule::Min:
+ {
+ if( nFirstLineOfs < pSpace->GetLineHeight() )
+ nFirstLineOfs = pSpace->GetLineHeight();
+ break;
+ }
+ case SvxLineSpaceRule::Fix:
+ nFirstLineOfs = pSpace->GetLineHeight();
break;
+ default: OSL_FAIL( ": unknown LineSpaceRule" );
}
- case SvxInterLineSpaceRule::Fix:
+ switch( pSpace->GetInterLineSpaceRule() )
{
- nFirstLineOfs += pSpace->GetInterLineSpace();
+ case SvxInterLineSpaceRule::Off:
break;
+ case SvxInterLineSpaceRule::Prop:
+ {
+ tools::Long nTmp = pSpace->GetPropLineSpace();
+ // 50% is the minimum, at 0% we switch to
+ // the default value 100%...
+ if( nTmp < 50 )
+ nTmp = nTmp ? 50 : 100;
+
+ nTmp *= nFirstLineOfs;
+ nTmp /= 100;
+ if( !nTmp )
+ ++nTmp;
+ nFirstLineOfs = nTmp;
+ break;
+ }
+ case SvxInterLineSpaceRule::Fix:
+ {
+ nFirstLineOfs += pSpace->GetInterLineSpace();
+ break;
+ }
+ default: OSL_FAIL( ": unknown InterLineSpaceRule" );
}
- default: OSL_FAIL( ": unknown InterLineSpaceRule" );
}
}
}
@@ -320,7 +323,7 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
else
{
mnFirst = m_pFrame->getFrameArea().Left() +
- std::max( rSpace.GetTextLeft() + nLMWithNum+ nFirstLineOfs,
+ std::max(rTextLeftMargin.GetTextLeft() + nLMWithNum + nFirstLineOfs,
m_pFrame->getFramePrintArea().Left() );
}
@@ -396,6 +399,38 @@ void SwTextCursor::CtorInitTextCursor( SwTextFrame *pNewFrame, SwTextSizeInfo *p
// GetInfo().SetOut( GetInfo().GetWin() );
}
+static bool isTrailingDecoration(SwLinePortion* p)
+{
+ // Optional no-width portion, followed only by no-width portions and/or terminating portions?
+ for (; p; p = p->GetNextPortion())
+ {
+ if (p->IsMarginPortion() || p->IsBreakPortion())
+ return true;
+ if (p->Width())
+ return false;
+ }
+ return true; // no more portions
+}
+
+// tdf#120715 tdf#43100: Make width for some HolePortions, so cursor will be able to move into it.
+// It should not change the layout, so this should be called after the layout is calculated.
+void SwTextCursor::AddExtraBlankWidth()
+{
+ SwLinePortion* pPos = m_pCurr->GetNextPortion();
+ while (pPos)
+ {
+ SwLinePortion* pNextPos = pPos->GetNextPortion();
+ // Do it only if it is the last portion that able to handle the cursor,
+ // else the next portion would miscalculate the cursor position
+ if (pPos->ExtraBlankWidth() && isTrailingDecoration(pNextPos))
+ {
+ pPos->Width(pPos->Width() + pPos->ExtraBlankWidth());
+ pPos->ExtraBlankWidth(0);
+ }
+ pPos = pNextPos;
+ }
+}
+
// 1170: Ancient bug: Shift-End forgets the last character ...
void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
SwCursorMoveState* pCMS, const tools::Long nMax )
@@ -428,7 +463,7 @@ void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
tools::Long nLast = 0;
SwLinePortion *pPor = m_pCurr->GetFirstPortion();
- sal_uInt16 nTmpHeight, nTmpAscent;
+ SwTwips nTmpHeight, nTmpAscent;
CalcAscentAndHeight( nTmpAscent, nTmpHeight );
sal_uInt16 nPorHeight = nTmpHeight;
sal_uInt16 nPorAscent = nTmpAscent;
@@ -436,7 +471,7 @@ void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
// Search for the last Text/EndPortion of the line
while( pPor )
{
- nX = nX + pPor->Width();
+ nX += pPor->Width();
if( pPor->InTextGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
&& !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
{
@@ -479,7 +514,7 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
SwTextSizeInfo aInf( GetInfo(), &aText, m_nStart );
if( GetPropFont() )
aInf.GetFont()->SetProportion( GetPropFont() );
- sal_uInt16 nTmpAscent, nTmpHeight; // Line height
+ SwTwips nTmpAscent, nTmpHeight; // Line height
CalcAscentAndHeight( nTmpAscent, nTmpHeight );
const Size aCharSize( 1, nTmpHeight );
const Point aCharPos;
@@ -505,8 +540,8 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
}
else
{
- sal_uInt16 nPorHeight = nTmpHeight;
- sal_uInt16 nPorAscent = nTmpAscent;
+ SwTwips nPorHeight = nTmpHeight;
+ SwTwips nPorAscent = nTmpAscent;
SwTwips nX = 0;
SwTwips nTmpFirst = 0;
SwLinePortion *pPor = m_pCurr->GetFirstPortion();
@@ -882,9 +917,21 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
}
if ( pPor->PrtWidth() )
{
+ // tdf#30731: To get the correct nOfst width, we need
+ // to send the whole portion string to GetTextSize()
+ // and ask it to return the width of nOfst by calling
+ // SetMeasureLen(). Cutting the string at nOfst can
+ // give the wrong width if nOfst is in e.g. the middle
+ // of a ligature. See SwFntObj::DrawText().
TextFrameIndex const nOldLen = pPor->GetLen();
- pPor->SetLen( nOfst - aInf.GetIdx() );
aInf.SetLen( pPor->GetLen() );
+ pPor->SetLen( nOfst - aInf.GetIdx() );
+ aInf.SetMeasureLen(pPor->GetLen());
+ if (aInf.GetLen() < aInf.GetMeasureLen())
+ {
+ pPor->SetLen(aInf.GetMeasureLen());
+ aInf.SetLen(pPor->GetLen());
+ }
if( nX || !pPor->InNumberGrp() )
{
SeekAndChg( aInf );
@@ -900,7 +947,12 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
if( bWidth )
{
pPor->SetLen(pPor->GetLen() + TextFrameIndex(1));
- aInf.SetLen( pPor->GetLen() );
+ aInf.SetMeasureLen(pPor->GetLen());
+ if (aInf.GetLen() < aInf.GetMeasureLen())
+ {
+ pPor->SetLen(aInf.GetMeasureLen());
+ aInf.SetLen(pPor->GetLen());
+ }
aInf.SetOnWin( false ); // no BULLETs!
nTmp += pPor->GetTextSize( aInf ).Width();
aInf.SetOnWin( bOldOnWin );
@@ -935,7 +987,7 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
{
nX -= GetInfo().GetFont()->GetRightBorderSpace();
}
- }
+ }
}
bWidth = false;
break;
@@ -957,7 +1009,7 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
while( pNext && !pNext->InFieldGrp() )
{
OSL_ENSURE( !pNext->GetLen(), "Where's my field follow?" );
- nAddX = nAddX + pNext->Width();
+ nAddX += pNext->Width();
pNext = pNext->GetNextPortion();
}
if( !pNext )
@@ -1077,8 +1129,14 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
{
const bool bOldOnWin = aInf.OnWin();
TextFrameIndex const nOldLen = pPor->GetLen();
- pPor->SetLen( TextFrameIndex(1) );
aInf.SetLen( pPor->GetLen() );
+ pPor->SetLen( TextFrameIndex(1) );
+ aInf.SetMeasureLen(pPor->GetLen());
+ if (aInf.GetLen() < aInf.GetMeasureLen())
+ {
+ pPor->SetLen(aInf.GetMeasureLen());
+ aInf.SetLen(pPor->GetLen());
+ }
SeekAndChg( aInf );
aInf.SetOnWin( false ); // no BULLETs!
aInf.SetKanaComp( pKanaComp );
@@ -1197,21 +1255,18 @@ void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
++nFindOfst;
// skip lines for fields which cover more than one line
- for ( sal_uInt16 i = 0; i < pCMS->m_pSpecialPos->nLineOfst; i++ )
+ for ( sal_Int32 i = 0; i < pCMS->m_pSpecialPos->nLineOfst; i++ )
Next();
}
// If necessary, as catch up, do the adjustment
GetAdjusted();
+ AddExtraBlankWidth();
const Point aCharPos( GetTopLeft() );
GetCharRect_( pOrig, nFindOfst, pCMS );
- // This actually would have to be "-1 LogicToPixel", but that seems too
- // expensive, so it's a value (-12), that should hopefully be OK.
- const SwTwips nTmpRight = Right() - 12;
-
pOrig->Pos().AdjustX(aCharPos.X() );
pOrig->Pos().AdjustY(aCharPos.Y() );
@@ -1223,11 +1278,6 @@ void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
pCMS->m_p2Lines->aPortion.Pos().AdjustY(aCharPos.Y() );
}
- const bool bTabOverMargin = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_OVER_MARGIN);
- // Make sure the cursor respects the right margin, unless in compat mode, where the tab size has priority over the margin size.
- if( pOrig->Left() > nTmpRight && !bTabOverMargin)
- pOrig->Pos().setX( nTmpRight );
-
if( nMax )
{
if( pOrig->Top() + pOrig->Height() > nMax )
@@ -1248,23 +1298,13 @@ void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
pCMS->m_aRealHeight.setY( nMax - nTmp );
}
}
- tools::Long nOut = pOrig->Right() - GetTextFrame()->getFrameArea().Right();
- if( nOut > 0 )
- {
- if( GetTextFrame()->getFrameArea().Width() < GetTextFrame()->getFramePrintArea().Left()
- + GetTextFrame()->getFramePrintArea().Width() )
- nOut += GetTextFrame()->getFrameArea().Width() - GetTextFrame()->getFramePrintArea().Left()
- - GetTextFrame()->getFramePrintArea().Width();
- if( nOut > 0 )
- pOrig->Pos().AdjustX( -(nOut + 10) );
- }
}
/**
* Determines if SwTextCursor::GetModelPositionForViewPoint() should consider the next portion when calculating the
* doc model position from a Point.
*/
-static bool ConsiderNextPortionForCursorOffset(const SwLinePortion* pPor, sal_uInt16 nWidth30, sal_uInt16 nX)
+static bool ConsiderNextPortionForCursorOffset(const SwLinePortion* pPor, SwTwips nWidth30, sal_uInt16 nX)
{
if (!pPor->GetNextPortion() || pPor->IsBreakPortion())
{
@@ -1313,21 +1353,17 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
if( bLeftOver )
x = nLeftMargin;
const bool bRightOver = x > nRightMargin;
- if( bRightOver )
- x = nRightMargin;
-
const bool bRightAllowed = pCMS && ( pCMS->m_eState == CursorMoveState::NONE );
// Until here everything in document coordinates.
x -= nLeftMargin;
- sal_uInt16 nX = sal_uInt16( x );
+ SwTwips nX = x;
// If there are attribute changes in the line, search for the paragraph,
// in which nX is situated.
SwLinePortion *pPor = m_pCurr->GetFirstPortion();
TextFrameIndex nCurrStart = m_nStart;
- bool bHolePortion = false;
bool bLastHyph = false;
std::deque<sal_uInt16> *pKanaComp = m_pCurr->GetpKanaComp();
@@ -1340,13 +1376,13 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
// nWidth is the width of the line, or the width of
// the paragraph with the font change, in which nX is situated.
- sal_uInt16 nWidth = pPor->Width();
+ SwTwips nWidth = pPor->Width();
if ( m_pCurr->IsSpaceAdd() || pKanaComp )
{
if ( pPor->InSpaceGrp() && nSpaceAdd )
{
const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
- nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
+ nWidth += pPor->CalcSpacing( nSpaceAdd, GetInfo() );
}
if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
@@ -1370,7 +1406,7 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
}
}
- sal_uInt16 nWidth30;
+ SwTwips nWidth30;
if ( pPor->IsPostItsPortion() )
nWidth30 = 0;
else
@@ -1380,9 +1416,8 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
while (ConsiderNextPortionForCursorOffset(pPor, nWidth30, nX))
{
- nX = nX - nWidth;
- nCurrStart = nCurrStart + pPor->GetLen();
- bHolePortion = pPor->IsHolePortion();
+ nX -= nWidth;
+ nCurrStart += pPor->GetLen();
pPor = pPor->GetNextPortion();
nWidth = pPor->Width();
if ( m_pCurr->IsSpaceAdd() || pKanaComp )
@@ -1390,7 +1425,7 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
if ( pPor->InSpaceGrp() && nSpaceAdd )
{
const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
- nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
+ nWidth += pPor->CalcSpacing( nSpaceAdd, GetInfo() );
}
if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
@@ -1432,7 +1467,7 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
SwLinePortion *pNextPor = pPor->GetNextPortion();
while( pNextPor && pNextPor->InFieldGrp() && !pNextPor->Width() )
{
- nCurrStart = nCurrStart + pPor->GetLen();
+ nCurrStart += pPor->GetLen();
pPor = pNextPor;
if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
bLastHyph = pPor->InHyphGrp();
@@ -1483,7 +1518,7 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
return TextFrameIndex(0);
// 7849, 7816: pPor->GetHyphPortion is mandatory!
- if( bHolePortion || ( !bRightAllowed && bLastHyph ) ||
+ if( ( !bRightAllowed && bLastHyph ) ||
( pPor->IsMarginPortion() && !pPor->GetNextPortion() &&
// 46598: Consider the situation: We might end up behind the last character,
// in the last line of a centered paragraph
@@ -1503,7 +1538,11 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
--nCurrStart;
}
}
- return nCurrStart;
+ if (!pPor->InFieldGrp() || !static_cast<SwFieldPortion const*>(pPor)->IsFollow()
+ || !pCMS || !pCMS->m_pSpecialPos)
+ {
+ return nCurrStart;
+ }
}
if (TextFrameIndex(1) == nLength || pPor->InFieldGrp())
{
@@ -1582,8 +1621,11 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
// Skip space at the end of the line
if( bLastPortion && (m_pCurr->GetNext() || m_pFrame->GetFollow() )
- && rText[sal_Int32(nCurrStart + nLength) - 1] == ' ' )
+ && sal_Int32(nLength) != 0
+ && rText[sal_Int32(nCurrStart + nLength) - 1] == ' ')
+ {
--nLength;
+ }
if( nWidth > nX ||
( nWidth == nX && pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsDouble() ) )
@@ -1625,14 +1667,14 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
nTmpY = pPor->Height() - nTmpY;
if( nTmpY < 0 )
nTmpY = 0;
- nX = static_cast<sal_uInt16>(nTmpY);
+ nX = o3tl::narrowing<sal_uInt16>(nTmpY);
}
if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
{
const sal_uInt16 nPreWidth = static_cast<SwDoubleLinePortion*>(pPor)->PreWidth();
if ( nX > nPreWidth )
- nX = nX - nPreWidth;
+ nX -= nPreWidth;
else
nX = 0;
}
@@ -1640,7 +1682,7 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
return GetModelPositionForViewPoint( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
bChgNode, pCMS );
}
- if( pPor->InTextGrp() )
+ if( pPor->InTextGrp() || pPor->IsHolePortion() )
{
sal_uInt8 nOldProp;
if( GetPropFont() )
@@ -1653,19 +1695,23 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
{
SwTextSizeInfo aSizeInf( GetInfo(), &rText, nCurrStart );
const_cast<SwTextCursor*>(this)->SeekAndChg( aSizeInf );
- SwTextSlot aDiffText( &aSizeInf, static_cast<SwTextPortion*>(pPor), false, false );
+ SwTextSlot aDiffText( &aSizeInf, pPor, false, false );
SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ?
static_cast<SwDropPortion*>(pPor)->GetFnt() : nullptr );
SwParaPortion* pPara = const_cast<SwParaPortion*>(GetInfo().GetParaPortion());
OSL_ENSURE( pPara, "No paragraph!" );
+ // protect against bugs elsewhere
+ SAL_WARN_IF( aSizeInf.GetIdx().get() + pPor->GetLen().get() > aSizeInf.GetText().getLength(), "sw", "portion and text are out of sync" );
+ TextFrameIndex nSafeLen( std::min(pPor->GetLen().get(), aSizeInf.GetText().getLength() - aSizeInf.GetIdx().get()) );
+
SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
*aSizeInf.GetOut(),
&pPara->GetScriptInfo(),
aSizeInf.GetText(),
aSizeInf.GetIdx(),
- pPor->GetLen() );
+ nSafeLen );
// Drop portion works like a multi portion, just its parts are not portions
if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
@@ -1689,12 +1735,12 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
}
pCurrPart = pCurrPart->GetFollow();
}
- nX = std::max(0, nX - nSumBorderWidth);
+ nX = std::max(static_cast<SwTwips>(0), nX - nSumBorderWidth);
}
// Shift the offset with the left border width
else if( GetInfo().GetFont()->GetLeftBorder() && !pPor->GetJoinBorderWithPrev() )
{
- nX = std::max(0, nX - GetInfo().GetFont()->GetLeftBorderSpace());
+ nX = std::max(static_cast<SwTwips>(0), nX - GetInfo().GetFont()->GetLeftBorderSpace());
}
aDrawInf.SetOffset( nX );
@@ -1729,6 +1775,43 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
{
pCMS->m_pSpecialPos->nCharOfst = sal_Int32(nLength);
+ // follow portions: need to add the length of all previous
+ // portions for the same field
+ if (static_cast<SwFieldPortion const*>(pPor)->IsFollow())
+ {
+ int nLines(0);
+ std::vector<SwFieldPortion const*> portions;
+ for (SwLineLayout const* pLine = GetInfo().GetParaPortion();
+ true; pLine = pLine->GetNext())
+ {
+ for (SwLinePortion const* pLP = pLine; pLP && pLP != pPor; pLP = pLP->GetNextPortion())
+ {
+ if (pLP->InFieldGrp())
+ {
+ SwFieldPortion const* pField(static_cast<SwFieldPortion const*>(pLP));
+ if (!pField->IsFollow())
+ {
+ nLines = 0;
+ portions.clear();
+ }
+ if (pLine == m_pCurr)
+ {
+ portions.emplace_back(pField);
+ }
+ }
+ }
+ if (pLine == m_pCurr)
+ {
+ break;
+ }
+ ++nLines;
+ }
+ for (SwFieldPortion const* pField : portions)
+ {
+ pCMS->m_pSpecialPos->nCharOfst += pField->GetExp().getLength();
+ }
+ pCMS->m_pSpecialPos->nLineOfst = nLines;
+ }
nLength = TextFrameIndex(0);
}
@@ -1736,11 +1819,6 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
if ( pCMS )
pCMS->m_nCursorBidiLevel =
aDrawInf.GetCursorBidiLevel();
-
- if( bFieldInfo && nLength == pPor->GetLen() &&
- ( ! pPor->GetNextPortion() ||
- ! pPor->GetNextPortion()->IsPostItsPortion() ) )
- --nLength;
}
if( nOldProp )
const_cast<SwFont*>(GetFnt())->SetProportion( nOldProp );
@@ -1755,8 +1833,10 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
// (BugId: 9692 + Change in feshview)
SwFlyInContentFrame *pTmp = pFlyPor->GetFlyFrame();
SwFrame* pLower = pTmp->GetLower();
+ // Allow non-text-frames to get SwGrfNode for as-char anchored images into pPos
+ // instead of the closest SwTextNode, to be consistent with at-char behavior.
bool bChgNodeInner = pLower
- && (pLower->IsTextFrame() || pLower->IsLayoutFrame());
+ && (pLower->IsTextFrame() || pLower->IsLayoutFrame() || pLower->IsNoTextFrame());
Point aTmpPoint( rPoint );
if ( m_pFrame->IsRightToLeft() )
@@ -1765,7 +1845,7 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
if ( m_pFrame->IsVertical() )
m_pFrame->SwitchHorizontalToVertical( aTmpPoint );
- if( bChgNodeInner && pTmp->getFrameArea().IsInside( aTmpPoint ) &&
+ if( bChgNodeInner && pTmp->getFrameArea().Contains( aTmpPoint ) &&
!( pTmp->IsProtected() ) )
{
pFlyPor->GetFlyCursorOfst(aTmpPoint, *pPos, pCMS);
@@ -1817,7 +1897,7 @@ bool SwTextFrame::FillSelection( SwSelectionList& rSelList, const SwRect& rRect
bool bRet = false;
// GetPaintArea() instead getFrameArea() for negative indents
SwRect aTmpFrame( GetPaintArea() );
- if( !rRect.IsOver( aTmpFrame ) )
+ if( !rRect.Overlaps( aTmpFrame ) )
return false;
if( rSelList.checkContext( this ) )
{
@@ -1831,7 +1911,7 @@ bool SwTextFrame::FillSelection( SwSelectionList& rSelList, const SwRect& rRect
}
else if( aRect.HasArea() )
{
- SwPosition aOld(aPosL.nNode.GetNodes().GetEndOfContent());
+ SwPosition aOld(aPosL.GetNodes().GetEndOfContent());
SwPosition aPosR( aPosL );
Point aPoint;
SwTextInfo aInf( const_cast<SwTextFrame*>(this) );
@@ -1924,7 +2004,7 @@ bool SwTextFrame::FillSelection( SwSelectionList& rSelList, const SwRect& rRect
const SwSortedObjs &rObjs = *GetDrawObjs();
for (SwAnchoredObject* pAnchoredObj : rObjs)
{
- const SwFlyFrame* pFly = dynamic_cast<const SwFlyFrame*>(pAnchoredObj);
+ const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
if( !pFly )
continue;
if( pFly->IsFlyInContentFrame() && pFly->FillSelection( rSelList, rRect ) )
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index d35159f12d84..046aa6bde1ea 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -45,6 +45,8 @@
#include "porhyph.hxx"
#include "pordrop.hxx"
#include "redlnitr.hxx"
+#include <sortedobjs.hxx>
+#include <fmtanchr.hxx>
#include <pagefrm.hxx>
#include <tgrditem.hxx>
#include <doc.hxx>
@@ -54,8 +56,18 @@
#include <IDocumentSettingAccess.hxx>
#include <IMark.hxx>
#include <IDocumentMarkAccess.hxx>
-
-#include <vector>
+#include <comphelper/processfactory.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <comphelper/string.hxx>
+#include <docsh.hxx>
+#include <unocrsrhelper.hxx>
+#include <textcontentcontrol.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>
using namespace ::com::sun::star;
@@ -143,6 +155,16 @@ sal_uInt16 SwTextFormatter::GetFrameRstHeight() const
return sal_uInt16( nHeight );
}
+bool SwTextFormatter::ClearIfIsFirstOfBorderMerge(const SwLinePortion* pPortion)
+{
+ if (pPortion == m_pFirstOfBorderMerge)
+ {
+ m_pFirstOfBorderMerge = nullptr;
+ return true;
+ }
+ return false;
+}
+
SwLinePortion *SwTextFormatter::Underflow( SwTextFormatInfo &rInf )
{
// Save values and initialize rInf
@@ -235,7 +257,7 @@ SwLinePortion *SwTextFormatter::Underflow( SwTextFormatInfo &rInf )
// line width is adjusted, so that pPor does not fit to current
// line anymore
- rInf.Width( static_cast<sal_uInt16>(rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0)) );
+ rInf.Width( rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0) );
rInf.SetLen( pPor->GetLen() );
rInf.SetFull( false );
if( pFly )
@@ -271,11 +293,8 @@ SwLinePortion *SwTextFormatter::Underflow( SwTextFormatInfo &rInf )
SwLinePortion* pNext = pPor->GetNextPortion();
while (pNext)
{
- if (pNext == m_pFirstOfBorderMerge)
- {
- m_pFirstOfBorderMerge = nullptr;
+ if (ClearIfIsFirstOfBorderMerge(pNext))
break;
- }
pNext = pNext->GetNextPortion();
}
pPor->Truncate();
@@ -327,6 +346,8 @@ void SwTextFormatter::InsertPortion( SwTextFormatInfo &rInf,
m_pCurr->Height( pPor->Height(), pPor->IsTextPortion() );
if( m_pCurr->GetAscent() < pPor->GetAscent() )
m_pCurr->SetAscent( pPor->GetAscent() );
+ if( m_pCurr->GetHangingBaseline() < pPor->GetHangingBaseline() )
+ m_pCurr->SetHangingBaseline( pPor->GetHangingBaseline() );
if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY))
{
@@ -385,7 +406,38 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
rInf.SetFull(true);
}
- SwLinePortion *pPor = NewPortion( rInf );
+ ::std::optional<TextFrameIndex> oMovedFlyIndex;
+ if (SwTextFrame const*const pFollow = GetTextFrame()->GetFollow())
+ {
+ // flys are always on master!
+ if (GetTextFrame()->GetDrawObjs() && pFollow->GetUpper() != GetTextFrame()->GetUpper())
+ {
+ for (SwAnchoredObject const*const pAnchoredObj : *GetTextFrame()->GetDrawObjs())
+ {
+ // tdf#146500 try to stop where a fly is anchored in the follow
+ // that has recently been moved (presumably by splitting this
+ // frame); similar to check in SwFlowFrame::MoveBwd()
+ if (pAnchoredObj->RestartLayoutProcess()
+ && !pAnchoredObj->IsTmpConsiderWrapInfluence())
+ {
+ SwFormatAnchor const& rAnchor(pAnchoredObj->GetFrameFormat()->GetAnchor());
+ assert(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA);
+ TextFrameIndex const nAnchor(GetTextFrame()->MapModelToViewPos(*rAnchor.GetContentAnchor()));
+ if (pFollow->GetOffset() <= nAnchor
+ && (pFollow->GetFollow() == nullptr
+ || nAnchor < pFollow->GetFollow()->GetOffset()))
+ {
+ if (!oMovedFlyIndex || nAnchor < *oMovedFlyIndex)
+ {
+ oMovedFlyIndex.emplace(nAnchor);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ SwLinePortion *pPor = NewPortion(rInf, oMovedFlyIndex);
// Asian grid stuff
SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
@@ -424,7 +476,7 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
{
SwFontScript nNxtActual = rInf.GetFont()->GetActual();
SwFontScript nLstActual = nNxtActual;
- sal_uInt16 nLstHeight = static_cast<sal_uInt16>(rInf.GetFont()->GetHeight());
+ sal_uInt16 nLstHeight = o3tl::narrowing<sal_uInt16>(rInf.GetFont()->GetHeight());
bool bAllowBehind = false;
const CharClass& rCC = GetAppCharClass();
@@ -467,7 +519,7 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
if ( pTmpFnt )
{
nLstActual = pTmpFnt->GetActual();
- nLstHeight = static_cast<sal_uInt16>(pTmpFnt->GetHeight());
+ nLstHeight = o3tl::narrowing<sal_uInt16>(pTmpFnt->GetHeight());
}
}
}
@@ -482,18 +534,29 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
nLstHeight /= 5;
// does the kerning portion still fit into the line?
if( bAllowBefore && ( nLstActual != nNxtActual ) &&
+ // tdf#89288 we want to insert space between CJK and non-CJK text only.
+ ( nLstActual == SwFontScript::CJK || nNxtActual == SwFontScript::CJK ) &&
nLstHeight && rInf.X() + nLstHeight <= rInf.Width() &&
! pPor->InTabGrp() )
{
SwKernPortion* pKrn =
new SwKernPortion( *rInf.GetLast(), nLstHeight,
pLast->InFieldGrp() && pPor->InFieldGrp() );
+
+ // ofz#58550 Direct-leak, pKrn adds itself as the NextPortion
+ // of rInf.GetLast(), but may use CopyLinePortion to add a copy
+ // of itself, which will then be left dangling with the following
+ // SetNextPortion(nullptr)
+ SwLinePortion *pNext = rInf.GetLast()->GetNextPortion();
+ if (pNext != pKrn)
+ delete pNext;
+
rInf.GetLast()->SetNextPortion( nullptr );
InsertPortion( rInf, pKrn );
}
}
}
- else if ( bHasGrid && pGrid->IsSnapToChars() && ! pGridKernPortion && ! m_pMulti && ! pPor->InTabGrp() )
+ else if ( bHasGrid && ! pGridKernPortion && ! m_pMulti && ! pPor->InTabGrp() )
{
// insert a grid kerning portion
pGridKernPortion = pPor->IsKernPortion() ?
@@ -528,7 +591,7 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
const SwTwips nRestWidth = rInf.Width() - rInf.X();
if ( nKernWidth <= nRestWidth )
- pGridKernPortion->Width( static_cast<sal_uInt16>(nKernWidth) );
+ pGridKernPortion->Width( nKernWidth );
}
if ( pGridKernPortion != pPor )
@@ -615,7 +678,7 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
(m_pScriptInfo->ScriptType(nTmp - TextFrameIndex(1)) == css::i18n::ScriptType::ASIAN ||
m_pScriptInfo->ScriptType(nTmp) == css::i18n::ScriptType::ASIAN) )
{
- const sal_uInt16 nDist = static_cast<sal_uInt16>(rInf.GetFont()->GetHeight()/5);
+ const SwTwips nDist = rInf.GetFont()->GetHeight()/5;
if( nDist )
{
@@ -636,7 +699,7 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
}
}
- if ( bHasGrid && pGrid->IsSnapToChars() && pPor != pGridKernPortion && ! m_pMulti && ! pPor->InTabGrp() )
+ if ( bHasGrid && pPor != pGridKernPortion && ! m_pMulti && ! pPor->InTabGrp() )
{
TextFrameIndex const nTmp = rInf.GetIdx() + pPor->GetLen();
const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width();
@@ -670,15 +733,18 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
0;
const SwTwips nTmpWidth = i * nGridWidth;
const SwTwips nKernWidth = std::min(nTmpWidth - nSumWidth, nRestWidth);
- const sal_uInt16 nKernWidth_1 = static_cast<sal_uInt16>(nKernWidth / 2);
+ const SwTwips nKernWidth_1 = pGrid->IsSnapToChars() ?
+ nKernWidth / 2 : 0;
OSL_ENSURE( nKernWidth <= nRestWidth,
"Not enough space left for adjusting non-asian text in grid mode" );
+ if (nKernWidth_1)
+ {
+ pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 );
+ rInf.X( rInf.X() + nKernWidth_1 );
+ }
- pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 );
- rInf.X( rInf.X() + nKernWidth_1 );
-
- if ( ! bFull )
+ if ( ! bFull && nKernWidth - nKernWidth_1 > 0 )
new SwKernPortion( *pPor, static_cast<short>(nKernWidth - nKernWidth_1),
false, true );
@@ -704,7 +770,7 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
{
(void) rInf.CheckCurrentPosBookmark(); // bookmark was already created inside MultiPortion!
}
- pPor = NewPortion( rInf );
+ pPor = NewPortion(rInf, oMovedFlyIndex);
}
if( !rInf.IsStop() )
@@ -786,6 +852,7 @@ void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor )
// In empty lines the attributes are switched on via SeekStart
const bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
+
if ( pPor->IsQuoVadisPortion() )
bChg = SeekStartAndChg( rInf, true );
else
@@ -794,10 +861,16 @@ void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor )
{
if( !rInf.GetText().isEmpty() )
{
- if ( pPor->GetLen() || !rInf.GetIdx()
- || ( m_pCurr != pLast && !pLast->IsFlyPortion() )
- || !m_pCurr->IsRest() ) // instead of !rInf.GetRest()
+ if ((rInf.GetIdx() != TextFrameIndex(rInf.GetText().getLength())
+ || rInf.GetRest() // field continued - not empty
+ || !GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::APPLY_TEXT_ATTR_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH))
+ && (pPor->GetLen() || !rInf.GetIdx()
+ || (m_pCurr != pLast && !pLast->IsFlyPortion())
+ || !m_pCurr->IsRest())) // instead of !rInf.GetRest()
+ {
bChg = SeekAndChg( rInf );
+ }
else
bChg = SeekAndChgBefore( rInf );
}
@@ -817,6 +890,7 @@ void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor )
if( bChg || bFirstPor || !pPor->GetAscent()
|| !rInf.GetLast()->InTextGrp() )
{
+ pPor->SetHangingBaseline( rInf.GetHangingBaseline() );
pPor->SetAscent( rInf.GetAscent() );
pPor->Height( rInf.GetTextHeight() );
bCalc = true;
@@ -842,32 +916,248 @@ namespace {
class SwMetaPortion : public SwTextPortion
{
+ Color m_aShadowColor;
public:
SwMetaPortion() { SetWhichPor( PortionType::Meta ); }
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ void SetShadowColor(const Color& rCol ) { m_aShadowColor = rCol; }
};
+/// A content control portion is a text portion that is inside RES_TXTATR_CONTENTCONTROL.
+class SwContentControlPortion : public SwTextPortion
+{
+ SwTextContentControl* m_pTextContentControl;
+public:
+ SwContentControlPortion(SwTextContentControl* pTextContentControl);
+ virtual void Paint(const SwTextPaintInfo& rInf) const override;
+
+ /// Emits a PDF form widget for this portion on success, does nothing on failure.
+ bool DescribePDFControl(const SwTextPaintInfo& rInf) const;
+};
}
void SwMetaPortion::Paint( const SwTextPaintInfo &rInf ) const
{
if ( Width() )
{
- rInf.DrawViewOpt( *this, PortionType::Meta );
+ rInf.DrawViewOpt( *this, PortionType::Meta,
+ // custom shading (RDF metadata)
+ COL_BLACK == m_aShadowColor
+ ? nullptr
+ : &m_aShadowColor );
+
SwTextPortion::Paint( rInf );
}
}
+SwContentControlPortion::SwContentControlPortion(SwTextContentControl* pTextContentControl)
+ : m_pTextContentControl(pTextContentControl)
+{
+ SetWhichPor(PortionType::ContentControl);
+}
+
+bool SwContentControlPortion::DescribePDFControl(const SwTextPaintInfo& rInf) const
+{
+ auto pPDFExtOutDevData = dynamic_cast<vcl::PDFExtOutDevData*>(rInf.GetOut()->GetExtOutDevData());
+ if (!pPDFExtOutDevData)
+ {
+ return false;
+ }
+
+ if (!pPDFExtOutDevData->GetIsExportFormFields())
+ {
+ return false;
+ }
+
+ if (!m_pTextContentControl)
+ {
+ return false;
+ }
+
+ const SwFormatContentControl& rFormatContentControl = m_pTextContentControl->GetContentControl();
+ const std::shared_ptr<SwContentControl>& pContentControl = rFormatContentControl.GetContentControl();
+ if (!pContentControl)
+ {
+ return false;
+ }
+
+ SwTextNode* pTextNode = pContentControl->GetTextNode();
+ SwDoc& rDoc = pTextNode->GetDoc();
+ if (rDoc.IsInHeaderFooter(*pTextNode))
+ {
+ // Form control in header/footer makes no sense, would allow multiple values for the same
+ // control.
+ return false;
+ }
+
+ // Check if this is the first content control portion of this content control.
+ sal_Int32 nStart = m_pTextContentControl->GetStart();
+ sal_Int32 nEnd = *m_pTextContentControl->GetEnd();
+ TextFrameIndex nViewStart = rInf.GetTextFrame()->MapModelToView(pTextNode, nStart);
+ TextFrameIndex nViewEnd = rInf.GetTextFrame()->MapModelToView(pTextNode, nEnd);
+ // The content control portion starts 1 char after the starting dummy character.
+ if (rInf.GetIdx() != nViewStart + TextFrameIndex(1))
+ {
+ // Ignore: don't process and also don't emit plain text fallback.
+ return true;
+ }
+
+ const SwPaM aPam(*pTextNode, nEnd, *pTextNode, nStart);
+ static sal_Unicode const aForbidden[] = {
+ CH_TXTATR_BREAKWORD,
+ 0
+ };
+ const OUString aText = comphelper::string::removeAny(aPam.GetText(), aForbidden);
+
+ std::unique_ptr<vcl::PDFWriter::AnyWidget> pDescriptor;
+ switch (pContentControl->GetType())
+ {
+ case SwContentControlType::RICH_TEXT:
+ case SwContentControlType::PLAIN_TEXT:
+ {
+ pDescriptor = std::make_unique<vcl::PDFWriter::EditWidget>();
+ auto pEditWidget = static_cast<vcl::PDFWriter::EditWidget*>(pDescriptor.get());
+ pEditWidget->MultiLine = true;
+ break;
+ }
+ case SwContentControlType::CHECKBOX:
+ {
+ pDescriptor = std::make_unique<vcl::PDFWriter::CheckBoxWidget>();
+ auto pCheckBoxWidget = static_cast<vcl::PDFWriter::CheckBoxWidget*>(pDescriptor.get());
+ pCheckBoxWidget->Checked = pContentControl->GetChecked();
+ // If it's checked already, then leave the default "Yes" OnValue unchanged, so the
+ // appropriate appearance is found by PDF readers.
+ if (!pCheckBoxWidget->Checked)
+ {
+ pCheckBoxWidget->OnValue = pContentControl->GetCheckedState();
+ pCheckBoxWidget->OffValue = pContentControl->GetUncheckedState();
+ }
+ break;
+ }
+ case SwContentControlType::DROP_DOWN_LIST:
+ {
+ pDescriptor = std::make_unique<vcl::PDFWriter::ListBoxWidget>();
+ auto pListWidget = static_cast<vcl::PDFWriter::ListBoxWidget*>(pDescriptor.get());
+ pListWidget->DropDown = true;
+ sal_Int32 nIndex = 0;
+ for (const auto& rItem : pContentControl->GetListItems())
+ {
+ pListWidget->Entries.push_back(rItem.m_aDisplayText);
+ if (rItem.m_aDisplayText == aText)
+ pListWidget->SelectedEntries.push_back(nIndex);
+ ++nIndex;
+ }
+ break;
+ }
+ case SwContentControlType::COMBO_BOX:
+ {
+ pDescriptor = std::make_unique<vcl::PDFWriter::ComboBoxWidget>();
+ auto pComboWidget = static_cast<vcl::PDFWriter::ComboBoxWidget*>(pDescriptor.get());
+ for (const auto& rItem : pContentControl->GetListItems())
+ {
+ pComboWidget->Entries.push_back(rItem.m_aDisplayText);
+ }
+ break;
+ }
+ case SwContentControlType::DATE:
+ {
+ pDescriptor = std::make_unique<vcl::PDFWriter::EditWidget>();
+ auto pEditWidget = static_cast<vcl::PDFWriter::EditWidget*>(pDescriptor.get());
+ pEditWidget->Format = vcl::PDFWriter::Date;
+ // GetDateFormat() uses a syntax that works with SvNumberFormatter::PutEntry(), PDF's
+ // AFDate_FormatEx() uses a similar syntax, but uses lowercase characters in case of
+ // "Y", "M" and "D" at least.
+ pEditWidget->DateFormat = pContentControl->GetDateFormat().toAsciiLowerCase();
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!pDescriptor)
+ {
+ return false;
+ }
+
+ const SwFont* pFont = rInf.GetFont();
+ if (pFont)
+ {
+ pDescriptor->TextFont = pFont->GetActualFont();
+ }
+
+ // Description for accessibility purposes.
+ if (!pContentControl->GetAlias().isEmpty())
+ {
+ pDescriptor->Description = pContentControl->GetAlias();
+ }
+
+ // Map the text of the content control to the descriptor's text.
+ pDescriptor->Text = aText;
+
+ // Calculate the bounding rectangle of this content control, which can be one or more layout
+ // portions in one or more lines.
+ SwRect aLocation;
+ auto pTextFrame = const_cast<SwTextFrame*>(rInf.GetTextFrame());
+ SwTextSizeInfo aInf(pTextFrame);
+ SwTextCursor aLine(pTextFrame, &aInf);
+ SwRect aStartRect, aEndRect;
+ aLine.GetCharRect(&aStartRect, nViewStart);
+ aLine.GetCharRect(&aEndRect, nViewEnd);
+
+ // Handling RTL text direction
+ if(rInf.GetTextFrame()->IsRightToLeft())
+ {
+ rInf.GetTextFrame()->SwitchLTRtoRTL( aStartRect );
+ rInf.GetTextFrame()->SwitchLTRtoRTL( aEndRect );
+ }
+ // TODO: handle rInf.GetTextFrame()->IsVertical()
+
+ aLocation = aStartRect;
+ aLocation.Union(aEndRect);
+ pDescriptor->Location = aLocation.SVRect();
+
+ pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Form);
+ pPDFExtOutDevData->CreateControl(*pDescriptor);
+ pPDFExtOutDevData->EndStructureElement();
+
+ return true;
+}
+
+void SwContentControlPortion::Paint(const SwTextPaintInfo& rInf) const
+{
+ if (Width())
+ {
+ rInf.DrawViewOpt(*this, PortionType::ContentControl);
+
+ if (DescribePDFControl(rInf))
+ {
+ return;
+ }
+
+ SwTextPortion::Paint(rInf);
+ }
+}
+
namespace sw::mark {
OUString ExpandFieldmark(IFieldmark* pBM)
{
+ if (pBM->GetFieldname() == ODF_FORMCHECKBOX)
+ {
+ ::sw::mark::ICheckboxFieldmark const*const pCheckboxFm(
+ dynamic_cast<ICheckboxFieldmark const*>(pBM));
+ assert(pCheckboxFm);
+ return pCheckboxFm->IsChecked()
+ ? u"\u2612"_ustr
+ : u"\u2610"_ustr;
+ }
+ assert(pBM->GetFieldname() == ODF_FORMDROPDOWN);
const IFieldmark::parameter_map_t* const pParameters = pBM->GetParameters();
sal_Int32 nCurrentIdx = 0;
- const IFieldmark::parameter_map_t::const_iterator pResult = pParameters->find(OUString(ODF_FORMDROPDOWN_RESULT));
+ const IFieldmark::parameter_map_t::const_iterator pResult = pParameters->find(ODF_FORMDROPDOWN_RESULT);
if(pResult != pParameters->end())
pResult->second >>= nCurrentIdx;
- const IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(OUString(ODF_FORMDROPDOWN_LISTENTRY));
+ const IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
if (pListEntries != pParameters->end())
{
uno::Sequence< OUString > vListEntries;
@@ -892,7 +1182,6 @@ SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const
{
if (rInf.GetOpt().IsFieldName())
{
- OUString aFieldName = SwFieldType::GetTypeStr(SwFieldTypesEnum::Input);
// assume this is only the *first* portion and follows will be created elsewhere => input field must start at Idx
assert(rInf.GetText()[sal_Int32(rInf.GetIdx())] == CH_TXT_ATR_INPUTFIELDSTART);
TextFrameIndex nFieldLen(-1);
@@ -906,7 +1195,7 @@ SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const
}
}
assert(2 <= sal_Int32(nFieldLen));
- pPor = new SwFieldPortion(aFieldName, nullptr, false, nFieldLen);
+ pPor = new SwFieldPortion(SwFieldType::GetTypeStr(SwFieldTypesEnum::Input), nullptr, nFieldLen);
}
else
{
@@ -919,7 +1208,66 @@ SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const
pPor = new SwRefPortion;
else if (GetFnt()->IsMeta())
{
- pPor = new SwMetaPortion;
+ auto pMetaPor = new SwMetaPortion;
+
+ // set custom LO_EXT_SHADING color, if it exists
+ SwTextFrame const*const pFrame(rInf.GetTextFrame());
+ SwPosition aPosition(pFrame->MapViewToModelPos(rInf.GetIdx()));
+ SwPaM aPam(aPosition);
+ uno::Reference<text::XTextContent> const xRet(
+ SwUnoCursorHelper::GetNestedTextContent(
+ *aPam.GetPointNode().GetTextNode(), aPosition.GetContentIndex(), false) );
+ if (xRet.is())
+ {
+ const SwDoc & rDoc = rInf.GetTextFrame()->GetDoc();
+ 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 css::uno::Reference<css::rdf::XResource> xSubject(xRet, 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);
+
+ while (xEnum->hasMoreElements())
+ {
+ rdf::Statement stmt;
+ if (!(xEnum->nextElement() >>= stmt)) {
+ throw uno::RuntimeException();
+ }
+ const uno::Reference<rdf::XLiteral> xObject(stmt.Object, uno::UNO_QUERY);
+ if (!xObject.is()) continue;
+ if (xEnum->hasMoreElements()) {
+ SAL_INFO("sw.uno", "ignoring other odf:shading statements");
+ }
+ Color rColor = Color::STRtoRGB(xObject->getValue());
+ pMetaPor->SetShadowColor(rColor);
+ break;
+ }
+ }
+ pPor = pMetaPor;
+ }
+ else if (GetFnt()->IsContentControl())
+ {
+ SwTextFrame const*const pFrame(rInf.GetTextFrame());
+ SwPosition aPosition(pFrame->MapViewToModelPos(rInf.GetIdx()));
+ SwTextNode* pTextNode = aPosition.GetNode().GetTextNode();
+ SwTextContentControl* pTextContentControl = nullptr;
+ if (pTextNode)
+ {
+ sal_Int32 nIndex = aPosition.GetContentIndex();
+ if (SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent))
+ {
+ pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ }
+ }
+ pPor = new SwContentControlPortion(pTextContentControl);
}
else
{
@@ -927,10 +1275,10 @@ SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const
// If pCurr does not have a width, it can however already have content.
// E.g. for non-displayable characters
- auto const ch(rInf.GetText()[sal_Int32(rInf.GetIdx())]);
+ auto const ch(rInf.GetChar(rInf.GetIdx()));
SwTextFrame const*const pFrame(rInf.GetTextFrame());
SwPosition aPosition(pFrame->MapViewToModelPos(rInf.GetIdx()));
- sw::mark::IFieldmark *pBM = pFrame->GetDoc().getIDocumentMarkAccess()->getFieldmarkFor(aPosition);
+ sw::mark::IFieldmark *pBM = pFrame->GetDoc().getIDocumentMarkAccess()->getInnerFieldmarkFor(aPosition);
if(pBM != nullptr && pBM->GetFieldname( ) == ODF_FORMDATE)
{
if (ch == CH_TXT_ATR_FIELDSTART)
@@ -970,7 +1318,7 @@ SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const
}
if( !pPor )
{
- if( !rInf.X() && !m_pCurr->GetNextPortion() && !m_pCurr->GetLen() )
+ if( !rInf.X() && !m_pCurr->GetNextPortion() && !m_pCurr->GetLen() && !GetFnt()->IsURL() )
pPor = m_pCurr;
else
pPor = new SwTextPortion;
@@ -993,25 +1341,20 @@ SwTextPortion *SwTextFormatter::NewTextPortion( SwTextFormatInfo &rInf )
Seek( rInf.GetIdx() );
SwTextPortion *pPor = WhichTextPor( rInf );
+ TextFrameIndex nNextChg(rInf.GetText().getLength());
+
// until next attribute change:
const TextFrameIndex nNextAttr = GetNextAttr();
- TextFrameIndex nNextChg = std::min(nNextAttr, TextFrameIndex(rInf.GetText().getLength()));
-
// end of script type:
const TextFrameIndex nNextScript = m_pScriptInfo->NextScriptChg(rInf.GetIdx());
- nNextChg = std::min( nNextChg, nNextScript );
-
// end of direction:
const TextFrameIndex nNextDir = m_pScriptInfo->NextDirChg(rInf.GetIdx());
- nNextChg = std::min( nNextChg, nNextDir );
-
// hidden change (potentially via bookmark):
const TextFrameIndex nNextHidden = m_pScriptInfo->NextHiddenChg(rInf.GetIdx());
- nNextChg = std::min( nNextChg, nNextHidden );
-
// bookmarks
const TextFrameIndex nNextBookmark = m_pScriptInfo->NextBookmark(rInf.GetIdx());
- nNextChg = std::min(nNextChg, nNextBookmark);
+
+ nNextChg = std::min({ nNextChg, nNextAttr, nNextScript, nNextDir, nNextHidden, nNextBookmark });
// Turbo boost:
// We assume that font characters are not larger than twice
@@ -1030,13 +1373,12 @@ SwTextPortion *SwTextFormatter::NewTextPortion( SwTextFormatInfo &rInf )
CalcAscent( rInf, pPor );
const SwFont* pTmpFnt = rInf.GetFont();
- sal_Int32 nExpect = std::min( sal_Int32( pTmpFnt->GetHeight() ),
- sal_Int32( pPor->GetAscent() ) ) / 8;
- if ( !nExpect )
- nExpect = 1;
- nExpect = sal_Int32(rInf.GetIdx()) + (rInf.GetLineWidth() / nExpect);
- if (TextFrameIndex(nExpect) > rInf.GetIdx() && nNextChg > TextFrameIndex(nExpect))
- nNextChg = TextFrameIndex(std::min(nExpect, rInf.GetText().getLength()));
+ auto nCharWidthGuess = std::min(pTmpFnt->GetHeight(), pPor->GetAscent()) / 8;
+ if (!nCharWidthGuess)
+ nCharWidthGuess = 1;
+ auto nExpect = rInf.GetIdx() + TextFrameIndex(rInf.GetLineWidth() / nCharWidthGuess);
+ if (nExpect > rInf.GetIdx() && nNextChg > nExpect)
+ nNextChg = nExpect;
// we keep an invariant during method calls:
// there are no portion ending characters like hard spaces
@@ -1196,32 +1538,13 @@ SwLinePortion *SwTextFormatter::WhichFirstPortion(SwTextFormatInfo &rInf)
// check this *last* so that BuildMultiPortion() can find it!
if (!pPor && rInf.CheckCurrentPosBookmark())
{
- auto const bookmark(m_pScriptInfo->GetBookmark(rInf.GetIdx()));
- if (static_cast<bool>(bookmark))
+ const auto& bookmark = m_pScriptInfo->GetBookmarks(rInf.GetIdx());
+ if (!bookmark.empty())
{
- sal_Unicode mark;
- if ((bookmark & (SwScriptInfo::MarkKind::Start|SwScriptInfo::MarkKind::End))
- == (SwScriptInfo::MarkKind::Start|SwScriptInfo::MarkKind::End))
- {
- //mark = u'\u2336'; // not in OpenSymbol :(
- mark = '|';
- // hmm ... paint U+2345 over U+2346 should be same width?
- // and U+237F // or U+2E20/U+2E21
- }
- else if (bookmark & SwScriptInfo::MarkKind::Start)
- {
- mark = '[';
- }
- else if (bookmark & SwScriptInfo::MarkKind::End)
- {
- mark = ']';
- }
- else
- {
- assert(bookmark & SwScriptInfo::MarkKind::Point);
- mark = '|';
- }
- pPor = new SwBookmarkPortion(mark);
+ // only for character width, maybe replaced with ] later
+ sal_Unicode mark = '[';
+
+ pPor = new SwBookmarkPortion(mark, bookmark);
}
}
@@ -1256,8 +1579,16 @@ static bool lcl_OldFieldRest( const SwLineLayout* pCurr )
* -> CalcFlyWidth emulates the width and return portion, if needed
*/
-SwLinePortion *SwTextFormatter::NewPortion( SwTextFormatInfo &rInf )
+SwLinePortion *SwTextFormatter::NewPortion(SwTextFormatInfo &rInf,
+ ::std::optional<TextFrameIndex> const oMovedFlyIndex)
{
+ if (oMovedFlyIndex && *oMovedFlyIndex <= rInf.GetIdx())
+ {
+ SAL_WARN_IF(*oMovedFlyIndex != rInf.GetIdx(), "sw.core", "stopping too late, no portion break at fly anchor?");
+ rInf.SetStop(true);
+ return nullptr;
+ }
+
// Underflow takes precedence
rInf.SetStopUnderflow( false );
if( rInf.GetUnderflow() )
@@ -1335,7 +1666,7 @@ SwLinePortion *SwTextFormatter::NewPortion( SwTextFormatInfo &rInf )
// We open a multiportion part, if we enter a multi-line part
// of the paragraph.
TextFrameIndex nEnd = rInf.GetIdx();
- std::unique_ptr<SwMultiCreator> pCreate = rInf.GetMultiCreator( nEnd, m_pMulti );
+ std::optional<SwMultiCreator> pCreate = rInf.GetMultiCreator( nEnd, m_pMulti );
if( pCreate )
{
SwMultiPortion* pTmp = nullptr;
@@ -1394,7 +1725,11 @@ SwLinePortion *SwTextFormatter::NewPortion( SwTextFormatInfo &rInf )
pPor = NewTabPortion( rInf, false ); break;
case CH_BREAK:
- pPor = new SwBreakPortion( *rInf.GetLast() ); break;
+ {
+ SwTextAttr* pHint = GetAttr(rInf.GetIdx());
+ pPor = new SwBreakPortion(*rInf.GetLast(), pHint);
+ break;
+ }
case CHAR_SOFTHYPHEN: // soft hyphen
pPor = new SwSoftHyphPortion; break;
@@ -1431,7 +1766,7 @@ SwLinePortion *SwTextFormatter::NewPortion( SwTextFormatInfo &rInf )
PortionType::TabDecimal == pLastTabPortion->GetWhichPor() )
{
OSL_ENSURE( rInf.X() >= pLastTabPortion->GetFix(), "Decimal tab stop position cannot be calculated" );
- const sal_uInt16 nWidthOfPortionsUpToDecimalPosition = static_cast<sal_uInt16>(rInf.X() - pLastTabPortion->GetFix() );
+ const sal_uInt16 nWidthOfPortionsUpToDecimalPosition = o3tl::narrowing<sal_uInt16>(rInf.X() - pLastTabPortion->GetFix() );
static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition );
rInf.SetTabDecimal( 0 );
}
@@ -1494,11 +1829,9 @@ SwLinePortion *SwTextFormatter::NewPortion( SwTextFormatInfo &rInf )
pInfo = &pDoc->GetFootnoteInfo();
const SwAttrSet& rSet = pInfo->GetAnchorCharFormat(const_cast<SwDoc&>(*pDoc))->GetAttrSet();
- const SfxPoolItem* pItem;
Degree10 nDir(0);
- if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_ROTATE,
- true, &pItem ))
- nDir = static_cast<const SvxCharRotateItem*>(pItem)->GetValue();
+ if( const SvxCharRotateItem* pItem = rSet.GetItemIfSet( RES_CHRATR_ROTATE ) )
+ nDir = pItem->GetValue();
if ( nDir )
{
@@ -1574,11 +1907,12 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
// Recycling must be suppressed by changed line height and also
// by changed ascent (lowering of baseline).
- const sal_uInt16 nOldHeight = m_pCurr->Height();
- const sal_uInt16 nOldAscent = m_pCurr->GetAscent();
+ const SwTwips nOldHeight = m_pCurr->Height();
+ const SwTwips nOldAscent = m_pCurr->GetAscent();
m_pCurr->SetEndHyph( false );
m_pCurr->SetMidHyph( false );
+ m_pCurr->SetLastHyph( false );
// fly positioning can make it necessary format a line several times
// for this, we have to keep a copy of our rest portion
@@ -1618,6 +1952,7 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
// These values must not be reset by FormatReset();
const bool bOldNumDone = GetInfo().IsNumDone();
+ const bool bOldFootnoteDone = GetInfo().IsFootnoteDone();
const bool bOldArrowDone = GetInfo().IsArrowDone();
const bool bOldErgoDone = GetInfo().IsErgoDone();
@@ -1625,6 +1960,7 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
FormatReset( GetInfo() );
GetInfo().SetNumDone( bOldNumDone );
+ GetInfo().SetFootnoteDone(bOldFootnoteDone);
GetInfo().SetArrowDone( bOldArrowDone );
GetInfo().SetErgoDone( bOldErgoDone );
@@ -1636,6 +1972,14 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
m_pCurr->SetLen(TextFrameIndex(0));
m_pCurr->Height( GetFrameRstHeight() + 1, false );
m_pCurr->SetRealHeight( GetFrameRstHeight() + 1 );
+
+ // Don't oversize the line in case of split flys, so we don't try to move the anchor
+ // of a precede fly forward, next to its follow.
+ if (m_pFrame->HasNonLastSplitFlyDrawObj())
+ {
+ m_pCurr->SetRealHeight(GetFrameRstHeight());
+ }
+
m_pCurr->Width(0);
m_pCurr->Truncate();
return nStartPos;
@@ -1655,7 +1999,7 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
if ( IsFlyInCntBase() && (!IsQuick() || (pPorTmp && pPorTmp->IsFlyCntPortion() && !pPorTmp->GetNextPortion() &&
m_pCurr->Height() > pPorTmp->Height())))
{
- sal_uInt16 nTmpAscent, nTmpHeight;
+ SwTwips nTmpAscent, nTmpHeight;
CalcAscentAndHeight( nTmpAscent, nTmpHeight );
AlignFlyInCntBase( Y() + tools::Long( nTmpAscent ) );
m_pCurr->CalcLine( *this, GetInfo() );
@@ -1674,7 +2018,15 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
|| GetInfo().CheckFootnotePortion(m_pCurr);
if( bBuild )
{
- GetInfo().SetNumDone( bOldNumDone );
+ // fdo44018-2.doc: only restore m_bNumDone if a SwNumberPortion will be truncated
+ for (SwLinePortion * pPor = m_pCurr->GetNextPortion(); pPor; pPor = pPor->GetNextPortion())
+ {
+ if (pPor->InNumberGrp())
+ {
+ GetInfo().SetNumDone( bOldNumDone );
+ break;
+ }
+ }
GetInfo().ResetMaxWidthDiff();
// delete old rest
@@ -1772,7 +2124,7 @@ void SwTextFormatter::RecalcRealHeight()
void SwTextFormatter::CalcRealHeight( bool bNewLine )
{
- sal_uInt16 nLineHeight = m_pCurr->Height();
+ SwTwips nLineHeight = m_pCurr->Height();
m_pCurr->SetClipping( false );
SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
@@ -1806,7 +2158,7 @@ void SwTextFormatter::CalcRealHeight( bool bNewLine )
nTmp = 100;
nTmp *= nLineHeight;
- nLineHeight = static_cast<sal_uInt16>(nTmp / 100);
+ nLineHeight = nTmp / 100;
}
m_pCurr->SetRealHeight( nLineHeight );
@@ -1841,7 +2193,7 @@ void SwTextFormatter::CalcRealHeight( bool bNewLine )
nTmp /= 100;
if( !nTmp )
++nTmp;
- nLineHeight = static_cast<sal_uInt16>(nTmp);
+ nLineHeight = nTmp;
sal_uInt16 nAsc = ( 4 * nLineHeight ) / 5; // 80%
#if 0
// could do clipping here (like Word does)
@@ -1899,7 +2251,7 @@ void SwTextFormatter::CalcRealHeight( bool bNewLine )
nTmp += nLineHeight;
if (nTmp < 1)
nTmp = 1;
- nLineHeight = static_cast<sal_uInt16>(nTmp);
+ nLineHeight = nTmp;
break;
}
case SvxInterLineSpaceRule::Fix:
@@ -2099,8 +2451,8 @@ void SwTextFormatter::UpdatePos( SwLineLayout *pCurrent, Point aStart,
SwTwips nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc;
pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc );
- const sal_uInt16 nTmpHeight = pCurrent->GetRealHeight();
- sal_uInt16 nAscent = pCurrent->GetAscent() + nTmpHeight - pCurrent->Height();
+ const SwTwips nTmpHeight = pCurrent->GetRealHeight();
+ SwTwips nAscent = pCurrent->GetAscent() + nTmpHeight - pCurrent->Height();
AsCharFlags nFlags = AsCharFlags::UlSpace;
if( GetMulti() )
{
@@ -2285,7 +2637,7 @@ bool SwTextFormatter::ChkFlyUnderflow( SwTextFormatInfo &rInf ) const
// New flys from below?
if( !pPos->IsFlyPortion() )
{
- if( aInter.IsOver( aLine ) )
+ if( aInter.Overlaps( aLine ) )
{
aInter.Intersection_( aLine );
if( aInter.HasArea() )
@@ -2302,7 +2654,7 @@ bool SwTextFormatter::ChkFlyUnderflow( SwTextFormatInfo &rInf ) const
else
{
// The fly portion is not intersected by a fly anymore
- if ( ! aInter.IsOver( aLine ) )
+ if ( ! aInter.Overlaps( aLine ) )
{
rInf.SetLineHeight( nHeight );
rInf.SetLineNetHeight( m_pCurr->Height() );
@@ -2378,17 +2730,34 @@ void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
SwRect aLine( rInf.X() + nLeftMin, nTop, rInf.RealWidth() - rInf.X()
+ nLeftMar - nLeftMin , nHeight );
+ bool bWordFlyWrap = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
// tdf#116486: consider also the upper margin from getFramePrintArea because intersections
// with this additional space should lead to repositioning of paragraphs
// For compatibility we grab a related compat flag:
- if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS)
- && IsFirstTextLine())
+ if (bWordFlyWrap && IsFirstTextLine())
{
- const tools::Long nUpper = m_pFrame->getFramePrintArea().Top();
+ tools::Long nUpper = m_pFrame->getFramePrintArea().Top();
+ // Make sure that increase only happens in case the upper spacing comes from the upper
+ // margin of the current text frame, not because of a lower spacing of the previous text
+ // frame.
+ nUpper -= m_pFrame->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
// Increase the rectangle
if( nUpper > 0 && nTop >= nUpper )
aLine.SubTop( nUpper );
}
+
+ if (IsFirstTextLine())
+ {
+ // Check if a compatibility mode requires considering also the lower margin of this text
+ // frame, intersections with this additional space should lead to shifting the paragraph
+ // marker down.
+ SwTwips nLower = m_pFrame->GetLowerMarginForFlyIntersect();
+ if (nLower > 0)
+ {
+ aLine.AddBottom(nLower);
+ }
+ }
+
SwRect aLineVert( aLine );
if ( m_pFrame->IsRightToLeft() )
m_pFrame->SwitchLTRtoRTL( aLineVert );
@@ -2413,23 +2782,52 @@ void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
aInter.Height(0);
}
- if( !aInter.IsOver( aLine ) )
+ if( !aInter.Overlaps( aLine ) )
return;
aLine.Left( rInf.X() + nLeftMar );
bool bForced = false;
+ bool bSplitFly = false;
+ for (const auto& pObj : rTextFly.GetAnchoredObjList())
+ {
+ auto pFlyFrame = pObj->DynCastFlyFrame();
+ if (!pFlyFrame)
+ {
+ continue;
+ }
+
+ if (!pFlyFrame->IsFlySplitAllowed())
+ {
+ continue;
+ }
+
+ bSplitFly = true;
+ break;
+ }
if( aInter.Left() <= nLeftMin )
{
SwTwips nFrameLeft = GetTextFrame()->getFrameArea().Left();
- if( GetTextFrame()->getFramePrintArea().Left() < 0 )
+ SwTwips nFramePrintAreaLeft = GetTextFrame()->getFramePrintArea().Left();
+ if( nFramePrintAreaLeft < 0 )
nFrameLeft += GetTextFrame()->getFramePrintArea().Left();
if( aInter.Left() < nFrameLeft )
- aInter.Left( nFrameLeft );
+ {
+ aInter.Left(nFrameLeft); // both sets left and reduces width
+ if (bSplitFly && nFramePrintAreaLeft > 0 && nFramePrintAreaLeft < aInter.Width())
+ {
+ // We wrap around a split fly, the fly portion is on the
+ // left of the paragraph and we have a positive
+ // paragraph margin. Don't take space twice in this case
+ // (margin, fly portion), decrease the width of the fly
+ // portion accordingly.
+ aInter.Right(aInter.Right() - nFramePrintAreaLeft);
+ }
+ }
tools::Long nAddMar = 0;
- if ( m_pFrame->IsRightToLeft() )
+ if (GetTextFrame()->IsRightToLeft())
{
- nAddMar = m_pFrame->getFrameArea().Right() - Right();
+ nAddMar = GetTextFrame()->getFrameArea().Right() - Right();
if ( nAddMar < 0 )
nAddMar = 0;
}
@@ -2447,8 +2845,22 @@ void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
if( !aInter.HasArea() )
return;
- const bool bFullLine = aLine.Left() == aInter.Left() &&
+ bool bFullLine = aLine.Left() == aInter.Left() &&
aLine.Right() == aInter.Right();
+ if (!bFullLine && bWordFlyWrap && !GetTextFrame()->IsInTab())
+ {
+ // Word style: if there is minimal space remaining, then handle that similar to a full line
+ // and put the actual empty paragraph below the fly.
+ SwTwips nLimit = MINLAY;
+ if (bSplitFly)
+ {
+ // We wrap around a floating table, that has a larger minimal wrap distance.
+ nLimit = TEXT_MIN_SMALL;
+ }
+
+ bFullLine = std::abs(aLine.Left() - aInter.Left()) < nLimit
+ && std::abs(aLine.Right() - aInter.Right()) < nLimit;
+ }
// Although no text is left, we need to format another line,
// because also empty lines need to avoid a Fly with no wrapping.
@@ -2465,7 +2877,7 @@ void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
if( bForced )
{
m_pCurr->SetForcedLeftMargin();
- rInf.ForcedLeftMargin( static_cast<sal_uInt16>(aInter.Width()) );
+ rInf.ForcedLeftMargin( o3tl::narrowing<sal_uInt16>(aInter.Width()) );
}
if( bFullLine )
@@ -2481,7 +2893,7 @@ void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
// created: here and in MakeFlyDummies.
// IsDummy() is evaluated in IsFirstTextLine(), when moving lines
// and in relation with DropCaps.
- pFly->Height( sal_uInt16(aInter.Height()) );
+ pFly->Height( aInter.Height() );
// nNextTop now contains the margin's bottom edge, which we avoid
// or the next margin's top edge, which we need to respect.
@@ -2494,10 +2906,10 @@ void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
{
SwTwips nH = nNextTop - aInter.Top();
if( nH < SAL_MAX_UINT16 )
- pFly->Height( sal_uInt16( nH ) );
+ pFly->Height( nH );
}
if( nAscent < pFly->Height() )
- pFly->SetAscent( sal_uInt16(nAscent) );
+ pFly->SetAscent( nAscent );
else
pFly->SetAscent( pFly->Height() );
}
@@ -2511,9 +2923,9 @@ void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
}
else
{
- pFly->Height( sal_uInt16(aInter.Height()) );
+ pFly->Height( aInter.Height() );
if( nAscent < pFly->Height() )
- pFly->SetAscent( sal_uInt16(nAscent) );
+ pFly->SetAscent( nAscent );
else
pFly->SetAscent( pFly->Height() );
}
@@ -2551,11 +2963,11 @@ void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
const SwTwips nOfst = nStartX - nGridOrigin;
const SwTwips nTmpWidth = rInf.Width() + nOfst;
- const sal_uLong i = nTmpWidth / nGridWidth + 1;
+ const SwTwips i = nTmpWidth / nGridWidth + 1;
- const tools::Long nNewWidth = ( i - 1 ) * nGridWidth - nOfst;
+ const SwTwips nNewWidth = ( i - 1 ) * nGridWidth - nOfst;
if ( nNewWidth > 0 )
- rInf.Width( static_cast<sal_uInt16>(nNewWidth) );
+ rInf.Width( nNewWidth );
else
rInf.Width( 0 );
@@ -2570,7 +2982,11 @@ SwFlyCntPortion *SwTextFormatter::NewFlyCntPortion( SwTextFormatInfo &rInf,
SwFlyInContentFrame *pFly;
SwFrameFormat* pFrameFormat = static_cast<SwTextFlyCnt*>(pHint)->GetFlyCnt().GetFrameFormat();
if( RES_FLYFRMFMT == pFrameFormat->Which() )
+ {
+ // set Lock pFrame to avoid m_pCurr getting deleted
+ TextFrameLockGuard aGuard(m_pFrame);
pFly = static_cast<SwTextFlyCnt*>(pHint)->GetFlyFrame(pFrame);
+ }
else
pFly = nullptr;
// aBase is the document-global position, from which the new extra portion is placed
@@ -2588,7 +3004,7 @@ SwFlyCntPortion *SwTextFormatter::NewFlyCntPortion( SwTextFormatInfo &rInf,
// we use this one when calculating the base, or the frame would be positioned
// too much to the top, sliding down after all causing a repaint in an area
// he actually never was in.
- sal_uInt16 nAscent = 0;
+ SwTwips nAscent = 0;
const bool bTextFrameVertical = GetInfo().GetTextFrame()->IsVertical();
@@ -2598,9 +3014,9 @@ SwFlyCntPortion *SwTextFormatter::NewFlyCntPortion( SwTextFormatInfo &rInf,
pFly->GetRefPoint().Y() );
if ( bUseFlyAscent )
- nAscent = static_cast<sal_uInt16>( std::abs( int( bTextFrameVertical ?
+ nAscent = std::abs( int( bTextFrameVertical ?
pFly->GetRelPos().X() :
- pFly->GetRelPos().Y() ) ) );
+ pFly->GetRelPos().Y() ) );
// Check if be prefer to use the ascent of the last portion:
if ( IsQuick() ||
diff --git a/sw/source/core/text/itrform2.hxx b/sw/source/core/text/itrform2.hxx
index bd986e4be324..2d71fad34cd5 100644
--- a/sw/source/core/text/itrform2.hxx
+++ b/sw/source/core/text/itrform2.hxx
@@ -45,7 +45,7 @@ class SwTextFormatter : public SwTextPainter
std::unique_ptr<sw::MergedAttrIterByEnd> m_pByEndIter; // HACK for TryNewNoLengthPortion
SwLinePortion* m_pFirstOfBorderMerge; // The first text portion of a joined border (during portion building)
- SwLinePortion *NewPortion( SwTextFormatInfo &rInf );
+ SwLinePortion *NewPortion(SwTextFormatInfo &rInf, ::std::optional<TextFrameIndex>);
SwTextPortion *NewTextPortion( SwTextFormatInfo &rInf );
SwLinePortion *NewExtraPortion( SwTextFormatInfo &rInf );
SwTabPortion *NewTabPortion( SwTextFormatInfo &rInf, bool bAuto ) const;
@@ -238,6 +238,8 @@ public:
* @param rInf contain information
**/
void MergeCharacterBorder( SwLinePortion& rPortion, SwLinePortion const *pPrev, SwTextFormatInfo& rInf );
+
+ bool ClearIfIsFirstOfBorderMerge(SwLinePortion const *pPortion);
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx
index 75493bad82be..f02beed8ce5b 100644
--- a/sw/source/core/text/itrpaint.cxx
+++ b/sw/source/core/text/itrpaint.cxx
@@ -32,6 +32,8 @@
#include <txtfrm.hxx>
#include <swfont.hxx>
#include "txtpaint.hxx"
+#include "porfld.hxx"
+#include "porfly.hxx"
#include "portab.hxx"
#include <txatbase.hxx>
#include <charfmt.hxx>
@@ -39,6 +41,7 @@
#include "porrst.hxx"
#include "pormulti.hxx"
#include <doc.hxx>
+#include <fmturl.hxx>
// Returns, if we have an underline breaking situation
// Adding some more conditions here means you also have to change them
@@ -69,7 +72,7 @@ void SwTextPainter::CtorInitTextPainter( SwTextFrame *pNewFrame, SwTextPaintInfo
m_bPaintDrop = false;
}
-SwLinePortion *SwTextPainter::CalcPaintOfst( const SwRect &rPaint )
+SwLinePortion *SwTextPainter::CalcPaintOfst(const SwRect &rPaint, bool& rbSkippedNumPortions)
{
SwLinePortion *pPor = m_pCurr->GetFirstPortion();
GetInfo().SetPaintOfst( 0 );
@@ -97,6 +100,11 @@ SwLinePortion *SwTextPainter::CalcPaintOfst( const SwRect &rPaint )
}
else
pPor->Move( GetInfo() );
+ if (pPor->InNumberGrp()
+ && !static_cast<SwNumberPortion const*>(pPor)->HasFollow())
+ {
+ rbSkippedNumPortions = true; // all numbering portions were skipped?
+ }
pLast = pPor;
pPor = pPor->GetNextPortion();
}
@@ -118,7 +126,10 @@ SwLinePortion *SwTextPainter::CalcPaintOfst( const SwRect &rPaint )
// (objectively slow, subjectively fast)
// Since the user usually judges subjectively the second method is set as default.
void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
- const bool bUnderSized )
+ const bool bUnderSized,
+ ::std::optional<SwTaggedPDFHelper> & roTaggedLabel,
+ ::std::optional<SwTaggedPDFHelper> & roTaggedParagraph,
+ bool const isPDFTaggingEnabled)
{
#if OSL_DEBUG_LEVEL > 1
// sal_uInt16 nFntHeight = GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), GetInfo().GetOut() );
@@ -127,6 +138,7 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
// maybe catch-up adjustment
GetAdjusted();
+ AddExtraBlankWidth();
GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
GetInfo().ResetSpaceIdx();
GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
@@ -140,13 +152,28 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
// 6882: blank lines can't be optimized by removing them if Formatting Marks are shown
const bool bEndPor = GetInfo().GetOpt().IsParagraph() && GetInfo().GetText().isEmpty();
- SwLinePortion *pPor = bEndPor ? m_pCurr->GetFirstPortion() : CalcPaintOfst( rPaint );
+ bool bSkippedNumPortions(false);
+ SwLinePortion *pPor = bEndPor ? m_pCurr->GetFirstPortion() : CalcPaintOfst(rPaint, bSkippedNumPortions);
+
+ if (bSkippedNumPortions // ugly but hard to check earlier in PaintSwFrame:
+ && !GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
+ { // there is a num portion but it is outside of the frame area and not painted
+ assert(!roTaggedLabel);
+ assert(!roTaggedParagraph);
+ Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
+ roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *GetInfo().GetOut());
+ }
+
+ SwTaggedPDFHelper::EndCurrentLink(*GetInfo().GetOut());
// Optimization!
SwTwips nMaxRight = std::min<SwTwips>( rPaint.Right(), Right() );
const SwTwips nTmpLeft = GetInfo().X();
- //compatibility setting: allow tabstop text to exceed right margin
- if (GetInfo().GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_OVER_MARGIN))
+ //compatibility settings: allow tabstop text to exceed right margin
+ const auto& iDSA = GetInfo().GetTextFrame()->GetDoc().getIDocumentSettingAccess();
+ const bool bTabOverMargin = iDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
+ const bool bTabOverSpacing = iDSA.get(DocumentSettingId::TAB_OVER_SPACING);
+ if (bTabOverMargin || bTabOverSpacing)
{
SwLinePortion* pPorIter = pPor;
while( pPorIter )
@@ -178,7 +205,7 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
&& GetDropLines() >= GetLineNr();
}
- sal_uInt16 nTmpHeight, nTmpAscent;
+ SwTwips nTmpHeight, nTmpAscent;
CalcAscentAndHeight( nTmpAscent, nTmpHeight );
// bClip decides if there's a need to clip
@@ -243,7 +270,7 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
SwTextGridItem const*const pGrid(GetGridItem(GetTextFrame()->FindPageFrame()));
const bool bAdjustBaseLine =
GetLineInfo().HasSpecialAlign( GetTextFrame()->IsVertical() ) ||
- ( nullptr != pGrid );
+ ( nullptr != pGrid ) || m_pCurr->GetHangingBaseline();
const SwTwips nLineBaseLine = GetInfo().GetPos().Y() + nTmpAscent;
if ( ! bAdjustBaseLine )
GetInfo().Y( nLineBaseLine );
@@ -385,9 +412,21 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
GetInfo().SetUnderFnt( nullptr );
}
+ // multiple numbering portions are possible :(
+ if ((pPor->InNumberGrp() // also footnote label
+ // weird special case, bullet with soft hyphen
+ || (pPor->InHyphGrp() && pNext && pNext->InNumberGrp()))
+ && !GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline()
+ && !roTaggedLabel) // note: CalcPaintOfst may skip some portions
+ {
+ assert(isPDFTaggingEnabled);
+ Por_Info aPorInfo(*pPor, *this, 1); // open Lbl
+ roTaggedLabel.emplace(nullptr, nullptr, &aPorInfo, *pOut);
+ }
+
{
// #i16816# tagged pdf support
- Por_Info aPorInfo( *pPor, *this );
+ Por_Info aPorInfo(*pPor, *this, 0);
SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, &aPorInfo, *pOut );
if( pPor->IsMultiPortion() )
@@ -396,6 +435,25 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
pPor->Paint( GetInfo() );
}
+ // lazy open LBody and paragraph tag after num portions have been painted to Lbl
+ if (pPor->InNumberGrp() // also footnote label
+ // note: numbering portion may be split if it has multiple scripts
+ && !static_cast<SwNumberPortion const*>(pPor)->HasFollow()) // so wait for the last one
+ {
+ if (!GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
+ {
+ assert(roTaggedLabel);
+ roTaggedLabel.reset(); // close Lbl
+ assert(!roTaggedParagraph);
+ Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
+ roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *pOut);
+ }
+ else
+ {
+ assert(!roTaggedLabel);
+ }
+ }
+
// reset underline font
if ( pOldUnderLineFnt )
GetInfo().SetUnderFnt( pOldUnderLineFnt );
@@ -403,6 +461,24 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
// reset (for special vertical alignment)
GetInfo().Y( nOldY );
+ if (GetFnt()->IsURL() && pPor->InTextGrp())
+ GetInfo().NotifyURL(*pPor);
+ else if (pPor->IsFlyCntPortion())
+ {
+ if (auto* pFlyContentPortion = dynamic_cast<sw::FlyContentPortion*>(pPor))
+ {
+ if (auto* pFlyFrame = pFlyContentPortion->GetFlyFrame())
+ {
+ if (auto* pFormat = pFlyFrame->GetFormat())
+ {
+ auto& url = pFormat->GetURL();
+ if (!url.GetURL().isEmpty()) // TODO: url.GetMap() ?
+ GetInfo().NotifyURL(*pPor);
+ }
+ }
+ }
+ }
+
bFirst &= !pPor->GetLen();
if( pNext || !pPor->IsMarginPortion() )
pPor->Move( GetInfo() );
@@ -416,6 +492,44 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
pNext && pNext->IsHolePortion() ) ?
pNext :
nullptr;
+ if (!pPor && isPDFTaggingEnabled && (roTaggedLabel || !roTaggedParagraph))
+ { // check if the end of the list label is off-screen
+ auto FindEndOfNumbering = [&](SwLinePortion const* pP) {
+ while (pP)
+ {
+ if (pP->InNumberGrp()
+ && !static_cast<SwNumberPortion const*>(pP)->HasFollow())
+ {
+ if (roTaggedLabel)
+ {
+ roTaggedLabel.reset();
+ } // else, if the numbering isn't visible at all, no Lbl
+ if (!GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
+ {
+ Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
+ roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *GetInfo().GetOut());
+ }
+ return true;
+ }
+ pP = pP->GetNextPortion();
+ }
+ return false;
+ };
+ if (!FindEndOfNumbering(pNext)) // check rest of current line
+ {
+ // check lines that will be cut off
+ if (rPaint.Bottom() < Y() + GetLineHeight())
+ {
+ for (SwLineLayout const* pLine = GetNext(); pLine; pLine = pLine->GetNext())
+ {
+ if (FindEndOfNumbering(pLine->GetFirstPortion()))
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
}
// delete underline font
@@ -543,12 +657,11 @@ void SwTextPainter::CheckSpecialUnderline( const SwLinePortion* pPor,
sal_Int32 nTmp(0);
for (auto const& e : pMerged->extents)
{
- const SfxPoolItem* pItem;
- if (SfxItemState::SET == e.pNode->GetSwAttrSet().GetItemState(
- RES_CHRATR_UNDERLINE, true, &pItem))
+ if (const SvxUnderlineItem* pItem = e.pNode->GetSwAttrSet().GetItemIfSet(
+ RES_CHRATR_UNDERLINE))
{
const bool bUnderSelect(m_pFont->GetUnderline() ==
- static_cast<SvxUnderlineItem const*>(pItem)->GetLineStyle());
+ pItem->GetLineStyle());
aUnderMulti.Select(Range(nTmp, nTmp + e.nEnd - e.nStart - 1),
bUnderSelect);
}
diff --git a/sw/source/core/text/itrpaint.hxx b/sw/source/core/text/itrpaint.hxx
index c016cff4e735..893db371dbb4 100644
--- a/sw/source/core/text/itrpaint.hxx
+++ b/sw/source/core/text/itrpaint.hxx
@@ -21,14 +21,17 @@
#include "itrtxt.hxx"
+#include <optional>
+
class SwSaveClip; // SwTextPainter
class SwMultiPortion;
+class SwTaggedPDFHelper;
class SwTextPainter : public SwTextCursor
{
bool m_bPaintDrop;
- SwLinePortion *CalcPaintOfst( const SwRect &rPaint );
+ SwLinePortion *CalcPaintOfst(const SwRect &rPaint, bool& rbSkippedNumPortions);
void CheckSpecialUnderline( const SwLinePortion* pPor,
tools::Long nAdjustBaseLine = 0 );
protected:
@@ -46,7 +49,10 @@ public:
CtorInitTextPainter( pTextFrame, pTextPaintInf );
}
void DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
- const bool bUnderSz );
+ const bool bUnderSz,
+ ::std::optional<SwTaggedPDFHelper> & roTaggedLabel,
+ ::std::optional<SwTaggedPDFHelper> & roTaggedParagraph,
+ bool isPDFTaggingEnabled);
void PaintDropPortion();
// if PaintMultiPortion is called recursively, we have to pass the
// surrounding SwBidiPortion
diff --git a/sw/source/core/text/itrtxt.cxx b/sw/source/core/text/itrtxt.cxx
index 220977a24d6d..1d1eed3e0837 100644
--- a/sw/source/core/text/itrtxt.cxx
+++ b/sw/source/core/text/itrtxt.cxx
@@ -27,6 +27,7 @@
#include <pagefrm.hxx>
#include <tgrditem.hxx>
#include "porfld.hxx"
+#include "porrst.hxx"
#include "itrtxt.hxx"
#include <txtfrm.hxx>
@@ -60,7 +61,7 @@ void SwTextIter::Init()
m_nLineNr = 1;
}
-void SwTextIter::CalcAscentAndHeight( sal_uInt16 &rAscent, sal_uInt16 &rHeight ) const
+void SwTextIter::CalcAscentAndHeight( SwTwips &rAscent, SwTwips &rHeight ) const
{
rHeight = GetLineHeight();
rAscent = m_pCurr->GetAscent() + rHeight - m_pCurr->Height();
@@ -211,9 +212,9 @@ const SwLineLayout *SwTextCursor::CharCursorToLine(TextFrameIndex const nPositio
return bPrevious ? PrevLine() : m_pCurr;
}
-sal_uInt16 SwTextCursor::AdjustBaseLine( const SwLineLayout& rLine,
+SwTwips SwTextCursor::AdjustBaseLine( const SwLineLayout& rLine,
const SwLinePortion* pPor,
- sal_uInt16 nPorHeight, sal_uInt16 nPorAscent,
+ SwTwips nPorHeight, SwTwips nPorAscent,
const bool bAutoToCentered ) const
{
if ( pPor )
@@ -222,7 +223,7 @@ sal_uInt16 SwTextCursor::AdjustBaseLine( const SwLineLayout& rLine,
nPorAscent = pPor->GetAscent();
}
- sal_uInt16 nOfst = rLine.GetRealHeight() - rLine.Height();
+ SwTwips nOfst = rLine.GetRealHeight() - rLine.Height();
SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
@@ -283,13 +284,54 @@ sal_uInt16 SwTextCursor::AdjustBaseLine( const SwLineLayout& rLine,
if (GetInfo().GetTextFrame()->IsVertLR() && !GetInfo().GetTextFrame()->IsVertLRBT())
nOfst += rLine.Height() - ( rLine.Height() - nPorHeight ) / 2 - nPorAscent;
else
- nOfst += ( rLine.Height() - nPorHeight ) / 2 + nPorAscent;
+ {
+ SwTwips nLineHeight = 0;
+ bool bHadClearingBreak = false;
+ if (GetInfo().GetTextFrame()->IsVertical())
+ {
+ // Ignore the height of clearing break portions in the automatic
+ // alignment case.
+ const SwLinePortion* pLinePor = rLine.GetFirstPortion();
+ while (pLinePor)
+ {
+ bool bClearingBreak = false;
+ if (pLinePor->IsBreakPortion())
+ {
+ auto pBreakPortion = static_cast<const SwBreakPortion*>(pLinePor);
+ bClearingBreak = pBreakPortion->GetClear() != SwLineBreakClear::NONE;
+ if (bClearingBreak)
+ {
+ bHadClearingBreak = true;
+ }
+ }
+ if (!bClearingBreak && pLinePor->Height() > nLineHeight)
+ {
+ nLineHeight = pLinePor->Height();
+ }
+ pLinePor = pLinePor->GetNextPortion();
+ }
+ }
+
+ if (!bHadClearingBreak)
+ {
+ nLineHeight = rLine.Height();
+ }
+
+ nOfst += ( nLineHeight - nPorHeight ) / 2 + nPorAscent;
+ }
break;
}
[[fallthrough]];
case SvxParaVertAlignItem::Align::Baseline :
// base line
- nOfst = nOfst + rLine.GetAscent();
+ if (pPor && pPor->GetHangingBaseline())
+ {
+ nOfst += rLine.GetAscent() // Romn baseline of the line.
+ - rLine.GetHangingBaseline() // Hanging baseline of the line.
+ + pPor->GetHangingBaseline(); // Romn baseline of the portion.
+ }
+ else
+ nOfst = nOfst + rLine.GetAscent();
break;
}
}
diff --git a/sw/source/core/text/itrtxt.hxx b/sw/source/core/text/itrtxt.hxx
index f155ee6cc484..b5ea78369d5f 100644
--- a/sw/source/core/text/itrtxt.hxx
+++ b/sw/source/core/text/itrtxt.hxx
@@ -16,8 +16,8 @@
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
-#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_ITRTXT_HXX
-#define INCLUDED_SW_SOURCE_CORE_TEXT_ITRTXT_HXX
+#pragma once
+
#include <swtypes.hxx>
#include "itratr.hxx"
#include "inftxt.hxx"
@@ -40,7 +40,7 @@ protected:
SwTwips m_nRegStart; // The register's start position (Y)
TextFrameIndex m_nStart; // Start in the text string, end = pCurr->GetLen()
sal_uInt16 m_nRegDiff; // Register's line distance
- sal_uInt16 m_nLineNr; // Line number
+ sal_Int32 m_nLineNr; // Line number
bool m_bPrev : 1;
bool m_bRegisterOn : 1; // Keep in register
bool m_bOneBlock : 1; // Justified text: Dispose single words
@@ -84,7 +84,7 @@ public:
const SwLineLayout *GetNext() const { return m_pCurr->GetNext(); }
const SwLineLayout *GetPrev();
TextFrameIndex GetLength() const { return m_pCurr->GetLen(); }
- sal_uInt16 GetLineNr() const { return m_nLineNr; }
+ sal_Int32 GetLineNr() const { return m_nLineNr; }
TextFrameIndex GetStart() const { return m_nStart; }
TextFrameIndex GetEnd() const { return GetStart() + GetLength(); }
SwTwips Y() const { return m_nY; }
@@ -113,8 +113,8 @@ public:
// Truncates all after pCurr
void TruncLines( bool bNoteFollow = false );
- sal_uInt16 GetLineHeight() const { return m_pCurr->GetRealHeight(); }
- void CalcAscentAndHeight( sal_uInt16 &rAscent, sal_uInt16 &rHeight ) const;
+ SwTwips GetLineHeight() const { return m_pCurr->GetRealHeight(); }
+ void CalcAscentAndHeight( SwTwips &rAscent, SwTwips &rHeight ) const;
// Lots of trouble for querying pCurr == pPara
bool IsFirstTextLine() const
@@ -156,7 +156,7 @@ protected:
// For FormatQuoVadis
void Right( const SwTwips nNew ) { mnRight = nNew; }
- void CtorInitTextMargin( SwTextFrame *pFrame, SwTextSizeInfo *pInf );
+ SW_DLLPUBLIC void CtorInitTextMargin( SwTextFrame *pFrame, SwTextSizeInfo *pInf );
explicit SwTextMargin(SwTextNode const * pTextNode)
: SwTextIter(pTextNode)
, mnLeft(0)
@@ -270,6 +270,7 @@ class SwTextCursor : public SwTextAdjuster
protected:
void CtorInitTextCursor( SwTextFrame *pFrame, SwTextSizeInfo *pInf );
explicit SwTextCursor(SwTextNode const * pTextNode) : SwTextAdjuster(pTextNode) { }
+ void AddExtraBlankWidth();
public:
SwTextCursor( SwTextFrame *pTextFrame, SwTextSizeInfo *pTextSizeInf )
: SwTextAdjuster(pTextFrame->GetTextNodeFirst())
@@ -287,8 +288,8 @@ public:
// calculates baseline for portion rPor
// bAutoToCentered indicates, if AUTOMATIC mode means CENTERED or BASELINE
- sal_uInt16 AdjustBaseLine( const SwLineLayout& rLine, const SwLinePortion* pPor,
- sal_uInt16 nPorHeight = 0, sal_uInt16 nAscent = 0,
+ SwTwips AdjustBaseLine( const SwLineLayout& rLine, const SwLinePortion* pPor,
+ SwTwips nPorHeight = 0, SwTwips nAscent = 0,
const bool bAutoToCentered = false ) const;
static void SetRightMargin( const bool bNew ){ s_bRightMargin = bNew; }
@@ -335,6 +336,6 @@ inline SwTwips SwTextMargin::Left() const
return (mnDropLines >= m_nLineNr && 1 != m_nLineNr) ? mnFirst + mnDropLeft : mnLeft;
}
-#endif
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/noteurl.cxx b/sw/source/core/text/noteurl.cxx
index fa91ea252d5f..d66736280bb5 100644
--- a/sw/source/core/text/noteurl.cxx
+++ b/sw/source/core/text/noteurl.cxx
@@ -19,7 +19,42 @@
#include <noteurl.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/imaprect.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/outdev.hxx>
+
// Global variable
-SwNoteURL* pNoteURL = nullptr;
+thread_local SwNoteURL* pNoteURL = nullptr;
+
+void SwNoteURL::InsertURLNote(const OUString& rURL, const OUString& rTarget, const SwRect& rRect)
+{
+ const size_t nCount = m_List.size();
+ for (size_t i = 0; i < nCount; ++i)
+ if (rRect == m_List[i].GetRect())
+ return;
+
+ m_List.emplace_back(rURL, rTarget, rRect);
+}
+
+void SwNoteURL::FillImageMap(ImageMap* pMap, const Point& rPos, const MapMode& rMap)
+{
+ assert(pMap && "FillImageMap: No ImageMap, no cookies!");
+ const size_t nCount = m_List.size();
+ if (nCount)
+ {
+ MapMode aMap(MapUnit::Map100thMM);
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ const SwURLNote& rNote = m_List[i];
+ SwRect aSwRect(rNote.GetRect());
+ aSwRect -= rPos;
+ tools::Rectangle aRect(OutputDevice::LogicToLogic(aSwRect.SVRect(), rMap, aMap));
+ IMapRectangleObject aObj(aRect, rNote.GetURL(), OUString(), OUString(),
+ rNote.GetTarget(), OUString(), true, false);
+ pMap->InsertIMapObject(aObj);
+ }
+ }
+}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/pordrop.hxx b/sw/source/core/text/pordrop.hxx
index d89b0cfd1c9d..50dd00e5668e 100644
--- a/sw/source/core/text/pordrop.hxx
+++ b/sw/source/core/text/pordrop.hxx
@@ -60,6 +60,7 @@ public:
void SetJoinBorderWithNext( const bool bJoinNext ) { m_bJoinBorderWithNext = bJoinNext; }
};
+/// Text portion for the Format -> Paragraph -> Drop Caps functionality.
class SwDropPortion : public SwTextPortion
{
friend class SwDropCapCache;
diff --git a/sw/source/core/text/porexp.cxx b/sw/source/core/text/porexp.cxx
index 29f470c1490c..0884db6fce76 100644
--- a/sw/source/core/text/porexp.cxx
+++ b/sw/source/core/text/porexp.cxx
@@ -18,6 +18,7 @@
*/
#include <viewopt.hxx>
+#include <IDocumentSettingAccess.hxx>
#include <SwPortionHandler.hxx>
#include "inftxt.hxx"
#include "porexp.hxx"
@@ -38,6 +39,16 @@ void SwExpandPortion::HandlePortion( SwPortionHandler& rPH ) const
rPH.Special( GetLen(), OUString(), GetWhichPor() );
}
+void SwExpandPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExpandPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
SwPosSize SwExpandPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
{
SwTextSlot aDiffText( &rInf, this, false, false );
@@ -63,12 +74,15 @@ bool SwExpandPortion::Format( SwTextFormatInfo &rInf )
void SwExpandPortion::Paint( const SwTextPaintInfo &rInf ) const
{
+ rInf.DrawCSDFHighlighting(*this); // here it detects as CS and not DF
+
SwTextSlot aDiffText( &rInf, this, true, true );
const SwFont aOldFont = *rInf.GetFont();
if( GetJoinBorderWithPrev() )
const_cast<SwTextPaintInfo&>(rInf).GetFont()->SetLeftBorder(nullptr);
if( GetJoinBorderWithNext() )
const_cast<SwTextPaintInfo&>(rInf).GetFont()->SetRightBorder(nullptr);
+// rInf.DrawCSDFHighlighting(*this); // here it detects as DF and only the '/' is detected as CS
rInf.DrawBackBrush( *this );
rInf.DrawBorder( *this );
@@ -194,14 +208,52 @@ bool SwBlankPortion::Format( SwTextFormatInfo &rInf )
void SwBlankPortion::Paint( const SwTextPaintInfo &rInf ) const
{
- if( !m_bMulti ) // No gray background for multiportion brackets
- rInf.DrawViewOpt( *this, PortionType::Blank );
- SwExpandPortion::Paint( rInf );
+ // Draw field shade (can be disabled individually)
+ if (!m_bMulti) // No gray background for multiportion brackets
+ rInf.DrawViewOpt(*this, PortionType::Blank);
+ SwExpandPortion::Paint(rInf);
+
+ if (m_cChar == CHAR_HARDBLANK)
+ {
+ if (rInf.GetOpt().IsBlank())
+ {
+ // Draw tilde or degree sign
+ OUString aMarker = (rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess()
+ .get(DocumentSettingId::USE_VARIABLE_WIDTH_NBSP)
+ ? u"~"_ustr
+ : u"°"_ustr);
+
+ SwPosSize aMarkerSize(rInf.GetTextSize(aMarker));
+ Point aPos(rInf.GetPos());
+
+ std::shared_ptr<SwRect> pPortionRect = std::make_shared<SwRect>();
+ rInf.CalcRect(*this, pPortionRect.get());
+ aPos.AdjustX((pPortionRect->Width() / 2) - (aMarkerSize.Width() / 2));
+
+ SwTextPaintInfo aInf(rInf, &aMarker);
+ aInf.SetPos(aPos);
+ SwTextPortion aMarkerPor;
+ aMarkerPor.Width(aMarkerSize.Width());
+ aMarkerPor.Height(aMarkerSize.Height());
+ aMarkerPor.SetAscent(GetAscent());
+
+ Color colorBackup = aInf.GetFont()->GetColor();
+ aInf.GetFont()->SetColor(NON_PRINTING_CHARACTER_COLOR);
+ aInf.DrawText(aMarkerPor, TextFrameIndex(aMarker.getLength()), true);
+ aInf.GetFont()->SetColor(colorBackup);
+ }
+ }
}
-bool SwBlankPortion::GetExpText( const SwTextSizeInfo&, OUString &rText ) const
+bool SwBlankPortion::GetExpText( const SwTextSizeInfo& rInf, OUString &rText ) const
{
- rText = OUString(m_cChar);
+ if (m_cChar == CHAR_HARDBLANK
+ && rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::USE_VARIABLE_WIDTH_NBSP))
+ rText = OUString(CH_BLANK);
+ else
+ rText = OUString(m_cChar);
+
return true;
}
@@ -210,6 +262,21 @@ void SwBlankPortion::HandlePortion( SwPortionHandler& rPH ) const
rPH.Special( GetLen(), OUString( m_cChar ), GetWhichPor() );
}
+void SwBlankPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwBlankPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("char"),
+ BAD_CAST(OUString(m_cChar).toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("multi"),
+ BAD_CAST(OString::boolean(m_bMulti).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
SwPostItsPortion::SwPostItsPortion( bool bScrpt )
: m_bScript( bScrpt )
{
diff --git a/sw/source/core/text/porexp.hxx b/sw/source/core/text/porexp.hxx
index 9c7be5be5aaf..82de5b1be4fc 100644
--- a/sw/source/core/text/porexp.hxx
+++ b/sw/source/core/text/porexp.hxx
@@ -33,8 +33,12 @@ public:
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
};
+/// Non-breaking space or non-breaking hyphen.
class SwBlankPortion : public SwExpandPortion
{
sal_Unicode m_cChar;
@@ -54,6 +58,9 @@ public:
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
};
class SwPostItsPortion : public SwExpandPortion
diff --git a/sw/source/core/text/porfld.cxx b/sw/source/core/text/porfld.cxx
index fdb2e4442916..d2ae4be00d1b 100644
--- a/sw/source/core/text/porfld.cxx
+++ b/sw/source/core/text/porfld.cxx
@@ -21,10 +21,15 @@
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <utility>
+
+#include <comphelper/string.hxx>
#include <vcl/graph.hxx>
#include <editeng/brushitem.hxx>
#include <vcl/metric.hxx>
#include <vcl/outdev.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <vcl/pdfwriter.hxx>
#include <viewopt.hxx>
#include <SwPortionHandler.hxx>
#include "porlay.hxx"
@@ -42,7 +47,8 @@
#include <accessibilityoptions.hxx>
#include <editeng/lrspitem.hxx>
#include <unicode/ubidi.h>
-#include <bookmrk.hxx>
+#include <bookmark.hxx>
+#include <docufld.hxx>
using namespace ::com::sun::star;
@@ -58,7 +64,7 @@ SwFieldPortion *SwFieldPortion::Clone( const OUString &rExpand ) const
}
// #i107143#
// pass placeholder property to created <SwFieldPortion> instance.
- SwFieldPortion* pClone = new SwFieldPortion( rExpand, std::move(pNewFnt), m_bPlaceHolder );
+ SwFieldPortion* pClone = new SwFieldPortion(rExpand, std::move(pNewFnt));
pClone->SetNextOffset( m_nNextOffset );
pClone->m_bNoLength = m_bNoLength;
return pClone;
@@ -68,19 +74,18 @@ void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField )
{
OSL_ENSURE( pField, "TakeNextOffset: Missing Source" );
m_nNextOffset = pField->GetNextOffset();
- m_aExpand = m_aExpand.replaceAt(0, sal_Int32(m_nNextOffset), "");
+ m_aExpand = m_aExpand.replaceAt(0, sal_Int32(m_nNextOffset), u"");
m_bFollow = true;
}
-SwFieldPortion::SwFieldPortion(const OUString &rExpand, std::unique_ptr<SwFont> pFont, bool bPlaceHold, TextFrameIndex const nFieldLen)
- : m_aExpand(rExpand), m_pFont(std::move(pFont)), m_nNextOffset(0)
+SwFieldPortion::SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFont, TextFrameIndex const nFieldLen)
+ : m_aExpand(std::move(aExpand)), m_pFont(std::move(pFont)), m_nNextOffset(0)
, m_nNextScriptChg(COMPLETE_STRING), m_nFieldLen(nFieldLen), m_nViewWidth(0)
, m_bFollow( false ), m_bLeft( false), m_bHide( false)
, m_bCenter (false), m_bHasFollow( false )
, m_bAnimated( false), m_bNoPaint( false)
- , m_bReplace( false), m_bPlaceHolder( bPlaceHold )
+ , m_bReplace(false)
, m_bNoLength( false )
- , m_nAttrFieldType(0)
{
SetWhichPor( PortionType::Field );
}
@@ -100,9 +105,7 @@ SwFieldPortion::SwFieldPortion( const SwFieldPortion& rField )
, m_bAnimated ( rField.m_bAnimated )
, m_bNoPaint( rField.m_bNoPaint)
, m_bReplace( rField.m_bReplace )
- , m_bPlaceHolder( rField.m_bPlaceHolder )
, m_bNoLength( rField.m_bNoLength )
- , m_nAttrFieldType( rField.m_nAttrFieldType)
{
if ( rField.HasFont() )
m_pFont.reset( new SwFont( *rField.GetFont() ) );
@@ -120,7 +123,7 @@ sal_uInt16 SwFieldPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
// even though this is const, nViewWidth should be computed at the very end:
SwFieldPortion* pThis = const_cast<SwFieldPortion*>(this);
if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
- !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
+ !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings() )
{
if( !m_nViewWidth )
pThis->m_nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
@@ -137,11 +140,12 @@ namespace {
*/
class SwFieldSlot
{
- std::shared_ptr<vcl::TextLayoutCache> m_pOldCachedVclData;
+ std::shared_ptr<const vcl::text::TextLayoutCache> m_pOldCachedVclData;
const OUString *pOldText;
OUString aText;
TextFrameIndex nIdx;
TextFrameIndex nLen;
+ sal_Unicode nOrigHookChar;
SwTextFormatInfo *pInf;
bool bOn;
public:
@@ -155,6 +159,7 @@ SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pP
: pOldText(nullptr)
, nIdx(0)
, nLen(0)
+ , nOrigHookChar(0)
, pInf(nullptr)
{
bOn = pPor->GetExpText( *pNew, aText );
@@ -167,6 +172,7 @@ SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pP
nIdx = pInf->GetIdx();
nLen = pInf->GetLen();
pOldText = &(pInf->GetText());
+ nOrigHookChar = pInf->GetHookChar();
m_pOldCachedVclData = pInf->GetCachedVclData();
pInf->SetLen(TextFrameIndex(aText.getLength()));
pInf->SetCachedVclData(nullptr);
@@ -175,10 +181,18 @@ SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pP
pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
pInf->SetIdx(TextFrameIndex(0));
}
- else if (nIdx < TextFrameIndex(pOldText->getLength()))
+ else
{
- sal_Int32 const nFieldLen(pPor->GetFieldLen());
- aText = (*pOldText).replaceAt(sal_Int32(nIdx), nFieldLen, aText);
+ TextFrameIndex nEnd(pOldText->getLength());
+ if (nIdx < nEnd)
+ {
+ sal_Int32 const nFieldLen(pPor->GetFieldLen());
+ aText = (*pOldText).replaceAt(sal_Int32(nIdx), nFieldLen, aText);
+ }
+ else if (nIdx == nEnd)
+ aText = *pOldText + aText;
+ else
+ SAL_WARN("sw.core", "SwFieldSlot bad SwFieldPortion index.");
}
pInf->SetText( aText );
}
@@ -189,6 +203,13 @@ SwFieldSlot::~SwFieldSlot()
{
pInf->SetCachedVclData(m_pOldCachedVclData);
pInf->SetText( *pOldText );
+ // ofz#64109 at last for ruby-text when we restore the original text to
+ // continue laying out the 'body' text of the ruby, then a tab or other
+ // 'hook char' in the text drawn above it shouldn't affect the 'body'
+ // While there are other cases, such as tdf#148360, where the tab in an
+ // inline expanded field, that should affect the body.
+ if (pInf->IsRuby())
+ pInf->SetHookChar(nOrigHookChar);
pInf->SetIdx( nIdx );
pInf->SetLen( nLen );
pInf->SetFakeLineStart( false );
@@ -381,25 +402,37 @@ bool SwFieldPortion::Format( SwTextFormatInfo &rInf )
// These characters should not be contained in the follow
// field portion. They are handled via the HookChar mechanism.
const sal_Unicode nNew = !aNew.isEmpty() ? aNew[0] : 0;
- switch (nNew)
+ auto IsHook = [](const sal_Unicode cNew, bool const isSpace = false) -> bool
{
- case CH_BREAK : bFull = true;
- [[fallthrough]];
- case ' ' :
- case CH_TAB :
- case CHAR_HARDHYPHEN: // non-breaking hyphen
- case CHAR_SOFTHYPHEN:
- case CHAR_HARDBLANK:
- case CHAR_ZWSP :
- case CHAR_WJ :
- case CH_TXTATR_BREAKWORD:
- case CH_TXTATR_INWORD:
+ switch (cNew)
{
- aNew = aNew.copy( 1 );
- ++nNextOfst;
- break;
+ case ' ': // tdf#159101 this one is not in ScanPortionEnd
+ // but is required for justified text
+ return isSpace;
+ case CH_BREAK:
+ case CH_TAB:
+ case CHAR_HARDHYPHEN: // non-breaking hyphen
+ case CHAR_SOFTHYPHEN:
+ case CHAR_HARDBLANK:
+ case CHAR_ZWSP:
+ case CHAR_WJ:
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ {
+ return true;
+ }
+ default:
+ return false;
}
- default: ;
+ };
+ if (IsHook(nNew, true))
+ {
+ if (nNew == CH_BREAK)
+ {
+ bFull = true;
+ }
+ aNew = aNew.copy(1);
+ ++nNextOfst;
}
// Even if there is no more text left for a follow field,
@@ -410,8 +443,17 @@ bool SwFieldPortion::Format( SwTextFormatInfo &rInf )
{
pField->SetFont( std::make_unique<SwFont>( *rInf.GetFont() ) );
}
- pField->SetFollow( true );
- SetHasFollow( true );
+ if (IsFollow() || Compress())
+ { // empty this will be deleted in SwLineLayout::CalcLine()
+ // anyway so make sure pField doesn't have a stale flag
+ pField->SetFollow( true );
+ }
+ if (pField->Compress() && !std::all_of(std::u16string_view(aNew).begin(),
+ std::u16string_view(aNew).end(), IsHook))
+ { // empty pField will be deleted in SwLineLayout::CalcLine()
+ // anyway so make sure this one doesn't have a stale flag
+ SetHasFollow( true );
+ }
// For a newly created field, nNextOffset contains the Offset
// of its start of the original string
@@ -433,7 +475,7 @@ void SwFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
SwFontSave aSave( rInf, m_pFont.get() );
// OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
- if( Width() && ( !m_bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
+ if (Width() && !m_bContentControl)
{
// A very liberal use of the background
rInf.DrawViewOpt( *this, PortionType::Field );
@@ -446,7 +488,7 @@ bool SwFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) c
rText = m_aExpand;
if( rText.isEmpty() && rInf.OnWin() &&
!rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
- SwViewOption::IsFieldShadings() &&
+ rInf.GetOpt().IsFieldShadings() &&
!HasFollow() )
rText = " ";
return true;
@@ -454,14 +496,24 @@ bool SwFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) c
void SwFieldPortion::HandlePortion( SwPortionHandler& rPH ) const
{
- sal_Int32 nH = 0;
- sal_Int32 nW = 0;
+ rPH.Special( GetLen(), m_aExpand, GetWhichPor() );
+}
+
+void SwFieldPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("expand"), BAD_CAST(m_aExpand.toUtf8().getStr()));
+
if (m_pFont)
{
- nH = m_pFont->GetSize(m_pFont->GetActual()).Height();
- nW = m_pFont->GetSize(m_pFont->GetActual()).Width();
+ m_pFont->dumpAsXml(pWriter);
}
- rPH.Special( GetLen(), m_aExpand, GetWhichPor(), nH, nW, m_pFont.get() );
+
+ (void)xmlTextWriterEndElement(pWriter);
}
SwPosSize SwFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
@@ -501,10 +553,10 @@ SwNumberPortion::SwNumberPortion( const OUString &rExpand,
const bool bCntr,
const sal_uInt16 nMinDst,
const bool bLabelAlignmentPosAndSpaceModeActive )
- : SwFieldPortion( rExpand, std::move(pFont) ),
- m_nFixWidth(0),
- m_nMinDist( nMinDst ),
- mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
+ : SwFieldPortion(rExpand, std::move(pFont), TextFrameIndex(0))
+ , m_nFixWidth(0)
+ , m_nMinDist(nMinDst)
+ , mbLabelAlignmentPosAndSpaceModeActive(bLabelAlignmentPosAndSpaceModeActive)
{
SetWhichPor( PortionType::Number );
SetLeft( bLft );
@@ -550,13 +602,16 @@ bool SwNumberPortion::Format( SwTextFormatInfo &rInf )
if ( !mbLabelAlignmentPosAndSpaceModeActive )
{
- if (!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
+ if ((!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
// #i32902#
- !IsFootnoteNumPortion() )
+ !IsFootnoteNumPortion()) ||
+ // tdf#159382
+ (IsFootnoteNumPortion() &&
+ rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::NO_GAP_AFTER_NOTE_NUMBER)))
{
nDiff = rInf.Left()
+ rInf.GetTextFrame()->GetTextNodeForParaProps()->
- GetSwAttrSet().GetLRSpace().GetTextFirstLineOffset()
+ GetSwAttrSet().GetFirstLineIndent().GetTextFirstLineOffset()
- rInf.First()
+ rInf.ForcedLeftMargin();
}
@@ -595,10 +650,10 @@ bool SwNumberPortion::Format( SwTextFormatInfo &rInf )
if ( rInf.IsMulti() )
{
if ( Height() < nDiff )
- Height( sal_uInt16( nDiff ) );
+ Height( nDiff );
}
else if( Width() < nDiff )
- Width( sal_uInt16(nDiff) );
+ Width( nDiff );
}
return bFull;
}
@@ -785,7 +840,7 @@ SwGrfNumPortion::SwGrfNumPortion(
m_nYPos = 0;
m_eOrient = text::VertOrientation::TOP;
}
- Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) );
+ Width( rGrfSize.Width() + 2 * GRFNUM_SECURE );
m_nFixWidth = Width();
m_nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
Height( sal_uInt16(m_nGrfHeight) );
@@ -834,7 +889,7 @@ bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
if( bFull )
{
- Width( rInf.Width() - static_cast<sal_uInt16>(rInf.X()) );
+ Width( rInf.Width() - rInf.X() );
if( bFly )
{
SetLen(TextFrameIndex(0));
@@ -869,7 +924,7 @@ bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
}
if( Width() < nDiff )
- Width( sal_uInt16(nDiff) );
+ Width( nDiff );
return bFull;
}
@@ -935,7 +990,7 @@ void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
SetId( reinterpret_cast<sal_IntPtr>( rInf.GetTextFrame() ) );
rInf.GetTextFrame()->SetAnimation();
}
- if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw )
+ if( aTmp.Overlaps( rInf.GetPaintRect() ) && !bDraw )
{
rInf.NoteAnimation();
const SwViewShell* pViewShell = rInf.GetVsh();
@@ -959,8 +1014,10 @@ void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
if (pGraph)
{
+ const OutputDevice* pOut = rInf.GetOut();
+ assert(pOut);
pGraph->StartAnimation(
- const_cast<OutputDevice*>(rInf.GetOut()), aPos, aSize, m_nId );
+ *const_cast<OutputDevice*>(pOut), aPos, aSize, m_nId);
}
}
@@ -993,7 +1050,9 @@ void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
if( bDraw && aTmp.HasArea() )
{
- DrawGraphic( m_pBrush.get(), const_cast<OutputDevice*>(rInf.GetOut()),
+ const OutputDevice* pOut = rInf.GetOut();
+ assert(pOut);
+ DrawGraphic( m_pBrush.get(), *const_cast<OutputDevice*>(pOut),
aTmp, aRepaint, m_bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
}
}
@@ -1062,6 +1121,9 @@ void SwTextFrame::StopAnimation( const OutputDevice* pOut )
*/
SwCombinedPortion::SwCombinedPortion( const OUString &rText )
: SwFieldPortion( rText )
+ , m_aWidth{ static_cast<sal_uInt16>(0),
+ static_cast<sal_uInt16>(0),
+ static_cast<sal_uInt16>(0) }
, m_nUpPos(0)
, m_nLowPos(0)
, m_nProportion(55)
@@ -1166,7 +1228,7 @@ bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
{
rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( m_aScrType[i] ) );
m_aWidth[ m_aScrType[i] ] =
- static_cast<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetFontSize().Width() / 3);
+ o3tl::narrowing<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetFontSize().Width() / 3);
}
}
}
@@ -1213,7 +1275,7 @@ bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
SwDrawTextInfo aDrawInf(pSh, *rInf.GetOut(), m_aExpand, i, 1);
Size aSize = aTmpFont.GetTextSize_( aDrawInf );
const sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
- m_aPos[ i ] = static_cast<sal_uInt16>(aSize.Width());
+ m_aPos[ i ] = o3tl::narrowing<sal_uInt16>(aSize.Width());
if( i == nTop ) // enter the second line
{
m_nLowPos = nMaxDescent;
@@ -1228,7 +1290,7 @@ bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
if( nAsc > nMaxAscent )
nMaxAscent = nAsc;
if( aSize.Height() - nAsc > nMaxDescent )
- nMaxDescent = static_cast<sal_uInt16>(aSize.Height() - nAsc);
+ nMaxDescent = aSize.Height() - nAsc;
}
// for one or two characters we double the width of the portion
if( nCount < 3 )
@@ -1291,7 +1353,7 @@ bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
{
if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFieldGrp()
|| !static_cast<SwFieldPortion*>(rInf.GetLast())->IsFollow() ) )
- Width( static_cast<sal_uInt16>( rInf.Width() - rInf.X() ) );
+ Width( rInf.Width() - rInf.X() );
else
{
Truncate();
@@ -1350,4 +1412,57 @@ void SwFieldFormDatePortion::Paint( const SwTextPaintInfo &rInf ) const
}
}
+SwFieldPortion* SwJumpFieldPortion::Clone(const OUString& rExpand) const
+{
+ auto pRet = new SwJumpFieldPortion(*this);
+ pRet->m_aExpand = rExpand;
+ return pRet;
+}
+
+bool SwJumpFieldPortion::DescribePDFControl(const SwTextPaintInfo& rInf) const
+{
+ auto pPDFExtOutDevData
+ = dynamic_cast<vcl::PDFExtOutDevData*>(rInf.GetOut()->GetExtOutDevData());
+ if (!pPDFExtOutDevData)
+ return false;
+
+ if (!pPDFExtOutDevData->GetIsExportFormFields())
+ return false;
+
+ if (m_nFormat != SwJumpEditFormat::JE_FMT_TEXT)
+ return false;
+
+ vcl::PDFWriter::EditWidget aDescriptor;
+
+ aDescriptor.Border = true;
+ aDescriptor.BorderColor = COL_BLACK;
+
+ SwRect aLocation;
+ rInf.CalcRect(*this, &aLocation);
+ aDescriptor.Location = aLocation.SVRect();
+
+ // Map the text of the field to the descriptor's text.
+ static sal_Unicode constexpr aForbidden[] = { CH_TXTATR_BREAKWORD, 0 };
+ aDescriptor.Text = comphelper::string::removeAny(GetExp(), aForbidden);
+
+ // Description for accessibility purposes.
+ if (!m_sHelp.isEmpty())
+ aDescriptor.Description = m_sHelp;
+
+ pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Form);
+ pPDFExtOutDevData->CreateControl(aDescriptor);
+ pPDFExtOutDevData->EndStructureElement();
+
+ return true;
+}
+
+void SwJumpFieldPortion::Paint(const SwTextPaintInfo& rInf) const
+{
+ if (Width() && DescribePDFControl(rInf))
+ return;
+
+ if (rInf.GetOpt().IsShowPlaceHolderFields())
+ SwFieldPortion::Paint(rInf);
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porfld.hxx b/sw/source/core/text/porfld.hxx
index 519e56f8f58c..b92372942425 100644
--- a/sw/source/core/text/porfld.hxx
+++ b/sw/source/core/text/porfld.hxx
@@ -50,8 +50,8 @@ protected:
bool m_bAnimated : 1; // Used by SwGrfNumPortion
bool m_bNoPaint : 1; // Used by SwGrfNumPortion
bool m_bReplace : 1; // Used by SwGrfNumPortion
- const bool m_bPlaceHolder : 1;
bool m_bNoLength : 1; // HACK for meta suffix (no CH_TXTATR)
+ bool m_bContentControl = false;
void SetFont( std::unique_ptr<SwFont> pNew ) { m_pFont = std::move(pNew); }
bool IsNoLength() const { return m_bNoLength; }
@@ -59,10 +59,9 @@ protected:
public:
SwFieldPortion( const SwFieldPortion& rField );
- SwFieldPortion(const OUString &rExpand, std::unique_ptr<SwFont> pFnt = nullptr, bool bPlaceHolder = false, TextFrameIndex nLen = TextFrameIndex(1));
+ SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFnt = nullptr, TextFrameIndex nLen = TextFrameIndex(1));
virtual ~SwFieldPortion() override;
- sal_uInt16 m_nAttrFieldType;
void TakeNextOffset( const SwFieldPortion* pField );
void CheckScript( const SwTextSizeInfo &rInf );
bool HasFont() const { return nullptr != m_pFont; }
@@ -107,6 +106,11 @@ public:
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void SetContentControl(bool bContentControl) { m_bContentControl = bContentControl; }
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
};
/**
@@ -207,7 +211,7 @@ public:
class SwCombinedPortion : public SwFieldPortion
{
sal_uInt16 m_aPos[6]; // up to six X positions
- o3tl::enumarray<SwFontScript,sal_uInt16> m_aWidth = {}; // one width for every scripttype
+ o3tl::enumarray<SwFontScript,sal_uInt16> m_aWidth; // one width for every scripttype
SwFontScript m_aScrType[6]; // scripttype of every character
sal_uInt16 m_nUpPos; // the Y position of the upper baseline
sal_uInt16 m_nLowPos; // the Y position of the lower baseline
@@ -257,4 +261,25 @@ private:
bool m_bStart;
};
+class SwJumpFieldPortion final : public SwFieldPortion
+{
+public:
+ explicit SwJumpFieldPortion(OUString aExpand, OUString aHelp, std::unique_ptr<SwFont> pFont,
+ sal_uInt32 nFormat)
+ : SwFieldPortion(std::move(aExpand), std::move(pFont))
+ , m_nFormat(nFormat)
+ , m_sHelp(std::move(aHelp))
+ {
+ }
+ virtual SwFieldPortion* Clone(const OUString& rExpand) const override;
+
+ virtual void Paint(const SwTextPaintInfo& rInf) const override;
+
+private:
+ sal_uInt32 m_nFormat; // SwJumpEditFormat from SwField::GetFormat()
+ OUString m_sHelp;
+
+ bool DescribePDFControl(const SwTextPaintInfo& rInf) const;
+};
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porfly.cxx b/sw/source/core/text/porfly.cxx
index 34edc3082b71..19ac692cf42f 100644
--- a/sw/source/core/text/porfly.cxx
+++ b/sw/source/core/text/porfly.cxx
@@ -26,11 +26,11 @@
#include <frmfmt.hxx>
#include <viewsh.hxx>
#include <textboxhelper.hxx>
+#include <IDocumentState.hxx>
#include <sal/log.hxx>
#include <fmtanchr.hxx>
#include <fmtflcnt.hxx>
-#include <fmtornt.hxx>
#include <flyfrms.hxx>
#include <txatbase.hxx>
#include "porfly.hxx"
@@ -39,6 +39,7 @@
#include <sortedobjs.hxx>
#include <officecfg/Office/Common.hxx>
+#include <PostItMgr.hxx>
/**
* class SwFlyPortion => we expect a frame-locale SwRect!
@@ -57,7 +58,17 @@ bool SwFlyPortion::Format( SwTextFormatInfo &rInf )
rInf.GetLastTab()->FormatEOL( rInf );
rInf.GetLast()->FormatEOL( rInf );
- PrtWidth( static_cast<sal_uInt16>(GetFix() - rInf.X() + PrtWidth()) );
+
+ SetBlankWidth(0);
+ if (auto blankWidth = rInf.GetLast()->ExtraBlankWidth())
+ {
+ // Swallow previous blank width
+ SetBlankWidth(blankWidth);
+ rInf.GetLast()->ExtraBlankWidth(0);
+ rInf.X(rInf.X() - blankWidth); // Step back
+ }
+
+ PrtWidth( o3tl::narrowing<sal_uInt16>(GetFix() - rInf.X() + PrtWidth()) );
if( !Width() )
{
OSL_ENSURE( Width(), "+SwFlyPortion::Format: a fly is a fly is a fly" );
@@ -77,11 +88,11 @@ bool SwFlyPortion::Format( SwTextFormatInfo &rInf )
&& ' ' != rInf.GetChar(rInf.GetIdx() - TextFrameIndex(1))
&& ( !rInf.GetLast() || !rInf.GetLast()->IsBreakPortion() ) )
{
- SetBlankWidth( rInf.GetTextSize(OUString(' ')).Width() );
+ SetBlankWidth(GetBlankWidth() + rInf.GetTextSize(OUString(' ')).Width());
SetLen(TextFrameIndex(1));
}
- const sal_uInt16 nNewWidth = static_cast<sal_uInt16>(rInf.X() + PrtWidth());
+ const sal_uInt16 nNewWidth = o3tl::narrowing<sal_uInt16>(rInf.X() + PrtWidth());
if( rInf.Width() <= nNewWidth )
{
Truncate();
@@ -108,7 +119,7 @@ bool SwFlyCntPortion::Format( SwTextFormatInfo &rInf )
// KerningPortions at beginning of line, e.g., for grid layout
// must be considered.
const SwLinePortion* pLastPor = rInf.GetLast();
- const sal_uInt16 nLeft = ( pLastPor &&
+ const auto nLeft = ( pLastPor &&
( pLastPor->IsKernPortion() ||
pLastPor->IsErgoSumPortion() ) ) ?
pLastPor->Width() :
@@ -157,14 +168,14 @@ void SwTextFrame::MoveFlyInCnt(SwTextFrame *pNew,
{
// Consider changed type of <SwSortedList> entries
SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
- const SwFormatAnchor& rAnch = pAnchoredObj->GetFrameFormat().GetAnchor();
+ const SwFormatAnchor& rAnch = pAnchoredObj->GetFrameFormat()->GetAnchor();
if (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
{
const SwPosition* pPos = rAnch.GetContentAnchor();
TextFrameIndex const nIndex(MapModelToViewPos(*pPos));
if (nStart <= nIndex && nIndex < nEnd)
{
- if ( auto pFlyFrame = dynamic_cast<SwFlyFrame *>( pAnchoredObj ) )
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
{
RemoveFly( pFlyFrame );
pNew->AppendFly( pFlyFrame );
@@ -211,7 +222,7 @@ void sw::FlyContentPortion::Paint(const SwTextPaintInfo& rInf) const
rInf.GetTextFrame()->SwitchHorizontalToVertical(aRepaintRect);
if(!((m_pFly->IsCompletePaint() ||
- m_pFly->getFrameArea().IsOver(aRepaintRect)) &&
+ m_pFly->getFrameArea().Overlaps(aRepaintRect)) &&
SwFlyFrame::IsPaint(m_pFly->GetVirtDrawObj(), m_pFly->getRootFrame()->GetCurrShell())))
return;
@@ -226,7 +237,7 @@ void sw::FlyContentPortion::Paint(const SwTextPaintInfo& rInf) const
// track changes: cross out the image, if it is deleted
const SwFrame *pFrame = m_pFly->Lower();
- if ( IsDeleted() && pFrame )
+ if ( GetAuthor() != std::string::npos && IsDeleted() && pFrame )
{
SwRect aPaintRect( pFrame->GetPaintArea() );
@@ -236,7 +247,8 @@ void sw::FlyContentPortion::Paint(const SwTextPaintInfo& rInf) const
const_cast<vcl::RenderContext&>(*rInf.GetOut()).SetAntialiasing(AntialiasingFlags::Enable);
tools::Long startX = aPaintRect.Left( ), endX = aPaintRect.Right();
tools::Long startY = aPaintRect.Top( ), endY = aPaintRect.Bottom();
- const_cast<vcl::RenderContext&>(*rInf.GetOut()).SetLineColor(NON_PRINTING_CHARACTER_COLOR);
+ const_cast<vcl::RenderContext&>(*rInf.GetOut()).SetLineColor(
+ SwPostItMgr::GetColorAnchor(GetAuthor()) );
const_cast<vcl::RenderContext&>(*rInf.GetOut()).DrawLine(Point(startX, startY), Point(endX, endY));
const_cast<vcl::RenderContext&>(*rInf.GetOut()).DrawLine(Point(startX, endY), Point(endX, startY));
if ( bIsAntiAliasing )
@@ -270,6 +282,7 @@ void sw::DrawFlyCntPortion::Paint(const SwTextPaintInfo&) const
SwFlyCntPortion::SwFlyCntPortion()
: m_bMax(false)
, m_bDeleted(false)
+ , m_nAuthor(std::string::npos)
, m_eAlign(sw::LineAlign::NONE)
{
mnLineLength = TextFrameIndex(1);
@@ -359,51 +372,20 @@ void SwFlyCntPortion::SetBase( const SwTextFrame& rFrame, const Point &rBase,
aObjPositioning.CalcPosition();
}
- SwFrameFormat* pShape = FindFrameFormat(pSdrObj);
- const SwFormatAnchor& rAnchor(pShape->GetAnchor());
- if (rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ if (auto pFormat = FindFrameFormat(pSdrObj))
{
- // This is an inline draw shape, see if it has a textbox.
- SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
- if (pTextBox)
+ if (pFormat->GetOtherTextBoxFormats()
+ && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
{
- // It has, so look up its text rectangle, and adjust the position
- // of the textbox accordingly.
- // Both rectangles are absolute, SwFormatHori/VertOrient's position
- // is relative to the print area of the anchor text frame.
- tools::Rectangle aTextRectangle = SwTextBoxHelper::getTextRectangle(pShape);
- tools::Long nXoffs = SwTextBoxHelper::getTextRectangle(pShape, false).getX();
-
- const auto aPos(pShape->GetAnchor().GetContentAnchor());
- SwFormatVertOrient aVert(pTextBox->GetVertOrient());
- SwFormatHoriOrient aHori(pTextBox->GetHoriOrient());
-
- // tdf#138598 Replace vertical alignment of As_char textboxes in footer
- // tdf#140158 Remove horizontal positioning of As_char textboxes, because
- // the anchor moving does the same for it.
- if (!aPos->nNode.GetNode().FindFooterStartNode())
- {
- aVert.SetVertOrient(css::text::VertOrientation::NONE);
- aVert.SetRelationOrient(css::text::RelOrientation::FRAME);
- sal_Int32 const nTop = aTextRectangle.getY() - rFrame.getFrameArea().Top()
- - rFrame.getFramePrintArea().Top();
- aVert.SetPos(nTop);
- }
- else
- {
- aVert.SetVertOrient(css::text::VertOrientation::NONE);
- aVert.SetPos(SwTextBoxHelper::getTextRectangle(pShape, false).getY());
- }
-
- SwFormatAnchor aNewTxBxAnchor(pTextBox->GetAnchor());
- aNewTxBxAnchor.SetAnchor(aPos);
- aHori.SetPos(nXoffs);
-
- pTextBox->LockModify();
- pTextBox->SetFormatAttr(aNewTxBxAnchor);
- pTextBox->SetFormatAttr(aVert);
- pTextBox->SetFormatAttr(aHori);
- pTextBox->UnlockModify();
+ // TODO: Improve security with moving this sync call to other place,
+ // where it works for typing but not during layout calc.
+ const bool bModified = pFormat->GetDoc()->getIDocumentState().IsEnableSetModified();
+ pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(false);
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::changeAnchor, pFormat,
+ pFormat->FindRealSdrObject());
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::syncTextBoxSize,
+ pFormat, pFormat->FindRealSdrObject());
+ pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(bModified);
}
}
@@ -421,14 +403,14 @@ void SwFlyCntPortion::SetBase( const SwTextFrame& rFrame, const Point &rBase,
SwTwips nRelPos = aObjPositioning.GetRelPosY();
if ( nRelPos < 0 )
{
- mnAscent = static_cast<sal_uInt16>(-nRelPos);
+ mnAscent = -nRelPos;
if( mnAscent > Height() )
Height( mnAscent );
}
else
{
mnAscent = 0;
- Height( Height() + static_cast<sal_uInt16>(nRelPos) );
+ Height( Height() + o3tl::narrowing<sal_uInt16>(nRelPos) );
}
}
else
diff --git a/sw/source/core/text/porfly.hxx b/sw/source/core/text/porfly.hxx
index fdd974050513..2c56563a4436 100644
--- a/sw/source/core/text/porfly.hxx
+++ b/sw/source/core/text/porfly.hxx
@@ -46,6 +46,7 @@ class SwFlyCntPortion : public SwLinePortion
Point m_aRef; // Relatively to this point we calculate the AbsPos
bool m_bMax; // Line adjustment and height == line height
bool m_bDeleted; // Part of tracked deletion: it needs strikethrough
+ size_t m_nAuthor; // Redline author for color of the strikethrough
sw::LineAlign m_eAlign;
virtual SdrObject* GetSdrObj(const SwTextFrame&) =0;
@@ -55,6 +56,8 @@ public:
const Point& GetRefPoint() const { return m_aRef; }
bool IsMax() const { return m_bMax; }
bool IsDeleted() const { return m_bDeleted; }
+ void SetAuthor(size_t nAuthor) { m_nAuthor = nAuthor; }
+ size_t GetAuthor() const { return m_nAuthor; }
sw::LineAlign GetAlign() const { return m_eAlign; }
void SetAlign(sw::LineAlign eAlign) { m_eAlign = eAlign; }
void SetMax(bool bMax) { m_bMax = bMax; }
@@ -73,6 +76,7 @@ namespace sw
FlyContentPortion(SwFlyInContentFrame* pFly);
static FlyContentPortion* Create(const SwTextFrame& rFrame, SwFlyInContentFrame* pFly, const Point& rBase, tools::Long nAscent, tools::Long nDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags);
SwFlyInContentFrame* GetFlyFrame() { return m_pFly; }
+ const SwFlyInContentFrame* GetFlyFrame() const { return m_pFly; }
void GetFlyCursorOfst(Point& rPoint, SwPosition& rPos, SwCursorMoveState* pCMS) const { m_pFly->GetModelPositionForViewPoint(&rPos, rPoint, pCMS); };
virtual void Paint(const SwTextPaintInfo& rInf) const override;
virtual ~FlyContentPortion() override;
diff --git a/sw/source/core/text/porftn.hxx b/sw/source/core/text/porftn.hxx
index a5ecda64d072..925a04f7793b 100644
--- a/sw/source/core/text/porftn.hxx
+++ b/sw/source/core/text/porftn.hxx
@@ -68,7 +68,7 @@ class SwQuoVadisPortion : public SwFieldPortion
{
OUString m_aErgo;
public:
- SwQuoVadisPortion( const OUString &rExp, const OUString& rStr );
+ SwQuoVadisPortion( const OUString &rExp, OUString aStr );
virtual bool Format( SwTextFormatInfo &rInf ) override;
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
diff --git a/sw/source/core/text/porglue.cxx b/sw/source/core/text/porglue.cxx
index 7c09ded23a2f..5f1fd2a7dc8d 100644
--- a/sw/source/core/text/porglue.cxx
+++ b/sw/source/core/text/porglue.cxx
@@ -54,7 +54,7 @@ bool SwGluePortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) co
if( GetLen() && rInf.OnWin() &&
rInf.GetOpt().IsBlank() && rInf.IsNoSymbol() )
{
- OUStringBuffer aBuf;
+ OUStringBuffer aBuf(GetLen().get());
comphelper::string::padToLength(aBuf, sal_Int32(GetLen()), CH_BULLET);
rText = aBuf.makeStringAndClear();
return true;
@@ -69,8 +69,9 @@ void SwGluePortion::Paint( const SwTextPaintInfo &rInf ) const
if( rInf.GetFont()->IsPaintBlank() )
{
- OUStringBuffer aBuf;
- comphelper::string::padToLength(aBuf, GetFixWidth() / sal_Int32(GetLen()), ' ');
+ const sal_Int32 nCount = GetFixWidth() / sal_Int32(GetLen());
+ OUStringBuffer aBuf(nCount);
+ comphelper::string::padToLength(aBuf, nCount, ' ');
OUString aText(aBuf.makeStringAndClear());
SwTextPaintInfo aInf( rInf, &aText );
aInf.DrawText(*this, TextFrameIndex(aText.getLength()), true);
@@ -128,6 +129,18 @@ void SwGluePortion::Join( SwGluePortion *pVictim )
delete pVictim;
}
+void SwGluePortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwGluePortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fix-width"), BAD_CAST(OString::number(m_nFixWidth).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
/**
* We're expecting a frame-local SwRect!
*/
@@ -144,6 +157,18 @@ SwFixPortion::SwFixPortion()
SetWhichPor( PortionType::Fix );
}
+void SwFixPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFixPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fix"),
+ BAD_CAST(OString::number(m_nFix).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
SwMarginPortion::SwMarginPortion()
:SwGluePortion( 0 )
{
diff --git a/sw/source/core/text/porglue.hxx b/sw/source/core/text/porglue.hxx
index 05f72beb9fb3..aaaeec339f31 100644
--- a/sw/source/core/text/porglue.hxx
+++ b/sw/source/core/text/porglue.hxx
@@ -24,6 +24,9 @@
class SwRect;
class SwLineLayout;
+/// A glue portion is either a base class for other portions that want to have a certain width to
+/// push text out (e.g. tab portions) or used to align SwQuoVadisPortion instances on the right for
+/// footnotes.
class SwGluePortion : public SwLinePortion
{
private:
@@ -44,6 +47,8 @@ public:
virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const override;
};
class SwFixPortion : public SwGluePortion
@@ -54,6 +59,8 @@ public:
SwFixPortion();
void SetFix( const sal_uInt16 nNewFix ) { m_nFix = nNewFix; }
sal_uInt16 GetFix() const { return m_nFix; }
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const override;
};
class SwMarginPortion : public SwGluePortion
diff --git a/sw/source/core/text/porhyph.hxx b/sw/source/core/text/porhyph.hxx
index 43341ce1daf9..dc3c6651d0a8 100644
--- a/sw/source/core/text/porhyph.hxx
+++ b/sw/source/core/text/porhyph.hxx
@@ -36,6 +36,9 @@ public:
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
};
class SwHyphStrPortion : public SwHyphPortion
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: */
diff --git a/sw/source/core/text/porlay.hxx b/sw/source/core/text/porlay.hxx
index 249ed81f088f..fe8aeb861e50 100644
--- a/sw/source/core/text/porlay.hxx
+++ b/sw/source/core/text/porlay.hxx
@@ -75,18 +75,19 @@ public:
/// Collection of SwLinePortion instances, representing one line of text.
/// Typically owned by an SwParaPortion.
-class SwLineLayout : public SwTextPortion
+class SAL_DLLPUBLIC_RTTI SwLineLayout : public SwTextPortion
{
private:
SwLineLayout *m_pNext; // The next Line
std::unique_ptr<std::vector<tools::Long>> m_pLLSpaceAdd; // Used for justified alignment
std::unique_ptr<std::deque<sal_uInt16>> m_pKanaComp; // Used for Kana compression
- sal_uInt16 m_nRealHeight; // The height resulting from line spacing and register
- sal_uInt16 m_nTextHeight; // The max height of all non-FlyCnt portions in this Line
+ SwTwips m_nRealHeight; // The height resulting from line spacing and register
+ SwTwips m_nTextHeight; // The max height of all non-FlyCnt portions in this Line
bool m_bFormatAdj : 1;
bool m_bDummy : 1;
bool m_bEndHyph : 1;
bool m_bMidHyph : 1;
+ bool m_bLastHyph : 1;
bool m_bFly : 1;
bool m_bRest : 1;
bool m_bBlinking : 1;
@@ -108,12 +109,12 @@ private:
public:
// From SwPosSize
using SwPosSize::Height;
- virtual void Height(const sal_uInt16 nNew, const bool bText = true) override;
+ virtual void Height(const SwTwips nNew, const bool bText = true) override;
// From SwLinePortion
virtual SwLinePortion *Insert( SwLinePortion *pPortion ) override;
virtual SwLinePortion *Append( SwLinePortion *pPortion ) override;
- SwLinePortion *GetFirstPortion() const;
+ SW_DLLPUBLIC SwLinePortion *GetFirstPortion() const;
// Flags
void ResetFlags();
@@ -123,6 +124,8 @@ public:
bool IsEndHyph() const { return m_bEndHyph; }
void SetMidHyph( const bool bNew ) { m_bMidHyph = bNew; }
bool IsMidHyph() const { return m_bMidHyph; }
+ void SetLastHyph( const bool bNew ) { m_bLastHyph = bNew; }
+ bool IsLastHyph() const { return m_bLastHyph; }
void SetFly( const bool bNew ) { m_bFly = bNew; }
bool IsFly() const { return m_bFly; }
void SetRest( const bool bNew ) { m_bRest = bNew; }
@@ -165,10 +168,10 @@ public:
// Collects the data for the line
void CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf );
- void SetRealHeight( sal_uInt16 nNew ) { m_nRealHeight = nNew; }
- sal_uInt16 GetRealHeight() const { return m_nRealHeight; }
+ void SetRealHeight( SwTwips nNew ) { m_nRealHeight = nNew; }
+ SwTwips GetRealHeight() const { return m_nRealHeight; }
- sal_uInt16 GetTextHeight() const { return m_nTextHeight; }
+ SwTwips GetTextHeight() const { return m_nTextHeight; }
// Creates the glue chain for short lines
SwMarginPortion *CalcLeftMargin();
@@ -240,6 +243,9 @@ public:
SwTwips& _orObjDescent,
const SwLinePortion* _pDontConsiderPortion = nullptr,
const bool _bNoFlyCntPorAndLinePor = false ) const;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
};
/// Collection of SwLineLayout instances, represents the paragraph text in Writer layout.
@@ -265,7 +271,7 @@ class SwParaPortion : public SwLineLayout
bool m_bFollowField : 1; // We have a bit of field left for the Follow
bool m_bFixLineHeight : 1; // Fixed line height
- bool m_bFootnoteNum : 1; // contains a footnotenumberportion
+ bool m_bFootnoteNum : 1; // is the frame that may contain a footnotenumberportion
bool m_bMargin : 1; // contains a hanging punctuation in the margin
public:
@@ -283,8 +289,8 @@ public:
const SwRepaint& GetRepaint() const { return m_aRepaint; }
SwCharRange& GetReformat() { return m_aReformat; }
const SwCharRange& GetReformat() const { return m_aReformat; }
- tools::Long& GetDelta() { return m_nDelta; }
- const tools::Long& GetDelta() const { return m_nDelta; }
+ void SetDelta(tools::Long nDelta) { m_nDelta = nDelta; }
+ tools::Long GetDelta() const { return m_nDelta; }
SwScriptInfo& GetScriptInfo() { return m_aScriptInfo; }
const SwScriptInfo& GetScriptInfo() const { return m_aScriptInfo; }
@@ -316,11 +322,17 @@ public:
bool IsFootnoteNum() const { return m_bFootnoteNum; }
void SetMargin( const bool bNew = true ) { m_bMargin = bNew; }
bool IsMargin() const { return m_bMargin; }
+ enum FootnoteOrNot { OnlyNumbering, FootnoteToo };
+ bool HasNumberingPortion(FootnoteOrNot) const;
+ bool HasContentPortions() const;
// Set nErgo in the QuoVadisPortion
void SetErgoSumNum( const OUString &rErgo );
const SwDropPortion *FindDropPortion() const;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
};
inline void SwParaPortion::ResetPreps()
diff --git a/sw/source/core/text/porlin.cxx b/sw/source/core/text/porlin.cxx
index 035f670d464b..a420d64301ff 100644
--- a/sw/source/core/text/porlin.cxx
+++ b/sw/source/core/text/porlin.cxx
@@ -21,6 +21,7 @@
#include <SwPortionHandler.hxx>
#include "porlin.hxx"
+#include "portxt.hxx"
#include "inftxt.hxx"
#include "pormulti.hxx"
#if OSL_DEBUG_LEVEL > 0
@@ -67,6 +68,7 @@ SwLinePortion::SwLinePortion( ) :
mpNextPortion( nullptr ),
mnLineLength( 0 ),
mnAscent( 0 ),
+ mnHangingBaseline( 0 ),
mnWhichPor( PortionType::NONE ),
m_bJoinBorderWithPrev(false),
m_bJoinBorderWithNext(false)
@@ -85,16 +87,16 @@ void SwLinePortion::PrePaint( const SwTextPaintInfo& rInf,
return;
const sal_uInt16 nHalfView = nViewWidth / 2;
- sal_uInt16 nLastWidth = pLast->Width();
+ sal_uInt16 nLastWidth = pLast->Width() + pLast->ExtraBlankWidth();
- if ( pLast->InSpaceGrp() && rInf.GetSpaceAdd() )
- nLastWidth = nLastWidth + static_cast<sal_uInt16>(pLast->CalcSpacing( rInf.GetSpaceAdd(), rInf ));
+ if ( pLast->InSpaceGrp() && rInf.GetSpaceAdd(/*bShrink=*/true) )
+ nLastWidth += pLast->CalcSpacing( rInf.GetSpaceAdd(/*bShrink=*/true), rInf );
sal_uInt16 nPos;
SwTextPaintInfo aInf( rInf );
const bool bBidiPor = rInf.GetTextFrame()->IsRightToLeft() !=
- bool( ComplexTextLayoutFlags::BiDiRtl & rInf.GetOut()->GetLayoutMode() );
+ bool( vcl::text::ComplexTextLayoutFlags::BiDiRtl & rInf.GetOut()->GetLayoutMode() );
Degree10 nDir = bBidiPor ?
1800_deg10 :
@@ -201,6 +203,9 @@ SwLinePortion *SwLinePortion::Cut( SwLinePortion *pVictim )
{
SwLinePortion *pPrev = pVictim->FindPrevPortion( this );
OSL_ENSURE( pPrev, "SwLinePortion::Cut(): can't cut" );
+ // note: if pVictim is a follow then clearing pPrev's m_bHasFollow here is
+ // tricky because it could be that the HookChar inserted a tab portion
+ // between 2 field portions
pPrev->SetNextPortion( pVictim->GetNextPortion() );
pVictim->SetNextPortion(nullptr);
return pVictim;
@@ -246,7 +251,7 @@ bool SwLinePortion::Format( SwTextFormatInfo &rInf )
const SwLinePortion *pLast = rInf.GetLast();
Height( pLast->Height() );
SetAscent( pLast->GetAscent() );
- const sal_uInt16 nNewWidth = static_cast<sal_uInt16>(rInf.X() + PrtWidth());
+ const sal_uInt16 nNewWidth = o3tl::narrowing<sal_uInt16>(rInf.X() + PrtWidth());
// Only portions with true width can return true
// Notes for example never set bFull==true
if( rInf.Width() <= nNewWidth && PrtWidth() && ! IsKernPortion() )
@@ -265,16 +270,17 @@ bool SwLinePortion::Format( SwTextFormatInfo &rInf )
void SwLinePortion::FormatEOL( SwTextFormatInfo & )
{ }
-void SwLinePortion::Move( SwTextPaintInfo &rInf )
+void SwLinePortion::Move(SwTextPaintInfo & rInf) const
{
bool bB2T = rInf.GetDirection() == DIR_BOTTOM2TOP;
const bool bFrameDir = rInf.GetTextFrame()->IsRightToLeft();
bool bCounterDir = ( ! bFrameDir && DIR_RIGHT2LEFT == rInf.GetDirection() ) ||
( bFrameDir && DIR_LEFT2RIGHT == rInf.GetDirection() );
- if ( InSpaceGrp() && rInf.GetSpaceAdd() )
+ SwTwips nTmp = PrtWidth() + ExtraBlankWidth();
+ if ( InSpaceGrp() && rInf.GetSpaceAdd(/*bShrink=*/true) )
{
- SwTwips nTmp = PrtWidth() + CalcSpacing( rInf.GetSpaceAdd(), rInf );
+ nTmp += CalcSpacing( rInf.GetSpaceAdd(/*bShrink=*/true), rInf );
if( rInf.IsRotated() )
rInf.Y( rInf.Y() + ( bB2T ? -nTmp : nTmp ) );
else if ( bCounterDir )
@@ -290,19 +296,19 @@ void SwLinePortion::Move( SwTextPaintInfo &rInf )
rInf.IncKanaIdx();
}
if( rInf.IsRotated() )
- rInf.Y( rInf.Y() + ( bB2T ? -PrtWidth() : PrtWidth() ) );
+ rInf.Y(rInf.Y() + (bB2T ? -nTmp : nTmp));
else if ( bCounterDir )
- rInf.X( rInf.X() - PrtWidth() );
+ rInf.X(rInf.X() - nTmp);
else
- rInf.X( rInf.X() + PrtWidth() );
+ rInf.X(rInf.X() + nTmp);
}
- if( IsMultiPortion() && static_cast<SwMultiPortion*>(this)->HasTabulator() )
+ if (IsMultiPortion() && static_cast<SwMultiPortion const*>(this)->HasTabulator())
rInf.IncSpaceIdx();
rInf.SetIdx( rInf.GetIdx() + GetLen() );
}
-tools::Long SwLinePortion::CalcSpacing( tools::Long , const SwTextSizeInfo & ) const
+SwTwips SwLinePortion::CalcSpacing( tools::Long , const SwTextSizeInfo & ) const
{
return 0;
}
@@ -314,7 +320,39 @@ bool SwLinePortion::GetExpText( const SwTextSizeInfo &, OUString & ) const
void SwLinePortion::HandlePortion( SwPortionHandler& rPH ) const
{
- rPH.Special( GetLen(), OUString(), GetWhichPor(), Height(), Width() );
+ rPH.Special( GetLen(), OUString(), GetWhichPor() );
+}
+
+void SwLinePortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwLinePortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwLinePortion::dumpAsXmlAttributes(xmlTextWriterPtr pWriter, std::u16string_view rText, TextFrameIndex nOffset) const
+{
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), BAD_CAST(typeid(*this).name()));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("width"),
+ BAD_CAST(OString::number(Width()).getStr()));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("height"),
+ BAD_CAST(OString::number(Height()).getStr()));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("length"),
+ BAD_CAST(OString::number(static_cast<sal_Int32>(mnLineLength)).getStr()));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("type"),
+ BAD_CAST(sw::PortionTypeToString(GetWhichPor())));
+ OUString aText( rText.substr(sal_Int32(nOffset), sal_Int32(GetLen())) );
+ for (int i = 0; i < 32; ++i)
+ aText = aText.replace(i, '*');
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("portion"),
+ BAD_CAST(aText.toUtf8().getStr()));
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porlin.hxx b/sw/source/core/text/porlin.hxx
index 3d2b8dad29e4..d2298bdfb03c 100644
--- a/sw/source/core/text/porlin.hxx
+++ b/sw/source/core/text/porlin.hxx
@@ -18,6 +18,8 @@
*/
#pragma once
+#include <libxml/xmlwriter.h>
+
#include "possiz.hxx"
#include <txttypes.hxx>
#include <TextFrameIndex.hxx>
@@ -28,38 +30,24 @@ class SwTextPaintInfo;
class SwTextFormatInfo;
class SwPortionHandler;
-/// Portion groups
-/// @see enum PortionType in txttypes.hxx
-#define PORGRP_TXT 0x8000
-#define PORGRP_EXP 0x4000
-#define PORGRP_FLD 0x2000
-#define PORGRP_HYPH 0x1000
-#define PORGRP_NUMBER 0x0800
-#define PORGRP_GLUE 0x0400
-#define PORGRP_FIX 0x0200
-#define PORGRP_TAB 0x0100
-// Small special groups
-#define PORGRP_FIXMARG 0x0040
-//#define PORGRP_? 0x0020
-#define PORGRP_TABNOTLFT 0x0010
-#define PORGRP_TOXREF 0x0008
-
/// Base class for anything that can be part of a line in the Writer layout.
/// Typically owned by SwLineLayout.
-class SwLinePortion: public SwPosSize
+class SAL_DLLPUBLIC_RTTI SwLinePortion: public SwPosSize
{
protected:
// Here we have areas with different attributes
SwLinePortion *mpNextPortion;
// Count of chars and spaces on the line
TextFrameIndex mnLineLength;
- sal_uInt16 mnAscent; // Maximum ascender
+ SwTwips mnAscent; // Maximum ascender
+ SwTwips mnHangingBaseline;
SwLinePortion();
private:
PortionType mnWhichPor; // Who's who?
bool m_bJoinBorderWithPrev;
bool m_bJoinBorderWithNext;
+ SwTwips m_nExtraBlankWidth = 0; // width of spaces after the break
void Truncate_();
@@ -73,13 +61,17 @@ public:
TextFrameIndex GetLen() const { return mnLineLength; }
void SetLen(TextFrameIndex const nLen) { mnLineLength = nLen; }
void SetNextPortion( SwLinePortion *pNew ){ mpNextPortion = pNew; }
- sal_uInt16 &GetAscent() { return mnAscent; }
- sal_uInt16 GetAscent() const { return mnAscent; }
- void SetAscent( const sal_uInt16 nNewAsc ) { mnAscent = nNewAsc; }
- void PrtWidth( sal_uInt16 nNewWidth ) { Width( nNewWidth ); }
- sal_uInt16 PrtWidth() const { return Width(); }
- void AddPrtWidth( const sal_uInt16 nNew ) { Width( Width() + nNew ); }
- void SubPrtWidth( const sal_uInt16 nNew ) { Width( Width() - nNew ); }
+ SwTwips &GetAscent() { return mnAscent; }
+ SwTwips GetAscent() const { return mnAscent; }
+ void SetAscent( const SwTwips nNewAsc ) { mnAscent = nNewAsc; }
+ void PrtWidth( SwTwips nNewWidth ) { Width( nNewWidth ); }
+ SwTwips PrtWidth() const { return Width(); }
+ void AddPrtWidth( const SwTwips nNew ) { Width( Width() + nNew ); }
+ void SubPrtWidth( const SwTwips nNew ) { Width( Width() - nNew ); }
+ SwTwips ExtraBlankWidth() const { return m_nExtraBlankWidth; }
+ void ExtraBlankWidth(const SwTwips nNew) { m_nExtraBlankWidth = nNew; }
+ SwTwips GetHangingBaseline() const { return mnHangingBaseline; }
+ void SetHangingBaseline( const SwTwips nNewBaseline ) { mnHangingBaseline = nNewBaseline; }
// Insert methods
virtual SwLinePortion *Insert( SwLinePortion *pPortion );
@@ -152,7 +144,7 @@ public:
virtual bool Format( SwTextFormatInfo &rInf );
// Is called for the line's last portion
virtual void FormatEOL( SwTextFormatInfo &rInf );
- void Move( SwTextPaintInfo &rInf );
+ void Move(SwTextPaintInfo & rInf) const;
// For SwTextSlot
virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const;
@@ -161,7 +153,7 @@ public:
virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const;
// for text- and multi-portions
- virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const;
+ virtual SwTwips CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const;
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const;
@@ -170,6 +162,11 @@ public:
bool GetJoinBorderWithNext() const { return m_bJoinBorderWithNext; }
void SetJoinBorderWithPrev( const bool bJoinPrev ) { m_bJoinBorderWithPrev = bJoinPrev; }
void SetJoinBorderWithNext( const bool bJoinNext ) { m_bJoinBorderWithNext = bJoinNext; }
+
+ virtual void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& rOffset) const;
+ void dumpAsXmlAttributes(xmlTextWriterPtr writer, std::u16string_view rText,
+ TextFrameIndex nOffset) const;
};
inline SwLinePortion &SwLinePortion::operator=(const SwLinePortion &rPortion)
@@ -177,9 +174,11 @@ inline SwLinePortion &SwLinePortion::operator=(const SwLinePortion &rPortion)
*static_cast<SwPosSize*>(this) = rPortion;
mnLineLength = rPortion.mnLineLength;
mnAscent = rPortion.mnAscent;
+ mnHangingBaseline = rPortion.mnHangingBaseline;
mnWhichPor = rPortion.mnWhichPor;
m_bJoinBorderWithPrev = rPortion.m_bJoinBorderWithPrev;
m_bJoinBorderWithNext = rPortion.m_bJoinBorderWithNext;
+ m_nExtraBlankWidth = rPortion.m_nExtraBlankWidth;
return *this;
}
@@ -188,9 +187,11 @@ inline SwLinePortion::SwLinePortion(const SwLinePortion &rPortion) :
mpNextPortion( nullptr ),
mnLineLength( rPortion.mnLineLength ),
mnAscent( rPortion.mnAscent ),
+ mnHangingBaseline( rPortion.mnHangingBaseline ),
mnWhichPor( rPortion.mnWhichPor ),
m_bJoinBorderWithPrev( rPortion.m_bJoinBorderWithPrev ),
- m_bJoinBorderWithNext( rPortion.m_bJoinBorderWithNext )
+ m_bJoinBorderWithNext( rPortion.m_bJoinBorderWithNext ),
+ m_nExtraBlankWidth(rPortion.m_nExtraBlankWidth)
{
}
diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx
index c51fb973ad29..aaf8339b37ef 100644
--- a/sw/source/core/text/pormulti.cxx
+++ b/sw/source/core/text/pormulti.cxx
@@ -31,6 +31,7 @@
#include <charfmt.hxx>
#include <layfrm.hxx>
#include <SwPortionHandler.hxx>
+#include <EnhancedPDFExportHelper.hxx>
#include "pormulti.hxx"
#include "inftxt.hxx"
#include "itrpaint.hxx"
@@ -119,7 +120,7 @@ void SwMultiPortion::CalcSize( SwTextFormatter& rLine, SwTextFormatInfo &rInf )
SetAscent( nTmp );
}
-tools::Long SwMultiPortion::CalcSpacing( tools::Long , const SwTextSizeInfo & ) const
+SwTwips SwMultiPortion::CalcSpacing( tools::Long , const SwTextSizeInfo & ) const
{
return 0;
}
@@ -134,6 +135,31 @@ void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const
rPH.Text( GetLen(), GetWhichPor() );
}
+void SwMultiPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwMultiPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ // Intentionally not incrementing nOffset here, one of the child portions will do that.
+
+ const SwLineLayout* pLine = &GetRoot();
+ while (pLine)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwLineLayout"));
+ pLine->dumpAsXmlAttributes(pWriter, rText, nOffset);
+ const SwLinePortion* pPor = pLine->GetFirstPortion();
+ while (pPor)
+ {
+ pPor->dumpAsXml(pWriter, rText, nOffset);
+ pPor = pPor->GetNextPortion();
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ pLine = pLine->GetNext();
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
// sets the tabulator-flag, if there's any tabulator-portion inside.
void SwMultiPortion::ActualizeTabulator()
{
@@ -193,7 +219,7 @@ SwBidiPortion::SwBidiPortion(TextFrameIndex const nEnd, sal_uInt8 nLv)
SetDirection( DIR_LEFT2RIGHT );
}
-tools::Long SwBidiPortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo& rInf ) const
+SwTwips SwBidiPortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo& rInf ) const
{
return HasTabulator() ? 0 : sal_Int32(GetSpaceCnt(rInf)) * nSpaceAdd / SPACING_PRECISION_FACTOR;
}
@@ -351,12 +377,12 @@ void SwDoubleLinePortion::PaintBracket( SwTextPaintInfo &rInf,
aBlank.Width( nChWidth );
aBlank.Height( m_pBracket->nHeight );
{
- std::unique_ptr<SwFont> pTmpFnt( new SwFont( *rInf.GetFont() ) );
+ SwFont aTmpFnt( *rInf.GetFont() );
SwFontScript nAct = bOpen ? m_pBracket->nPreScript : m_pBracket->nPostScript;
if( SW_SCRIPTS > nAct )
- pTmpFnt->SetActual( nAct );
- pTmpFnt->SetProportion( 100 );
- SwFontSave aSave( rInf, pTmpFnt.get() );
+ aTmpFnt.SetActual( nAct );
+ aTmpFnt.SetProportion( 100 );
+ SwFontSave aSave( rInf, &aTmpFnt );
aBlank.Paint( rInf );
}
if( bOpen )
@@ -384,21 +410,21 @@ void SwDoubleLinePortion::SetBrackets( const SwDoubleLinePortion& rDouble )
void SwDoubleLinePortion::FormatBrackets( SwTextFormatInfo &rInf, SwTwips& nMaxWidth )
{
nMaxWidth -= rInf.X();
- std::unique_ptr<SwFont> pTmpFnt( new SwFont( *rInf.GetFont() ) );
- pTmpFnt->SetProportion( 100 );
+ SwFont aTmpFnt( *rInf.GetFont() );
+ aTmpFnt.SetProportion( 100 );
m_pBracket->nAscent = 0;
m_pBracket->nHeight = 0;
if( m_pBracket->cPre )
{
OUString aStr( m_pBracket->cPre );
- SwFontScript nActualScr = pTmpFnt->GetActual();
+ SwFontScript nActualScr = aTmpFnt.GetActual();
if( SW_SCRIPTS > m_pBracket->nPreScript )
- pTmpFnt->SetActual( m_pBracket->nPreScript );
- SwFontSave aSave( rInf, pTmpFnt.get() );
+ aTmpFnt.SetActual( m_pBracket->nPreScript );
+ SwFontSave aSave( rInf, &aTmpFnt );
SwPosSize aSize = rInf.GetTextSize( aStr );
m_pBracket->nAscent = rInf.GetAscent();
m_pBracket->nHeight = aSize.Height();
- pTmpFnt->SetActual( nActualScr );
+ aTmpFnt.SetActual( nActualScr );
if( nMaxWidth > aSize.Width() )
{
m_pBracket->nPreWidth = aSize.Width();
@@ -417,8 +443,8 @@ void SwDoubleLinePortion::FormatBrackets( SwTextFormatInfo &rInf, SwTwips& nMaxW
{
OUString aStr( m_pBracket->cPost );
if( SW_SCRIPTS > m_pBracket->nPostScript )
- pTmpFnt->SetActual( m_pBracket->nPostScript );
- SwFontSave aSave( rInf, pTmpFnt.get() );
+ aTmpFnt.SetActual( m_pBracket->nPostScript );
+ SwFontSave aSave( rInf, &aTmpFnt );
SwPosSize aSize = rInf.GetTextSize( aStr );
const sal_uInt16 nTmpAsc = rInf.GetAscent();
if( nTmpAsc > m_pBracket->nAscent )
@@ -479,7 +505,7 @@ void SwDoubleLinePortion::CalcBlanks( SwTextFormatInfo &rInf )
rInf.SetIdx( nStart );
}
-tools::Long SwDoubleLinePortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo & ) const
+SwTwips SwDoubleLinePortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo & ) const
{
return HasTabulator() ? 0 : sal_Int32(GetSpaceCnt()) * nSpaceAdd / SPACING_PRECISION_FACTOR;
}
@@ -592,7 +618,7 @@ SwRubyPortion::SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt,
}
OUString aStr = rRuby.GetText().copy( sal_Int32(nOffs) );
- SwFieldPortion *pField = new SwFieldPortion( aStr, std::move(pRubyFont) );
+ SwFieldPortion *pField = new SwFieldPortion( std::move(aStr), std::move(pRubyFont) );
pField->SetNextOffset( nOffs );
pField->SetFollow( true );
@@ -654,9 +680,9 @@ void SwRubyPortion::Adjust_( SwTextFormatInfo &rInf )
TextFrameIndex nSub(0);
switch ( m_nAdjustment )
{
- case css::text::RubyAdjust_CENTER: nRight = static_cast<sal_uInt16>(nLineDiff / 2);
+ case css::text::RubyAdjust_CENTER: nRight = o3tl::narrowing<sal_uInt16>(nLineDiff / 2);
[[fallthrough]];
- case css::text::RubyAdjust_RIGHT: nLeft = static_cast<sal_uInt16>(nLineDiff - nRight); break;
+ case css::text::RubyAdjust_RIGHT: nLeft = o3tl::narrowing<sal_uInt16>(nLineDiff - nRight); break;
case css::text::RubyAdjust_BLOCK: nSub = TextFrameIndex(1);
[[fallthrough]];
case css::text::RubyAdjust_INDENT_BLOCK:
@@ -683,8 +709,8 @@ void SwRubyPortion::Adjust_( SwTextFormatInfo &rInf )
}
if( nLineDiff > 1 )
{
- nRight = static_cast<sal_uInt16>(nLineDiff / 2);
- nLeft = static_cast<sal_uInt16>(nLineDiff - nRight);
+ nRight = o3tl::narrowing<sal_uInt16>(nLineDiff / 2);
+ nLeft = o3tl::narrowing<sal_uInt16>(nLineDiff - nRight);
}
break;
}
@@ -846,12 +872,14 @@ namespace sw {
}
if (m_pMerged)
{
- while (m_CurrentExtent < m_pMerged->extents.size())
+ const auto nExtentsSize = m_pMerged->extents.size();
+ while (m_CurrentExtent < nExtentsSize)
{
sw::Extent const& rExtent(m_pMerged->extents[m_CurrentExtent]);
if (SwpHints const*const pHints = rExtent.pNode->GetpSwpHints())
{
- while (m_CurrentHint < pHints->Count())
+ auto nHintsCount = pHints->Count();
+ while (m_CurrentHint < nHintsCount)
{
SwTextAttr const*const pHint(pHints->Get(m_CurrentHint));
if (rExtent.nEnd < pHint->GetStart())
@@ -867,7 +895,7 @@ namespace sw {
}
}
++m_CurrentExtent;
- if (m_CurrentExtent < m_pMerged->extents.size() &&
+ if (m_CurrentExtent < nExtentsSize &&
rExtent.pNode != m_pMerged->extents[m_CurrentExtent].pNode)
{
m_CurrentHint = 0; // reset
@@ -904,7 +932,7 @@ namespace sw {
// interrupts the first attribute.
// E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute
// with different brackets interrupts another 2-line-attribute.
-std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
+std::optional<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
SwMultiPortion const * pMulti ) const
{
SwScriptInfo& rSI = const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
@@ -936,26 +964,26 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
{
rPos = bFieldBidi ? rPos + TextFrameIndex(1) : rSI.NextDirChg(rPos, &nCurrLevel);
if (TextFrameIndex(COMPLETE_STRING) == rPos)
- return nullptr;
- std::unique_ptr<SwMultiCreator> pRet(new SwMultiCreator);
- pRet->pItem = nullptr;
- pRet->pAttr = nullptr;
- pRet->nStartOfAttr = TextFrameIndex(-1);
- pRet->nId = SwMultiCreatorId::Bidi;
- pRet->nLevel = nCurrLevel + 1;
- return pRet;
+ return {};
+ SwMultiCreator aRet;
+ aRet.pItem = nullptr;
+ aRet.pAttr = nullptr;
+ aRet.nStartOfAttr = TextFrameIndex(-1);
+ aRet.nId = SwMultiCreatorId::Bidi;
+ aRet.nLevel = nCurrLevel + 1;
+ return aRet;
}
// a bidi portion can only contain other bidi portions
if ( pMulti )
- return nullptr;
+ return {};
// need the node that contains input rPos
std::pair<SwTextNode const*, sal_Int32> startPos(m_pFrame->MapViewToModel(rPos));
const SvxCharRotateItem* pActiveRotateItem(nullptr);
- const SfxPoolItem* pNodeRotateItem(nullptr);
+ const SvxCharRotateItem* pNodeRotateItem(nullptr);
const SvxTwoLinesItem* pActiveTwoLinesItem(nullptr);
- const SfxPoolItem* pNodeTwoLinesItem(nullptr);
+ const SvxTwoLinesItem* pNodeTwoLinesItem(nullptr);
SwTextAttr const* pActiveTwoLinesHint(nullptr);
SwTextAttr const* pActiveRotateHint(nullptr);
const SwTextAttr *pRuby = nullptr;
@@ -1004,59 +1032,57 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
}
}
}
- else if (pNode) // !pAttr && pNode means the node changed
+ // !pAttr && pNode means the node changed
+ if (startPos.first->GetIndex() < pNode->GetIndex())
+ {
+ break; // only one node initially
+ }
+ if (startPos.first->GetIndex() == pNode->GetIndex())
{
- if (startPos.first->GetIndex() < pNode->GetIndex())
+ iterAtStartOfNode.Assign(iter);
+ if (SfxItemState::SET == pNode->GetSwAttrSet().GetItemState(
+ RES_CHRATR_ROTATE, true, &pNodeRotateItem) &&
+ pNodeRotateItem->GetValue())
{
- break; // only one node initially
+ pActiveRotateItem = pNodeRotateItem;
}
- if (startPos.first->GetIndex() == pNode->GetIndex())
+ else
{
- iterAtStartOfNode.Assign(iter);
- if (SfxItemState::SET == pNode->GetSwAttrSet().GetItemState(
- RES_CHRATR_ROTATE, true, &pNodeRotateItem) &&
- static_cast<const SvxCharRotateItem*>(pNodeRotateItem)->GetValue())
- {
- pActiveRotateItem = static_cast<const SvxCharRotateItem*>(pNodeRotateItem);
- }
- else
- {
- pNodeRotateItem = nullptr;
- }
- if (SfxItemState::SET == startPos.first->GetSwAttrSet().GetItemState(
- RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem) &&
- static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetValue())
- {
- pActiveTwoLinesItem = static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem);
- }
- else
- {
- pNodeTwoLinesItem = nullptr;
- }
+ pNodeRotateItem = nullptr;
+ }
+ if (SfxItemState::SET == startPos.first->GetSwAttrSet().GetItemState(
+ RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem) &&
+ pNodeTwoLinesItem->GetValue())
+ {
+ pActiveTwoLinesItem = pNodeTwoLinesItem;
+ }
+ else
+ {
+ pNodeTwoLinesItem = nullptr;
}
}
}
if (!pRuby && !pActiveTwoLinesItem && !pActiveRotateItem)
- return nullptr;
+ return {};
if( pRuby )
{ // The winner is ... a ruby attribute and so
// the end of the multiportion is the end of the ruby attribute.
rPos = m_pFrame->MapModelToView(startPos.first, *pRuby->End());
- std::unique_ptr<SwMultiCreator> pRet(new SwMultiCreator);
- pRet->pItem = nullptr;
- pRet->pAttr = pRuby;
- pRet->nStartOfAttr = m_pFrame->MapModelToView(startPos.first, pRet->pAttr->GetStart());
- pRet->nId = SwMultiCreatorId::Ruby;
- pRet->nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
- return pRet;
+ SwMultiCreator aRet;
+ aRet.pItem = nullptr;
+ aRet.pAttr = pRuby;
+ aRet.nStartOfAttr = m_pFrame->MapModelToView(startPos.first, aRet.pAttr->GetStart());
+ aRet.nId = SwMultiCreatorId::Ruby;
+ aRet.nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
+ return aRet;
}
if (pActiveTwoLinesHint ||
- (pNodeTwoLinesItem && pNodeTwoLinesItem == pActiveTwoLinesItem &&
+ (pNodeTwoLinesItem && SfxPoolItem::areSame(pNodeTwoLinesItem, pActiveTwoLinesItem) &&
rPos < TextFrameIndex(GetText().getLength())))
{ // The winner is a 2-line-attribute,
// the end of the multiportion depends on the following attributes...
- std::unique_ptr<SwMultiCreator> pRet(new SwMultiCreator);
+ SwMultiCreator aRet;
// We note the endpositions of the 2-line attributes in aEnd as stack
std::deque<TextFrameIndex> aEnd;
@@ -1068,31 +1094,31 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
if (pActiveTwoLinesHint)
{
- pRet->pItem = nullptr;
- pRet->pAttr = pActiveTwoLinesHint;
- pRet->nStartOfAttr = m_pFrame->MapModelToView(startPos.first, pRet->pAttr->GetStart());
+ aRet.pItem = nullptr;
+ aRet.pAttr = pActiveTwoLinesHint;
+ aRet.nStartOfAttr = m_pFrame->MapModelToView(startPos.first, aRet.pAttr->GetStart());
if (pNodeTwoLinesItem)
{
aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
- bOn = static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetEndBracket() ==
+ bOn = pNodeTwoLinesItem->GetEndBracket() ==
pActiveTwoLinesItem->GetEndBracket() &&
- static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetStartBracket() ==
+ pNodeTwoLinesItem->GetStartBracket() ==
pActiveTwoLinesItem->GetStartBracket();
}
else
{
- aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *pRet->pAttr->End()));
+ aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *aRet.pAttr->End()));
}
}
else
{
- pRet->pItem = pNodeTwoLinesItem;
- pRet->pAttr = nullptr;
- pRet->nStartOfAttr = TextFrameIndex(-1);
+ aRet.pItem = pNodeTwoLinesItem;
+ aRet.pAttr = nullptr;
+ aRet.nStartOfAttr = TextFrameIndex(-1);
aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
}
- pRet->nId = SwMultiCreatorId::Double;
- pRet->nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
+ aRet.nId = SwMultiCreatorId::Double;
+ aRet.nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
// pActiveTwoLinesHint is the last 2-line-attribute, which contains
// the actual position.
@@ -1134,9 +1160,8 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
}
else
{
- pNodeTwoLinesItem = nullptr;
- pNode->GetSwAttrSet().GetItemState(
- RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem);
+ pNodeTwoLinesItem = pNode->GetSwAttrSet().GetItemIfSet(
+ RES_CHRATR_TWO_LINES);
nTmpStart = m_pFrame->MapModelToView(pNode, 0);
nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
assert(rPos <= nTmpEnd); // next node must not have smaller index
@@ -1171,7 +1196,7 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
}
// A ruby attribute stops the 2-line immediately
if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
- return pRet;
+ return aRet;
if (pTmp ? lcl_Has2Lines(*pTmp, pActiveTwoLinesItem, bTwo)
: lcl_Check2Lines(pNodeTwoLinesItem, pActiveTwoLinesItem, bTwo))
{ // We have an interesting attribute...
@@ -1198,15 +1223,15 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
}
if( bOn && !aEnd.empty() )
rPos = aEnd.back();
- return pRet;
+ return aRet;
}
if (pActiveRotateHint ||
- (pNodeRotateItem && pNodeRotateItem == pActiveRotateItem &&
+ (pNodeRotateItem && SfxPoolItem::areSame(pNodeRotateItem, pActiveRotateItem) &&
rPos < TextFrameIndex(GetText().getLength())))
{ // The winner is a rotate-attribute,
// the end of the multiportion depends on the following attributes...
- std::unique_ptr<SwMultiCreator> pRet(new SwMultiCreator);
- pRet->nId = SwMultiCreatorId::Rotate;
+ SwMultiCreator aRet;
+ aRet.nId = SwMultiCreatorId::Rotate;
// We note the endpositions of the 2-line attributes in aEnd as stack
std::deque<TextFrameIndex> aEnd;
@@ -1239,9 +1264,8 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
}
else
{
- pNodeTwoLinesItem = nullptr;
- pNode->GetSwAttrSet().GetItemState(
- RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem);
+ pNodeTwoLinesItem = pNode->GetSwAttrSet().GetItemIfSet(
+ RES_CHRATR_TWO_LINES);
nTmpStart = m_pFrame->MapModelToView(pNode, 0);
nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
assert(n2Start <= nTmpEnd); // next node must not have smaller index
@@ -1299,25 +1323,25 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
bOn = true;
if (pActiveRotateHint)
{
- pRet->pItem = nullptr;
- pRet->pAttr = pActiveRotateHint;
- pRet->nStartOfAttr = m_pFrame->MapModelToView(startPos.first, pRet->pAttr->GetStart());
+ aRet.pItem = nullptr;
+ aRet.pAttr = pActiveRotateHint;
+ aRet.nStartOfAttr = m_pFrame->MapModelToView(startPos.first, aRet.pAttr->GetStart());
if (pNodeRotateItem)
{
aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
- bOn = static_cast<const SvxCharRotateItem*>(pNodeRotateItem)->GetValue() ==
+ bOn = pNodeRotateItem->GetValue() ==
pActiveRotateItem->GetValue();
}
else
{
- aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *pRet->pAttr->End()));
+ aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *aRet.pAttr->End()));
}
}
else
{
- pRet->pItem = pNodeRotateItem;
- pRet->pAttr = nullptr;
- pRet->nStartOfAttr = TextFrameIndex(-1);
+ aRet.pItem = pNodeRotateItem;
+ aRet.pAttr = nullptr;
+ aRet.nStartOfAttr = TextFrameIndex(-1);
aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
}
for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
@@ -1340,9 +1364,8 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
}
else
{
- pNodeRotateItem = nullptr;
- pNode->GetSwAttrSet().GetItemState(
- RES_CHRATR_ROTATE, true, &pNodeRotateItem);
+ pNodeRotateItem = pNode->GetSwAttrSet().GetItemIfSet(
+ RES_CHRATR_ROTATE);
nTmpStart = m_pFrame->MapModelToView(pNode, 0);
nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
assert(rPos <= nTmpEnd); // next node must not have smaller index
@@ -1394,9 +1417,9 @@ std::unique_ptr<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &
rPos = aEnd.back();
if( rPos > n2Start )
rPos = n2Start;
- return pRet;
+ return aRet;
}
- return nullptr;
+ return {};
}
namespace {
@@ -1557,7 +1580,7 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
SwSpaceManipulator aManip( GetInfo(), rMulti );
- std::unique_ptr<SwFontSave> pFontSave;
+ std::optional<SwFontSave> oFontSave;
std::unique_ptr<SwFont> pTmpFnt;
if( rMulti.IsDouble() )
@@ -1568,16 +1591,19 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
SetPropFont( 50 );
pTmpFnt->SetProportion( GetPropFont() );
}
- pFontSave.reset(new SwFontSave( GetInfo(), pTmpFnt.get(), this ));
+ oFontSave.emplace( GetInfo(), pTmpFnt.get(), this );
}
else
{
- pFontSave = nullptr;
pTmpFnt = nullptr;
}
if( rMulti.HasBrackets() )
{
+ // WP is mandatory
+ Por_Info const por(rMulti, *this, 1);
+ SwTaggedPDFHelper const tag(nullptr, nullptr, &por, *GetInfo().GetOut());
+
TextFrameIndex const nTmpOldIdx = GetInfo().GetIdx();
GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart);
SeekAndChg( GetInfo() );
@@ -1636,8 +1662,18 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
OSL_ENSURE( nullptr == GetInfo().GetUnderFnt() || rMulti.IsBidi(),
" Only BiDi portions are allowed to use the common underlining font" );
- if ( rMulti.IsRuby() )
+ ::std::optional<SwTaggedPDFHelper> oTag;
+ if (rMulti.IsDouble())
+ {
+ Por_Info const por(rMulti, *this, 2);
+ oTag.emplace(nullptr, nullptr, &por, *GetInfo().GetOut());
+ }
+ else if (rMulti.IsRuby())
+ {
+ Por_Info const por(rMulti, *this, bRubyTop ? 1 : 2);
+ oTag.emplace(nullptr, nullptr, &por, *GetInfo().GetOut());
GetInfo().SetRuby( rMulti.OnTop() );
+ }
do
{
@@ -1679,7 +1715,7 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
}
else if ( rMulti.IsRuby() && rMulti.OnRight() && GetInfo().IsRuby() )
{
- SwTwips nLineDiff = std::max(( rMulti.GetRoot().Height() - pPor->Width() ) / 2, 0 );
+ SwTwips nLineDiff = std::max(( rMulti.GetRoot().Height() - pPor->Width() ) / 2, static_cast<SwTwips>(0) );
GetInfo().Y( nOfst + nLineDiff );
// Draw the ruby text on top of the preserved space.
GetInfo().X( GetInfo().X() - pPor->Height() );
@@ -1740,7 +1776,15 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
PaintMultiPortion( rPaint, static_cast<SwMultiPortion&>(*pPor), &rMulti );
}
else
+ {
+ Por_Info const por(*pPor, *this, 0);
+ SwTaggedPDFHelper const tag(nullptr, nullptr, &por, *GetInfo().GetOut());
+
pPor->Paint( GetInfo() );
+ }
+
+ if (GetFnt()->IsURL() && pPor->InTextGrp())
+ GetInfo().NotifyURL(*pPor);
bFirst &= !pPor->GetLen();
if( pNext || !pPor->IsMarginPortion() )
@@ -1799,9 +1843,20 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
// We switch to the baseline of the next inner line
nOfst += rMulti.GetRoot().Height();
}
+ if (rMulti.IsRuby())
+ {
+ oTag.reset();
+ Por_Info const por(rMulti, *this, bRubyTop ? 2 : 1);
+ oTag.emplace(nullptr, nullptr, &por, *GetInfo().GetOut());
+ }
}
} while( pPor );
+ if (rMulti.IsDouble())
+ {
+ oTag.reset();
+ }
+
if ( bRubyInGrid )
GetInfo().SetSnapToGrid( bOldGridModeAllowed );
@@ -1817,6 +1872,10 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
if( rMulti.HasBrackets() )
{
+ // WP is mandatory
+ Por_Info const por(rMulti, *this, 1);
+ SwTaggedPDFHelper const tag(nullptr, nullptr, &por, *GetInfo().GetOut());
+
TextFrameIndex const nTmpOldIdx = GetInfo().GetIdx();
GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart);
SeekAndChg( GetInfo() );
@@ -1828,7 +1887,7 @@ void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
// Restore the saved values
GetInfo().X( nOldX );
GetInfo().SetLen( nOldLen );
- pFontSave.reset();
+ oFontSave.reset();
pTmpFnt.reset();
SetPropFont( 0 );
}
@@ -1898,16 +1957,17 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
}
SeekAndChg( rInf );
- std::unique_ptr<SwFontSave> xFontSave;
+ std::optional<SwFontSave> oFontSave;
+ std::unique_ptr<SwFont> xTmpFont;
if( rMulti.IsDouble() )
{
- SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
+ xTmpFont.reset(new SwFont( *rInf.GetFont() ));
if( rMulti.IsDouble() )
{
SetPropFont( 50 );
- pTmpFnt->SetProportion( GetPropFont() );
+ xTmpFont->SetProportion( GetPropFont() );
}
- xFontSave.reset(new SwFontSave(rInf, pTmpFnt, this));
+ oFontSave.emplace(rInf, xTmpFont.get(), this);
}
SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
@@ -1995,7 +2055,7 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
// save some values
const OUString* pOldText = &(rInf.GetText());
const SwTwips nOldPaintOfst = rInf.GetPaintOfst();
- std::shared_ptr<vcl::TextLayoutCache> const pOldCachedVclData(rInf.GetCachedVclData());
+ std::shared_ptr<const vcl::text::TextLayoutCache> const pOldCachedVclData(rInf.GetCachedVclData());
rInf.SetCachedVclData(nullptr);
OUString const aMultiStr( rInf.GetText().copy(0, sal_Int32(nMultiLen + rInf.GetIdx())) );
@@ -2285,6 +2345,9 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
// we try to keep our ruby portion together
lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
pTmp = nullptr;
+ // A follow field portion may still be waiting. If nobody wants
+ // it, we delete it.
+ delete pNextSecond;
}
}
else if( rMulti.HasRotation() )
@@ -2354,10 +2417,15 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
SeekAndChg( rInf );
delete pFirstRest;
delete pSecondRest;
- xFontSave.reset();
+ oFontSave.reset();
return bRet;
}
+static bool IsIncompleteRuby(const SwMultiPortion& rHelpMulti)
+{
+ return rHelpMulti.IsRuby() && static_cast<const SwRubyPortion&>(rHelpMulti).GetRubyOffset() < TextFrameIndex(COMPLETE_STRING);
+}
+
// When a fieldportion at the end of line breaks and needs a following
// fieldportion in the next line, then the "restportion" of the formatinfo
// has to be set. Normally this happens during the formatting of the first
@@ -2453,7 +2521,7 @@ SwLinePortion* SwTextFormatter::MakeRestPortion( const SwLineLayout* pLine,
return pRest;
nPosition = nMultiPos + pHelpMulti->GetLen();
- std::unique_ptr<SwMultiCreator> pCreate = GetInfo().GetMultiCreator( nMultiPos, nullptr );
+ std::optional<SwMultiCreator> pCreate = GetInfo().GetMultiCreator( nMultiPos, nullptr );
if ( !pCreate )
{
@@ -2466,19 +2534,19 @@ SwLinePortion* SwTextFormatter::MakeRestPortion( const SwLineLayout* pLine,
if (!pCreate)
return pRest;
- if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() &&
- static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset() < TextFrameIndex(COMPLETE_STRING)))
+ if( pRest || nMultiPos > nPosition || IsIncompleteRuby(*pHelpMulti))
{
SwMultiPortion* pTmp;
if( pHelpMulti->IsDouble() )
pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos );
else if( pHelpMulti->IsBidi() )
pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel );
- else if( pHelpMulti->IsRuby() )
+ else if (IsIncompleteRuby(*pHelpMulti) && pCreate->pAttr)
{
+ TextFrameIndex nRubyOffset = static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset();
pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(),
m_pFrame->GetDoc().getIDocumentSettingAccess(),
- nMultiPos, static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset(),
+ nMultiPos, nRubyOffset,
GetInfo() );
}
else if( pHelpMulti->HasRotation() )
@@ -2510,7 +2578,7 @@ SwLinePortion* SwTextFormatter::MakeRestPortion( const SwLineLayout* pLine,
SwTextCursorSave::SwTextCursorSave( SwTextCursor* pCursor,
SwMultiPortion* pMulti,
SwTwips nY,
- sal_uInt16& nX,
+ SwTwips& nX,
TextFrameIndex const nCurrStart,
tools::Long nSpaceAdd )
: pTextCursor(pCursor),
@@ -2544,7 +2612,7 @@ SwTextCursorSave::SwTextCursorSave( SwTextCursor* pCursor,
}
if( nSpaceAdd > 0 && !pMulti->HasTabulator() )
- pCursor->m_pCurr->Width( static_cast<sal_uInt16>(nWidth + nSpaceAdd * sal_Int32(nSpaceCnt) / SPACING_PRECISION_FACTOR) );
+ pCursor->m_pCurr->Width( o3tl::narrowing<sal_uInt16>(nWidth + nSpaceAdd * sal_Int32(nSpaceCnt) / SPACING_PRECISION_FACTOR) );
// For a BidiPortion we have to calculate the offset from the
// end of the portion
diff --git a/sw/source/core/text/pormulti.hxx b/sw/source/core/text/pormulti.hxx
index c94dd3125629..e5a3da2b329c 100644
--- a/sw/source/core/text/pormulti.hxx
+++ b/sw/source/core/text/pormulti.hxx
@@ -77,7 +77,7 @@ struct SwBracket
// or phonetics (ruby)
// or combined characters
// or a rotated portion.
-class SwMultiPortion : public SwLinePortion
+class SAL_DLLPUBLIC_RTTI SwMultiPortion : public SwLinePortion
{
SwLineLayout m_aRoot; // One or more lines
bool m_bTab1 :1; // First line tabulator
@@ -136,7 +136,7 @@ public:
void ActualizeTabulator();
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
- virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
+ virtual SwTwips CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
virtual bool ChgSpaceAdd( SwLineLayout* pCurr, tools::Long nSpaceAdd ) const;
// Summarize the internal lines to calculate the (external) size
@@ -149,6 +149,9 @@ public:
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
};
class SwDoubleLinePortion : public SwMultiPortion
@@ -180,7 +183,7 @@ public:
TextFrameIndex GetSmallerSpaceCnt() const
{ return ( m_nLineDiff < 0 ) ? m_nBlank1 : m_nBlank2; }
- virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
+ virtual SwTwips CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
virtual bool ChgSpaceAdd( SwLineLayout* pCurr, tools::Long nSpaceAdd ) const override;
};
@@ -224,7 +227,7 @@ public:
// Get number of blanks for justified alignment
TextFrameIndex GetSpaceCnt(const SwTextSizeInfo &rInf) const;
// Calculates extra spacing based on number of blanks
- virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
+ virtual SwTwips CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
// Manipulate the spacing array at pCurr
virtual bool ChgSpaceAdd( SwLineLayout* pCurr, tools::Long nSpaceAdd ) const override;
};
@@ -241,7 +244,7 @@ class SwTextCursorSave
bool bSpaceChg;
public:
SwTextCursorSave( SwTextCursor* pTextCursor, SwMultiPortion* pMulti,
- SwTwips nY, sal_uInt16& nX, TextFrameIndex nCurrStart, tools::Long nSpaceAdd);
+ SwTwips nY, SwTwips& nX, TextFrameIndex nCurrStart, tools::Long nSpaceAdd);
~SwTextCursorSave();
};
diff --git a/sw/source/core/text/porref.cxx b/sw/source/core/text/porref.cxx
index 502b681c0803..a64f2df1fe1d 100644
--- a/sw/source/core/text/porref.cxx
+++ b/sw/source/core/text/porref.cxx
@@ -45,7 +45,7 @@ sal_uInt16 SwIsoRefPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
// Although we are const, nViewWidth should be calculated in the last
// moment possible
SwIsoRefPortion* pThis = const_cast<SwIsoRefPortion*>(this);
- if( !Width() && rInf.OnWin() && SwViewOption::IsFieldShadings() &&
+ if( !Width() && rInf.OnWin() && rInf.GetOpt().IsFieldShadings() &&
!rInf.GetOpt().IsReadonly() && !rInf.GetOpt().IsPagePreview() )
{
if( !m_nViewWidth )
diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx
index 6f578e0744cd..ff1e029ae141 100644
--- a/sw/source/core/text/porrst.cxx
+++ b/sw/source/core/text/porrst.cxx
@@ -22,7 +22,10 @@
#include <editeng/escapementitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/pgrditem.hxx>
+#include <editeng/fontitem.hxx>
#include <vcl/svapp.hxx>
+#include <comphelper/scopeguard.hxx>
+
#include <viewsh.hxx>
#include <viewopt.hxx>
#include <ndtxt.hxx>
@@ -39,12 +42,19 @@
#include "redlnitr.hxx"
#include "atrhndl.hxx"
#include <rootfrm.hxx>
+#include <formatlinebreak.hxx>
+#include <txatbase.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
#include <crsrsh.hxx>
+#include <swtypes.hxx>
+#include <strings.hrc>
+#include <flyfrms.hxx>
+#include <bodyfrm.hxx>
SwTmpEndPortion::SwTmpEndPortion( const SwLinePortion &rPortion,
const FontLineStyle eUL,
@@ -66,6 +76,16 @@ void SwTmpEndPortion::Paint( const SwTextPaintInfo &rInf ) const
SwFont aFont(*pOldFnt);
+ const SwDoc& rDoc = rInf.GetTextFrame()->GetDoc();
+ if (aFont.IsSymbol(rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()))
+ {
+ const SvxFontItem& rFontItem = rDoc.GetDefault(RES_CHRATR_FONT);
+ aFont.SetName( rFontItem.GetFamilyName(), SwFontScript::Latin );
+ aFont.SetStyleName( rFontItem.GetStyleName(), SwFontScript::Latin );
+ aFont.SetFamily( rFontItem.GetFamily(), SwFontScript::Latin );
+ aFont.SetPitch( rFontItem.GetPitch(), SwFontScript::Latin );
+ aFont.SetCharSet( rFontItem.GetCharSet(), SwFontScript::Latin );
+ }
// Paint strikeout/underline based on redline color and settings
// (with an extra pilcrow in the background, because there is
// no SetStrikeoutColor(), also SetUnderColor() doesn't work()).
@@ -93,12 +113,19 @@ void SwTmpEndPortion::Paint( const SwTextPaintInfo &rInf ) const
const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt));
}
-SwBreakPortion::SwBreakPortion( const SwLinePortion &rPortion )
+SwBreakPortion::SwBreakPortion( const SwLinePortion &rPortion, const SwTextAttr* pAttr )
: SwLinePortion( rPortion )
{
mnLineLength = TextFrameIndex(1);
m_eRedline = RedlineType::None;
SetWhichPor( PortionType::Break );
+
+ m_eClear = SwLineBreakClear::NONE;
+ if (pAttr && pAttr->Which() == RES_TXTATR_LINEBREAK)
+ {
+ m_eClear = pAttr->GetLineBreak().GetValue();
+ }
+ m_nTextHeight = 0;
}
TextFrameIndex SwBreakPortion::GetModelPositionForViewPoint(const sal_uInt16) const
@@ -117,37 +144,58 @@ void SwBreakPortion::Paint( const SwTextPaintInfo &rInf ) const
if( !(rInf.OnWin() && rInf.GetOpt().IsLineBreak()) )
return;
+ // Reduce height to text height for the duration of the print, so the vertical height will look
+ // correct for the line break character, even for clearing breaks.
+ SwTwips nHeight = Height();
+ SwTwips nVertPosOffset = (nHeight - m_nTextHeight) / 2;
+ auto pPortion = const_cast<SwBreakPortion*>(this);
+ pPortion->Height(m_nTextHeight, false);
+ if (rInf.GetTextFrame()->IsVertical())
+ {
+ // Compensate for the offset done in SwTextCursor::AdjustBaseLine() for the vertical case.
+ const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() + nVertPosOffset);
+ }
+ comphelper::ScopeGuard g(
+ [pPortion, nHeight, &rInf, nVertPosOffset]
+ {
+ if (rInf.GetTextFrame()->IsVertical())
+ {
+ const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() - nVertPosOffset);
+ }
+ pPortion->Height(nHeight, false);
+ });
+
rInf.DrawLineBreak( *this );
// paint redlining
- if (m_eRedline != RedlineType::None)
+ if (m_eRedline == RedlineType::None)
+ return;
+
+ sal_Int16 nNoBreakWidth = rInf.GetTextSize(S_NOBREAK_FOR_REDLINE).Width();
+ if ( nNoBreakWidth > 0 )
{
- sal_Int16 nNoBreakWidth = rInf.GetTextSize(S_NOBREAK_FOR_REDLINE).Width();
- if ( nNoBreakWidth > 0 )
- {
- // approximate portion size with multiple no-break spaces
- // and draw these spaces (at least a single one) by DrawText
- // painting the requested redline underline/strikeout
- sal_Int16 nSpaces = (LINE_BREAK_WIDTH + nNoBreakWidth/2) / nNoBreakWidth;
- OUStringBuffer aBuf(S_NOBREAK_FOR_REDLINE);
- for (sal_Int16 i = 1; i < nSpaces; ++i)
- aBuf.append(S_NOBREAK_FOR_REDLINE);
+ // approximate portion size with multiple no-break spaces
+ // and draw these spaces (at least a single one) by DrawText
+ // painting the requested redline underline/strikeout
+ sal_Int16 nSpaces = (LINE_BREAK_WIDTH + nNoBreakWidth/2) / nNoBreakWidth;
+ OUStringBuffer aBuf(S_NOBREAK_FOR_REDLINE);
+ for (sal_Int16 i = 1; i < nSpaces; ++i)
+ aBuf.append(S_NOBREAK_FOR_REDLINE);
- const SwFont* pOldFnt = rInf.GetFont();
+ const SwFont* pOldFnt = rInf.GetFont();
- SwFont aFont(*pOldFnt);
+ SwFont aFont(*pOldFnt);
- if (m_eRedline == RedlineType::Delete)
- aFont.SetUnderline( LINESTYLE_NONE );
- else
- aFont.SetStrikeout( STRIKEOUT_NONE );
+ if (m_eRedline == RedlineType::Delete)
+ aFont.SetUnderline( LINESTYLE_NONE );
+ else
+ aFont.SetStrikeout( STRIKEOUT_NONE );
- const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
+ const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
- rInf.DrawText(aBuf.makeStringAndClear(), *this);
+ rInf.DrawText(aBuf.makeStringAndClear(), *this);
- const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt));
- }
+ const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt));
}
}
@@ -156,6 +204,29 @@ bool SwBreakPortion::Format( SwTextFormatInfo &rInf )
const SwLinePortion *pRoot = rInf.GetRoot();
Width( 0 );
Height( pRoot->Height() );
+ m_nTextHeight = Height();
+
+ // See if this is a clearing break. If so, calculate how much we need to "jump down" so the next
+ // line can again use the full text width.
+ SwLineBreakClear eClear = m_eClear;
+ if (rInf.GetTextFrame()->IsRightToLeft() && eClear != SwLineBreakClear::ALL)
+ {
+ // RTL ignores left/right breaks.
+ eClear = SwLineBreakClear::NONE;
+ }
+ if (eClear != SwLineBreakClear::NONE)
+ {
+ SwTextFly& rTextFly = rInf.GetTextFly();
+ if (rTextFly.IsOn())
+ {
+ SwTwips nHeight = rTextFly.GetMaxBottom(*this, rInf) - rInf.Y();
+ if (nHeight > Height())
+ {
+ Height(nHeight, /*bText=*/false);
+ }
+ }
+ }
+
SetAscent( pRoot->GetAscent() );
if (rInf.GetIdx() + TextFrameIndex(1) == TextFrameIndex(rInf.GetText().getLength()))
rInf.SetNewLine( true );
@@ -167,6 +238,21 @@ void SwBreakPortion::HandlePortion( SwPortionHandler& rPH ) const
rPH.Text( GetLen(), GetWhichPor() );
}
+void SwBreakPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex&
+ nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwBreakPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("text-height"),
+ BAD_CAST(OString::number(m_nTextHeight).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwLineBreakClear SwBreakPortion::GetClear() const { return m_eClear; }
+
SwKernPortion::SwKernPortion( SwLinePortion &rPortion, short nKrn,
bool bBG, bool bGK ) :
m_nKern( nKrn ), m_bBackground( bBG ), m_bGridKern( bGK )
@@ -243,7 +329,7 @@ SwArrowPortion::SwArrowPortion( const SwLinePortion &rPortion ) :
SwArrowPortion::SwArrowPortion( const SwTextPaintInfo &rInf )
: m_bLeft( false )
{
- Height( static_cast<sal_uInt16>(rInf.GetTextFrame()->getFramePrintArea().Height()) );
+ Height( o3tl::narrowing<sal_uInt16>(rInf.GetTextFrame()->getFramePrintArea().Height()) );
m_aPos.setX( rInf.GetTextFrame()->getFrameArea().Left() +
rInf.GetTextFrame()->getFramePrintArea().Right() );
m_aPos.setY( rInf.GetTextFrame()->getFrameArea().Top() +
@@ -338,7 +424,18 @@ bool SwTextFrame::FormatEmpty()
bool bCollapse = EmptyHeight( ) == 1 && IsCollapse( );
// sw_redlinehide: just disable FormatEmpty optimisation for now
- if (HasFollow() || GetMergedPara() || GetTextNodeFirst()->GetpSwpHints() ||
+ // Split fly frames: non-last parts of the anchor want this optimization to clear the old
+ // content.
+ SwFlyAtContentFrame* pNonLastSplitFlyDrawObj = HasNonLastSplitFlyDrawObj();
+ bool bHasNonLastSplitFlyDrawObj = pNonLastSplitFlyDrawObj != nullptr;
+
+ if (pNonLastSplitFlyDrawObj && pNonLastSplitFlyDrawObj->IsWrapOnAllPages())
+ {
+ // Split fly: the anchor is non-empty on all pages in the "wrap on all pages" case.
+ bHasNonLastSplitFlyDrawObj = false;
+ }
+
+ if ((HasFollow() && !bHasNonLastSplitFlyDrawObj) || GetMergedPara() || (GetTextNodeFirst()->GetpSwpHints() && !bHasNonLastSplitFlyDrawObj) ||
nullptr != GetTextNodeForParaProps()->GetNumRule() ||
GetTextNodeFirst()->HasHiddenCharAttribute(true) ||
IsInFootnote() || ( HasPara() && GetPara()->IsPrepMustFit() ) )
@@ -352,18 +449,27 @@ bool SwTextFrame::FormatEmpty()
const SvxLineSpacingItem &rSpacing = aSet.GetLineSpacing();
if( !bCollapse && ( SvxLineSpaceRule::Min == rSpacing.GetLineSpaceRule() ||
SvxLineSpaceRule::Fix == rSpacing.GetLineSpaceRule() ||
- aSet.GetLRSpace().IsAutoFirst() ) )
+ aSet.GetFirstLineIndent().IsAutoFirst()))
+ {
return false;
+ }
SwTextFly aTextFly( this );
SwRect aRect;
bool bFirstFlyCheck = 0 != getFramePrintArea().Height();
if ( !bCollapse && bFirstFlyCheck &&
- aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) )
+ aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) && !bHasNonLastSplitFlyDrawObj )
return false;
+ if (IsEmptyWithSplitFly())
+ {
+ // We don't want this optimization in case the paragraph is not really empty, because it has
+ // a fly frame and it also needs space for the empty paragraph in a next line.
+ return false;
+ }
+
// only need to check one node because of early return on GetMerged()
- for (SwIndex const* pIndex = GetTextNodeFirst()->GetFirstIndex();
+ for (SwContentIndex const* pIndex = GetTextNodeFirst()->GetFirstIndex();
pIndex; pIndex = pIndex->GetNext())
{
sw::mark::IMark const*const pMark = pIndex->GetMark();
@@ -384,7 +490,26 @@ bool SwTextFrame::FormatEmpty()
}
SwRectFnSet aRectFnSet(this);
- const SwTwips nChg = nHeight - aRectFnSet.GetHeight(getFramePrintArea());
+ SwTwips nChg = nHeight - aRectFnSet.GetHeight(getFramePrintArea());
+ const SwBodyFrame* pBody = FindBodyFrame();
+ if (pNonLastSplitFlyDrawObj && pBody)
+ {
+ // See if we need to increase the text frame height due to split flys. This is necessary for
+ // anchors of inner floating tables, where moving to a next page moves indirectly, so we
+ // want a correct text frame height.
+ SwTwips nFrameBottom = aRectFnSet.GetBottom(getFrameArea()) + nChg;
+ SwTwips nFlyBottom = aRectFnSet.GetBottom(pNonLastSplitFlyDrawObj->getFrameArea());
+ SwTwips nBodyBottom = aRectFnSet.GetBottom(pBody->getFrameArea());
+ if (nFlyBottom > nBodyBottom)
+ {
+ // This is the legacy case where flys may overlap with footer frames.
+ nFlyBottom = nBodyBottom;
+ }
+ if (pNonLastSplitFlyDrawObj->isFrameAreaPositionValid() && nFlyBottom > nFrameBottom)
+ {
+ nChg += (nFlyBottom - nFrameBottom);
+ }
+ }
if( !nChg )
SetUndersized( false );
@@ -455,7 +580,7 @@ bool SwTextFrame::FillRegister( SwTwips& rRegStart, sal_uInt16& rRegDiff )
pOut = GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
if( pSh && !pOut )
- pOut = pSh->GetWin();
+ pOut = pSh->GetWin()->GetOutDev();
if( !pOut )
pOut = Application::GetDefaultDevice();
@@ -493,7 +618,7 @@ bool SwTextFrame::FillRegister( SwTwips& rRegStart, sal_uInt16& rRegDiff )
nTmp /= 100;
if( !nTmp )
++nTmp;
- rRegDiff = static_cast<sal_uInt16>(nTmp);
+ rRegDiff = o3tl::narrowing<sal_uInt16>(nTmp);
nNetHeight = rRegDiff;
break;
}
@@ -527,7 +652,7 @@ void SwHiddenTextPortion::Paint( const SwTextPaintInfo & rInf) const
{
#ifdef DBG_UTIL
OutputDevice* pOut = const_cast<OutputDevice*>(rInf.GetOut());
- Color aCol( SwViewOption::GetFieldShadingsColor() );
+ Color aCol( rInf.GetOpt().GetFieldShadingsColor() );
Color aOldColor( pOut->GetFillColor() );
pOut->SetFillColor( aCol );
Point aPos( rInf.GetPos() );
@@ -549,10 +674,10 @@ bool SwHiddenTextPortion::Format( SwTextFormatInfo &rInf )
return false;
};
-bool SwControlCharPortion::DoPaint(SwTextPaintInfo const&,
+bool SwControlCharPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo,
OUString & rOutString, SwFont & rTmpFont, int &) const
{
- if (mcChar == CHAR_WJ || !SwViewOption::IsFieldShadings())
+ if (mcChar == CHAR_WJ || !rTextPaintInfo.GetOpt().IsFieldShadings())
{
return false;
}
@@ -580,6 +705,7 @@ bool SwControlCharPortion::DoPaint(SwTextPaintInfo const&,
bool SwBookmarkPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo,
OUString & rOutString, SwFont & rFont, int & rDeltaY) const
{
+ // custom color is visible without field shading, too
if (!rTextPaintInfo.GetOpt().IsShowBookmarks())
{
return false;
@@ -594,10 +720,11 @@ bool SwBookmarkPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo,
Size aSize(rFont.GetSize(rFont.GetActual()));
// use also the external leading (line gap) of the portion, but don't use
// 100% of it because i can't figure out how to baseline align that
- auto const nFactor = (Height() * 95) / aSize.Height();
+ assert(aSize.Height() != 0);
+ auto const nFactor = aSize.Height() > 0 ? (Height() * 95) / aSize.Height() : Height();
rFont.SetProportion(nFactor);
rFont.SetWeight(WEIGHT_THIN, rFont.GetActual());
- rFont.SetColor(NON_PRINTING_CHARACTER_COLOR);
+ rFont.SetColor(rTextPaintInfo.GetOpt().GetFieldShadingsColor());
// reset these to default...
rFont.SetAlign(ALIGN_BASELINE);
rFont.SetUnderline(LINESTYLE_NONE);
@@ -668,6 +795,151 @@ void SwControlCharPortion::Paint( const SwTextPaintInfo &rInf ) const
const_cast< SwTextPaintInfo& >( rInf ).SetPos( aOldPos );
}
+void SwBookmarkPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if ( !Width() ) // is only set during prepaint mode
+ return;
+
+ rInf.DrawViewOpt(*this, GetWhichPor());
+
+ int deltaY(0);
+ SwFont aTmpFont( *rInf.GetFont() );
+ OUString aOutString;
+
+ if (!(rInf.OnWin()
+ && !rInf.GetOpt().IsPagePreview()
+ && !rInf.GetOpt().IsReadonly()
+ && DoPaint(rInf, aOutString, aTmpFont, deltaY)))
+ return;
+
+ SwFontSave aFontSave( rInf, &aTmpFont );
+
+ if ( !mnHalfCharWidth )
+ mnHalfCharWidth = rInf.GetTextSize( aOutString ).Width() / 2;
+
+ Point aOldPos = rInf.GetPos();
+ Point aNewPos( aOldPos );
+ auto const deltaX((Width() / 2) - mnHalfCharWidth);
+ switch (rInf.GetFont()->GetOrientation(rInf.GetTextFrame()->IsVertical()).get())
+ {
+ case 0:
+ aNewPos.AdjustX(deltaX);
+ aNewPos.AdjustY(deltaY);
+ break;
+ case 900:
+ aNewPos.AdjustY(-deltaX);
+ aNewPos.AdjustX(deltaY);
+ break;
+ case 2700:
+ aNewPos.AdjustY(deltaX);
+ aNewPos.AdjustX(-deltaY);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ // draw end marks before the character position
+ if ( m_nStart == 0 || m_nEnd == 0 )
+ {
+ // single type boundary marks are there outside of the bookmark text
+ // some |text| here
+ // [[ ]]
+ if (m_nStart > 1)
+ aNewPos.AdjustX(static_cast<tools::Long>(mnHalfCharWidth) * -2 * (m_aColors.size() - 1));
+ }
+ else if ( m_nStart != 0 && m_nEnd != 0 )
+ // both end and start boundary marks: adjust them around the bookmark position
+ // |te|xt|
+ // ]] [[
+ aNewPos.AdjustX(static_cast<tools::Long>(mnHalfCharWidth) * -(2 * m_nEnd - 1 + m_nPoint) );
+
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
+
+ for ( const auto& it : m_aColors )
+ {
+ // set bold for custom colored bookmark symbol
+ // and draw multiple symbols showing all custom colors
+ aTmpFont.SetWeight( COL_TRANSPARENT == std::get<1>(it) ? WEIGHT_THIN : WEIGHT_BOLD, aTmpFont.GetActual() );
+ aTmpFont.SetColor( COL_TRANSPARENT == std::get<1>(it) ? rInf.GetOpt().GetFieldShadingsColor() : std::get<1>(it) );
+ aOutString = OUString(std::get<0>(it) == SwScriptInfo::MarkKind::Start ? '[' : ']');
+
+ // MarkKind::Point: drawn I-beam (e.g. U+2336) as overlapping ][
+ if ( std::get<0>(it) == SwScriptInfo::MarkKind::Point )
+ {
+ aNewPos.AdjustX(-mnHalfCharWidth * 5/16);
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
+ rInf.DrawText( aOutString, *this );
+
+ // when the overlapping vertical lines are 50 pixel width on the screen,
+ // this distance (half width * 5/8) still results precise overlapping
+ aNewPos.AdjustX(mnHalfCharWidth * 5/8);
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
+ aOutString = OUString('[');
+ }
+ rInf.DrawText( aOutString, *this );
+ // place the next symbol after the previous one
+ // TODO: fix orientation and start/end
+ aNewPos.AdjustX(mnHalfCharWidth * 2);
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
+ }
+
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aOldPos );
+}
+
+void SwBookmarkPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ OUStringBuffer aStr;
+ for ( const auto& it : m_aColors )
+ {
+ aStr.append("#" + std::get<2>(it) + " " + SwResId(STR_BOOKMARK_DEF_NAME));
+ switch (std::get<0>(it))
+ {
+ case SwScriptInfo::MarkKind::Point:
+ break;
+ case SwScriptInfo::MarkKind::Start:
+ aStr.append(" " + SwResId(STR_CAPTION_BEGINNING));
+ break;
+ case SwScriptInfo::MarkKind::End:
+ aStr.append(" " + SwResId(STR_CAPTION_END));
+ break;
+ }
+ }
+
+ rPH.Special( GetLen(), aStr.makeStringAndClear(), GetWhichPor() );
+}
+
+void SwBookmarkPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwBookmarkPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ if (!m_aColors.empty())
+ {
+ OUStringBuffer aStr;
+ for (const auto& rColor : m_aColors)
+ {
+ aStr.append("#" + std::get<2>(rColor) + " " + SwResId(STR_BOOKMARK_DEF_NAME));
+ switch (std::get<0>(rColor))
+ {
+ case SwScriptInfo::MarkKind::Point:
+ break;
+ case SwScriptInfo::MarkKind::Start:
+ aStr.append(" " + SwResId(STR_CAPTION_BEGINNING));
+ break;
+ case SwScriptInfo::MarkKind::End:
+ aStr.append(" " + SwResId(STR_CAPTION_END));
+ break;
+ }
+ }
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("colors"),
+ BAD_CAST(aStr.makeStringAndClear().toUtf8().getStr()));
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
bool SwControlCharPortion::Format( SwTextFormatInfo &rInf )
{
const SwLinePortion* pRoot = rInf.GetRoot();
diff --git a/sw/source/core/text/porrst.hxx b/sw/source/core/text/porrst.hxx
index c92c50db3faa..56108cd08846 100644
--- a/sw/source/core/text/porrst.hxx
+++ b/sw/source/core/text/porrst.hxx
@@ -24,6 +24,7 @@
#include <txttypes.hxx>
#include <txtfrm.hxx>
#include <svx/ctredlin.hxx>
+#include <scriptinfo.hxx>
#include "porlin.hxx"
#include "portxt.hxx"
@@ -53,12 +54,20 @@ public:
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
};
+enum class SwLineBreakClear;
+
class SwBreakPortion : public SwLinePortion
{
RedlineType m_eRedline;
+ /// Tracks the type of the breaking clear from SwTextLineBreak, if there is one.
+ SwLineBreakClear m_eClear;
+
+ /// Height of the line-break character itself, without spacing added for clearing.
+ SwTwips m_nTextHeight;
+
public:
- explicit SwBreakPortion( const SwLinePortion &rPortion );
+ explicit SwBreakPortion(const SwLinePortion& rPortion, const SwTextAttr* pAttr);
// Returns 0 if we have no usable data
virtual SwLinePortion *Compress() override;
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
@@ -69,8 +78,13 @@ public:
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
- static constexpr OUStringLiteral S_NOBREAK_FOR_REDLINE = u"\u00A0";
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+
+ static constexpr OUString S_NOBREAK_FOR_REDLINE = u"\u00A0"_ustr;
void SetRedline( const RedlineType eRedline ) { m_eRedline = eRedline; }
+
+ SwLineBreakClear GetClear() const;
};
class SwKernPortion : public SwLinePortion
@@ -97,6 +111,7 @@ public:
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
};
+/// Indicator that the content does not fit into a fixed height frame (red triangle on the UI).
class SwArrowPortion : public SwLinePortion
{
Point m_aPos;
@@ -146,8 +161,8 @@ class SwControlCharPortion : public SwLinePortion
private:
mutable sal_uInt16 mnViewWidth; // used to cache a calculated value
- mutable sal_uInt16 mnHalfCharWidth; // used to cache a calculated value
protected:
+ mutable sal_uInt16 mnHalfCharWidth; // used to cache a calculated value
sal_Unicode mcChar;
public:
@@ -169,17 +184,39 @@ public:
/// SwControlCharPortion these do not have a character in the text.
class SwBookmarkPortion : public SwControlCharPortion
{
+ // custom colors defined by metadata
+ std::vector<std::tuple<SwScriptInfo::MarkKind, Color, OUString>> m_aColors;
+ // number of MarkKind marks
+ sal_Int16 m_nStart, m_nEnd, m_nPoint;
+ bool m_bHasCustomColor;
+
public:
- explicit SwBookmarkPortion(sal_Unicode const cChar)
- : SwControlCharPortion(cChar)
+ explicit SwBookmarkPortion(sal_Unicode const cChar, std::vector<std::tuple<SwScriptInfo::MarkKind, Color, OUString>> aColors)
+ : SwControlCharPortion(cChar), m_aColors(std::move(aColors)), m_nStart(0), m_nEnd(0), m_nPoint(0), m_bHasCustomColor(false)
{
SetWhichPor(PortionType::Bookmark);
SetLen(TextFrameIndex(0));
+ for (const auto& it : m_aColors)
+ {
+ if (std::get<0>(it) == SwScriptInfo::MarkKind::Start)
+ m_nStart++;
+ else if (std::get<0>(it) == SwScriptInfo::MarkKind::End)
+ m_nEnd++;
+ else
+ m_nPoint++;
+
+ if (!m_bHasCustomColor && COL_TRANSPARENT != std::get<1>(it))
+ m_bHasCustomColor = true;
+ }
}
virtual bool DoPaint(SwTextPaintInfo const& rInf,
OUString & rOutString, SwFont & rTmpFont, int & rDeltaY) const override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
virtual SwLinePortion * Compress() override { return this; }
+ virtual void HandlePortion(SwPortionHandler& rPH) const override;
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& rOffset) const override;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/portox.cxx b/sw/source/core/text/portox.cxx
index 982ec4f5fb25..bd72e83b042d 100644
--- a/sw/source/core/text/portox.cxx
+++ b/sw/source/core/text/portox.cxx
@@ -48,7 +48,7 @@ sal_uInt16 SwIsoToxPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
// nViewWidth need to be calculated
if( !Width() && rInf.OnWin() &&
!rInf.GetOpt().IsPagePreview() &&
- !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
+ !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings() )
{
if( !m_nViewWidth )
pThis->m_nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx
index ed9d3c517bb5..c36b4fed21ef 100644
--- a/sw/source/core/text/portxt.cxx
+++ b/sw/source/core/text/portxt.cxx
@@ -113,7 +113,7 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf,
{
if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() )
{
- const sal_Int32 nKashRes = pSI->KashidaJustify( nullptr, nullptr, nPos, nEnd - nPos );
+ const sal_Int32 nKashRes = pSI->KashidaJustify(nullptr, nullptr, nPos, nEnd - nPos);
// i60591: need to check result of KashidaJustify
// determine if kashida justification is applicable
if (nKashRes != -1)
@@ -129,7 +129,7 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf,
if ( LANGUAGE_THAI == aLang )
{
- nCnt = SwScriptInfo::ThaiJustify( *pStr, nullptr, nullptr, nPos, nEnd - nPos );
+ nCnt = SwScriptInfo::ThaiJustify(*pStr, nullptr, nPos, nEnd - nPos);
const SwLinePortion* pPor = rPor.GetNextPortion();
if ( pPor && ( pPor->IsKernPortion() ||
@@ -221,7 +221,7 @@ void SwTextPortion::BreakCut( SwTextFormatInfo &rInf, const SwTextGuess &rGuess
// The word/char is larger than the line
// Special case 1: The word is larger than the line
// We truncate ...
- const sal_uInt16 nLineWidth = static_cast<sal_uInt16>(rInf.Width() - rInf.X());
+ const sal_uInt16 nLineWidth = o3tl::narrowing<sal_uInt16>(rInf.Width() - rInf.X());
TextFrameIndex nLen = rGuess.CutPos() - rInf.GetIdx();
if (nLen > TextFrameIndex(0))
{
@@ -302,7 +302,44 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
}
SwTextGuess aGuess;
- const bool bFull = !aGuess.Guess( *this, rInf, Height() );
+ bool bFull = !aGuess.Guess( *this, rInf, Height() );
+
+ // tdf#158776 for the last full text portion, call Guess() again to allow more text in the
+ // adjusted line by shrinking spaces using the know space count from the first Guess() call
+ const SvxAdjust& rAdjust = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust().GetAdjust();
+ if ( bFull && rAdjust == SvxAdjust::Block &&
+ aGuess.BreakPos() != TextFrameIndex(COMPLETE_STRING) &&
+ rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::JUSTIFY_LINES_WITH_SHRINKING) &&
+ // tdf#158436 avoid shrinking at underflow, e.g. no-break space after a
+ // very short word resulted endless loop
+ !rInf.IsUnderflow() )
+ {
+ sal_Int32 nSpacesInLine(0);
+ for (sal_Int32 i = sal_Int32(rInf.GetLineStart()); i < sal_Int32(aGuess.BreakPos()); ++i)
+ {
+ sal_Unicode cChar = rInf.GetText()[i];
+ if ( cChar == CH_BLANK )
+ ++nSpacesInLine;
+ }
+
+ // call with an extra space: shrinking can result a new word in the line
+ // and a new space before that, which is also a shrank space
+ // (except if the line was already broken inside a word with hyphenation)
+ // TODO: handle the case, if the line contains extra amount of spaces
+ if (
+ // no automatic hyphenation
+ !aGuess.HyphWord().is() &&
+ // no hyphenation at soft hyphen
+ aGuess.BreakPos() < TextFrameIndex(rInf.GetText().getLength()) &&
+ rInf.GetText()[sal_Int32(aGuess.BreakPos())] != CHAR_SOFTHYPHEN )
+ {
+ ++nSpacesInLine;
+ }
+
+ if ( nSpacesInLine > 0 )
+ bFull = !aGuess.Guess( *this, rInf, Height(), nSpacesInLine );
+ }
// these are the possible cases:
// A Portion fits to current line
@@ -321,6 +358,7 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
if ( !bFull )
{
Width( aGuess.BreakWidth() );
+ ExtraBlankWidth(aGuess.ExtraBlankWidth());
// Caution!
if( !InExpGrp() || InFieldGrp() )
SetLen( rInf.GetLen() );
@@ -336,7 +374,7 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
new SwKernPortion( *this, nKern );
}
// special case: hanging portion
- else if( bFull && aGuess.GetHangingPortion() )
+ else if( aGuess.GetHangingPortion() )
{
Width( aGuess.BreakWidth() );
SetLen( aGuess.BreakPos() - rInf.GetIdx() );
@@ -393,12 +431,7 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
! rInf.GetLast()->IsErgoSumPortion() &&
lcl_HasContent(*static_cast<SwFieldPortion*>(rInf.GetLast()),rInf ) ) ) ) )
{
- // GetLineWidth() takes care of DocumentSettingId::TAB_OVER_MARGIN.
- if (aGuess.BreakWidth() <= rInf.GetLineWidth())
- Width( aGuess.BreakWidth() );
- else
- // this actually should not happen
- Width( sal_uInt16(rInf.Width() - rInf.X()) );
+ Width( aGuess.BreakWidth() );
SetLen( aGuess.BreakPos() - rInf.GetIdx() );
@@ -409,7 +442,14 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
{
SwHolePortion *pNew = new SwHolePortion( *this );
pNew->SetLen( nRealStart - aGuess.BreakPos() );
+ pNew->Width(0);
+ pNew->ExtraBlankWidth( aGuess.ExtraBlankWidth() );
Insert( pNew );
+
+ // UAX #14 Unicode Line Breaking Algorithm Non-tailorable Line breaking rule LB6:
+ // https://www.unicode.org/reports/tr14/#LB6 Do not break before hard line breaks
+ if (auto ch = rInf.GetChar(aGuess.BreakStart()); !ch || ch == CH_BREAK)
+ bFull = false; // Keep following SwBreakPortion / para break in the same line
}
}
else // case C2, last exit
@@ -430,7 +470,7 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
}
else
// case C2, last exit
- BreakCut( rInf, aGuess );
+ BreakCut(rInf, aGuess);
}
return bFull;
@@ -491,9 +531,9 @@ void SwTextPortion::FormatEOL( SwTextFormatInfo &rInf )
Width( Width() - nBlankSize );
rInf.X( rInf.X() - nBlankSize );
SetLen( GetLen() - nHoleLen );
- SwLinePortion *pHole = new SwHolePortion( *this );
- static_cast<SwHolePortion *>( pHole )->SetBlankWidth( nBlankSize );
- static_cast<SwHolePortion *>( pHole )->SetLen( nHoleLen );
+ SwHolePortion* pHole = new SwHolePortion(*this);
+ pHole->SetBlankWidth(nBlankSize);
+ pHole->SetLen(nHoleLen);
Insert( pHole );
}
@@ -543,6 +583,8 @@ void SwTextPortion::Paint( const SwTextPaintInfo &rInf ) const
rInf.DrawBackBrush( *this );
rInf.DrawBorder( *this );
+ rInf.DrawCSDFHighlighting(*this);
+
// do we have to repaint a post it portion?
if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
mpNextPortion->PrePaint( rInf, this );
@@ -584,7 +626,10 @@ TextFrameIndex SwTextPortion::GetSpaceCnt(const SwTextSizeInfo &rInf,
if ( InExpGrp() || PortionType::InputField == GetWhichPor() )
{
- if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
+ if (OUString ExpOut;
+ (!IsBlankPortion()
+ || (GetExpText(rInf, ExpOut) && OUStringChar(CH_BLANK) == ExpOut))
+ && !InNumberGrp() && !IsCombinedPortion())
{
// OnWin() likes to return a blank instead of an empty string from
// time to time. We cannot use that here at all, however.
@@ -608,7 +653,7 @@ TextFrameIndex SwTextPortion::GetSpaceCnt(const SwTextSizeInfo &rInf,
return nCnt;
}
-tools::Long SwTextPortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const
+SwTwips SwTextPortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const
{
TextFrameIndex nCnt(0);
@@ -621,7 +666,10 @@ tools::Long SwTextPortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeI
if ( InExpGrp() || PortionType::InputField == GetWhichPor() )
{
- if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
+ if (OUString ExpOut;
+ (!IsBlankPortion()
+ || (GetExpText(rInf, ExpOut) && OUStringChar(CH_BLANK) == ExpOut))
+ && !InNumberGrp() && !IsCombinedPortion())
{
// OnWin() likes to return a blank instead of an empty string from
// time to time. We cannot use that here at all, however.
@@ -662,16 +710,16 @@ tools::Long SwTextPortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeI
}
}
- return sal_Int32(nCnt) * nSpaceAdd / SPACING_PRECISION_FACTOR;
+ return sal_Int32(nCnt) * (nSpaceAdd > LONG_MAX/2 ? LONG_MAX/2 - nSpaceAdd : nSpaceAdd)
+ / SPACING_PRECISION_FACTOR;
}
void SwTextPortion::HandlePortion( SwPortionHandler& rPH ) const
{
- rPH.Text( GetLen(), GetWhichPor(), Height(), Width() );
+ rPH.Text( GetLen(), GetWhichPor() );
}
SwTextInputFieldPortion::SwTextInputFieldPortion()
- : SwTextPortion()
{
SetWhichPor( PortionType::InputField );
}
@@ -700,12 +748,12 @@ void SwTextInputFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
if (aIntersect.HasArea()
&& rInf.OnWin()
- && SwViewOption::IsFieldShadings()
+ && rInf.GetOpt().IsFieldShadings()
&& !rInf.GetOpt().IsPagePreview())
{
OutputDevice* pOut = const_cast<OutputDevice*>(rInf.GetOut());
- pOut->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR);
- pOut->SetFillColor(SwViewOption::GetFieldShadingsColor());
+ pOut->Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
+ pOut->SetFillColor(rInf.GetOpt().GetFieldShadingsColor());
pOut->SetLineColor();
pOut->DrawRect(aIntersect.SVRect());
pOut->Pop();
@@ -747,25 +795,44 @@ SwHolePortion::SwHolePortion( const SwTextPortion &rPor )
{
SetLen( TextFrameIndex(1) );
Height( rPor.Height() );
+ Width(0);
SetAscent( rPor.GetAscent() );
SetWhichPor( PortionType::Hole );
}
SwLinePortion *SwHolePortion::Compress() { return this; }
+// The GetTextSize() assumes that the own length is correct
+SwPosSize SwHolePortion::GetTextSize(const SwTextSizeInfo& rInf) const
+{
+ SwPosSize aSize = rInf.GetTextSize();
+ if (!GetJoinBorderWithPrev())
+ aSize.Width(aSize.Width() + rInf.GetFont()->GetLeftBorderSpace());
+ if (!GetJoinBorderWithNext())
+ aSize.Width(aSize.Width() + rInf.GetFont()->GetRightBorderSpace());
+
+ aSize.Height(aSize.Height() +
+ rInf.GetFont()->GetTopBorderSpace() +
+ rInf.GetFont()->GetBottomBorderSpace());
+
+ return aSize;
+}
+
void SwHolePortion::Paint( const SwTextPaintInfo &rInf ) const
{
if( !rInf.GetOut() )
return;
+ bool bPDFExport = rInf.GetVsh()->GetViewOptions()->IsPDFExport();
+
// #i16816# export stuff only needed for tagged pdf support
- if (!SwTaggedPDFHelper::IsExportTaggedPDF( *rInf.GetOut()) )
+ if (bPDFExport && !SwTaggedPDFHelper::IsExportTaggedPDF( *rInf.GetOut()) )
return;
// #i68503# the hole must have no decoration for a consistent visual appearance
const SwFont* pOrigFont = rInf.GetFont();
std::unique_ptr<SwFont> pHoleFont;
- std::unique_ptr<SwFontSave> pFontSave;
+ std::optional<SwFontSave> oFontSave;
if( pOrigFont->GetUnderline() != LINESTYLE_NONE
|| pOrigFont->GetOverline() != LINESTYLE_NONE
|| pOrigFont->GetStrikeout() != STRIKEOUT_NONE )
@@ -774,12 +841,21 @@ void SwHolePortion::Paint( const SwTextPaintInfo &rInf ) const
pHoleFont->SetUnderline( LINESTYLE_NONE );
pHoleFont->SetOverline( LINESTYLE_NONE );
pHoleFont->SetStrikeout( STRIKEOUT_NONE );
- pFontSave.reset(new SwFontSave( rInf, pHoleFont.get() ));
+ oFontSave.emplace( rInf, pHoleFont.get() );
}
- rInf.DrawText(" ", *this, TextFrameIndex(0), TextFrameIndex(1));
+ if (bPDFExport)
+ {
+ rInf.DrawText(" ", *this, TextFrameIndex(0), TextFrameIndex(1));
+ }
+ else
+ {
+ // tdf#43244: Paint spaces even at end of line,
+ // but only if this paint is not called for pdf export, to keep that pdf export intact
+ rInf.DrawText(*this, rInf.GetLen());
+ }
- pFontSave.reset();
+ oFontSave.reset();
pHoleFont.reset();
}
@@ -793,6 +869,18 @@ void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const
rPH.Text( GetLen(), GetWhichPor() );
}
+void SwHolePortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHolePortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("blank-width"),
+ BAD_CAST(OString::number(m_nBlankWidth).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
void SwFieldMarkPortion::Paint( const SwTextPaintInfo & /*rInf*/) const
{
// These shouldn't be painted!
diff --git a/sw/source/core/text/portxt.hxx b/sw/source/core/text/portxt.hxx
index ec44a2bafc76..c826395272e1 100644
--- a/sw/source/core/text/portxt.hxx
+++ b/sw/source/core/text/portxt.hxx
@@ -38,7 +38,7 @@ public:
virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
- virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
+ virtual SwTwips CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
// Counts the spaces for justified paragraph
TextFrameIndex GetSpaceCnt(const SwTextSizeInfo &rInf, TextFrameIndex& rCnt) const;
@@ -69,10 +69,14 @@ public:
void SetBlankWidth( const sal_uInt16 nNew ) { m_nBlankWidth = nNew; }
virtual SwLinePortion *Compress() override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual SwPosSize GetTextSize(const SwTextSizeInfo& rInfo) const override;
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
};
class SwFieldMarkPortion : public SwTextPortion
diff --git a/sw/source/core/text/possiz.hxx b/sw/source/core/text/possiz.hxx
index fc31ffc11e39..f66bd1988ca4 100644
--- a/sw/source/core/text/possiz.hxx
+++ b/sw/source/core/text/possiz.hxx
@@ -19,22 +19,22 @@
#pragma once
#include <tools/gen.hxx>
-#include <sal/types.h>
+#include <swtypes.hxx>
// Compared to the SV sizes SwPosSize is always positive
class SwPosSize
{
- sal_uInt16 m_nWidth;
- sal_uInt16 m_nHeight;
+ SwTwips m_nWidth;
+ SwTwips m_nHeight;
public:
- SwPosSize( const sal_uInt16 nW = 0, const sal_uInt16 nH = 0 )
+ SwPosSize( const SwTwips nW = 0, const SwTwips nH = 0 )
: m_nWidth(nW)
, m_nHeight(nH)
{
}
explicit SwPosSize( const Size &rSize )
- : m_nWidth(sal_uInt16(rSize.Width()))
- ,m_nHeight(sal_uInt16(rSize.Height()))
+ : m_nWidth(SwTwips(rSize.Width()))
+ , m_nHeight(SwTwips(rSize.Height()))
{
}
#if defined(__COVERITY__)
@@ -46,25 +46,25 @@ public:
SwPosSize(SwPosSize &&) = default;
SwPosSize & operator =(SwPosSize const &) = default;
SwPosSize & operator =(SwPosSize &&) = default;
- sal_uInt16 Height() const { return m_nHeight; }
- virtual void Height(const sal_uInt16 nNew, const bool = true) { m_nHeight = nNew; }
- sal_uInt16 Width() const { return m_nWidth; }
- void Width( const sal_uInt16 nNew ) { m_nWidth = nNew; }
+ SwTwips Height() const { return m_nHeight; }
+ virtual void Height(const SwTwips nNew, const bool = true) { m_nHeight = nNew; }
+ SwTwips Width() const { return m_nWidth; }
+ void Width( const SwTwips nNew ) { m_nWidth = nNew; }
Size SvLSize() const { return Size( m_nWidth, m_nHeight ); }
void SvLSize( const Size &rSize )
{
- m_nWidth = sal_uInt16(rSize.Width());
- m_nHeight = sal_uInt16(rSize.Height());
+ m_nWidth = SwTwips(rSize.Width());
+ m_nHeight = SwTwips(rSize.Height());
}
void SvXSize( const Size &rSize )
{
- m_nHeight = sal_uInt16(rSize.Width());
- m_nWidth = sal_uInt16(rSize.Height());
+ m_nHeight = SwTwips(rSize.Width());
+ m_nWidth = SwTwips(rSize.Height());
}
SwPosSize& operator=( const Size &rSize )
{
- m_nWidth = sal_uInt16(rSize.Width());
- m_nHeight = sal_uInt16(rSize.Height());
+ m_nWidth = SwTwips(rSize.Width());
+ m_nHeight = SwTwips(rSize.Height());
return *this;
}
};
diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx
index faeb0fbbaa8d..afa87aba0963 100644
--- a/sw/source/core/text/redlnitr.cxx
+++ b/sw/source/core/text/redlnitr.cxx
@@ -37,7 +37,7 @@
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentMarkAccess.hxx>
#include <IMark.hxx>
-#include <bookmrk.hxx>
+#include <bookmark.hxx>
#include <rootfrm.hxx>
#include <breakit.hxx>
#include <vcl/commandevent.hxx>
@@ -47,6 +47,12 @@
#include <vcl/svapp.hxx>
#include "redlnitr.hxx"
#include <extinput.hxx>
+#include <fmtpdsc.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/udlnitem.hxx>
using namespace ::com::sun::star;
@@ -59,12 +65,15 @@ private:
IDocumentMarkAccess const& m_rIDMA;
bool const m_isHideRedlines;
sw::FieldmarkMode const m_eFieldmarkMode;
+ bool const m_isHideParagraphBreaks;
SwPosition const m_Start;
/// next redline
SwRedlineTable::size_type m_RedlineIndex;
/// next fieldmark
- std::pair<sw::mark::IFieldmark const*, std::unique_ptr<SwPosition>> m_Fieldmark;
+ std::pair<sw::mark::IFieldmark const*, std::optional<SwPosition>> m_Fieldmark;
std::optional<SwPosition> m_oNextFieldmarkHide;
+ /// previous paragraph break - because m_pStartPos/EndPos are non-owning
+ std::optional<std::pair<SwPosition, SwPosition>> m_oParagraphBreak;
/// current start/end pair
SwPosition const* m_pStartPos;
SwPosition const* m_pEndPos;
@@ -74,13 +83,15 @@ public:
SwPosition const* GetEndPos() const { return m_pEndPos; }
HideIterator(SwTextNode & rTextNode,
- bool const isHideRedlines, sw::FieldmarkMode const eMode)
+ bool const isHideRedlines, sw::FieldmarkMode const eMode,
+ sw::ParagraphBreakMode const ePBMode)
: m_rIDRA(rTextNode.getIDocumentRedlineAccess())
, m_rIDMA(*rTextNode.getIDocumentMarkAccess())
, m_isHideRedlines(isHideRedlines)
, m_eFieldmarkMode(eMode)
+ , m_isHideParagraphBreaks(ePBMode == sw::ParagraphBreakMode::Hidden)
, m_Start(rTextNode, 0)
- , m_RedlineIndex(m_rIDRA.GetRedlinePos(rTextNode, RedlineType::Any))
+ , m_RedlineIndex(isHideRedlines ? m_rIDRA.GetRedlinePos(rTextNode, RedlineType::Any) : SwRedlineTable::npos)
, m_pStartPos(nullptr)
, m_pEndPos(&m_Start)
{
@@ -101,22 +112,21 @@ public:
{
SwRangeRedline const*const pRed = m_rIDRA.GetRedlineTable()[m_RedlineIndex];
- if (m_pEndPos->nNode.GetIndex() < pRed->Start()->nNode.GetIndex())
+ if (m_pEndPos->GetNodeIndex() < pRed->Start()->GetNodeIndex())
break;
if (pRed->GetType() != RedlineType::Delete)
continue;
- SwPosition const*const pStart(pRed->Start());
- SwPosition const*const pEnd(pRed->End());
+ auto [pStart, pEnd] = pRed->StartEnd(); // SwPosition*
if (*pStart == *pEnd)
{ // only allowed while moving (either way?)
// assert(IDocumentRedlineAccess::IsHideChanges(rIDRA.GetRedlineFlags()));
continue;
}
- if (pStart->nNode.GetNode().IsTableNode())
+ if (pStart->GetNode().IsTableNode())
{
- assert(pEnd->nNode == m_Start.nNode && pEnd->nContent.GetIndex() == 0);
+ assert(pEnd->GetNode() == m_Start.GetNode() && pEnd->GetContentIndex() == 0);
continue; // known pathology, ignore it
}
if (*m_pEndPos <= *pStart)
@@ -134,16 +144,16 @@ public:
sal_Unicode const magic(m_eFieldmarkMode == sw::FieldmarkMode::ShowResult
? CH_TXT_ATR_FIELDSTART
: CH_TXT_ATR_FIELDSEP);
- SwTextNode* pTextNode = m_pEndPos->nNode.GetNode().GetTextNode();
+ SwTextNode* pTextNode = m_pEndPos->GetNode().GetTextNode();
sal_Int32 const nPos = pTextNode ? pTextNode->GetText().indexOf(
- magic, m_pEndPos->nContent.GetIndex()) : -1;
+ magic, m_pEndPos->GetContentIndex()) : -1;
if (nPos != -1)
{
m_oNextFieldmarkHide.emplace(*pTextNode, nPos);
sw::mark::IFieldmark const*const pFieldmark(
m_eFieldmarkMode == sw::FieldmarkMode::ShowResult
? m_rIDMA.getFieldmarkAt(*m_oNextFieldmarkHide)
- : m_rIDMA.getFieldmarkFor(*m_oNextFieldmarkHide));
+ : m_rIDMA.getInnerFieldmarkFor(*m_oNextFieldmarkHide));
assert(pFieldmark);
m_Fieldmark.first = pFieldmark;
// for cursor travelling, there should be 2 visible chars;
@@ -152,16 +162,15 @@ public:
// always hide the CH_TXT_ATR_FIELDSEP for now
if (m_eFieldmarkMode == sw::FieldmarkMode::ShowResult)
{
- m_Fieldmark.second.reset(
- new SwPosition(sw::mark::FindFieldSep(*m_Fieldmark.first)));
- ++m_Fieldmark.second->nContent;
- ++m_oNextFieldmarkHide->nContent; // skip start
+ m_Fieldmark.second.emplace(
+ sw::mark::FindFieldSep(*m_Fieldmark.first));
+ m_Fieldmark.second->AdjustContent(+1);
+ m_oNextFieldmarkHide->AdjustContent(+1); // skip start
}
else
{
- m_Fieldmark.second.reset(
- new SwPosition(pFieldmark->GetMarkEnd()));
- --m_Fieldmark.second->nContent;
+ m_Fieldmark.second.emplace(pFieldmark->GetMarkEnd());
+ m_Fieldmark.second->AdjustContent(-1);
}
}
}
@@ -184,15 +193,69 @@ public:
{
assert(!pNextRedlineHide || *m_oNextFieldmarkHide <= *pNextRedlineHide);
m_pStartPos = &*m_oNextFieldmarkHide;
- m_pEndPos = m_Fieldmark.second.get();
+ m_pEndPos = &*m_Fieldmark.second;
return true;
}
- else // nothing
+ else
{
assert(!pNextRedlineHide && !m_oNextFieldmarkHide);
- m_pStartPos = nullptr;
- m_pEndPos = nullptr;
- return false;
+ auto const hasHiddenItem = [](auto const& rNode) {
+ auto const& rpSet(rNode.GetAttr(RES_PARATR_LIST_AUTOFMT).GetStyleHandle());
+ return rpSet ? rpSet->Get(RES_CHRATR_HIDDEN).GetValue() : false;
+ };
+ auto const hasBreakBefore = [](SwTextNode const& rNode) {
+ if (rNode.GetAttr(RES_PAGEDESC).GetPageDesc())
+ {
+ return true;
+ }
+ switch (rNode.GetAttr(RES_BREAK).GetBreak())
+ {
+ case SvxBreak::ColumnBefore:
+ case SvxBreak::ColumnBoth:
+ case SvxBreak::PageBefore:
+ case SvxBreak::PageBoth:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ };
+ auto const hasBreakAfter = [](SwTextNode const& rNode) {
+ switch (rNode.GetAttr(RES_BREAK).GetBreak())
+ {
+ case SvxBreak::ColumnAfter:
+ case SvxBreak::ColumnBoth:
+ case SvxBreak::PageAfter:
+ case SvxBreak::PageBoth:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ };
+ if (m_isHideParagraphBreaks
+ && m_pEndPos->GetNode().IsTextNode() // ooo27109-1.sxw
+ // only merge if next node is also text node
+ && m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex()+1]->IsTextNode()
+ && hasHiddenItem(*m_pEndPos->GetNode().GetTextNode())
+ // no merge if there's a page break on any node
+ && !hasBreakBefore(*m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex()+1]->GetTextNode())
+ // first node, see SwTextFrame::GetBreak()
+ && !hasBreakAfter(*m_Start.GetNode().GetTextNode()))
+ {
+ m_oParagraphBreak.emplace(
+ SwPosition(*m_pEndPos->GetNode().GetTextNode(), m_pEndPos->GetNode().GetTextNode()->Len()),
+ SwPosition(*m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex()+1]->GetTextNode(), 0));
+ m_pStartPos = &m_oParagraphBreak->first;
+ m_pEndPos = &m_oParagraphBreak->second;
+ return true;
+ }
+ else // nothing
+ {
+ m_pStartPos = nullptr;
+ m_pEndPos = nullptr;
+ return false;
+ }
}
}
};
@@ -220,25 +283,27 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
sal_Int32 nLastEnd(0);
for (auto iter = HideIterator(rTextNode,
rFrame.getRootFrame()->IsHideRedlines(),
- rFrame.getRootFrame()->GetFieldmarkMode()); iter.Next(); )
+ rFrame.getRootFrame()->GetFieldmarkMode(),
+ rFrame.getRootFrame()->GetParagraphBreakMode());
+ iter.Next(); )
{
SwPosition const*const pStart(iter.GetStartPos());
SwPosition const*const pEnd(iter.GetEndPos());
bHaveRedlines = true;
- assert(pNode != &rTextNode || &pStart->nNode.GetNode() == &rTextNode); // detect calls with wrong start node
- if (pStart->nContent != nLastEnd) // not 0 so we eliminate adjacent deletes
+ assert(pNode != &rTextNode || &pStart->GetNode() == &rTextNode); // detect calls with wrong start node
+ if (pStart->GetContentIndex() != nLastEnd) // not 0 so we eliminate adjacent deletes
{
- extents.emplace_back(pNode, nLastEnd, pStart->nContent.GetIndex());
- mergedText.append(pNode->GetText().subView(nLastEnd, pStart->nContent.GetIndex() - nLastEnd));
+ extents.emplace_back(pNode, nLastEnd, pStart->GetContentIndex());
+ mergedText.append(pNode->GetText().subView(nLastEnd, pStart->GetContentIndex() - nLastEnd));
}
- if (&pEnd->nNode.GetNode() != pNode)
+ if (&pEnd->GetNode() != pNode)
{
if (pNode == &rTextNode)
{
pNode->SetRedlineMergeFlag(SwNode::Merge::First);
} // else: was already set before
int nLevel(0);
- for (sal_uLong j = pNode->GetIndex() + 1; j < pEnd->nNode.GetIndex(); ++j)
+ for (SwNodeOffset j = pNode->GetIndex() + 1; j < pEnd->GetNodeIndex(); ++j)
{
SwNode *const pTmp(pNode->GetNodes()[j]);
if (nLevel == 0)
@@ -268,12 +333,12 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
}
// note: in DelLastPara() case, the end node is not actually merged
// and is likely a SwTableNode!
- if (!pEnd->nNode.GetNode().IsTextNode())
+ if (!pEnd->GetNode().IsTextNode())
{
- assert(pEnd->nNode != pStart->nNode);
+ assert(pEnd->GetNode() != pStart->GetNode());
// must set pNode too because it will mark the last node
pNode = nodes.back();
- assert(pNode == pNode->GetNodes()[pEnd->nNode.GetIndex() - 1]);
+ assert(pNode == pNode->GetNodes()[pEnd->GetNodeIndex() - 1]);
if (pNode != &rTextNode)
{ // something might depend on last merged one being NonFirst?
pNode->SetRedlineMergeFlag(SwNode::Merge::NonFirst);
@@ -282,15 +347,15 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
}
else
{
- pNode = pEnd->nNode.GetNode().GetTextNode();
+ pNode = pEnd->GetNode().GetTextNode();
nodes.push_back(pNode);
pNode->SetRedlineMergeFlag(SwNode::Merge::NonFirst);
- nLastEnd = pEnd->nContent.GetIndex();
+ nLastEnd = pEnd->GetContentIndex();
}
}
else
{
- nLastEnd = pEnd->nContent.GetIndex();
+ nLastEnd = pEnd->GetContentIndex();
}
}
if (pNode == &rTextNode)
@@ -308,7 +373,7 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
// * the first SwTextNode inside each start node of the previous point
// Other (non-first) SwTextNodes in nested sections shouldn't be reset!
int nLevel(0);
- for (sal_uLong j = pNode->GetIndex() + 1; j < pNode->GetNodes().Count(); ++j)
+ for (SwNodeOffset j = pNode->GetIndex() + 1; j < pNode->GetNodes().Count(); ++j)
{
SwNode *const pTmp(pNode->GetNodes()[j]);
if (!pTmp->IsCreateFrameWhenHidingRedlines())
@@ -422,7 +487,7 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
}
for (auto const pSectionNode : sections)
{
- pSectionNode->DelFrames(rFrame.getRootFrame());
+ pSectionNode->GetSection().GetFormat()->DelFrames(/*rFrame.getRootFrame()*/);
}
}
auto pRet(std::make_unique<sw::MergedPara>(rFrame, std::move(extents),
@@ -441,7 +506,7 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
void SwAttrIter::InitFontAndAttrHandler(
SwTextNode const& rPropsNode,
SwTextNode const& rTextNode,
- OUString const& rText,
+ std::u16string_view aText,
bool const*const pbVertLayout,
bool const*const pbVertLayoutLRBT)
{
@@ -511,7 +576,7 @@ void SwAttrIter::InitFontAndAttrHandler(
m_pFont->GetFontCacheId( m_aFontCacheIds[ nTmp ], m_aFontIdx[ nTmp ], nTmp );
}
}
- while (nChg < TextFrameIndex(rText.getLength()));
+ while (nChg < TextFrameIndex(aText.size()));
}
void SwAttrIter::CtorInitAttrIter(SwTextNode & rTextNode,
@@ -646,14 +711,16 @@ SwRedlineItr::SwRedlineItr( const SwTextNode& rTextNd, SwFont& rFnt,
, m_nNdIdx( rTextNd.GetIndex() )
, m_nFirst( nRed )
, m_nAct( SwRedlineTable::npos )
+ , m_nStart( COMPLETE_STRING )
+ , m_nEnd( COMPLETE_STRING )
, m_bOn( false )
, m_eMode( mode )
{
if( pArr )
{
assert(pExtInputStart);
- m_pExt.reset( new SwExtend(*pArr, pExtInputStart->nNode.GetIndex(),
- pExtInputStart->nContent.GetIndex()) );
+ m_pExt.reset( new SwExtend(*pArr, pExtInputStart->GetNodeIndex(),
+ pExtInputStart->GetContentIndex()) );
}
else
m_pExt = nullptr;
@@ -670,7 +737,7 @@ SwRedlineItr::~SwRedlineItr() COVERITY_NOEXCEPT_FALSE
// The return value of SwRedlineItr::Seek tells you if the current font
// has been manipulated by leaving (-1) or accessing (+1) of a section
short SwRedlineItr::Seek(SwFont& rFnt,
- sal_uLong const nNode, sal_Int32 const nNew, sal_Int32 const nOld)
+ SwNodeOffset const nNode, sal_Int32 const nNew, sal_Int32 const nOld)
{
short nRet = 0;
if( ExtOn() )
@@ -703,17 +770,18 @@ short SwRedlineItr::Seek(SwFont& rFnt,
m_nStart = COMPLETE_STRING;
m_nEnd = COMPLETE_STRING;
+ const SwRedlineTable& rTable = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
- for ( ; m_nAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++m_nAct)
+ for ( ; m_nAct < rTable.size() ; ++m_nAct)
{
- m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ]->CalcStartEnd(nNode, m_nStart, m_nEnd);
+ rTable[ m_nAct ]->CalcStartEnd(nNode, m_nStart, m_nEnd);
if (nNew < m_nEnd)
{
if (nNew >= m_nStart) // only possible candidate
{
m_bOn = true;
- const SwRangeRedline *pRed = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ];
+ const SwRangeRedline *pRed = rTable[ m_nAct ];
if (m_pSet)
m_pSet->ClearItem();
@@ -721,7 +789,7 @@ short SwRedlineItr::Seek(SwFont& rFnt,
{
SwAttrPool& rPool =
const_cast<SwDoc&>(m_rDoc).GetAttrPool();
- m_pSet = std::make_unique<SfxItemSet>(rPool, svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END-1>{});
+ m_pSet = std::make_unique<SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1>>(rPool);
}
if( 1 < pRed->GetStackCount() )
@@ -729,12 +797,23 @@ short SwRedlineItr::Seek(SwFont& rFnt,
FillHints( pRed->GetAuthor(), pRed->GetType() );
SfxWhichIter aIter( *m_pSet );
+
+ // moved text: dark green with double underline or strikethrough
+ if ( pRed->IsMoved() )
+ {
+ m_pSet->Put(SvxColorItem( COL_GREEN, RES_CHRATR_COLOR ));
+ if (SfxItemState::SET == m_pSet->GetItemState(RES_CHRATR_CROSSEDOUT, true))
+ m_pSet->Put(SvxCrossedOutItem( STRIKEOUT_DOUBLE, RES_CHRATR_CROSSEDOUT ));
+ else
+ m_pSet->Put(SvxUnderlineItem( LINESTYLE_DOUBLE, RES_CHRATR_UNDERLINE ));
+ }
+
sal_uInt16 nWhich = aIter.FirstWhich();
while( nWhich )
{
const SfxPoolItem* pItem;
if( ( nWhich < RES_CHRATR_END ) &&
- ( SfxItemState::SET == m_pSet->GetItemState( nWhich, true, &pItem ) ) )
+ ( SfxItemState::SET == aIter.GetItemState( true, &pItem ) ) )
{
SwTextAttr* pAttr = MakeRedlineTextAttr(
const_cast<SwDoc&>(m_rDoc),
@@ -773,9 +852,9 @@ short SwRedlineItr::Seek(SwFont& rFnt,
m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[m_nAct]);
SwPosition const*const pStart(pRedline->Start());
if (pRedline->GetType() == RedlineType::Delete
- && (nNode < pStart->nNode.GetIndex()
- || (nNode == pStart->nNode.GetIndex()
- && nNew <= pStart->nContent.GetIndex())))
+ && (nNode < pStart->GetNodeIndex()
+ || (nNode == pStart->GetNodeIndex()
+ && nNew <= pStart->GetContentIndex())))
{
pRedline->CalcStartEnd(nNode, m_nStart, m_nEnd);
break;
@@ -837,7 +916,7 @@ void SwRedlineItr::Clear_( SwFont* pFnt )
m_rAttrHandler.PopAndChg( *hint, *pFnt );
else
m_rAttrHandler.Pop( *hint );
- SwTextAttr::Destroy(hint, const_cast<SwDoc&>(m_rDoc).GetAttrPool() );
+ SwTextAttr::Destroy(hint);
}
m_Hints.clear();
}
@@ -939,15 +1018,15 @@ bool SwRedlineItr::ChkSpecialUnderline_() const
}
bool SwRedlineItr::CheckLine(
- sal_uLong const nStartNode, sal_Int32 const nChkStart,
- sal_uLong const nEndNode, sal_Int32 nChkEnd, OUString& rRedlineText,
- bool& bRedlineEnd, RedlineType& eRedlineEnd, bool bFullLine)
+ SwNodeOffset const nStartNode, sal_Int32 const nChkStart,
+ SwNodeOffset const nEndNode, sal_Int32 nChkEnd, OUString& rRedlineText,
+ bool& bRedlineEnd, RedlineType& eRedlineEnd, size_t* pAuthorAtPos)
{
// note: previously this would return true in the (!m_bShow && m_pExt)
// case, but surely that was a bug?
if (m_nFirst == SwRedlineTable::npos || m_eMode != Mode::Show)
return false;
- if( nChkEnd == nChkStart && bFullLine ) // empty lines look one char further
+ if( nChkEnd == nChkStart && pAuthorAtPos == nullptr ) // empty lines look one char further
++nChkEnd;
sal_Int32 nOldStart = m_nStart;
sal_Int32 nOldEnd = m_nEnd;
@@ -978,6 +1057,8 @@ bool SwRedlineItr::CheckLine(
eRedlineEnd = pRedline->GetType();
bRedlineEnd = true;
isBreak = true;
+ if (pAuthorAtPos)
+ *pAuthorAtPos = pRedline->GetAuthor();
[[fallthrough]];
case SwComparePosition::OverlapBefore:
case SwComparePosition::CollideEnd:
@@ -1032,6 +1113,8 @@ void SwExtend::ActualizeFont( SwFont &rFnt, ExtTextInputAttr nAttr )
{
if ( nAttr & ExtTextInputAttr::Underline )
rFnt.SetUnderline( LINESTYLE_SINGLE );
+ else if ( nAttr & ExtTextInputAttr::DoubleUnderline )
+ rFnt.SetUnderline( LINESTYLE_DOUBLE );
else if ( nAttr & ExtTextInputAttr::BoldUnderline )
rFnt.SetUnderline( LINESTYLE_BOLD );
else if ( nAttr & ExtTextInputAttr::DottedUnderline )
@@ -1052,7 +1135,7 @@ void SwExtend::ActualizeFont( SwFont &rFnt, ExtTextInputAttr nAttr )
rFnt.SetGreyWave( true );
}
-short SwExtend::Enter(SwFont& rFnt, sal_uLong const nNode, sal_Int32 const nNew)
+short SwExtend::Enter(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
{
OSL_ENSURE( !m_pFont, "SwExtend: Enter with Font" );
if (nNode != m_nNode)
@@ -1068,7 +1151,7 @@ short SwExtend::Enter(SwFont& rFnt, sal_uLong const nNode, sal_Int32 const nNew)
return 0;
}
-bool SwExtend::Leave_(SwFont& rFnt, sal_uLong const nNode, sal_Int32 const nNew)
+bool SwExtend::Leave_(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
{
OSL_ENSURE(nNode == m_nNode && Inside(), "SwExtend: Leave without Enter");
if (nNode != m_nNode)
@@ -1093,7 +1176,7 @@ bool SwExtend::Leave_(SwFont& rFnt, sal_uLong const nNode, sal_Int32 const nNew)
return false;
}
-sal_Int32 SwExtend::Next(sal_uLong const nNode, sal_Int32 nNext)
+sal_Int32 SwExtend::Next(SwNodeOffset const nNode, sal_Int32 nNext)
{
if (nNode != m_nNode)
return nNext;
diff --git a/sw/source/core/text/redlnitr.hxx b/sw/source/core/text/redlnitr.hxx
index 0d0e013ff6d5..5c301312640e 100644
--- a/sw/source/core/text/redlnitr.hxx
+++ b/sw/source/core/text/redlnitr.hxx
@@ -39,18 +39,18 @@ class SwExtend
std::unique_ptr<SwFont> m_pFont;
const std::vector<ExtTextInputAttr> &m_rArr;
/// position of start of SwExtTextInput
- sal_uLong const m_nNode;
+ SwNodeOffset const m_nNode;
sal_Int32 const m_nStart;
/// current position (inside)
sal_Int32 m_nPos;
/// position of end of SwExtTextInput (in same node as start)
sal_Int32 const m_nEnd;
- bool Leave_(SwFont& rFnt, sal_uLong nNode, sal_Int32 nNew);
+ bool Leave_(SwFont& rFnt, SwNodeOffset nNode, sal_Int32 nNew);
bool Inside() const { return (m_nPos >= m_nStart && m_nPos < m_nEnd); }
static void ActualizeFont( SwFont &rFnt, ExtTextInputAttr nAttr );
public:
SwExtend(const std::vector<ExtTextInputAttr> &rArr,
- sal_uLong const nNode, sal_Int32 const nStart)
+ SwNodeOffset const nNode, sal_Int32 const nStart)
: m_rArr(rArr)
, m_nNode(nNode)
, m_nStart(nStart)
@@ -59,10 +59,10 @@ public:
{}
bool IsOn() const { return m_pFont != nullptr; }
void Reset() { m_pFont.reset(); m_nPos = COMPLETE_STRING; }
- bool Leave(SwFont& rFnt, sal_uLong const nNode, sal_Int32 const nNew)
+ bool Leave(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
{ return m_pFont && Leave_(rFnt, nNode, nNew); }
- short Enter(SwFont& rFnt, sal_uLong nNode, sal_Int32 nNew);
- sal_Int32 Next(sal_uLong nNode, sal_Int32 nNext);
+ short Enter(SwFont& rFnt, SwNodeOffset nNode, sal_Int32 nNew);
+ sal_Int32 Next(SwNodeOffset nNode, sal_Int32 nNext);
SwFont* GetFont() { return m_pFont.get(); }
void UpdateFont(SwFont &rFont) { ActualizeFont(rFont, m_rArr[m_nPos - m_nStart]); }
};
@@ -75,7 +75,7 @@ class SwRedlineItr
std::unique_ptr<SfxItemSet> m_pSet;
std::unique_ptr<SwExtend> m_pExt;
// note: this isn't actually used in the merged-para (Hide) case
- sal_uLong const m_nNdIdx;
+ SwNodeOffset const m_nNdIdx;
SwRedlineTable::size_type const m_nFirst;
SwRedlineTable::size_type m_nAct;
sal_Int32 m_nStart;
@@ -89,12 +89,12 @@ private:
void Clear_( SwFont* pFnt );
bool ChkSpecialUnderline_() const;
void FillHints( std::size_t nAuthor, RedlineType eType );
- short EnterExtend(SwFont& rFnt, sal_uLong const nNode, sal_Int32 const nNew)
+ short EnterExtend(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
{
if (m_pExt) return m_pExt->Enter(rFnt, nNode, nNew);
return 0;
}
- sal_Int32 NextExtend(sal_uLong const nNode, sal_Int32 const nNext) {
+ sal_Int32 NextExtend(SwNodeOffset const nNode, sal_Int32 const nNext) {
if (m_pExt) return m_pExt->Next(nNode, nNext);
return nNext;
}
@@ -108,7 +108,7 @@ public:
bool IsOn() const { return m_bOn || (m_pExt && m_pExt->IsOn()); }
void Clear( SwFont* pFnt ) { if (m_bOn) Clear_( pFnt ); }
void ChangeTextAttr( SwFont* pFnt, SwTextAttr const &rHt, bool bChg );
- short Seek(SwFont& rFnt, sal_uLong nNode, sal_Int32 nNew, sal_Int32 nOld);
+ short Seek(SwFont& rFnt, SwNodeOffset nNode, sal_Int32 nNew, sal_Int32 nOld);
void Reset() {
if (m_nAct != m_nFirst) m_nAct = SwRedlineTable::npos;
if (m_pExt) m_pExt->Reset();
@@ -117,10 +117,10 @@ public:
sal_Int32 nNext, SwTextNode const* pNode, SwRedlineTable::size_type & rAct);
bool ChkSpecialUnderline() const
{ return IsOn() && ChkSpecialUnderline_(); }
- bool CheckLine(sal_uLong nStartNode, sal_Int32 nChkStart, sal_uLong nEndNode,
+ bool CheckLine(SwNodeOffset nStartNode, sal_Int32 nChkStart, SwNodeOffset nEndNode,
sal_Int32 nChkEnd, OUString& rRedlineText, bool& bRedlineEnd,
- RedlineType& eRedlineEnd, bool bFullLine = true);
- bool LeaveExtend(SwFont& rFnt, sal_uLong const nNode, sal_Int32 const nNew)
+ RedlineType& eRedlineEnd, size_t* pAuthorAtPos = nullptr);
+ bool LeaveExtend(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
{ return m_pExt->Leave(rFnt, nNode, nNew); }
bool ExtOn() {
if (m_pExt) return m_pExt->IsOn();
diff --git a/sw/source/core/text/txtcache.hxx b/sw/source/core/text/txtcache.hxx
index e71be076fabb..ee18f14c3b0c 100644
--- a/sw/source/core/text/txtcache.hxx
+++ b/sw/source/core/text/txtcache.hxx
@@ -41,7 +41,10 @@ public:
void SetPara(SwParaPortion* pNew, bool bDelete)
{
if (!bDelete)
+ {
+ // coverity[leaked_storage] - intentional, ownership transferred
(void)m_pLine.release();
+ }
m_pLine.reset(pNew);
}
};
diff --git a/sw/source/core/text/txtdrop.cxx b/sw/source/core/text/txtdrop.cxx
index 312eb4dd71ba..afbe3b1470eb 100644
--- a/sw/source/core/text/txtdrop.cxx
+++ b/sw/source/core/text/txtdrop.cxx
@@ -36,6 +36,7 @@
#include <editeng/fhgtitem.hxx>
#include <calbck.hxx>
#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star;
@@ -324,7 +325,7 @@ void SwDropPortion::PaintText( const SwTextPaintInfo &rInf ) const
const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
if ( rInf.OnWin() &&
- !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() &&
+ !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings() &&
(!pCurrPart->GetFont().GetBackColor() || *pCurrPart->GetFont().GetBackColor() == COL_TRANSPARENT) )
{
rInf.DrawBackground( *this );
@@ -397,7 +398,7 @@ void SwDropPortion::Paint( const SwTextPaintInfo &rInf ) const
return;
if ( rInf.OnWin() &&
- !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
+ !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings() )
rInf.DrawBackground( *this );
// make sure that font is not rotated
@@ -484,8 +485,8 @@ void SwTextFormatter::CalcDropHeight( const sal_uInt16 nLines )
{
const SwLinePortion *const pOldCurr = GetCurr();
sal_uInt16 nDropHght = 0;
- sal_uInt16 nAscent = 0;
- sal_uInt16 nHeight = 0;
+ SwTwips nAscent = 0;
+ SwTwips nHeight = 0;
sal_uInt16 nDropLns = 0;
const bool bRegisterOld = IsRegisterOn();
m_bRegisterOn = false;
@@ -545,8 +546,8 @@ void SwTextFormatter::CalcDropHeight( const sal_uInt16 nLines )
void SwTextFormatter::GuessDropHeight( const sal_uInt16 nLines )
{
OSL_ENSURE( nLines, "GuessDropHeight: Give me more Lines!" );
- sal_uInt16 nAscent = 0;
- sal_uInt16 nHeight = 0;
+ SwTwips nAscent = 0;
+ SwTwips nHeight = 0;
SetDropLines( nLines );
if ( GetDropLines() > 1 )
{
@@ -678,7 +679,7 @@ void SwTextPainter::PaintDropPortion()
Point aLineOrigin( GetTopLeft() );
aLineOrigin.AdjustX(nX );
- sal_uInt16 nTmpAscent, nTmpHeight;
+ SwTwips nTmpAscent, nTmpHeight;
CalcAscentAndHeight( nTmpAscent, nTmpHeight );
aLineOrigin.AdjustY(nTmpAscent );
GetInfo().SetIdx( GetStart() );
@@ -775,7 +776,7 @@ void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf
m_aText[ nTmpIdx ] = aStr;
m_aWishedHeight[ nTmpIdx ] = sal_uInt16(nWishedHeight);
// save initial scaling factor
- m_aFactor[ nTmpIdx ] = static_cast<sal_uInt16>(nFactor);
+ m_aFactor[ nTmpIdx ] = o3tl::narrowing<sal_uInt16>(nFactor);
}
bool bGrow = (pDrop->GetLen() != TextFrameIndex(0));
@@ -793,10 +794,15 @@ void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf
OutputDevice* pOut = rInf.GetOut();
OutputDevice* pWin;
if( rInf.GetVsh() && rInf.GetVsh()->GetWin() )
- pWin = rInf.GetVsh()->GetWin();
+ pWin = rInf.GetVsh()->GetWin()->GetOutDev();
else
pWin = Application::GetDefaultDevice();
+ // adjust punctuation?
+ bool bKeepBaseline = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess()
+ .get(DocumentSettingId::DROP_CAP_PUNCTUATION) &&
+ !rInf.GetDropFormat()->GetWholeWord(); // && rInf.GetDropFormat()->GetChars() == 1;
+
while( bGrow )
{
// reset pCurrPart to first part
@@ -855,6 +861,14 @@ void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf
}
}
+ // extend rectangle to the baseline to avoid of giant dashes,
+ // quotation marks, bullet, asterisks etc.
+ if ( bKeepBaseline && aRect.Top() < 0 )
+ {
+ aRect.SetBottom(0);
+ aRect.SetTop(aRect.Top() - nAscent/60);
+ }
+
// Now we (hopefully) have a bounding rectangle for the
// glyphs of the current portion and the ascent of the current
// font
@@ -877,7 +891,7 @@ void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf
if( rFnt.GetTopBorder() )
{
aRect.setHeight(aRect.GetHeight() + rFnt.GetTopBorderSpace());
- aRect.setY(aRect.getY() - rFnt.GetTopBorderSpace());
+ aRect.SetPosY(aRect.Top() - rFnt.GetTopBorderSpace());
}
if( rFnt.GetBottomBorder() )
@@ -925,7 +939,7 @@ void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf
else
{
if ( bUseCache )
- m_aFactor[ nTmpIdx ] = static_cast<sal_uInt16>(nFactor);
+ m_aFactor[ nTmpIdx ] = o3tl::narrowing<sal_uInt16>(nFactor);
nMin = nFactor;
}
@@ -982,7 +996,7 @@ void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf
bool SwDropPortion::Format( SwTextFormatInfo &rInf )
{
bool bFull = false;
- m_nFix = static_cast<sal_uInt16>(rInf.X());
+ m_nFix = o3tl::narrowing<sal_uInt16>(rInf.X());
SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
aLayoutModeModifier.SetAuto();
@@ -1020,7 +1034,7 @@ bool SwDropPortion::Format( SwTextFormatInfo &rInf )
Width();
// set values
- pCurrPart->SetWidth( static_cast<sal_uInt16>(nTmpWidth) );
+ pCurrPart->SetWidth( o3tl::narrowing<sal_uInt16>(nTmpWidth) );
// Move
rInf.SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
@@ -1029,7 +1043,7 @@ bool SwDropPortion::Format( SwTextFormatInfo &rInf )
}
SetJoinBorderWithNext(false);
SetJoinBorderWithPrev(false);
- Width( static_cast<sal_uInt16>(rInf.X() - nOldX) );
+ Width( o3tl::narrowing<sal_uInt16>(rInf.X() - nOldX) );
}
// reset my length
@@ -1066,7 +1080,7 @@ bool SwDropPortion::Format( SwTextFormatInfo &rInf )
else
{
const sal_uInt16 nWant = Width() + GetDistance();
- const sal_uInt16 nRest = static_cast<sal_uInt16>(rInf.Width() - rInf.X());
+ const sal_uInt16 nRest = o3tl::narrowing<sal_uInt16>(rInf.Width() - rInf.X());
if( ( nWant > nRest ) ||
lcl_IsDropFlyInter( rInf, Width() + GetDistance(), m_nDropHeight ) )
m_nDistance = 0;
diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx
index e150f7f489f1..e522f4e08929 100644
--- a/sw/source/core/text/txtfld.cxx
+++ b/sw/source/core/text/txtfld.cxx
@@ -58,241 +58,143 @@
#include <editeng/colritem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/crossedoutitem.hxx>
+#include <officecfg/Office/Writer.hxx>
static bool lcl_IsInBody( SwFrame const *pFrame )
{
if ( pFrame->IsInDocBody() )
return true;
- else
- {
- const SwFrame *pTmp = pFrame;
- const SwFlyFrame *pFly;
- while ( nullptr != (pFly = pTmp->FindFlyFrame()) )
- pTmp = pFly->GetAnchorFrame();
- return pTmp->IsInDocBody();
- }
+
+ while (const SwFlyFrame* pFly = pFrame->FindFlyFrame())
+ pFrame = pFly->GetAnchorFrame();
+ return pFrame->IsInDocBody();
+}
+
+static OUString ExpandField(const SwField& rField, const SwTextFormatter& rFormatter,
+ const SwTextFormatInfo& rInf)
+{
+ if (rInf.GetOpt().IsFieldName())
+ return rField.GetFieldName();
+
+ const SwViewShell* pSh = rInf.GetVsh();
+ const SwDoc* pDoc(pSh ? pSh->GetDoc() : nullptr);
+ const bool bInClipboard(!pDoc || pDoc->IsClipBoard());
+ return rField.ExpandField(bInClipboard, rFormatter.GetTextFrame()->getRootFrame());
}
SwExpandPortion *SwTextFormatter::NewFieldPortion( SwTextFormatInfo &rInf,
const SwTextAttr *pHint ) const
{
- SwExpandPortion *pRet = nullptr;
- SwFrame *pFrame = m_pFrame;
SwField *pField = const_cast<SwField*>(pHint->GetFormatField().GetField());
const bool bName = rInf.GetOpt().IsFieldName();
- SwCharFormat* pChFormat = nullptr;
- bool bNewFlyPor = false;
- sal_uInt16 subType = 0;
-
// set language
const_cast<SwTextFormatter*>(this)->SeekAndChg( rInf );
if (pField->GetLanguage() != GetFnt()->GetLanguage())
- {
pField->SetLanguage( GetFnt()->GetLanguage() );
- // let the visual note know about its new language
- if (pField->GetTyp()->Which()==SwFieldIds::Postit)
- const_cast<SwFormatField*> (&pHint->GetFormatField())->Broadcast( SwFormatFieldHint( &pHint->GetFormatField(), SwFormatFieldHintWhich::LANGUAGE ) );
- }
SwViewShell *pSh = rInf.GetVsh();
- SwDoc *const pDoc( pSh ? pSh->GetDoc() : nullptr );
- bool const bInClipboard( pDoc == nullptr || pDoc->IsClipBoard() );
- bool bPlaceHolder = false;
- switch( pField->GetTyp()->Which() )
+ switch (pField->GetTyp()->Which())
{
case SwFieldIds::Script:
case SwFieldIds::Postit:
- pRet = new SwPostItsPortion( SwFieldIds::Script == pField->GetTyp()->Which() );
- break;
-
+ return new SwPostItsPortion(SwFieldIds::Script == pField->GetTyp()->Which());
case SwFieldIds::CombinedChars:
- {
- if( bName )
- pRet = new SwFieldPortion( pField->GetFieldName() );
- else
- pRet = new SwCombinedPortion( pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- }
+ if (!bName)
+ return new SwCombinedPortion(ExpandField(*pField, *this, rInf));
break;
-
case SwFieldIds::HiddenText:
- {
- OUString const aStr( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwHiddenPortion(aStr);
- }
- break;
-
+ return new SwHiddenPortion(ExpandField(*pField, *this, rInf));
case SwFieldIds::Chapter:
- if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() )
- {
- static_cast<SwChapterField*>(pField)->ChangeExpansion(*pFrame,
- &static_txtattr_cast<SwTextField const*>(pHint)->GetTextNode());
- }
+ if (!bName && pSh && !pSh->Imp()->IsUpdateExpFields())
{
- OUString const aStr( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion( aStr );
+ static_cast<SwChapterField*>(pField)->ChangeExpansion(
+ *m_pFrame, &static_txtattr_cast<SwTextField const*>(pHint)->GetTextNode());
}
break;
-
case SwFieldIds::DocStat:
- if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() )
- {
- static_cast<SwDocStatField*>(pField)->ChangeExpansion( pFrame );
- }
+ if (!bName && pSh && !pSh->Imp()->IsUpdateExpFields())
{
- OUString const aStr( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion( aStr );
+ static_cast<SwDocStatField*>(pField)->ChangeExpansion(m_pFrame);
}
- static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_PAGECOUNTFLD;
break;
-
case SwFieldIds::PageNumber:
- {
- if( !bName && pSh && pSh->GetLayout() && !pSh->Imp()->IsUpdateExpFields() )
+ if (!bName && pSh && pSh->GetLayout() && !pSh->Imp()->IsUpdateExpFields())
{
- SwPageNumberFieldType *pPageNr = static_cast<SwPageNumberFieldType *>(pField->GetTyp());
+ auto pPageNr = static_cast<SwPageNumberFieldType*>(pField->GetTyp());
const SwRootFrame* pTmpRootFrame = pSh->GetLayout();
const bool bVirt = pTmpRootFrame->IsVirtPageNum();
- sal_uInt16 nVirtNum = pFrame->GetVirtPageNum();
+ sal_uInt16 nVirtNum = m_pFrame->GetVirtPageNum();
sal_uInt16 nNumPages = pTmpRootFrame->GetPageNum();
SvxNumType nNumFormat = SvxNumType(-1);
- if(SVX_NUM_PAGEDESC == pField->GetFormat())
- nNumFormat = pFrame->FindPageFrame()->GetPageDesc()->GetNumType().GetNumberingType();
- static_cast<SwPageNumberField*>(pField)
- ->ChangeExpansion(nVirtNum, nNumPages);
- pPageNr->ChangeExpansion(pDoc,
- bVirt, nNumFormat != SvxNumType(-1) ? &nNumFormat : nullptr);
+ if (SVX_NUM_PAGEDESC == pField->GetFormat())
+ nNumFormat
+ = m_pFrame->FindPageFrame()->GetPageDesc()->GetNumType().GetNumberingType();
+ static_cast<SwPageNumberField*>(pField)->ChangeExpansion(nVirtNum, nNumPages);
+ pPageNr->ChangeExpansion(pSh->GetDoc(), bVirt,
+ nNumFormat != SvxNumType(-1) ? &nNumFormat : nullptr);
}
- {
- OUString const aStr( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion( aStr );
- }
- static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_PAGENUMBERFLD;
break;
- }
case SwFieldIds::GetExp:
- {
- if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() )
+ if (!bName && pSh && !pSh->Imp()->IsUpdateExpFields())
{
- SwGetExpField* pExpField = static_cast<SwGetExpField*>(pField);
- if( !::lcl_IsInBody( pFrame ) )
+ auto pExpField = static_cast<SwGetExpField*>(pField);
+ if (!::lcl_IsInBody(m_pFrame))
{
- pExpField->ChgBodyTextFlag( false );
- pExpField->ChangeExpansion(*pFrame,
- *static_txtattr_cast<SwTextField const*>(pHint));
+ pExpField->ChgBodyTextFlag(false);
+ pExpField->ChangeExpansion(*m_pFrame,
+ *static_txtattr_cast<SwTextField const*>(pHint));
}
- else if( !pExpField->IsInBodyText() )
+ else if (!pExpField->IsInBodyText())
{
// Was something else previously, thus: expand first, then convert it!
- pExpField->ChangeExpansion(*pFrame,
- *static_txtattr_cast<SwTextField const*>(pHint));
- pExpField->ChgBodyTextFlag( true );
+ pExpField->ChangeExpansion(*m_pFrame,
+ *static_txtattr_cast<SwTextField const*>(pHint));
+ pExpField->ChgBodyTextFlag(true);
}
}
- {
- OUString const aStr( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion( aStr );
- }
break;
- }
case SwFieldIds::Database:
- {
- if( !bName )
- {
- SwDBField* pDBField = static_cast<SwDBField*>(pField);
- pDBField->ChgBodyTextFlag( ::lcl_IsInBody( pFrame ) );
- }
+ if (!bName)
{
- OUString const aStr( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion(aStr);
+ static_cast<SwDBField*>(pField)->ChgBodyTextFlag(::lcl_IsInBody(m_pFrame));
}
break;
- }
case SwFieldIds::RefPageGet:
- if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() )
- {
- static_cast<SwRefPageGetField*>(pField)->ChangeExpansion(*pFrame,
- static_txtattr_cast<SwTextField const*>(pHint));
- }
+ if (!bName && pSh && !pSh->Imp()->IsUpdateExpFields())
{
- OUString const aStr( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion(aStr);
+ static_cast<SwRefPageGetField*>(pField)->ChangeExpansion(
+ *m_pFrame, static_txtattr_cast<SwTextField const*>(pHint));
}
break;
-
case SwFieldIds::JumpEdit:
- if( !bName )
- pChFormat = static_cast<SwJumpEditField*>(pField)->GetCharFormat();
- bNewFlyPor = true;
- bPlaceHolder = true;
- break;
- case SwFieldIds::GetRef:
- subType = static_cast<SwGetRefField*>(pField)->GetSubType();
+ {
+ std::unique_ptr<SwFont> pFont;
+ if (!bName)
{
- OUString const str( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion(str);
+ pFont = std::make_unique<SwFont>(*m_pFont);
+ pFont->SetDiffFnt(
+ &static_cast<SwJumpEditField*>(pField)->GetCharFormat()->GetAttrSet(),
+ &m_pFrame->GetDoc().getIDocumentSettingAccess());
}
- if( subType == REF_BOOKMARK )
- static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_BOOKMARKFLD;
- else if( subType == REF_SETREFATTR )
- static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_SETREFATTRFLD;
- break;
- case SwFieldIds::DateTime:
- subType = static_cast<SwDateTimeField*>(pField)->GetSubType();
+ return new SwJumpFieldPortion(ExpandField(*pField, *this, rInf), pField->GetPar2(),
+ std::move(pFont), pField->GetFormat());
+ }
+ case SwFieldIds::GetRef:
+ if (!bName)
{
- OUString const str( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion(str);
+ auto pGetRef = static_cast<SwGetRefField*>(pField);
+ if (pGetRef->GetSubType() == REF_STYLE)
+ pGetRef->UpdateField(static_txtattr_cast<SwTextField const*>(pHint), m_pFrame);
}
- if( subType & DATEFLD )
- static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_DATEFLD;
- else if( subType & TIMEFLD )
- static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_TIMEFLD;
break;
default:
- {
- OUString const aStr( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion(aStr);
- }
- }
-
- if( bNewFlyPor )
- {
- std::unique_ptr<SwFont> pTmpFnt;
- if( !bName )
- {
- pTmpFnt.reset(new SwFont( *m_pFont ));
- pTmpFnt->SetDiffFnt(&pChFormat->GetAttrSet(), &m_pFrame->GetDoc().getIDocumentSettingAccess());
- }
- OUString const aStr( bName
- ? pField->GetFieldName()
- : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
- pRet = new SwFieldPortion(aStr, std::move(pTmpFnt), bPlaceHolder);
+ break;
}
-
- return pRet;
+ return new SwFieldPortion(ExpandField(*pField, *this, rInf));
}
static SwFieldPortion * lcl_NewMetaPortion(SwTextAttr & rHint, const bool bPrefix)
@@ -304,7 +206,8 @@ static SwFieldPortion * lcl_NewMetaPortion(SwTextAttr & rHint, const bool bPrefi
OSL_ENSURE(pField, "lcl_NewMetaPortion: no meta field?");
if (pField)
{
- pField->GetPrefixAndSuffix(bPrefix ? &fix : nullptr, bPrefix ? nullptr : &fix);
+ OUString color;
+ pField->GetPrefixAndSuffix(bPrefix ? &fix : nullptr, bPrefix ? nullptr : &fix, &color);
}
return new SwFieldPortion( fix );
}
@@ -405,7 +308,12 @@ SwLinePortion *SwTextFormatter::NewExtraPortion( SwTextFormatInfo &rInf )
}
if( !pRet )
{
- pRet = new SwFieldPortion( "" );
+ auto pFieldPortion = new SwFieldPortion( "" );
+ if (pHint->Which() == RES_TXTATR_CONTENTCONTROL)
+ {
+ pFieldPortion->SetContentControl(true);
+ }
+ pRet = pFieldPortion;
rInf.SetLen(TextFrameIndex(1));
}
return pRet;
@@ -423,39 +331,9 @@ static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextForm
if( !pIDSA->get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING ))
return;
- SwFormatAutoFormat const& rListAutoFormat(static_cast<SwFormatAutoFormat const&>(rInf.GetTextFrame()->GetTextNodeForParaProps()->GetAttr(RES_PARATR_LIST_AUTOFMT)));
+ SwFormatAutoFormat const& rListAutoFormat(rInf.GetTextFrame()->GetTextNodeForParaProps()->GetAttr(RES_PARATR_LIST_AUTOFMT));
std::shared_ptr<SfxItemSet> pSet(rListAutoFormat.GetStyleHandle());
- // TODO remove this fallback (for WW8/RTF)
- bool isDOCX = pIDSA->get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
- if (!isDOCX && !pSet)
- {
- TextFrameIndex const nTextLen(rInf.GetTextFrame()->GetText().getLength());
- SwTextNode const* pNode(nullptr);
- sw::MergedAttrIterReverse iter(*rInf.GetTextFrame());
- for (SwTextAttr const* pHint = iter.PrevAttr(&pNode); pHint;
- pHint = iter.PrevAttr(&pNode))
- {
- TextFrameIndex const nHintEnd(
- rInf.GetTextFrame()->MapModelToView(pNode, pHint->GetAnyEnd()));
- if (nHintEnd < nTextLen)
- {
- break; // only those at para end are interesting
- }
- // Formatting for the paragraph mark is usually set to apply only to the
- // (non-existent) extra character at end of the text node, but there can be
- // other hints too (ending at nTextLen), so look for all matching hints.
- // Still the (non-existent) extra character at the end is preferred if it exists.
- if (pHint->Which() == RES_TXTATR_AUTOFMT)
- {
- pSet = pHint->GetAutoFormat().GetStyleHandle();
- // When we find an empty hint (start == end) we got what we are looking for.
- if (pHint->GetStart() == *pHint->End())
- break;
- }
- }
- }
-
// Check each item and in case it should be ignored, then clear it.
if (!pSet)
return;
@@ -473,7 +351,8 @@ static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextForm
{
if (!SwTextNode::IsIgnoredCharFormatForNumbering(nWhich, /*bIsCharStyle=*/true)
&& !pCleanedSet->HasItem(nWhich)
- && !(pFormat && pFormat->HasItem(nWhich)) )
+ && !(pFormat && pFormat->HasItem(nWhich))
+ && rStyleAttrs.GetItemState(nWhich) > SfxItemState::DEFAULT)
{
// Copy from parent sets only allowed items which will not overwrite
// values explicitly defined in current set (pCleanedSet) or in pFormat
@@ -530,63 +409,84 @@ static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextForm
if (oFontBackColor)
pNumFnt->SetBackColor(oFontBackColor);
- if (aHighlight != COL_TRANSPARENT)
+ if (aHighlight != COL_TRANSPARENT && !pCleanedSet->HasItem(RES_CHRATR_HIGHLIGHT))
pNumFnt->SetHighlightColor(aHighlight);
}
-static const SwRangeRedline* lcl_GetRedlineAtNodeInsertionOrDeletion( const SwTextNode& rTextNode )
+static const SwRangeRedline* lcl_GetRedlineAtNodeInsertionOrDeletion( const SwTextNode& rTextNode,
+ bool& bIsMoved )
{
const SwDoc& rDoc = rTextNode.GetDoc();
SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( rTextNode, RedlineType::Any );
if( SwRedlineTable::npos != nRedlPos )
{
- const sal_uLong nNdIdx = rTextNode.GetIndex();
- for( ; nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++nRedlPos )
+ const SwNodeOffset nNdIdx = rTextNode.GetIndex();
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for( ; nRedlPos < rTable.size() ; ++nRedlPos )
{
- const SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ const SwRangeRedline* pTmp = rTable[ nRedlPos ];
+ SwNodeOffset nStart = pTmp->GetPoint()->GetNodeIndex(),
+ nEnd = pTmp->GetMark()->GetNodeIndex();
+ if( nStart > nEnd )
+ std::swap(nStart, nEnd);
if( RedlineType::Delete == pTmp->GetType() ||
RedlineType::Insert == pTmp->GetType() )
{
- const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
- if( pRStt->nNode < nNdIdx && pREnd->nNode >= nNdIdx )
+ if( nStart <= nNdIdx && nEnd > nNdIdx )
+ {
+ bIsMoved = pTmp->IsMoved();
return pTmp;
+ }
}
+ if( nStart > nNdIdx )
+ break;
}
}
return nullptr;
}
-static void lcl_setRedlineAttr( SwTextFormatInfo &rInf, const SwTextNode& rTextNode, const std::unique_ptr<SwFont>& pNumFnt )
+static bool lcl_setRedlineAttr( SwTextFormatInfo &rInf, const SwTextNode& rTextNode, const std::unique_ptr<SwFont>& pNumFnt )
{
if ( rInf.GetVsh()->GetLayout()->IsHideRedlines() )
- return;
+ return false;
- const SwRangeRedline* pRedlineNum = lcl_GetRedlineAtNodeInsertionOrDeletion( rTextNode );
+ bool bIsMoved;
+ const SwRangeRedline* pRedlineNum = lcl_GetRedlineAtNodeInsertionOrDeletion( rTextNode, bIsMoved );
if (!pRedlineNum)
- return;
+ return false;
- std::unique_ptr<SfxItemSet> pSet;
+ // moved text: dark green with double underline or strikethrough
+ if ( bIsMoved )
+ {
+ pNumFnt->SetColor(COL_GREEN);
+ if ( RedlineType::Delete == pRedlineNum->GetType() )
+ pNumFnt->SetStrikeout(STRIKEOUT_DOUBLE);
+ else
+ pNumFnt->SetUnderline(LINESTYLE_DOUBLE);
+ return true;
+ }
SwAttrPool& rPool = rInf.GetVsh()->GetDoc()->GetAttrPool();
- pSet = std::make_unique<SfxItemSet>(rPool, svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END-1>{});
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1> aSet(rPool);
std::size_t aAuthor = (1 < pRedlineNum->GetStackCount())
? pRedlineNum->GetAuthor( 1 )
: pRedlineNum->GetAuthor();
if ( RedlineType::Delete == pRedlineNum->GetType() )
- SW_MOD()->GetDeletedAuthorAttr(aAuthor, *pSet);
+ SW_MOD()->GetDeletedAuthorAttr(aAuthor, aSet);
else
- SW_MOD()->GetInsertAuthorAttr(aAuthor, *pSet);
-
- const SfxPoolItem* pItem = nullptr;
- if (SfxItemState::SET == pSet->GetItemState(RES_CHRATR_COLOR, true, &pItem))
- pNumFnt->SetColor(static_cast<const SvxColorItem*>(pItem)->GetValue());
- if (SfxItemState::SET == pSet->GetItemState(RES_CHRATR_UNDERLINE, true, &pItem))
- pNumFnt->SetUnderline(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle());
- if (SfxItemState::SET == pSet->GetItemState(RES_CHRATR_CROSSEDOUT, true, &pItem))
- pNumFnt->SetStrikeout( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() );
+ SW_MOD()->GetInsertAuthorAttr(aAuthor, aSet);
+
+ if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR))
+ pNumFnt->SetColor(pItem->GetValue());
+ if (const SvxUnderlineItem* pItem = aSet.GetItemIfSet(RES_CHRATR_UNDERLINE))
+ pNumFnt->SetUnderline(pItem->GetLineStyle());
+ if (const SvxCrossedOutItem* pItem = aSet.GetItemIfSet(RES_CHRATR_CROSSEDOUT))
+ pNumFnt->SetStrikeout( pItem->GetStrikeout() );
+
+ return true;
}
SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) const
@@ -657,7 +557,7 @@ SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) con
if( SVX_NUM_CHAR_SPECIAL == rNumFormat.GetNumberingType() )
{
- const vcl::Font *pFormatFnt = rNumFormat.GetBulletFont();
+ const std::optional<vcl::Font> pFormatFnt = rNumFormat.GetBulletFont();
// Build a new bullet font basing on the current paragraph font:
std::unique_ptr<SwFont> pNumFnt(new SwFont( &rInf.GetCharAttr(), pIDSA ));
@@ -715,11 +615,37 @@ SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) con
}
else
{
- OUString aText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame()) );
- if ( !aText.isEmpty() )
+ // Show Changes mode shows the actual numbering (SwListRedlineType::HIDDEN) and
+ // the original one (SwListRedlineType::ORIGTEXT) instead of the fake numbering
+ // (SwListRedlineType::SHOW, which counts removed and inserted numbered paragraphs
+ // in a single list)
+ bool bHasHiddenNum = false;
+ OUString aText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame(), SwListRedlineType::HIDDEN) );
+ const SwDoc& rDoc = pTextNd->GetDoc();
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ if ( rTable.size() && !rInf.GetVsh()->GetLayout()->IsHideRedlines() )
{
- aText += pTextNd->GetLabelFollowedBy();
+ OUString aHiddenText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame(), SwListRedlineType::ORIGTEXT) );
+
+ if ( !aText.isEmpty() || !aHiddenText.isEmpty() )
+ {
+ bool bDisplayChangedParagraphNumbering = officecfg::Office::Writer::Comparison::DisplayChangedParagraphNumbering::get();
+ if (bDisplayChangedParagraphNumbering && aText != aHiddenText && !aHiddenText.isEmpty())
+ {
+ bHasHiddenNum = true;
+ // show also original number after the actual one enclosed in [ and ],
+ // and replace tabulator with space to avoid messy indentation
+ // resulted by the longer numbering, e.g. "1.[2.]" instead of "1.".
+ aText = aText + "[" + aHiddenText + "]"
+ + pTextNd->GetLabelFollowedBy().replaceAll("\t", " ");
+ }
+ else if (!aText.isEmpty())
+ aText += pTextNd->GetLabelFollowedBy();
+ }
}
+ else if (pTextNd->getIDocumentSettingAccess()->get(DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY)
+ || !aText.isEmpty())
+ aText += pTextNd->GetLabelFollowedBy();
// Not just an optimization ...
// A number portion without text will be assigned a width of 0.
@@ -748,7 +674,8 @@ SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) con
checkApplyParagraphMarkFormatToNumbering(pNumFnt.get(), rInf, pIDSA, pFormat);
- lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt );
+ if ( !lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt ) && bHasHiddenNum )
+ pNumFnt->SetColor(NON_PRINTING_CHARACTER_COLOR);
// we do not allow a vertical font
pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() );
diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx
index a5fb1f6b6731..7c4d9a2e160d 100644
--- a/sw/source/core/text/txtfly.cxx
+++ b/sw/source/core/text/txtfly.cxx
@@ -33,6 +33,8 @@
#include <frmtool.hxx>
#include <ndtxt.hxx>
#include <txtfly.hxx>
+#include "inftxt.hxx"
+#include "porrst.hxx"
#include "txtpaint.hxx"
#include <notxtfrm.hxx>
#include <fmtcnct.hxx>
@@ -48,6 +50,7 @@
#include <sortedobjs.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentSettingAccess.hxx>
+#include <formatlinebreak.hxx>
#include <svx/svdoedge.hxx>
#ifdef DBG_UTIL
@@ -159,7 +162,7 @@ SwRect SwContourCache::CalcBoundRect( const SwAnchoredObject* pAnchoredObj,
const bool bRight )
{
SwRect aRet;
- const SwFrameFormat* pFormat = &(pAnchoredObj->GetFrameFormat());
+ const SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat();
bool bHandleContour(pFormat->GetSurround().IsContour());
if(!bHandleContour)
@@ -176,12 +179,12 @@ SwRect SwContourCache::CalcBoundRect( const SwAnchoredObject* pAnchoredObj,
}
if( bHandleContour &&
- ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr ||
+ ( pAnchoredObj->DynCastFlyFrame() == nullptr ||
( static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower() &&
static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower()->IsNoTextFrame() ) ) )
{
aRet = pAnchoredObj->GetObjRectWithSpaces();
- if( aRet.IsOver( rLine ) )
+ if( aRet.Overlaps( rLine ) )
{
if( !pContourCache )
pContourCache = new SwContourCache;
@@ -216,7 +219,7 @@ SwRect SwContourCache::ContourRect( const SwFormat* pFormat,
mvItems.pop_back();
}
::basegfx::B2DPolyPolygon aPolyPolygon;
- std::unique_ptr<::basegfx::B2DPolyPolygon> pPolyPolygon;
+ std::optional<::basegfx::B2DPolyPolygon> pPolyPolygon;
if ( auto pVirtFlyDrawObj = dynamic_cast< const SwVirtFlyDrawObj *>( pObj ) )
{
@@ -224,27 +227,26 @@ SwRect SwContourCache::ContourRect( const SwFormat* pFormat,
// the graphic to change its size, call ClrObject()
tools::PolyPolygon aPoly;
if( !pVirtFlyDrawObj->GetFlyFrame()->GetContour( aPoly ) )
- aPoly = tools::PolyPolygon( static_cast<const SwVirtFlyDrawObj*>(pObj)->
+ aPoly = tools::PolyPolygon( pVirtFlyDrawObj->
GetFlyFrame()->getFrameArea().SVRect() );
aPolyPolygon.clear();
aPolyPolygon.append(aPoly.getB2DPolyPolygon());
}
else
{
- if( dynamic_cast< const E3dObject *>( pObj ) == nullptr )
+ if( DynCastE3dObject( pObj ) == nullptr )
{
aPolyPolygon = pObj->TakeXorPoly();
}
- ::basegfx::B2DPolyPolygon aContourPoly(pObj->TakeContour());
- pPolyPolygon.reset(new ::basegfx::B2DPolyPolygon(aContourPoly));
+ pPolyPolygon = pObj->TakeContour();
}
const SvxLRSpaceItem &rLRSpace = pFormat->GetLRSpace();
const SvxULSpaceItem &rULSpace = pFormat->GetULSpace();
CacheItem item {
pObj, // due to #37347 the Object must be entered only after GetContour()
- std::make_unique<TextRanger>( aPolyPolygon, pPolyPolygon.get(), 20,
- static_cast<sal_uInt16>(rLRSpace.GetLeft()), static_cast<sal_uInt16>(rLRSpace.GetRight()),
+ std::make_unique<TextRanger>( aPolyPolygon, pPolyPolygon ? &*pPolyPolygon : nullptr, 20,
+ o3tl::narrowing<sal_uInt16>(rLRSpace.GetLeft()), o3tl::narrowing<sal_uInt16>(rLRSpace.GetRight()),
pFormat->GetSurround().IsOutside(), false, pFrame->IsVertical() )
};
mvItems.insert(mvItems.begin(), std::move(item));
@@ -371,7 +373,7 @@ void SwTextFly::CtorInitTextFly( const SwTextFrame *pFrame )
m_bTopRule = true;
m_nMinBottom = 0;
m_nNextTop = 0;
- m_nCurrFrameNodeIndex = ULONG_MAX;
+ m_nCurrFrameNodeIndex = NODE_OFFSET_MAX;
}
SwRect SwTextFly::GetFrame_( const SwRect &rRect ) const
@@ -412,6 +414,12 @@ bool SwTextFly::IsAnyObj( const SwRect &rRect ) const
{
aRect = SwRect(m_pCurrFrame->getFrameArea().Pos() + m_pCurrFrame->getFramePrintArea().Pos(),
m_pCurrFrame->getFramePrintArea().SSize());
+
+ SwTwips nLower = m_pCurrFrame->GetLowerMarginForFlyIntersect();
+ if (nLower > 0)
+ {
+ aRect.AddBottom(nLower);
+ }
}
const SwSortedObjs *pSorted = m_pPage->GetSortedObjs();
@@ -429,7 +437,7 @@ bool SwTextFly::IsAnyObj( const SwRect &rRect ) const
continue;
// #i68520#
- if( mpCurrAnchoredObj != pObj && aBound.IsOver( aRect ) )
+ if( mpCurrAnchoredObj != pObj && aBound.Overlaps( aRect ) )
return true;
}
}
@@ -485,7 +493,7 @@ void SwTextFly::DrawTextOpaque( SwDrawTextInfo &rInf )
OSL_ENSURE( !m_bTopRule, "DrawTextOpaque: Wrong TopRule" );
// #i68520#
- const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList()->size() : 0 );
+ const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList().size() : 0 );
if (nCount > 0)
{
const SdrLayerID nHellId = m_pPage->getRootFrame()->GetCurrShell()->getIDocumentDrawModelAccess().GetHellId();
@@ -493,22 +501,21 @@ void SwTextFly::DrawTextOpaque( SwDrawTextInfo &rInf )
{
// #i68520#
const SwAnchoredObject* pTmpAnchoredObj = (*mpAnchoredObjList)[i];
- if( dynamic_cast<const SwFlyFrame*>(pTmpAnchoredObj) &&
- mpCurrAnchoredObj != pTmpAnchoredObj )
+ const SwFlyFrame* pFly = pTmpAnchoredObj->DynCastFlyFrame();
+ if( pFly && mpCurrAnchoredObj != pTmpAnchoredObj )
{
// #i68520#
- const SwFlyFrame& rFly = dynamic_cast<const SwFlyFrame&>(*pTmpAnchoredObj);
- if( aRegion.GetOrigin().IsOver( rFly.getFrameArea() ) )
+ if( aRegion.GetOrigin().Overlaps( pFly->getFrameArea() ) )
{
- const SwFrameFormat *pFormat = rFly.GetFormat();
+ const SwFrameFormat *pFormat = pFly->GetFormat();
const SwFormatSurround &rSur = pFormat->GetSurround();
const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
// Only the ones who are opaque and more to the top
- if( ! rFly.IsBackgroundTransparent() &&
+ if( ! pFly->IsBackgroundTransparent() &&
css::text::WrapTextMode_THROUGH == rSur.GetSurround() &&
( !rSur.IsAnchorOnly() ||
// #i68520#
- GetMaster() == rFly.GetAnchorFrame() ||
+ GetMaster() == pFly->GetAnchorFrame() ||
((RndStdIds::FLY_AT_PARA != rAnchor.GetAnchorId()) &&
(RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId())
)
@@ -520,14 +527,14 @@ void SwTextFly::DrawTextOpaque( SwDrawTextInfo &rInf )
{
// Except for the content is transparent
const SwNoTextFrame *pNoText =
- rFly.Lower() && rFly.Lower()->IsNoTextFrame()
- ? static_cast<const SwNoTextFrame*>(rFly.Lower())
+ pFly->Lower() && pFly->Lower()->IsNoTextFrame()
+ ? static_cast<const SwNoTextFrame*>(pFly->Lower())
: nullptr;
if ( !pNoText ||
(!pNoText->IsTransparent() && !rSur.IsContour()) )
{
bOpaque = true;
- aRegion -= rFly.getFrameArea();
+ aRegion -= pFly->getFrameArea();
}
}
}
@@ -571,7 +578,7 @@ void SwTextFly::DrawFlyRect( OutputDevice* pOut, const SwRect &rRect )
SwRegionRects aRegion( rRect );
OSL_ENSURE( !m_bTopRule, "DrawFlyRect: Wrong TopRule" );
// #i68520#
- const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList()->size() : 0 );
+ const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList().size() : 0 );
if (nCount > 0)
{
const SdrLayerID nHellId = m_pPage->getRootFrame()->GetCurrShell()->getIDocumentDrawModelAccess().GetHellId();
@@ -583,11 +590,11 @@ void SwTextFly::DrawFlyRect( OutputDevice* pOut, const SwRect &rRect )
continue;
// #i68520#
- const SwFlyFrame* pFly = dynamic_cast<const SwFlyFrame*>(pAnchoredObjTmp);
+ const SwFlyFrame* pFly = pAnchoredObjTmp->DynCastFlyFrame();
if (pFly)
{
// #i68520#
- const SwFormatSurround& rSur = pAnchoredObjTmp->GetFrameFormat().GetSurround();
+ const SwFormatSurround& rSur = pAnchoredObjTmp->GetFrameFormat()->GetSurround();
// OD 24.01.2003 #106593# - correct clipping of fly frame area.
// Consider that fly frame background/shadow can be transparent
@@ -648,8 +655,8 @@ bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj,
if( ( bInFootnote || bInFooterOrHeader ) && m_bTopRule )
{
// #i26945#
- const SwFrameFormat& rFrameFormat = _pAnchoredObj->GetFrameFormat();
- const SwFormatAnchor& rNewA = rFrameFormat.GetAnchor();
+ const SwFrameFormat* pFrameFormat = _pAnchoredObj->GetFrameFormat();
+ const SwFormatAnchor& rNewA = pFrameFormat->GetAnchor();
if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
{
if ( bInFootnote )
@@ -657,7 +664,7 @@ bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj,
if ( bInFooterOrHeader )
{
- const SwFormatVertOrient& aVert( rFrameFormat.GetVertOrient() );
+ const SwFormatVertOrient& aVert(pFrameFormat->GetVertOrient());
bool bVertPrt = aVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ||
aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA;
if( bVertPrt )
@@ -670,7 +677,24 @@ bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj,
// bEvade: consider pNew, if we are not inside a fly
// consider pNew, if pNew is lower of <mpCurrAnchoredObj>
bool bEvade = !mpCurrAnchoredObj ||
- Is_Lower_Of( dynamic_cast<const SwFlyFrame*>(mpCurrAnchoredObj), pNew);
+ Is_Lower_Of( mpCurrAnchoredObj->DynCastFlyFrame(), pNew);
+
+ auto pFly = _pAnchoredObj->DynCastFlyFrame();
+ if (pFly && pFly->IsFlySplitAllowed())
+ {
+ // Check if _pAnchoredObj is a split fly inside an other split fly. Always collect such
+ // flys, otherwise the inner anchor text will overlap with the inner fly.
+ SwFrame* pFlyAnchor = const_cast<SwAnchoredObject*>(_pAnchoredObj)
+ ->GetAnchorFrameContainingAnchPos();
+ if (pFlyAnchor && pFlyAnchor->IsInFly())
+ {
+ auto pOuterFly = pFlyAnchor->FindFlyFrame();
+ if (pOuterFly && pOuterFly->IsFlySplitAllowed())
+ {
+ return true;
+ }
+ }
+ }
if ( !bEvade )
{
@@ -686,13 +710,14 @@ bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj,
{
// Within chained Flys we only avoid Lower
// #i68520#
- const SwFormatChain &rChain = mpCurrAnchoredObj->GetFrameFormat().GetChain();
+ const SwFrameFormat* pCurObjFormat = mpCurrAnchoredObj->GetFrameFormat();
+ const SwFormatChain& rChain = pCurObjFormat->GetChain();
if ( !rChain.GetPrev() && !rChain.GetNext() )
{
// #i26945#
- const SwFormatAnchor& rNewA = _pAnchoredObj->GetFrameFormat().GetAnchor();
+ const SwFormatAnchor& rNewA = _pAnchoredObj->GetFrameFormat()->GetAnchor();
// #i68520#
- const SwFormatAnchor& rCurrA = mpCurrAnchoredObj->GetFrameFormat().GetAnchor();
+ const SwFormatAnchor& rCurrA = pCurObjFormat->GetAnchor();
// If <mpCurrAnchoredObj> is anchored as character, its content
// does not wrap around pNew
@@ -736,7 +761,7 @@ bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj,
{
// #i68520#
const SwRect& aTmp( _pAnchoredObj->GetObjRectWithSpaces() );
- if ( !aTmp.IsOver( mpCurrAnchoredObj->GetObjRectWithSpaces() ) )
+ if ( !aTmp.Overlaps( mpCurrAnchoredObj->GetObjRectWithSpaces() ) )
bEvade = false;
}
}
@@ -744,86 +769,109 @@ bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj,
if ( bEvade )
{
// #i26945#
- const SwFormatAnchor& rNewA = _pAnchoredObj->GetFrameFormat().GetAnchor();
- OSL_ENSURE( RndStdIds::FLY_AS_CHAR != rNewA.GetAnchorId(),
- "Don't call GetTop with a FlyInContentFrame" );
- if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
- return true; // We always avoid page anchored ones
-
- // If Flys anchored at paragraph are caught in a FlyCnt, then
- // their influence ends at the borders of the FlyCnt!
- // If we are currently formatting the text of the FlyCnt, then
- // it has to get out of the way of the Frame anchored at paragraph!
- // m_pCurrFrame is the anchor of pNew?
- // #i26945#
- const SwFrame* pTmp = _pAnchoredObj->GetAnchorFrame();
- if (pTmp == m_pCurrFrame)
- return true;
- if( pTmp->IsTextFrame() && ( pTmp->IsInFly() || pTmp->IsInFootnote() ) )
+ if (const SwFrameFormat* pAnchoredObjFormat = _pAnchoredObj->GetFrameFormat())
{
+ const SwFormatAnchor& rNewA = pAnchoredObjFormat->GetAnchor();
+ OSL_ENSURE(RndStdIds::FLY_AS_CHAR != rNewA.GetAnchorId(),
+ "Don't call GetTop with a FlyInContentFrame");
+ if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
+ return true; // We always avoid page anchored ones
+
+ // If Flys anchored at paragraph are caught in a FlyCnt, then
+ // their influence ends at the borders of the FlyCnt!
+ // If we are currently formatting the text of the FlyCnt, then
+ // it has to get out of the way of the Frame anchored at paragraph!
+ // m_pCurrFrame is the anchor of pNew?
// #i26945#
- Point aPos = _pAnchoredObj->GetObjRect().Pos();
- pTmp = GetVirtualUpper( pTmp, aPos );
- }
- // #i26945#
- // If <pTmp> is a text frame inside a table, take the upper
- // of the anchor frame, which contains the anchor position.
- else if ( pTmp->IsTextFrame() && pTmp->IsInTab() )
- {
- pTmp = const_cast<SwAnchoredObject*>(_pAnchoredObj)
- ->GetAnchorFrameContainingAnchPos()->GetUpper();
- }
- // #i28701# - consider all objects in same context,
- // if wrapping style is considered on object positioning.
- // Thus, text will wrap around negative positioned objects.
- // #i3317# - remove condition on checking,
- // if wrappings style is considered on object positioning.
- // Thus, text is wrapping around negative positioned objects.
- // #i35640# - no consideration of negative
- // positioned objects, if wrapping style isn't considered on
- // object position and former text wrapping is applied.
- // This condition is typically for documents imported from the
- // OpenOffice.org file format.
- const IDocumentSettingAccess* pIDSA = &m_pCurrFrame->GetDoc().getIDocumentSettingAccess();
- if ( ( pIDSA->get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) ||
- !pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ) &&
- ::FindContext( pTmp, SwFrameType::None ) == ::FindContext(m_pCurrFrame, SwFrameType::None))
- {
- return true;
- }
-
- const SwFrame* pHeader = nullptr;
- if (m_pCurrFrame->GetNext() != pTmp &&
- (IsFrameInSameContext( pTmp, m_pCurrFrame ) ||
- // #i13832#, #i24135# wrap around objects in page header
- ( !pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) &&
- nullptr != ( pHeader = pTmp->FindFooterOrHeader() ) &&
- m_pCurrFrame->IsInDocBody())))
- {
- if( pHeader || RndStdIds::FLY_AT_FLY == rNewA.GetAnchorId() )
+ const SwFrame* pTmp = _pAnchoredObj->GetAnchorFrame();
+ if (pTmp == m_pCurrFrame)
return true;
-
- // Compare indices:
- // The Index of the other is retrieved from the anchor attr.
- sal_uLong nTmpIndex = rNewA.GetContentAnchor()->nNode.GetIndex();
- // Now check whether the current paragraph is before the anchor
- // of the displaced object in the text, then we don't have to
- // get out of its way.
- // If possible determine Index via SwFormatAnchor because
- // otherwise it's quite expensive.
- if (ULONG_MAX == m_nCurrFrameNodeIndex)
- m_nCurrFrameNodeIndex = m_pCurrFrame->GetTextNodeFirst()->GetIndex();
-
- if (FrameContainsNode(*m_pCurrFrame, nTmpIndex) || nTmpIndex < m_nCurrFrameNodeIndex)
+ if (pTmp->IsTextFrame() && (pTmp->IsInFly() || pTmp->IsInFootnote()))
+ {
+ // #i26945#
+ Point aPos = _pAnchoredObj->GetObjRect().Pos();
+ pTmp = GetVirtualUpper(pTmp, aPos);
+ }
+ // #i26945#
+ // If <pTmp> is a text frame inside a table, take the upper
+ // of the anchor frame, which contains the anchor position.
+ else if (pTmp->IsTextFrame() && pTmp->IsInTab())
+ {
+ pTmp = const_cast<SwAnchoredObject*>(_pAnchoredObj)
+ ->GetAnchorFrameContainingAnchPos()
+ ->GetUpper();
+ }
+ // #i28701# - consider all objects in same context,
+ // if wrapping style is considered on object positioning.
+ // Thus, text will wrap around negative positioned objects.
+ // #i3317# - remove condition on checking,
+ // if wrappings style is considered on object positioning.
+ // Thus, text is wrapping around negative positioned objects.
+ // #i35640# - no consideration of negative
+ // positioned objects, if wrapping style isn't considered on
+ // object position and former text wrapping is applied.
+ // This condition is typically for documents imported from the
+ // OpenOffice.org file format.
+ const IDocumentSettingAccess* pIDSA
+ = &m_pCurrFrame->GetDoc().getIDocumentSettingAccess();
+ if ((pIDSA->get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION)
+ || !pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING))
+ && ::FindContext(pTmp, SwFrameType::None)
+ == ::FindContext(m_pCurrFrame, SwFrameType::None))
+ {
return true;
+ }
+
+ const SwFrame* pHeader = nullptr;
+ if (m_pCurrFrame->GetNext() != pTmp
+ && (IsFrameInSameContext(pTmp, m_pCurrFrame) ||
+ // #i13832#, #i24135# wrap around objects in page header
+ (!pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING)
+ && nullptr != (pHeader = pTmp->FindFooterOrHeader())
+ && m_pCurrFrame->IsInDocBody())))
+ {
+ if (pHeader || RndStdIds::FLY_AT_FLY == rNewA.GetAnchorId())
+ return true;
+
+ // Compare indices:
+ // The Index of the other is retrieved from the anchor attr.
+ SwNodeOffset nTmpIndex = rNewA.GetAnchorNode()->GetIndex();
+ // Now check whether the current paragraph is before the anchor
+ // of the displaced object in the text, then we don't have to
+ // get out of its way.
+ // If possible determine Index via SwFormatAnchor because
+ // otherwise it's quite expensive.
+ if (NODE_OFFSET_MAX == m_nCurrFrameNodeIndex)
+ m_nCurrFrameNodeIndex = m_pCurrFrame->GetTextNodeFirst()->GetIndex();
+
+ if (FrameContainsNode(*m_pCurrFrame, nTmpIndex)
+ || nTmpIndex < m_nCurrFrameNodeIndex)
+ return true;
+ }
}
}
}
return false;
}
+SwRect SwTextFly::GetFrameArea() const
+{
+ // i#28701 - consider complete frame area for new text wrapping
+ SwRect aRect;
+ if (m_pCurrFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING))
+ {
+ aRect = m_pCurrFrame->getFramePrintArea();
+ aRect += m_pCurrFrame->getFrameArea().Pos();
+ }
+ else
+ {
+ aRect = m_pCurrFrame->getFrameArea();
+ }
+ return aRect;
+}
+
// #i68520#
-SwAnchoredObjList* SwTextFly::InitAnchoredObjList()
+SwAnchoredObjList& SwTextFly::InitAnchoredObjList()
{
OSL_ENSURE( m_pCurrFrame, "InitFlyList: No Frame, no FlyList" );
// #i68520#
@@ -847,23 +895,12 @@ SwAnchoredObjList* SwTextFly::InitAnchoredObjList()
m_bOn = false;
+ // #i68520#
+ mpAnchoredObjList.reset(new SwAnchoredObjList);
+
if( nCount && bWrapAllowed )
{
- // #i68520#
- mpAnchoredObjList.reset(new SwAnchoredObjList );
-
- // #i28701# - consider complete frame area for new
- // text wrapping
- SwRect aRect;
- if ( pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) )
- {
- aRect = m_pCurrFrame->getFramePrintArea();
- aRect += m_pCurrFrame->getFrameArea().Pos();
- }
- else
- {
- aRect = m_pCurrFrame->getFrameArea();
- }
+ SwRect const aRect(GetFrameArea());
// Make ourselves a little smaller than we are,
// so that 1-Twip-overlappings are ignored (#49532)
SwRectFnSet aRectFnSet(m_pCurrFrame);
@@ -890,7 +927,7 @@ SwAnchoredObjList* SwTextFly::InitAnchoredObjList()
!pAnchoredObj->ConsiderForTextWrap() ||
( mbIgnoreObjsInHeaderFooter && !bFooterHeader &&
pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() ) ||
- ( bAllowCompatWrap && !pAnchoredObj->GetFrameFormat().GetFollowTextFlow().GetValue() )
+ ( bAllowCompatWrap && !pAnchoredObj->GetFrameFormat()->GetFollowTextFlow().GetValue() )
)
{
continue;
@@ -928,13 +965,13 @@ SwAnchoredObjList* SwTextFly::InitAnchoredObjList()
mpAnchoredObjList->insert( aInsPosIter, pAnchoredObj );
}
- const SwFormatSurround &rFlyFormat = pAnchoredObj->GetFrameFormat().GetSurround();
+ const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat();
+ const SwFormatSurround& rFlyFormat = pObjFormat->GetSurround();
// #i68520#
if ( rFlyFormat.IsAnchorOnly() &&
pAnchoredObj->GetAnchorFrame() == GetMaster() )
{
- const SwFormatVertOrient &rTmpFormat =
- pAnchoredObj->GetFrameFormat().GetVertOrient();
+ const SwFormatVertOrient &rTmpFormat = pObjFormat->GetVertOrient();
if( text::VertOrientation::BOTTOM != rTmpFormat.GetVertOrient() )
m_nMinBottom = ( aRectFnSet.IsVert() && m_nMinBottom ) ?
std::min( m_nMinBottom, aBound.Left() ) :
@@ -951,14 +988,9 @@ SwAnchoredObjList* SwTextFly::InitAnchoredObjList()
m_nMinBottom = nMax;
}
}
- else
- {
- // #i68520#
- mpAnchoredObjList.reset( new SwAnchoredObjList );
- }
// #i68520#
- return mpAnchoredObjList.get();
+ return *mpAnchoredObjList;
}
SwTwips SwTextFly::CalcMinBottom() const
@@ -974,11 +1006,11 @@ SwTwips SwTextFly::CalcMinBottom() const
for( size_t i = 0; i < nCount; ++i )
{
SwAnchoredObject* pAnchoredObj = (*pDrawObj)[ i ];
- const SwFormatSurround &rFlyFormat = pAnchoredObj->GetFrameFormat().GetSurround();
+ const SwFrameFormat* pObjFormat = pAnchoredObj->GetFrameFormat();
+ const SwFormatSurround& rFlyFormat = pObjFormat->GetSurround();
if( rFlyFormat.IsAnchorOnly() )
{
- const SwFormatVertOrient &rTmpFormat =
- pAnchoredObj->GetFrameFormat().GetVertOrient();
+ const SwFormatVertOrient &rTmpFormat = pObjFormat->GetVertOrient();
if( text::VertOrientation::BOTTOM != rTmpFormat.GetVertOrient() )
{
const SwRect& aBound( pAnchoredObj->GetObjRectWithSpaces() );
@@ -995,6 +1027,64 @@ SwTwips SwTextFly::CalcMinBottom() const
return nRet;
}
+SwTwips SwTextFly::GetMaxBottom(const SwBreakPortion& rPortion, const SwTextFormatInfo& rInfo) const
+{
+ // Note that m_pCurrFrame is already swapped at this stage, so it's correct to bypass
+ // SwRectFnSet here.
+ SwTwips nRet = 0;
+ size_t nCount(m_bOn ? GetAnchoredObjList().size() : 0);
+
+ // Get the horizontal position of the break portion in absolute twips. The frame area is in
+ // absolute twips, the frame's print area is relative to the frame area. Finally the portion's
+ // position is relative to the frame's print area.
+ SwTwips nX = rInfo.X();
+ nX += m_pCurrFrame->getFrameArea().Left();
+ nX += m_pCurrFrame->getFramePrintArea().Left();
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ const SwAnchoredObject* pAnchoredObj = (*mpAnchoredObjList)[i];
+
+ if (pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader())
+ {
+ // Anchored in the header or footer, ignore it for clearing break purposes.
+ continue;
+ }
+
+ SwRect aRect(pAnchoredObj->GetObjRectWithSpaces());
+
+ if (m_pCurrFrame->IsVertical())
+ {
+ m_pCurrFrame->SwitchVerticalToHorizontal(aRect);
+ }
+
+ if (rPortion.GetClear() == SwLineBreakClear::LEFT)
+ {
+ if (nX < aRect.Left())
+ {
+ // Want to jump down to the first line that's unblocked on the left. This object is
+ // on the right of the break, ignore it.
+ continue;
+ }
+ }
+ if (rPortion.GetClear() == SwLineBreakClear::RIGHT)
+ {
+ if (nX > aRect.Right())
+ {
+ // Want to jump down to the first line that's unblocked on the right. This object is
+ // on the left of the break, ignore it.
+ continue;
+ }
+ }
+ SwTwips nBottom = aRect.Top() + aRect.Height();
+ if (nBottom > nRet)
+ {
+ nRet = nBottom;
+ }
+ }
+ return nRet;
+}
+
bool SwTextFly::ForEach( const SwRect &rRect, SwRect* pRect, bool bAvoid ) const
{
SwSwapIfSwapped swap(const_cast<SwTextFrame *>(m_pCurrFrame));
@@ -1019,7 +1109,7 @@ bool SwTextFly::ForEach( const SwRect &rRect, SwRect* pRect, bool bAvoid ) const
bool bRet = false;
// #i68520#
- const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList()->size() : 0 );
+ const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList().size() : 0 );
if (nCount > 0)
{
for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i )
@@ -1033,10 +1123,10 @@ bool SwTextFly::ForEach( const SwRect &rRect, SwRect* pRect, bool bAvoid ) const
break;
// #i68520#
- if ( mpCurrAnchoredObj != pAnchoredObj && aRect.IsOver( rRect ) )
+ if ( mpCurrAnchoredObj != pAnchoredObj && aRect.Overlaps( rRect ) )
{
// #i68520#
- const SwFormat* pFormat( &(pAnchoredObj->GetFrameFormat()) );
+ const SwFormat* pFormat(pAnchoredObj->GetFrameFormat());
const SwFormatSurround &rSur = pFormat->GetSurround();
if( bAvoid )
{
@@ -1070,7 +1160,7 @@ bool SwTextFly::ForEach( const SwRect &rRect, SwRect* pRect, bool bAvoid ) const
{
// #i68520#
SwRect aFly = AnchoredObjToRect( pAnchoredObj, rRect );
- if( aFly.IsEmpty() || !aFly.IsOver( rRect ) )
+ if( aFly.IsEmpty() || !aFly.Overlaps( rRect ) )
continue;
if( !bRet || (
(!m_pCurrFrame->IsRightToLeft() &&
@@ -1098,7 +1188,7 @@ bool SwTextFly::ForEach( const SwRect &rRect, SwRect* pRect, bool bAvoid ) const
// #i68520#
SwAnchoredObjList::size_type SwTextFly::GetPos( const SwAnchoredObject* pAnchoredObj ) const
{
- SwAnchoredObjList::size_type nCount = GetAnchoredObjList()->size();
+ SwAnchoredObjList::size_type nCount = GetAnchoredObjList().size();
SwAnchoredObjList::size_type nRet = 0;
while ( nRet < nCount && pAnchoredObj != (*mpAnchoredObjList)[ nRet ] )
++nRet;
@@ -1125,7 +1215,7 @@ void SwTextFly::CalcRightMargin( SwRect &rFly,
// and protrudes into the same line.
// Flys with run-through are invisible for those below, i.e., they
// are ignored for computing the margins of other Flys.
- // 3301: pNext->getFrameArea().IsOver( rLine ) is necessary
+ // 3301: pNext->getFrameArea().Overlaps( rLine ) is necessary
// #i68520#
css::text::WrapTextMode eSurroundForTextWrap;
@@ -1178,7 +1268,7 @@ void SwTextFly::CalcRightMargin( SwRect &rFly,
aRectFnSet.GetTop(aLine) ) > 0 )
SetNextTop( 0 );
}
- if( aTmp.IsOver( aLine ) && nTmpRight > nFlyRight )
+ if( aTmp.Overlaps( aLine ) && nTmpRight > nFlyRight )
{
nFlyRight = nTmpRight;
if( css::text::WrapTextMode_RIGHT == eSurroundForTextWrap ||
@@ -1216,7 +1306,7 @@ void SwTextFly::CalcLeftMargin( SwRect &rFly,
// and protrudes into the same line.
// Flys with run-through are invisible for those below, i.e., they
// are ignored for computing the margins of other Flys.
- // 3301: pNext->getFrameArea().IsOver( rLine ) is necessary
+ // 3301: pNext->getFrameArea().Overlaps( rLine ) is necessary
// #i68520#
SwAnchoredObjList::size_type nMyPos = nFlyPos;
@@ -1244,7 +1334,7 @@ void SwTextFly::CalcLeftMargin( SwRect &rFly,
const SwRect aTmp( SwContourCache::CalcBoundRect
(pNext, aLine, m_pCurrFrame, nFlyLeft, false) );
- if( aRectFnSet.GetLeft(aTmp) < nFlyLeft && aTmp.IsOver( aLine ) )
+ if( aRectFnSet.GetLeft(aTmp) < nFlyLeft && aTmp.Overlaps( aLine ) )
{
// #118796# - no '+1', because <..fnGetRight>
// returns the correct value.
@@ -1318,15 +1408,12 @@ SwRect SwTextFly::AnchoredObjToRect( const SwAnchoredObject* pAnchoredObj,
// Wrap only on sides with at least 2cm space for the text
#define TEXT_MIN 1134
-// MS Word wraps on sides with even less space (value guessed).
-#define TEXT_MIN_SMALL 300
-
// Wrap on both sides up to a frame width of 1.5cm
#define FRAME_MAX 850
css::text::WrapTextMode SwTextFly::GetSurroundForTextWrap( const SwAnchoredObject* pAnchoredObj ) const
{
- const SwFrameFormat* pFormat = &(pAnchoredObj->GetFrameFormat());
+ const SwFrameFormat* pFormat = pAnchoredObj->GetFrameFormat();
const SwFormatSurround &rFlyFormat = pFormat->GetSurround();
css::text::WrapTextMode eSurroundForTextWrap = rFlyFormat.GetSurround();
diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx
index 6a91cfd36a3d..8e5a1a904b80 100644
--- a/sw/source/core/text/txtfrm.cxx
+++ b/sw/source/core/text/txtfrm.cxx
@@ -17,6 +17,8 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
+#include <config_wasm_strip.h>
+
#include <hintids.hxx>
#include <hints.hxx>
#include <svl/ctloptions.hxx>
@@ -24,7 +26,7 @@
#include <editeng/lrspitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/pgrditem.hxx>
-#include <unotools/configmgr.hxx>
+#include <comphelper/configuration.hxx>
#include <swmodule.hxx>
#include <SwSmartTagMgr.hxx>
#include <doc.hxx>
@@ -66,7 +68,7 @@
#include <fmtflcnt.hxx>
#include <fmtcntnt.hxx>
#include <numrule.hxx>
-#include <IGrammarContact.hxx>
+#include <GrammarContact.hxx>
#include <calbck.hxx>
#include <ftnidx.hxx>
#include <ftnfrm.hxx>
@@ -285,15 +287,15 @@ namespace sw {
}
}
- bool FrameContainsNode(SwContentFrame const& rFrame, sal_uLong const nNodeIndex)
+ bool FrameContainsNode(SwContentFrame const& rFrame, SwNodeOffset const nNodeIndex)
{
if (rFrame.IsTextFrame())
{
SwTextFrame const& rTextFrame(static_cast<SwTextFrame const&>(rFrame));
if (sw::MergedPara const*const pMerged = rTextFrame.GetMergedPara())
{
- sal_uLong const nFirst(pMerged->pFirstNode->GetIndex());
- sal_uLong const nLast(pMerged->pLastNode->GetIndex());
+ SwNodeOffset const nFirst(pMerged->pFirstNode->GetIndex());
+ SwNodeOffset const nLast(pMerged->pLastNode->GetIndex());
return (nFirst <= nNodeIndex && nNodeIndex <= nLast);
}
else
@@ -325,16 +327,16 @@ namespace sw {
}
SwTextNode *
- GetParaPropsNode(SwRootFrame const& rLayout, SwNodeIndex const& rPos)
+ GetParaPropsNode(SwRootFrame const& rLayout, SwNode const& rPos)
{
- SwTextNode *const pTextNode(rPos.GetNode().GetTextNode());
+ const SwTextNode *const pTextNode(rPos.GetTextNode());
if (pTextNode && !sw::IsParaPropsNode(rLayout, *pTextNode))
{
return static_cast<SwTextFrame*>(pTextNode->getLayoutFrame(&rLayout))->GetMergedPara()->pParaPropsNode;
}
else
{
- return pTextNode;
+ return const_cast<SwTextNode*>(pTextNode);
}
}
@@ -342,19 +344,16 @@ namespace sw {
GetParaPropsPos(SwRootFrame const& rLayout, SwPosition const& rPos)
{
SwPosition pos(rPos);
- SwTextNode const*const pNode(pos.nNode.GetNode().GetTextNode());
+ SwTextNode const*const pNode(pos.GetNode().GetTextNode());
if (pNode)
- {
- pos.nNode = *sw::GetParaPropsNode(rLayout, *pNode);
- pos.nContent.Assign(pos.nNode.GetNode().GetContentNode(), 0);
- }
+ pos.Assign( *sw::GetParaPropsNode(rLayout, *pNode) );
return pos;
}
std::pair<SwTextNode *, SwTextNode *>
- GetFirstAndLastNode(SwRootFrame const& rLayout, SwNodeIndex const& rPos)
+ GetFirstAndLastNode(SwRootFrame const& rLayout, SwNode const& rPos)
{
- SwTextNode *const pTextNode(rPos.GetNode().GetTextNode());
+ SwTextNode *const pTextNode(const_cast<SwTextNode*>(rPos.GetTextNode()));
if (pTextNode && rLayout.HasMergedParas())
{
if (SwTextFrame const*const pFrame = static_cast<SwTextFrame*>(pTextNode->getLayoutFrame(&rLayout)))
@@ -383,8 +382,7 @@ namespace sw {
rFormatSet.ClearItem(RES_BREAK);
static_assert(RES_PAGEDESC + 1 == sal_uInt16(RES_BREAK),
"first-node items must be adjacent");
- SfxItemSet firstSet(*rFormatSet.GetPool(),
- svl::Items<RES_PAGEDESC, RES_BREAK>{});
+ SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> firstSet(*rFormatSet.GetPool());
pMerged->pFirstNode->SwContentNode::GetAttr(firstSet);
rFormatSet.Put(firstSet);
@@ -402,10 +400,10 @@ namespace sw {
{
rFormatSet.ClearItem(i);
}
- SfxItemSet propsSet(*rFormatSet.GetPool(),
- svl::Items<RES_PARATR_BEGIN, RES_PAGEDESC,
+ SfxItemSetFixed<RES_PARATR_BEGIN, RES_PAGEDESC,
RES_BREAK+1, RES_FRMATR_END,
- XATTR_FILL_FIRST, XATTR_FILL_LAST+1>{});
+ XATTR_FILL_FIRST, XATTR_FILL_LAST+1>
+ propsSet(*rFormatSet.GetPool());
pMerged->pParaPropsNode->SwContentNode::GetAttr(propsSet);
rFormatSet.Put(propsSet);
return *pMerged->pParaPropsNode;
@@ -713,13 +711,13 @@ SwLayoutModeModifier::~SwLayoutModeModifier()
void SwLayoutModeModifier::Modify( bool bChgToRTL )
{
const_cast<OutputDevice&>(m_rOut).SetLayoutMode( bChgToRTL ?
- ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl :
- ComplexTextLayoutFlags::BiDiStrong );
+ vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl :
+ vcl::text::ComplexTextLayoutFlags::BiDiStrong );
}
void SwLayoutModeModifier::SetAuto()
{
- const ComplexTextLayoutFlags nNewLayoutMode = m_nOldLayoutMode & ~ComplexTextLayoutFlags::BiDiStrong;
+ const vcl::text::ComplexTextLayoutFlags nNewLayoutMode = m_nOldLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong;
const_cast<OutputDevice&>(m_rOut).SetLayoutMode( nNewLayoutMode );
}
@@ -727,11 +725,11 @@ SwDigitModeModifier::SwDigitModeModifier( const OutputDevice& rOutp, LanguageTyp
rOut( rOutp ), nOldLanguageType( rOutp.GetDigitLanguage() )
{
LanguageType eLang = eCurLang;
- if (utl::ConfigManager::IsFuzzing())
+ if (comphelper::IsFuzzing())
eLang = LANGUAGE_ENGLISH_US;
else
{
- const SvtCTLOptions::TextNumerals nTextNumerals = SW_MOD()->GetCTLOptions().GetCTLTextNumerals();
+ const SvtCTLOptions::TextNumerals nTextNumerals = SvtCTLOptions::GetCTLTextNumerals();
if ( SvtCTLOptions::NUMERALS_HINDI == nTextNumerals )
eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
@@ -795,6 +793,124 @@ SwTextFrame::SwTextFrame(SwTextNode * const pNode, SwFrame* pSib,
m_pMergedPara = CheckParaRedlineMerge(*this, *pNode, eMode);
}
+void SwTextFrame::dumpAsXmlAttributes(xmlTextWriterPtr writer) const
+{
+ SwContentFrame::dumpAsXmlAttributes(writer);
+
+ const SwTextNode *pTextNode = GetTextNodeFirst();
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIdINT32, sal_Int32(pTextNode->GetIndex()) );
+
+ OString aMode = "Horizontal"_ostr;
+ if (IsVertLRBT())
+ {
+ aMode = "VertBTLR"_ostr;
+ }
+ else if (IsVertLR())
+ {
+ aMode = "VertLR"_ostr;
+ }
+ else if (IsVertical())
+ {
+ aMode = "Vertical"_ostr;
+ }
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("WritingMode"), BAD_CAST(aMode.getStr()));
+}
+
+void SwTextFrame::dumpAsXml(xmlTextWriterPtr writer) const
+{
+ (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("txt"));
+ dumpAsXmlAttributes( writer );
+ if ( HasFollow() )
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
+
+ if (m_pPrecede != nullptr)
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwTextFrame*>(m_pPrecede)->GetFrameId() );
+
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("offset"), BAD_CAST(OString::number(static_cast<sal_Int32>(mnOffset)).getStr()));
+
+ sw::MergedPara const*const pMerged(GetMergedPara());
+ if (pMerged)
+ {
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "merged" ) );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "paraPropsNodeIndex" ), "%" SAL_PRIdINT32, sal_Int32(pMerged->pParaPropsNode->GetIndex()) );
+ for (auto const& e : pMerged->extents)
+ {
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "extent" ) );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIdINT32, sal_Int32(e.pNode->GetIndex()) );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "start" ), "%" SAL_PRIdINT32, e.nStart );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "end" ), "%" SAL_PRIdINT32, e.nEnd );
+ (void)xmlTextWriterEndElement( writer );
+ }
+ (void)xmlTextWriterEndElement( writer );
+ }
+
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
+ dumpInfosAsXml(writer);
+ (void)xmlTextWriterEndElement(writer);
+
+ // Dump Anchored objects if any
+ const SwSortedObjs* pAnchored = GetDrawObjs();
+ if ( pAnchored && pAnchored->size() > 0 )
+ {
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "anchored" ) );
+
+ for (SwAnchoredObject* pObject : *pAnchored)
+ {
+ pObject->dumpAsXml( writer );
+ }
+
+ (void)xmlTextWriterEndElement( writer );
+ }
+
+ // Dump the children
+ OUString aText = GetText( );
+ for ( int i = 0; i < 32; i++ )
+ {
+ aText = aText.replace( i, '*' );
+ }
+ auto nTextOffset = static_cast<sal_Int32>(GetOffset());
+ sal_Int32 nTextLength = aText.getLength() - nTextOffset;
+ if (const SwTextFrame* pTextFrameFollow = GetFollow())
+ {
+ nTextLength = static_cast<sal_Int32>(pTextFrameFollow->GetOffset() - GetOffset());
+ }
+ if (nTextLength > 0)
+ {
+ OString aText8
+ = OUStringToOString(aText.subView(nTextOffset, nTextLength), RTL_TEXTENCODING_UTF8);
+ (void)xmlTextWriterWriteString( writer,
+ reinterpret_cast<const xmlChar *>(aText8.getStr( )) );
+ }
+ if (const SwParaPortion* pPara = GetPara())
+ {
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("SwParaPortion"));
+ TextFrameIndex nOffset(0);
+ const OUString& rText = GetText();
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pPara);
+ const SwLineLayout* pLine = pPara;
+ if (IsFollow())
+ {
+ nOffset += GetOffset();
+ }
+ while (pLine)
+ {
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("SwLineLayout"));
+ pLine->dumpAsXmlAttributes(writer, rText, nOffset);
+ const SwLinePortion* pPor = pLine->GetFirstPortion();
+ while (pPor)
+ {
+ pPor->dumpAsXml(writer, rText, nOffset);
+ pPor = pPor->GetNextPortion();
+ }
+ (void)xmlTextWriterEndElement(writer);
+ pLine = pLine->GetNext();
+ }
+ (void)xmlTextWriterEndElement(writer);
+ }
+
+ (void)xmlTextWriterEndElement(writer);
+}
+
namespace sw {
SwTextFrame * MakeTextFrame(SwTextNode & rNode, SwFrame *const pSibling,
@@ -813,13 +929,13 @@ void RemoveFootnotesForNode(
}
const SwFootnoteIdxs &rFootnoteIdxs = rTextNode.GetDoc().GetFootnoteIdxs();
size_t nPos = 0;
- sal_uLong const nIndex = rTextNode.GetIndex();
+ SwNodeOffset const nIndex = rTextNode.GetIndex();
rFootnoteIdxs.SeekEntry( rTextNode, &nPos );
if (nPos < rFootnoteIdxs.size())
{
- while (nPos && &rTextNode == &(rFootnoteIdxs[ nPos ]->GetTextNode()))
+ while (nPos && rTextNode == (rFootnoteIdxs[ nPos ]->GetTextNode()))
--nPos;
- if (nPos || &rTextNode != &(rFootnoteIdxs[ nPos ]->GetTextNode()))
+ if (nPos || rTextNode != (rFootnoteIdxs[ nPos ]->GetTextNode()))
++nPos;
}
size_t iter(0);
@@ -1261,15 +1377,14 @@ TextFrameIndex SwTextFrame::MapModelToView(SwTextNode const*const pNode, sal_Int
}
else
{
- assert(static_cast<SwTextNode*>(const_cast<sw::BroadcastingModify*>(SwFrame::GetDep())) == pNode);
return TextFrameIndex(nIndex);
}
}
TextFrameIndex SwTextFrame::MapModelToViewPos(SwPosition const& rPos) const
{
- SwTextNode const*const pNode(rPos.nNode.GetNode().GetTextNode());
- sal_Int32 const nIndex(rPos.nContent.GetIndex());
+ SwTextNode const*const pNode(rPos.GetNode().GetTextNode());
+ sal_Int32 const nIndex(rPos.GetContentIndex());
return MapModelToView(pNode, nIndex);
}
@@ -1285,7 +1400,7 @@ void SwTextFrame::SetMergedPara(std::unique_ptr<sw::MergedPara> p)
}
else
{
- pFirst->Add(this); // must register at node again
+ pFirst->Add(*this); // must register at node again
}
}
// postcondition: frame must be listening somewhere
@@ -1343,11 +1458,11 @@ SwDoc const& SwTextFrame::GetDoc() const
}
LanguageType SwTextFrame::GetLangOfChar(TextFrameIndex const nIndex,
- sal_uInt16 const nScript, bool const bNoChar) const
+ sal_uInt16 const nScript, bool const bNoChar, bool const bNoneIfNoHyphenation) const
{
// a single character can be mapped uniquely!
std::pair<SwTextNode const*, sal_Int32> const pos(MapViewToModel(nIndex));
- return pos.first->GetLang(pos.second, bNoChar ? 0 : 1, nScript);
+ return pos.first->GetLang(pos.second, bNoChar ? 0 : 1, nScript, bNoneIfNoHyphenation);
}
void SwTextFrame::ResetPreps()
@@ -1369,6 +1484,9 @@ bool SwTextFrame::IsHiddenNow() const
return true;
}
+ if (SwContentFrame::IsHiddenNow())
+ return true;
+
bool bHiddenCharsHidePara(false);
bool bHiddenParaField(false);
if (m_pMergedPara)
@@ -1434,6 +1552,19 @@ bool SwTextFrame::IsHiddenNow() const
( bHiddenCharsHidePara &&
!pVsh->GetViewOptions()->IsShowHiddenChar() ) )
{
+ // in order to put the cursor in the body text, one paragraph must
+ // be visible - check this for the 1st body paragraph
+ if (IsInDocBody() && FindPrevCnt() == nullptr)
+ {
+ for (SwContentFrame const* pNext = FindNextCnt(true);
+ pNext != nullptr; pNext = pNext->FindNextCnt(true))
+ {
+ if (!pNext->IsHiddenNow())
+ return true;
+ }
+ SAL_INFO("sw.core", "unhiding one body paragraph");
+ return false;
+ }
return true;
}
}
@@ -1487,7 +1618,7 @@ void SwTextFrame::HideFootnotes(TextFrameIndex const nStart, TextFrameIndex cons
*/
bool sw_HideObj( const SwTextFrame& _rFrame,
const RndStdIds _eAnchorType,
- SwPosition const& rAnchorPos,
+ SwFormatAnchor const& rFormatAnchor,
SwAnchoredObject* _pAnchoredObj )
{
bool bRet( true );
@@ -1501,9 +1632,9 @@ bool sw_HideObj( const SwTextFrame& _rFrame,
pIDSA->get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
_rFrame.IsInDocBody() && !_rFrame.FindNextCnt() )
{
- SwTextNode const& rNode(*rAnchorPos.nNode.GetNode().GetTextNode());
+ SwTextNode const& rNode(*rFormatAnchor.GetAnchorNode()->GetTextNode());
assert(FrameContainsNode(_rFrame, rNode.GetIndex()));
- sal_Int32 const nObjAnchorPos(rAnchorPos.nContent.GetIndex());
+ sal_Int32 const nObjAnchorPos(rFormatAnchor.GetAnchorContentOffset());
const sal_Unicode cAnchorChar = nObjAnchorPos < rNode.Len()
? rNode.GetText()[nObjAnchorPos]
: 0;
@@ -1562,7 +1693,7 @@ void SwTextFrame::HideAndShowObjects()
// under certain conditions
const RndStdIds eAnchorType( pContact->GetAnchorId() );
if ((eAnchorType != RndStdIds::FLY_AT_CHAR) ||
- sw_HideObj(*this, eAnchorType, pContact->GetContentAnchor(),
+ sw_HideObj(*this, eAnchorType, pContact->GetAnchorFormat(),
i ))
{
pContact->MoveObjToInvisibleLayer( pObj );
@@ -1596,13 +1727,13 @@ void SwTextFrame::HideAndShowObjects()
{
sal_Int32 nHiddenStart;
sal_Int32 nHiddenEnd;
- const SwPosition& rAnchor = pContact->GetContentAnchor();
+ const SwFormatAnchor& rAnchorFormat = pContact->GetAnchorFormat();
SwScriptInfo::GetBoundsOfHiddenRange(
- *rAnchor.nNode.GetNode().GetTextNode(),
- rAnchor.nContent.GetIndex(), nHiddenStart, nHiddenEnd);
+ *rAnchorFormat.GetAnchorNode()->GetTextNode(),
+ rAnchorFormat.GetAnchorContentOffset(), nHiddenStart, nHiddenEnd);
// Under certain conditions
if ( nHiddenStart != COMPLETE_STRING && bShouldBeHidden &&
- sw_HideObj(*this, eAnchorType, rAnchor, i))
+ sw_HideObj(*this, eAnchorType, rAnchorFormat, i))
{
pContact->MoveObjToInvisibleLayer( pObj );
}
@@ -1632,15 +1763,15 @@ void SwTextFrame::HideAndShowObjects()
* line has to be formatted as well.
* nFound is <= nEndLine.
*/
-TextFrameIndex SwTextFrame::FindBrk(const OUString &rText,
+TextFrameIndex SwTextFrame::FindBrk(std::u16string_view aText,
const TextFrameIndex nStart,
const TextFrameIndex nEnd)
{
sal_Int32 nFound = sal_Int32(nStart);
- const sal_Int32 nEndLine = std::min(sal_Int32(nEnd), rText.getLength() - 1);
+ const sal_Int32 nEndLine = std::min(sal_Int32(nEnd), sal_Int32(aText.size()) - 1);
// Skip all leading blanks.
- while( nFound <= nEndLine && ' ' == rText[nFound] )
+ while( nFound <= nEndLine && ' ' == aText[nFound] )
{
nFound++;
}
@@ -1649,7 +1780,7 @@ TextFrameIndex SwTextFrame::FindBrk(const OUString &rText,
// "Dr.$Meyer" at the beginning of the second line. Typing a blank after that
// doesn't result in the word moving into first line, even though that would work.
// For this reason we don't skip the dummy char.
- while( nFound <= nEndLine && ' ' != rText[nFound] )
+ while( nFound <= nEndLine && ' ' != aText[nFound] )
{
nFound++;
}
@@ -1659,6 +1790,8 @@ TextFrameIndex SwTextFrame::FindBrk(const OUString &rText,
bool SwTextFrame::IsIdxInside(TextFrameIndex const nPos, TextFrameIndex const nLen) const
{
+ if (nPos == TextFrameIndex(COMPLETE_STRING)) // the "not found" range
+ return false;
// Silence over-eager warning emitted at least by GCC trunk towards 6:
#if defined __GNUC__ && !defined __clang__
#pragma GCC diagnostic push
@@ -1709,7 +1842,7 @@ void SwTextFrame::InvalidateRange_( const SwCharRange &aRange, const tools::Long
// linelengths are being added, that's why it's negative
// if chars have been added and positive, if chars have
// deleted
- pPara->GetDelta() += nD;
+ pPara->SetDelta(pPara->GetDelta() + nD);
bInv = true;
}
SwCharRange &rReformat = pPara->GetReformat();
@@ -1735,7 +1868,7 @@ void SwTextFrame::CalcLineSpace()
return;
if( GetDrawObjs() ||
- GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace().IsAutoFirst())
+ GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent().IsAutoFirst())
{
Init();
return;
@@ -1802,48 +1935,48 @@ static void lcl_SetWrong( SwTextFrame& rFrame, SwTextNode const& rNode,
{
if ( !rFrame.IsFollow() )
{
- SwTextNode* pTextNode = const_cast<SwTextNode*>(&rNode);
- IGrammarContact* pGrammarContact = getGrammarContact( *pTextNode );
+ SwTextNode& rTextNode = const_cast<SwTextNode&>(rNode);
+ sw::GrammarContact* pGrammarContact = sw::getGrammarContactFor(rTextNode);
SwGrammarMarkUp* pWrongGrammar = pGrammarContact ?
- pGrammarContact->getGrammarCheck( *pTextNode, false ) :
- pTextNode->GetGrammarCheck();
- bool bGrammarProxy = pWrongGrammar != pTextNode->GetGrammarCheck();
+ pGrammarContact->getGrammarCheck( rTextNode, false ) :
+ rTextNode.GetGrammarCheck();
+ bool bGrammarProxy = pWrongGrammar != rTextNode.GetGrammarCheck();
if( bMove )
{
- if( pTextNode->GetWrong() )
- pTextNode->GetWrong()->Move( nPos, nCnt );
+ if( rTextNode.GetWrong() )
+ rTextNode.GetWrong()->Move( nPos, nCnt );
if( pWrongGrammar )
pWrongGrammar->MoveGrammar( nPos, nCnt );
- if( bGrammarProxy && pTextNode->GetGrammarCheck() )
- pTextNode->GetGrammarCheck()->MoveGrammar( nPos, nCnt );
- if( pTextNode->GetSmartTags() )
- pTextNode->GetSmartTags()->Move( nPos, nCnt );
+ if( bGrammarProxy && rTextNode.GetGrammarCheck() )
+ rTextNode.GetGrammarCheck()->MoveGrammar( nPos, nCnt );
+ if( rTextNode.GetSmartTags() )
+ rTextNode.GetSmartTags()->Move( nPos, nCnt );
}
else
{
- if( pTextNode->GetWrong() )
- pTextNode->GetWrong()->Invalidate( nPos, nCnt );
+ if( rTextNode.GetWrong() )
+ rTextNode.GetWrong()->Invalidate( nPos, nCnt );
if( pWrongGrammar )
pWrongGrammar->Invalidate( nPos, nCnt );
- if( pTextNode->GetSmartTags() )
- pTextNode->GetSmartTags()->Invalidate( nPos, nCnt );
+ if( rTextNode.GetSmartTags() )
+ rTextNode.GetSmartTags()->Invalidate( nPos, nCnt );
}
const sal_Int32 nEnd = nPos + (nCnt > 0 ? nCnt : 1 );
- if ( !pTextNode->GetWrong() && !pTextNode->IsWrongDirty() )
+ if ( !rTextNode.GetWrong() && !rTextNode.IsWrongDirty() )
{
- pTextNode->SetWrong( new SwWrongList( WRONGLIST_SPELL ) );
- pTextNode->GetWrong()->SetInvalid( nPos, nEnd );
+ rTextNode.SetWrong( std::make_unique<SwWrongList>( WRONGLIST_SPELL ) );
+ rTextNode.GetWrong()->SetInvalid( nPos, nEnd );
}
- if ( !pTextNode->GetSmartTags() && !pTextNode->IsSmartTagDirty() )
+ if ( !rTextNode.GetSmartTags() && !rTextNode.IsSmartTagDirty() )
{
- pTextNode->SetSmartTags( new SwWrongList( WRONGLIST_SMARTTAG ) );
- pTextNode->GetSmartTags()->SetInvalid( nPos, nEnd );
+ rTextNode.SetSmartTags( std::make_unique<SwWrongList>( WRONGLIST_SMARTTAG ) );
+ rTextNode.GetSmartTags()->SetInvalid( nPos, nEnd );
}
- pTextNode->SetWrongDirty(SwTextNode::WrongState::TODO);
- pTextNode->SetGrammarCheckDirty( true );
- pTextNode->SetWordCountDirty( true );
- pTextNode->SetAutoCompleteWordDirty( true );
- pTextNode->SetSmartTagDirty( true );
+ rTextNode.SetWrongDirty(sw::WrongState::TODO);
+ rTextNode.SetGrammarCheckDirty( true );
+ rTextNode.SetWordCountDirty( true );
+ rTextNode.SetAutoCompleteWordDirty( true );
+ rTextNode.SetSmartTagDirty( true );
}
SwRootFrame *pRootFrame = rFrame.getRootFrame();
@@ -1936,7 +2069,7 @@ void UpdateMergedParaForMove(sw::MergedPara & rMerged,
// assert(nDeleted == it.second - it.first);
if(nDeleted)
{
- // InvalidateRange/lcl_SetScriptInval was called sufficiently for SwInsText
+ // InvalidateRange/lcl_SetScriptInval was called sufficiently for InsertText
lcl_SetWrong(rTextFrame, rDestNode, nStart, it.first - it.second, false);
TextFrameIndex const nIndex(sw::MapModelToView(rMerged, &rDestNode, nStart));
lcl_ModifyOfst(rTextFrame, nIndex, nDeleted, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
@@ -1950,6 +2083,7 @@ void UpdateMergedParaForMove(sw::MergedPara & rMerged,
* Related: fdo#56031 filter out attribute changes that don't matter for
* humans/a11y to stop flooding the destination mortal with useless noise
*/
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
static bool isA11yRelevantAttribute(sal_uInt16 nWhich)
{
return nWhich != RES_CHRATR_RSID;
@@ -1963,6 +2097,7 @@ static bool hasA11yRelevantAttribute( const std::vector<sal_uInt16>& rWhichFmtAt
return false;
}
+#endif // ENABLE_WASM_STRIP_ACCESSIBILITY
// Note: for now this overrides SwClient::SwClientNotify; the intermediary
// classes still override SwClient::Modify, which should continue to work
@@ -1975,16 +2110,55 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
SfxPoolItem const* pOld(nullptr);
SfxPoolItem const* pNew(nullptr);
sw::MoveText const* pMoveText(nullptr);
+ sw::InsertText const* pInsertText(nullptr);
+ sw::DeleteText const* pDeleteText(nullptr);
+ sw::DeleteChar const* pDeleteChar(nullptr);
sw::RedlineDelText const* pRedlineDelText(nullptr);
sw::RedlineUnDelText const* pRedlineUnDelText(nullptr);
sal_uInt16 nWhich = 0;
- if (auto const pHint = dynamic_cast<sw::LegacyModifyHint const*>(&rHint))
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
{
+ auto pHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
pOld = pHint->m_pOld;
pNew = pHint->m_pNew;
nWhich = pHint->GetWhich();
}
+ else if (rHint.GetId() == SfxHintId::SwInsertText)
+ {
+ pInsertText = static_cast<const sw::InsertText*>(&rHint);
+ }
+ else if (rHint.GetId() == SfxHintId::SwDeleteText)
+ {
+ pDeleteText = static_cast<const sw::DeleteText*>(&rHint);
+ }
+ else if (rHint.GetId() == SfxHintId::SwDeleteChar)
+ {
+ pDeleteChar = static_cast<const sw::DeleteChar*>(&rHint);
+ }
+ else if (rHint.GetId() == SfxHintId::SwDocPosUpdateAtIndex)
+ {
+ auto pDocPosAt = static_cast<const sw::DocPosUpdateAtIndex*>(&rHint);
+ Broadcast(SfxHint()); // notify SwAccessibleParagraph
+ if(IsLocked())
+ return;
+ if(pDocPosAt->m_nDocPos > getFrameArea().Top())
+ return;
+ TextFrameIndex const nIndex(MapModelToView(
+ &pDocPosAt->m_rNode,
+ pDocPosAt->m_nIndex));
+ InvalidateRange(SwCharRange(nIndex, TextFrameIndex(1)));
+ return;
+ }
+ else if (rHint.GetId() == SfxHintId::SwVirtPageNumHint)
+ {
+ auto& rVirtPageNumHint = const_cast<sw::VirtPageNumHint&>(static_cast<const sw::VirtPageNumHint&>(rHint));
+ if(!IsInDocBody() || IsFollow() || rVirtPageNumHint.IsFound())
+ return;
+ if(const SwPageFrame* pPage = FindPageFrame())
+ pPage->UpdateVirtPageNumInfo(rVirtPageNumHint, this);
+ return;
+ }
else if (auto const pHt = dynamic_cast<sw::MoveText const*>(&rHint))
{
pMoveText = pHt;
@@ -2116,7 +2290,7 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
&& m_pMergedPara->pFirstNode->GetIndex() <= pMoveText->pDestNode->GetIndex()
&& pMoveText->pDestNode->GetIndex() <= m_pMergedPara->pLastNode->GetIndex())
{ // if it's not 2 nodes in merged frame, assume the target node doesn't have frames at all
- assert(std::abs(static_cast<tools::Long>(rNode.GetIndex()) - static_cast<tools::Long>(pMoveText->pDestNode->GetIndex())) == 1);
+ assert(abs(rNode.GetIndex() - pMoveText->pDestNode->GetIndex()) == SwNodeOffset(1));
UpdateMergedParaForMove(*m_pMergedPara,
*this,
bRecalcFootnoteFlag,
@@ -2131,23 +2305,28 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
// assert(!m_pMergedPara || !getRootFrame()->IsHideRedlines() || !pMoveText->pDestNode->getLayoutFrame(getRootFrame()));
}
}
- else switch (nWhich)
+ else if (pInsertText)
{
- case RES_LINENUMBER:
+ nPos = MapModelToView(&rNode, pInsertText->nPos);
+ // unlike redlines, inserting into fieldmark must be explicitly handled
+ bool isHidden(false);
+ switch (getRootFrame()->GetFieldmarkMode())
{
- assert(false); // should have been forwarded to SwContentFrame
- InvalidateLineNum();
+ case sw::FieldmarkMode::ShowCommand:
+ isHidden = pInsertText->isInsideFieldmarkResult;
+ break;
+ case sw::FieldmarkMode::ShowResult:
+ isHidden = pInsertText->isInsideFieldmarkCommand;
+ break;
+ case sw::FieldmarkMode::ShowBoth: // just to avoid the warning
+ break;
}
- break;
- case RES_INS_TXT:
+ if (!isHidden)
{
- sal_Int32 const nNPos = static_cast<const SwInsText*>(pNew)->nPos;
- sal_Int32 const nNLen = static_cast<const SwInsText*>(pNew)->nLen;
- nPos = MapModelToView(&rNode, nNPos);
- nLen = TextFrameIndex(nNLen);
+ nLen = TextFrameIndex(pInsertText->nLen);
if (m_pMergedPara)
{
- UpdateMergedParaForInsert(*m_pMergedPara, true, rNode, nNPos, nNLen);
+ UpdateMergedParaForInsert(*m_pMergedPara, true, rNode, pInsertText->nPos, pInsertText->nLen);
}
if( IsIdxInside( nPos, nLen ) )
{
@@ -2160,64 +2339,67 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
Prepare();
}
else
- InvalidateRange_( SwCharRange( nPos, nLen ), nNLen );
+ InvalidateRange_( SwCharRange( nPos, nLen ), pInsertText->nLen );
}
- lcl_SetWrong( *this, rNode, nNPos, nNLen, true );
lcl_SetScriptInval( *this, nPos );
bSetFieldsDirty = true;
lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator+<sal_Int32, Tag_TextFrameIndex>);
}
- break;
- case RES_DEL_CHR:
+ lcl_SetWrong( *this, rNode, pInsertText->nPos, pInsertText->nLen, true );
+ }
+ else if (pDeleteText)
+ {
+ nPos = MapModelToView(&rNode, pDeleteText->nStart);
+ if (m_pMergedPara)
+ { // update merged before doing anything else
+ nLen = UpdateMergedParaForDelete(*m_pMergedPara, true, rNode, pDeleteText->nStart, pDeleteText->nLen);
+ }
+ else
{
- sal_Int32 const nNPos = static_cast<const SwDelChr*>(pNew)->nPos;
- nPos = MapModelToView(&rNode, nNPos);
- if (m_pMergedPara)
- {
- nLen = UpdateMergedParaForDelete(*m_pMergedPara, true, rNode, nNPos, 1);
- }
- else
- {
- nLen = TextFrameIndex(1);
- }
- lcl_SetWrong( *this, rNode, nNPos, -1, true );
- if (nLen)
- {
- InvalidateRange( SwCharRange(nPos, nLen), -1 );
- lcl_SetScriptInval( *this, nPos );
- bSetFieldsDirty = bRecalcFootnoteFlag = true;
- lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
- }
+ nLen = TextFrameIndex(pDeleteText->nLen);
}
- break;
- case RES_DEL_TXT:
+ const sal_Int32 m = -pDeleteText->nLen;
+ if ((!m_pMergedPara || nLen) && IsIdxInside(nPos, nLen))
{
- sal_Int32 const nNPos = static_cast<const SwDelText*>(pNew)->nStart;
- sal_Int32 const nNLen = static_cast<const SwDelText*>(pNew)->nLen;
- nPos = MapModelToView(&rNode, nNPos);
- if (m_pMergedPara)
- { // update merged before doing anything else
- nLen = UpdateMergedParaForDelete(*m_pMergedPara, true, rNode, nNPos, nNLen);
- }
+ if( !nLen )
+ InvalidateSize();
else
- {
- nLen = TextFrameIndex(nNLen);
- }
- const sal_Int32 m = -nNLen;
- if ((!m_pMergedPara || nLen) && IsIdxInside(nPos, nLen))
- {
- if( !nLen )
- InvalidateSize();
- else
- InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), m );
- }
- lcl_SetWrong( *this, rNode, nNPos, m, true );
- if (nLen)
- {
- lcl_SetScriptInval( *this, nPos );
- bSetFieldsDirty = bRecalcFootnoteFlag = true;
- lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
- }
+ InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), m );
+ }
+ lcl_SetWrong( *this, rNode, pDeleteText->nStart, m, true );
+ if (nLen)
+ {
+ lcl_SetScriptInval( *this, nPos );
+ bSetFieldsDirty = bRecalcFootnoteFlag = true;
+ lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
+ }
+ }
+ else if (pDeleteChar)
+ {
+ nPos = MapModelToView(&rNode, pDeleteChar->m_nPos);
+ if (m_pMergedPara)
+ {
+ nLen = UpdateMergedParaForDelete(*m_pMergedPara, true, rNode, pDeleteChar->m_nPos, 1);
+ }
+ else
+ {
+ nLen = TextFrameIndex(1);
+ }
+ lcl_SetWrong( *this, rNode, pDeleteChar->m_nPos, -1, true );
+ if (nLen)
+ {
+ InvalidateRange( SwCharRange(nPos, nLen), -1 );
+ lcl_SetScriptInval( *this, nPos );
+ bSetFieldsDirty = bRecalcFootnoteFlag = true;
+ lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
+ }
+ }
+ else switch (nWhich)
+ {
+ case RES_LINENUMBER:
+ {
+ assert(false); // should have been forwarded to SwContentFrame
+ InvalidateLineNum();
}
break;
case RES_UPDATE_ATTR:
@@ -2249,6 +2431,7 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
}
}
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
if( isA11yRelevantAttribute( pNewUpdate->getWhichAttr() ) &&
hasA11yRelevantAttribute( pNewUpdate->getFmtAttrs() ) )
{
@@ -2258,6 +2441,7 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
pViewSh->InvalidateAccessibleParaAttrs( *this );
}
}
+#endif
}
break;
case RES_OBJECTDYING:
@@ -2292,7 +2476,7 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
nPos = MapModelToView(&rNode, nNPos);
if (IsIdxInside(nPos, TextFrameIndex(1)))
{
- if( pNew == pOld )
+ if (SfxPoolItem::areSame( pNew, pOld ))
{
// only repaint
// opt: invalidate window?
@@ -2315,7 +2499,7 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
{ // the hint may be sent from the anchor node, or from a
// node in the footnote; the anchor index is only valid in the
// anchor node!
- assert(&rNode == &static_cast<const SwFormatFootnote*>(pNew)->GetTextFootnote()->GetTextNode());
+ assert(rNode == static_cast<const SwFormatFootnote*>(pNew)->GetTextFootnote()->GetTextNode());
nPos = MapModelToView(&rNode,
static_cast<const SwFormatFootnote*>(pNew)->GetTextFootnote()->GetStart());
}
@@ -2332,29 +2516,26 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
InvalidateLineNum();
const SwAttrSet& rNewSet = *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet();
- const SfxPoolItem* pItem = nullptr;
int nClear = 0;
sal_uInt16 nCount = rNewSet.Count();
- if( SfxItemState::SET == rNewSet.GetItemState( RES_TXTATR_FTN, false, &pItem ))
+ if( const SwFormatFootnote* pItem = rNewSet.GetItemIfSet( RES_TXTATR_FTN, false ) )
{
- nPos = MapModelToView(&rNode,
- static_cast<const SwFormatFootnote*>(pItem)->GetTextFootnote()->GetStart());
+ nPos = MapModelToView(&rNode, pItem->GetTextFootnote()->GetStart());
if (IsIdxInside(nPos, TextFrameIndex(1)))
Prepare( PrepareHint::FootnoteInvalidation, pNew );
nClear = 0x01;
--nCount;
}
- if( SfxItemState::SET == rNewSet.GetItemState( RES_TXTATR_FIELD, false, &pItem ))
+ if( const SwFormatField* pItem = rNewSet.GetItemIfSet( RES_TXTATR_FIELD, false ) )
{
- nPos = MapModelToView(&rNode,
- static_cast<const SwFormatField*>(pItem)->GetTextField()->GetStart());
+ nPos = MapModelToView(&rNode, pItem->GetTextField()->GetStart());
if (IsIdxInside(nPos, TextFrameIndex(1)))
{
const SfxPoolItem* pOldItem = pOld ?
&(static_cast<const SwAttrSetChg*>(pOld)->GetChgSet()->Get(RES_TXTATR_FIELD)) : nullptr;
- if( pItem == pOldItem )
+ if (SfxPoolItem::areSame( pItem, pOldItem ))
{
InvalidatePage();
SetCompletePaint();
@@ -2423,7 +2604,7 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
for ( size_t i = 0; GetDrawObjs() && i < pObjs->size(); ++i )
{
SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
- if ( auto pFly = dynamic_cast<SwFlyFrame *>( pAnchoredObj ) )
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
{
if( !pFly->IsFlyInContentFrame() )
{
@@ -2555,6 +2736,7 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
SwContentFrame::SwClientNotify(rModify, sw::LegacyModifyHint(pOld, pNew));
}
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
if (isA11yRelevantAttribute(nWhich))
{
SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
@@ -2563,25 +2745,9 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
pViewSh->InvalidateAccessibleParaAttrs( *this );
}
}
+#endif
}
break;
-
- // Process SwDocPosUpdate
- case RES_DOCPOS_UPDATE:
- {
- if( pOld && pNew )
- {
- const SwDocPosUpdate *pDocPos = static_cast<const SwDocPosUpdate*>(pOld);
- if( pDocPos->nDocPos <= getFrameArea().Top() )
- {
- const SwFormatField *pField = static_cast<const SwFormatField *>(pNew);
- TextFrameIndex const nIndex(MapModelToView(&rNode,
- pField->GetTextField()->GetStart()));
- InvalidateRange(SwCharRange(nIndex, TextFrameIndex(1)));
- }
- }
- break;
- }
case RES_PARATR_SPLIT:
if ( GetPrev() )
CheckKeep();
@@ -2608,38 +2774,12 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
} // switch
if( bSetFieldsDirty )
- GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rNode, 1 );
+ GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rNode, SwNodeOffset(1) );
if ( bRecalcFootnoteFlag )
CalcFootnoteFlag();
}
-bool SwTextFrame::GetInfo( SfxPoolItem &rHint ) const
-{
- if ( RES_VIRTPAGENUM_INFO == rHint.Which() && IsInDocBody() && ! IsFollow() )
- {
- SwVirtPageNumInfo &rInfo = static_cast<SwVirtPageNumInfo&>(rHint);
- const SwPageFrame *pPage = FindPageFrame();
- if ( pPage )
- {
- if ( pPage == rInfo.GetOrigPage() && !GetPrev() )
- {
- // this should be the one
- // (could only differ temporarily; is that disturbing?)
- rInfo.SetInfo( pPage, this );
- return false;
- }
- if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() &&
- (!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum()))
- {
- // this could be the one
- rInfo.SetInfo( pPage, this );
- }
- }
- }
- return true;
-}
-
void SwTextFrame::PrepWidows( const sal_uInt16 nNeed, bool bNotify )
{
OSL_ENSURE(GetFollow() && nNeed, "+SwTextFrame::Prepare: lost all friends");
@@ -2775,7 +2915,12 @@ bool SwTextFrame::Prepare( const PrepareHint ePrep, const void* pVoid,
}
}
- if( !HasPara() && PrepareHint::MustFit != ePrep )
+ // Split fly anchors are technically empty (have no SwParaPortion), but otherwise behave like
+ // other split text frames, which are non-empty.
+ bool bSplitFlyAnchor = GetOffset() == TextFrameIndex(0) && HasFollow()
+ && GetFollow()->GetOffset() == TextFrameIndex(0);
+
+ if( !HasPara() && !bSplitFlyAnchor && PrepareHint::MustFit != ePrep )
{
SetInvalidVert( true ); // Test
OSL_ENSURE( !IsLocked(), "SwTextFrame::Prepare: three of a perfect pair" );
@@ -2953,7 +3098,7 @@ bool SwTextFrame::Prepare( const PrepareHint ePrep, const void* pVoid,
SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[i];
// i#28701 - consider all
// to-character anchored objects
- if ( pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ if ( pAnchoredObj->GetFrameFormat()->GetAnchor().GetAnchorId()
== RndStdIds::FLY_AT_CHAR )
{
bFormat = true;
@@ -2968,7 +3113,13 @@ bool SwTextFrame::Prepare( const PrepareHint ePrep, const void* pVoid,
if( aTextFly.IsOn() )
{
// Does any free-flying frame overlap?
- bFormat = aTextFly.Relax() || IsUndersized();
+ const bool bRelaxed = aTextFly.Relax();
+ bFormat = bRelaxed || IsUndersized();
+ if (bRelaxed)
+ {
+ // It's possible that pPara was deleted above; retrieve it again
+ pPara = aAccess.GetPara();
+ }
}
}
}
@@ -3210,7 +3361,7 @@ bool SwTextFrame::TestFormat( const SwFrame* pPrv, SwTwips &rMaxHeight, bool &bS
SwTestFormat aSave( this, pPrv, rMaxHeight );
- return SwTextFrame::WouldFit( rMaxHeight, bSplit, true );
+ return SwTextFrame::WouldFit(rMaxHeight, bSplit, true, false);
}
/**
@@ -3225,7 +3376,7 @@ bool SwTextFrame::TestFormat( const SwFrame* pPrv, SwTwips &rMaxHeight, bool &bS
*
* @returns true if I can split
*/
-bool SwTextFrame::WouldFit( SwTwips &rMaxHeight, bool &bSplit, bool bTst )
+bool SwTextFrame::WouldFit(SwTwips &rMaxHeight, bool &bSplit, bool bTst, bool bMoveBwd)
{
OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
"SwTextFrame::WouldFit with swapped frame" );
@@ -3308,7 +3459,7 @@ bool SwTextFrame::WouldFit( SwTwips &rMaxHeight, bool &bSplit, bool bTst )
// is breaking necessary?
bSplit = !aFrameBreak.IsInside( aLine );
if ( bSplit )
- bRet = !aFrameBreak.IsKeepAlways() && aFrameBreak.WouldFit( aLine, rMaxHeight, bTst );
+ bRet = !aFrameBreak.IsKeepAlways() && aFrameBreak.WouldFit(aLine, rMaxHeight, bTst, bMoveBwd);
else
{
// we need the total height including the current line
@@ -3322,7 +3473,7 @@ bool SwTextFrame::WouldFit( SwTwips &rMaxHeight, bool &bSplit, bool bTst )
return bRet;
}
-sal_uInt16 SwTextFrame::GetParHeight() const
+SwTwips SwTextFrame::GetParHeight() const
{
OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
"SwTextFrame::GetParHeight with swapped frame" );
@@ -3330,11 +3481,11 @@ sal_uInt16 SwTextFrame::GetParHeight() const
if( !HasPara() )
{ // For non-empty paragraphs this is a special case
// For UnderSized we can simply just ask 1 Twip more
- sal_uInt16 nRet = static_cast<sal_uInt16>(getFramePrintArea().SSize().Height());
+ sal_uInt16 nRet = o3tl::narrowing<sal_uInt16>(getFramePrintArea().SSize().Height());
if( IsUndersized() )
{
if( IsEmpty() || GetText().isEmpty() )
- nRet = static_cast<sal_uInt16>(EmptyHeight());
+ nRet = o3tl::narrowing<sal_uInt16>(EmptyHeight());
else
++nRet;
}
@@ -3343,7 +3494,7 @@ sal_uInt16 SwTextFrame::GetParHeight() const
// TODO: Refactor and improve code
const SwLineLayout* pLineLayout = GetPara();
- sal_uInt16 nHeight = pLineLayout ? pLineLayout->GetRealHeight() : 0;
+ SwTwips nHeight = pLineLayout ? pLineLayout->GetRealHeight() : 0;
// Is this paragraph scrolled? Our height until now is at least
// one line height too low then
@@ -3485,7 +3636,7 @@ void SwTextFrame::CalcAdditionalFirstLineOffset()
nListLevel = MAXLEVEL - 1;
const SwNumFormat& rNumFormat =
- pTextNode->GetNumRule()->Get( static_cast<sal_uInt16>(nListLevel) );
+ pTextNode->GetNumRule()->Get( o3tl::narrowing<sal_uInt16>(nListLevel) );
if ( rNumFormat.GetPositionAndSpaceMode() != SvxNumberFormat::LABEL_ALIGNMENT )
return;
@@ -3732,19 +3883,33 @@ sal_uInt16 SwTextFrame::FirstLineHeight() const
if ( !HasPara() )
{
if( IsEmpty() && isFrameAreaDefinitionValid() )
- return IsVertical() ? static_cast<sal_uInt16>(getFramePrintArea().Width()) : static_cast<sal_uInt16>(getFramePrintArea().Height());
+ return IsVertical() ? o3tl::narrowing<sal_uInt16>(getFramePrintArea().Width()) : o3tl::narrowing<sal_uInt16>(getFramePrintArea().Height());
return USHRT_MAX;
}
const SwParaPortion *pPara = GetPara();
if ( !pPara )
return USHRT_MAX;
- return pPara->Height();
+ // tdf#146500 Lines with only fly overlap cannot be "moved", so the idea
+ // here is to continue until there's some text.
+ // FIXME ideally we want to count a fly to the line in which it is anchored
+ // - it may even be anchored in some other paragraph! SwFlyPortion doesn't
+ // have a pointer sadly so no way to find out.
+ sal_uInt16 nHeight(0);
+ for (SwLineLayout const* pLine = pPara; pLine; pLine = pLine->GetNext())
+ {
+ nHeight += pLine->Height();
+ if (::sw::FindNonFlyPortion(*pLine))
+ {
+ break;
+ }
+ }
+ return nHeight;
}
-sal_uInt16 SwTextFrame::GetLineCount(TextFrameIndex const nPos)
+sal_Int32 SwTextFrame::GetLineCount(TextFrameIndex const nPos)
{
- sal_uInt16 nRet = 0;
+ sal_Int32 nRet = 0;
SwTextFrame *pFrame = this;
do
{
@@ -3766,7 +3931,7 @@ sal_uInt16 SwTextFrame::GetLineCount(TextFrameIndex const nPos)
void SwTextFrame::ChgThisLines()
{
// not necessary to format here (GetFormatted etc.), because we have to come from there!
- sal_uInt32 nNew = 0;
+ sal_Int32 nNew = 0;
const SwLineNumberInfo &rInf = GetDoc().GetLineNumberInfo();
if ( !GetText().isEmpty() && HasPara() )
{
@@ -3826,9 +3991,9 @@ void SwTextFrame::RecalcAllLines()
if ( IsInTab() )
return;
- const sal_uLong nOld = GetAllLines();
+ const sal_Int32 nOld = GetAllLines();
const SwFormatLineNumber &rLineNum = GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber();
- sal_uLong nNewNum;
+ sal_Int32 nNewNum;
const bool bRestart = GetDoc().GetLineNumberInfo().IsRestartEachPage();
if ( !IsFollow() && rLineNum.GetStartValue() && rLineNum.IsCount() )
@@ -3892,7 +4057,7 @@ void SwTextFrame::VisitPortions( SwPortionHandler& rPH ) const
pPor = pPor->GetNextPortion();
}
- rPH.LineBreak(pLine->Width());
+ rPH.LineBreak();
pLine = pLine->GetNext();
}
}
diff --git a/sw/source/core/text/txtftn.cxx b/sw/source/core/text/txtftn.cxx
index 5b4b9f7492e0..c1fa749c93f5 100644
--- a/sw/source/core/text/txtftn.cxx
+++ b/sw/source/core/text/txtftn.cxx
@@ -21,6 +21,7 @@
#include <string_view>
+#include <utility>
#include <viewsh.hxx>
#include <doc.hxx>
#include <IDocumentLayoutAccess.hxx>
@@ -53,6 +54,13 @@
#include <frmtool.hxx>
#include <ndindex.hxx>
#include <IDocumentSettingAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <swmodule.hxx>
+#include <unotextrange.hxx>
+#include <redline.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/awt/CharSet.hpp>
@@ -240,7 +248,7 @@ static SwTwips lcl_GetFootnoteLower( const SwTextFrame* pFrame, SwTwips nLower )
{
SwRect aRect( pAnchoredObj->GetObjRect() );
- auto pFlyFrame = dynamic_cast<SwFlyFrame*>( pAnchoredObj );
+ auto pFlyFrame = pAnchoredObj->DynCastFlyFrame();
if ( !pFlyFrame ||
pFlyFrame->isFrameAreaDefinitionValid() )
{
@@ -288,7 +296,7 @@ SwTwips SwTextFrame::GetFootnoteLine( const SwTextFootnote *pFootnote ) const
&pFootnote->GetTextNode(), pFootnote->GetStart()));
aLine.CharToLine( nPos );
- nRet = aLine.Y() + SwTwips(aLine.GetLineHeight());
+ nRet = aLine.Y() + aLine.GetLineHeight();
if( IsVertical() )
nRet = SwitchHorizontalToVertical( nRet );
}
@@ -656,7 +664,7 @@ void SwTextFrame::ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDea
mbInFootnoteConnect = false;
return;
}
- else if( pSrcFrame )
+ else if (pSrcFrame && pFootnoteFrame)
{
SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
if( !pFootnoteBoss->IsInSct() ||
@@ -964,16 +972,15 @@ SwNumberPortion *SwTextFormatter::NewFootnoteNumPortion( SwTextFormatInfo const
pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CJK );
pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CTL );
- const auto xAnchor = rFootnote.getAnchor(*pDoc);
- uno::Reference<beans::XPropertySet> xAnchorProps(xAnchor, uno::UNO_QUERY);
- if (xAnchorProps.is())
+ const rtl::Reference<SwXTextRange> xAnchor = rFootnote.getAnchor(*pDoc);
+ if (xAnchor.is())
{
- auto aAny = xAnchorProps->getPropertyValue("CharFontCharSet");
+ auto aAny = xAnchor->getPropertyValue("CharFontCharSet");
sal_Int16 eCharSet;
if ((aAny >>= eCharSet) && eCharSet == awt::CharSet::SYMBOL)
{
OUString aFontName;
- aAny = xAnchorProps->getPropertyValue("CharFontName");
+ aAny = xAnchor->getPropertyValue("CharFontName");
if (aAny >>= aFontName)
{
pNumFnt->SetName(aFontName, SwFontScript::Latin);
@@ -990,6 +997,36 @@ SwNumberPortion *SwTextFormatter::NewFootnoteNumPortion( SwTextFormatInfo const
pNumFnt->SetDiffFnt(&rSet, pIDSA );
pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() );
+ // tdf#85610 apply redline coloring to the footnote numbering in the footnote area
+ SwUnoInternalPaM aPam(*pDoc);
+ if ( ::sw::XTextRangeToSwPaM(aPam, xAnchor) )
+ {
+ SwRedlineTable::size_type nRedlinePos = 0;
+ const SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
+ const SwRangeRedline* pRedline = rTable.FindAtPosition( *aPam.Start(), nRedlinePos );
+ if (pRedline)
+ {
+ SwAttrPool& rPool = pDoc->GetAttrPool();
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1> aSet(rPool);
+
+ std::size_t aAuthor = (1 < pRedline->GetStackCount())
+ ? pRedline->GetAuthor( 1 )
+ : pRedline->GetAuthor();
+
+ if ( RedlineType::Delete == pRedline->GetType() )
+ SW_MOD()->GetDeletedAuthorAttr(aAuthor, aSet);
+ else
+ SW_MOD()->GetInsertAuthorAttr(aAuthor, aSet);
+
+ if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR))
+ pNumFnt->SetColor(pItem->GetValue());
+ if (const SvxUnderlineItem* pItem = aSet.GetItemIfSet(RES_CHRATR_UNDERLINE))
+ pNumFnt->SetUnderline(pItem->GetLineStyle());
+ if (const SvxCrossedOutItem* pItem = aSet.GetItemIfSet(RES_CHRATR_CROSSEDOUT))
+ pNumFnt->SetStrikeout( pItem->GetStrikeout() );
+ }
+ }
+
SwFootnoteNumPortion* pNewPor = new SwFootnoteNumPortion( aFootnoteText, std::move(pNumFnt) );
pNewPor->SetLeft( !m_pFrame->IsRightToLeft() );
return pNewPor;
@@ -1181,7 +1218,7 @@ TextFrameIndex SwTextFormatter::FormatQuoVadis(TextFrameIndex const nOffset)
if( nDiff < 0 )
{
nLastLeft = pQuo->GetAscent();
- nQuoWidth = static_cast<sal_uInt16>(-nDiff + nLastLeft);
+ nQuoWidth = o3tl::narrowing<sal_uInt16>(-nDiff + nLastLeft);
}
else
{
@@ -1313,17 +1350,14 @@ SwFootnoteSave::SwFootnoteSave(const SwTextSizeInfo& rInf, const SwTextFootnote*
}
// set the correct rotation at the footnote font
- const SfxPoolItem* pItem;
- if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_ROTATE,
- true, &pItem ))
- m_pFnt->SetVertical(static_cast<const SvxCharRotateItem*>(pItem)->GetValue(),
+ if( const SvxCharRotateItem* pItem = rSet.GetItemIfSet( RES_CHRATR_ROTATE ) )
+ m_pFnt->SetVertical(pItem->GetValue(),
rInf.GetTextFrame()->IsVertical());
m_pFnt->ChgPhysFnt(m_pInf->GetVsh(), *m_pInf->GetOut());
- if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_BACKGROUND,
- true, &pItem ))
- m_pFnt->SetBackColor(static_cast<const SvxBrushItem*>(pItem)->GetColor());
+ if( const SvxBrushItem* pItem = rSet.GetItemIfSet( RES_CHRATR_BACKGROUND ) )
+ m_pFnt->SetBackColor(pItem->GetColor());
}
else
m_pFnt = nullptr;
@@ -1408,8 +1442,8 @@ SwFieldPortion *SwQuoVadisPortion::Clone( const OUString &rExpand ) const
return new SwQuoVadisPortion( rExpand, m_aErgo );
}
-SwQuoVadisPortion::SwQuoVadisPortion( const OUString &rExp, const OUString& rStr )
- : SwFieldPortion( rExp ), m_aErgo(rStr)
+SwQuoVadisPortion::SwQuoVadisPortion( const OUString &rExp, OUString aStr )
+ : SwFieldPortion( rExp ), m_aErgo(std::move(aStr))
{
SetLen(TextFrameIndex(0));
SetWhichPor( PortionType::QuoVadis );
diff --git a/sw/source/core/text/txthyph.cxx b/sw/source/core/text/txthyph.cxx
index 7f9f6f6cd611..a718296e8a2e 100644
--- a/sw/source/core/text/txthyph.cxx
+++ b/sw/source/core/text/txthyph.cxx
@@ -32,7 +32,6 @@
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
-using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::linguistic2;
using namespace ::com::sun::star::i18n;
@@ -355,6 +354,16 @@ void SwHyphPortion::HandlePortion( SwPortionHandler& rPH ) const
rPH.Special( GetLen(), OUString('-'), GetWhichPor() );
}
+void SwHyphPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHyphPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
bool SwHyphPortion::Format( SwTextFormatInfo &rInf )
{
const SwLinePortion *pLast = rInf.GetLast();
diff --git a/sw/source/core/text/txtinit.cxx b/sw/source/core/text/txtinit.cxx
index b8a6540f6318..2b8bb2e95d21 100644
--- a/sw/source/core/text/txtinit.cxx
+++ b/sw/source/core/text/txtinit.cxx
@@ -40,7 +40,7 @@ void TextInit_()
pSwFontCache = new SwFontCache; // Cache for SwTextFormatColl -> SwFontObj = { SwFont aSwFont, SfxPoolItem* pDefaultArray }
SwCache *pTextCache = new SwCache( 250 // Cache for SwTextFrame -> SwTextLine = { SwParaPortion* pLine }
#ifdef DBG_UTIL
- , "static SwTextFrame::s_pTextCache"
+ , "static SwTextFrame::s_pTextCache"_ostr
#endif
);
SwTextFrame::SetTextCache( pTextCache );
diff --git a/sw/source/core/text/txttab.cxx b/sw/source/core/text/txttab.cxx
index 8e8203c72270..e19dd8efe9b4 100644
--- a/sw/source/core/text/txttab.cxx
+++ b/sw/source/core/text/txttab.cxx
@@ -40,27 +40,39 @@
* If the tab stop is outside the print area, we
* return 0 if it is not the first tab stop.
*/
-const SvxTabStop *SwLineInfo::GetTabStop( const SwTwips nSearchPos, const SwTwips nRight ) const
+const SvxTabStop* SwLineInfo::GetTabStop(const SwTwips nSearchPos, SwTwips& nRight) const
{
- for( sal_uInt16 i = 0; i < m_pRuler->Count(); ++i )
+ for( sal_uInt16 i = 0; i < m_oRuler->Count(); ++i )
{
- const SvxTabStop &rTabStop = m_pRuler->operator[](i);
- if( rTabStop.GetTabPos() > SwTwips(nRight) )
+ const SvxTabStop &rTabStop = m_oRuler->operator[](i);
+ if (nRight && rTabStop.GetTabPos() > nRight)
+ {
+ // Consider the first tabstop to always be in-bounds.
+ if (!i)
+ nRight = rTabStop.GetTabPos();
return i ? nullptr : &rTabStop;
-
+ }
if( rTabStop.GetTabPos() > nSearchPos )
+ {
+ if (!i && !nRight)
+ nRight = rTabStop.GetTabPos();
return &rTabStop;
+ }
}
return nullptr;
}
sal_uInt16 SwLineInfo::NumberOfTabStops() const
{
- return m_pRuler->Count();
+ return m_oRuler->Count();
}
SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto ) const
{
+ IDocumentSettingAccess const& rIDSA(rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess());
+ const bool bTabOverSpacing = rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
+ const bool bTabsRelativeToIndent = rIDSA.get(DocumentSettingId::TABS_RELATIVE_TO_INDENT);
+
// Update search location - since Center/Decimal tabstops' width is dependent on the following text.
SwTabPortion* pTmpLastTab = rInf.GetLastTab();
if (pTmpLastTab && (pTmpLastTab->IsTabCenterPortion() || pTmpLastTab->IsTabDecimalPortion()))
@@ -78,8 +90,6 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
// nTabLeft: The absolute value, the tab stops are relative to: Tabs origin.
// #i91133#
- const bool bTabsRelativeToIndent =
- m_pFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT);
const SwTwips nTabLeft = bRTL
? m_pFrame->getFrameArea().Right() -
( bTabsRelativeToIndent ? GetTabLeft() : 0 )
@@ -121,6 +131,7 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
}
SwTwips nNextPos = 0;
+ bool bAbsoluteNextPos = false;
// #i24363# tab stops relative to indent
// nSearchPos: The current position relative to the tabs origin
@@ -132,8 +143,14 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
// any hard set tab stops:
// Note: If there are no user defined tab stops, there is always a
// default tab stop.
+ const SwTwips nOldRight = nMyRight;
+ // Accept left-tabstops beyond the paragraph margin for bTabOverSpacing
+ if (bTabOverSpacing)
+ nMyRight = 0;
const SvxTabStop* pTabStop = m_aLineInf.GetTabStop( nSearchPos, nMyRight );
- if ( pTabStop )
+ if (!nMyRight)
+ nMyRight = nOldRight;
+ if (pTabStop)
{
cFill = ' ' != pTabStop->GetFill() ? pTabStop->GetFill() : 0;
cDec = pTabStop->GetDecimal();
@@ -144,6 +161,21 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
//calculate default tab position of default tabs in negative indent
nNextPos = ( nSearchPos / nNextPos ) * nNextPos;
}
+ else if (pTabStop->GetTabPos() > nMyRight
+ && pTabStop->GetAdjustment() != SvxTabAdjust::Left)
+ {
+ // A rather special situation. The tabstop found is:
+ // 1.) in a document compatible with MS formats
+ // 2.) not a left tabstop.
+ // 3.) not the first tabstop (in that case nMyRight was adjusted to match tabPos).
+ // 4.) beyond the end of the text area
+ // Therefore, they act like right-tabstops at the edge of the para area.
+ // This benefits DOCX 2013+, and doesn't hurt the earlier formats,
+ // since up till now these were just treated as automatic tabstops.
+ eAdj = SvxTabAdjust::Right;
+ bAbsoluteNextPos = true;
+ nNextPos = rInf.Width();
+ }
bAutoTabStop = false;
}
else
@@ -152,9 +184,9 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
if( USHRT_MAX == nDefTabDist )
{
const SvxTabStopItem& rTab =
- m_pFrame->GetAttrSet()->GetPool()->GetDefaultItem( RES_PARATR_TABSTOP );
+ m_pFrame->GetAttrSet()->GetPool()->GetUserOrPoolDefaultItem( RES_PARATR_TABSTOP );
if( rTab.Count() )
- nDefTabDist = static_cast<sal_uInt16>(rTab[0].GetTabPos());
+ nDefTabDist = o3tl::narrowing<sal_uInt16>(rTab[0].GetTabPos());
else
nDefTabDist = SVX_TAB_DEFDIST;
m_aLineInf.SetDefTabStop( nDefTabDist );
@@ -247,7 +279,8 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
}
}
- nNextPos += bRTL ? nLinePos - nTabLeft : nTabLeft - nLinePos;
+ if (!bAbsoluteNextPos)
+ nNextPos += bRTL ? nLinePos - nTabLeft : nTabLeft - nLinePos;
OSL_ENSURE( nNextPos >= 0, "GetTabStop: Don't go back!" );
nNewTabPos = sal_uInt16(nNextPos);
}
@@ -287,6 +320,8 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
}
}
}
+ if (pTabPor)
+ rInf.UpdateTabSeen(pTabPor->GetWhichPor());
return pTabPor;
}
@@ -295,11 +330,11 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
* The base class is initialized without setting anything
*/
SwTabPortion::SwTabPortion( const sal_uInt16 nTabPosition, const sal_Unicode cFillChar, const bool bAutoTab )
- : SwFixPortion(), m_nTabPos(nTabPosition), m_cFill(cFillChar), m_bAutoTabStop( bAutoTab )
+ : m_nTabPos(nTabPosition), m_cFill(cFillChar), m_bAutoTabStop( bAutoTab )
{
mnLineLength = TextFrameIndex(1);
OSL_ENSURE(!IsFilled() || ' ' != m_cFill, "SwTabPortion::CTOR: blanks ?!");
- SetWhichPor( PortionType::Table );
+ SetWhichPor( PortionType::Tab );
}
bool SwTabPortion::Format( SwTextFormatInfo &rInf )
@@ -323,12 +358,14 @@ bool SwTabPortion::PreFormat( SwTextFormatInfo &rInf )
OSL_ENSURE( rInf.X() <= GetTabPos(), "SwTabPortion::PreFormat: rush hour" );
// Here we settle down ...
- SetFix( static_cast<sal_uInt16>(rInf.X()) );
+ SetFix( o3tl::narrowing<sal_uInt16>(rInf.X()) );
IDocumentSettingAccess const& rIDSA(rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess());
const bool bTabCompat = rIDSA.get(DocumentSettingId::TAB_COMPAT);
const bool bTabOverflow = rIDSA.get(DocumentSettingId::TAB_OVERFLOW);
const bool bTabOverMargin = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
+ const bool bTabOverSpacing = rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
+ const sal_Int32 nTextFrameWidth = rInf.GetTextFrame()->getFrameArea().Width();
// The minimal width of a tab is one blank at least.
// #i37686# In compatibility mode, the minimum width
@@ -339,14 +376,14 @@ bool SwTabPortion::PreFormat( SwTextFormatInfo &rInf )
// #i89179#
// tab portion representing the list tab of a list label gets the
// same font as the corresponding number portion
- std::unique_ptr< SwFontSave > pSave;
+ std::optional< SwFontSave > oSave;
if ( GetLen() == TextFrameIndex(0) &&
rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
static_cast<SwNumberPortion*>(rInf.GetLast())->HasFont() )
{
const SwFont* pNumberPortionFont =
static_cast<SwNumberPortion*>(rInf.GetLast())->GetFont();
- pSave.reset( new SwFontSave( rInf, const_cast<SwFont*>(pNumberPortionFont) ) );
+ oSave.emplace( rInf, const_cast<SwFont*>(pNumberPortionFont) );
}
OUString aTmp( ' ' );
SwTextSizeInfo aInf( rInf, &aTmp );
@@ -381,13 +418,23 @@ bool SwTabPortion::PreFormat( SwTextFormatInfo &rInf )
case PortionType::TabLeft:
{
// handle this case in PostFormat
- if( bTabOverMargin && !m_bAutoTabStop && GetTabPos() > rInf.Width() )
+ if ((bTabOverMargin || bTabOverSpacing) && GetTabPos() > rInf.Width()
+ && (!m_bAutoTabStop || (!bTabOverMargin && rInf.X() > rInf.Width())))
{
- rInf.SetLastTab( this );
- break;
+ if (bTabOverMargin || GetTabPos() < nTextFrameWidth)
+ {
+ rInf.SetLastTab(this);
+ break;
+ }
+ else
+ {
+ assert(!bTabOverMargin && bTabOverSpacing && GetTabPos() >= nTextFrameWidth);
+ bFull = true;
+ break;
+ }
}
- PrtWidth( static_cast<sal_uInt16>(GetTabPos() - rInf.X()) );
+ PrtWidth( o3tl::narrowing<sal_uInt16>(GetTabPos() - rInf.X()) );
bFull = rInf.Width() <= rInf.X() + PrtWidth();
// In tabulator compatibility mode, we reset the bFull flag
@@ -396,7 +443,7 @@ bool SwTabPortion::PreFormat( SwTextFormatInfo &rInf )
bool bAtParaEnd = rInf.GetIdx() + GetLen() == TextFrameIndex(rInf.GetText().getLength());
if ( bFull && bTabCompat &&
( ( bTabOverflow && ( rInf.IsTabOverflow() || !m_bAutoTabStop ) ) || bAtParaEnd ) &&
- GetTabPos() >= rInf.GetTextFrame()->getFrameArea().Width() )
+ GetTabPos() >= nTextFrameWidth)
{
bFull = false;
if ( bTabOverflow && !m_bAutoTabStop )
@@ -417,7 +464,7 @@ bool SwTabPortion::PreFormat( SwTextFormatInfo &rInf )
// line if there is a fly reducing the line width:
!rInf.GetFly() )
{
- PrtWidth( static_cast<sal_uInt16>(rInf.Width() - rInf.X()) );
+ PrtWidth( o3tl::narrowing<sal_uInt16>(rInf.Width() - rInf.X()) );
SetFixWidth( PrtWidth() );
}
else
@@ -443,13 +490,19 @@ bool SwTabPortion::PostFormat( SwTextFormatInfo &rInf )
{
bool bTabOverMargin = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
DocumentSettingId::TAB_OVER_MARGIN);
-
+ bool bTabOverSpacing = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::TAB_OVER_SPACING);
if (rInf.GetTextFrame()->IsInSct())
bTabOverMargin = false;
// If the tab position is larger than the right margin, it gets scaled down by default.
// However, if compat mode enabled, we allow tabs to go over the margin: the rest of the paragraph is not broken into lines.
- const sal_uInt16 nRight = bTabOverMargin ? GetTabPos() : std::min(GetTabPos(), rInf.Width());
+ const sal_uInt16 nRight
+ = bTabOverMargin
+ ? GetTabPos()
+ : bTabOverSpacing
+ ? std::min<long>(GetTabPos(), rInf.GetTextFrame()->getFrameArea().Right())
+ : std::min(GetTabPos(), rInf.Width());
const SwLinePortion *pPor = GetNextPortion();
sal_uInt16 nPorWidth = 0;
@@ -462,7 +515,7 @@ bool SwTabPortion::PostFormat( SwTextFormatInfo &rInf )
const PortionType nWhich = GetWhichPor();
const bool bTabCompat = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);
- if ( bTabOverMargin && PortionType::TabLeft == nWhich )
+ if ((bTabOverMargin || bTabOverSpacing) && PortionType::TabLeft == nWhich)
{
nPorWidth = 0;
}
@@ -489,7 +542,7 @@ bool SwTabPortion::PostFormat( SwTextFormatInfo &rInf )
// centered tabs are problematic:
// We have to detect how much fits into the line.
sal_uInt16 nNewWidth = nPorWidth /2;
- if( !bTabOverMargin && nNewWidth > rInf.Width() - nRight )
+ if (!bTabOverMargin && !bTabOverSpacing && nNewWidth > rInf.Width() - nRight)
nNewWidth = nPorWidth - (rInf.Width() - nRight);
nPorWidth = nNewWidth;
}
@@ -524,7 +577,7 @@ void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
// #i89179#
// tab portion representing the list tab of a list label gets the
// same font as the corresponding number portion
- std::unique_ptr< SwFontSave > pSave;
+ std::optional< SwFontSave > oSave;
bool bAfterNumbering = false;
if (GetLen() == TextFrameIndex(0))
{
@@ -536,7 +589,7 @@ void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
{
const SwFont* pNumberPortionFont =
static_cast<const SwNumberPortion*>(pPrevPortion)->GetFont();
- pSave.reset( new SwFontSave( rInf, const_cast<SwFont*>(pNumberPortionFont) ) );
+ oSave.emplace( rInf, const_cast<SwFont*>(pNumberPortionFont) );
bAfterNumbering = true;
}
}
@@ -553,7 +606,7 @@ void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
{
// filled tabs are shaded in gray
if( IsFilled() )
- rInf.DrawViewOpt( *this, PortionType::Table );
+ rInf.DrawViewOpt( *this, PortionType::Tab );
else
rInf.DrawTab( *this );
}
@@ -569,7 +622,7 @@ void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
{
// Always with kerning, also on printer!
sal_uInt16 nChar = Width() / nCharWidth;
- OUStringBuffer aBuf;
+ OUStringBuffer aBuf(nChar);
comphelper::string::padToLength(aBuf, nChar, ' ');
rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0),
TextFrameIndex(nChar), true);
@@ -591,7 +644,7 @@ void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
sal_uInt16 nChar = Width() / nCharWidth;
if ( m_cFill == '_' )
++nChar; // to avoid gaps
- OUStringBuffer aBuf;
+ OUStringBuffer aBuf(nChar);
comphelper::string::padToLength(aBuf, nChar, m_cFill);
rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0),
TextFrameIndex(nChar), true);
@@ -604,7 +657,7 @@ void SwAutoTabDecimalPortion::Paint( const SwTextPaintInfo & ) const
void SwTabPortion::HandlePortion( SwPortionHandler& rPH ) const
{
- rPH.Text( GetLen(), GetWhichPor(), Height(), Width() );
+ rPH.Text( GetLen(), GetWhichPor() );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/widorp.cxx b/sw/source/core/text/widorp.cxx
index 141fd9437a84..5446ac8cec15 100644
--- a/sw/source/core/text/widorp.cxx
+++ b/sw/source/core/text/widorp.cxx
@@ -36,6 +36,12 @@
#include <sectfrm.hxx>
#include <ftnfrm.hxx>
#include <pagefrm.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <sortedobjs.hxx>
+#include <anchoredobject.hxx>
+#include <flyfrm.hxx>
+
+#include <com/sun/star/text/ParagraphHyphenationKeepType.hpp>
#undef WIDOWTWIPS
@@ -127,15 +133,54 @@ bool SwTextFrameBreak::IsInside( SwTextMargin const &rLine ) const
aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*m_pFrame->GetUpper()), m_nOrigin );
SwTwips nDiff = nHeight - nLineHeight;
- // Hide whitespace may require not to insert a new page.
- SwPageFrame* pPageFrame = m_pFrame->FindPageFrame();
- if (!pPageFrame->CheckPageHeightValidForHideWhitespace(nDiff))
- nDiff = 0;
-
// If everything is inside the existing frame the result is true;
bFit = nDiff >= 0;
+ // If it didn't fit, try to add the space of footnotes that are anchored
+ // in frames below (in next-chain of) this one as they will need to move
+ // forward anyway if this frame is split.
+ // - except if in tables (need to check if row is splittable?
+ // also, multiple columns looks difficult)
+ if (!bFit && !m_pFrame->IsInTab())
+ {
+ if (SwFootnoteBossFrame const*const pBoss = m_pFrame->FindFootnoteBossFrame())
+ {
+ if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
+ {
+ SwContentFrame const* pContent(m_pFrame);
+ while (pContent->HasFollow())
+ {
+ pContent = pContent->GetFollow();
+ }
+ // start with first text frame that isn't a follow
+ // (ignoring Keep attribute for now, MakeAll should handle it?)
+ pContent = pContent->GetNextContentFrame();
+ ::std::set<SwContentFrame const*> nextFrames;
+ while (pBoss->IsAnLower(pContent))
+ {
+ nextFrames.insert(pContent);
+ pContent = pContent->GetNextContentFrame();
+ }
+ SwTwips nNextFootnotes(0);
+ for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
+ pFootnote != nullptr;
+ pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
+ {
+ SwContentFrame const*const pAnchor = pFootnote->GetRef();
+ if (nextFrames.find(pAnchor) != nextFrames.end())
+ {
+ nNextFootnotes += aRectFnSet.GetHeight(pFootnote->getFrameArea());
+ }
+ }
+ bFit = 0 <= nDiff + nNextFootnotes;
+ SAL_INFO_IF(bFit, "sw.core", "no text frame break because ignoring "
+ << nNextFootnotes << " footnote height");
+ }
+ }
+ }
if (!bFit && rLine.MaybeHasHints() && m_pFrame->GetFollow()
+ // tdf#153319 RemoveFootnote only works if this frame doesn't
+ && !rLine.GetNext() // contain the footnote portion
// if using same footnote container as the follow, pointless to try?
&& m_pFrame->FindFootnoteBossFrame() != m_pFrame->GetFollow()->FindFootnoteBossFrame())
{
@@ -194,6 +239,14 @@ bool SwTextFrameBreak::IsBreakNow( SwTextMargin &rLine )
bool bFirstLine = 1 == rLine.GetLineNr() && !rLine.GetPrev();
m_bBreak = true;
+
+ if (bFirstLine && m_pFrame->IsEmptyWithSplitFly())
+ {
+ // Not really the first line, visually we may have a previous line (including the fly
+ // frame) already.
+ bFirstLine = false;
+ }
+
if( ( bFirstLine && m_pFrame->GetIndPrev() )
|| ( rLine.GetLineNr() <= rLine.GetDropLines() ) )
{
@@ -267,7 +320,17 @@ WidowsAndOrphans::WidowsAndOrphans( SwTextFrame *pNewFrame, const SwTwips nRst,
bool bResetFlags = false;
- if ( m_pFrame->IsInTab() )
+ bool bWordTableCell = false;
+ if (m_pFrame->IsInFly())
+ {
+ // Enable widow / orphan control in Word-style table cells in split rows, at least inside
+ // flys.
+ const SwDoc& rDoc = m_pFrame->GetTextNodeForParaProps()->GetDoc();
+ const IDocumentSettingAccess& rIDSA = rDoc.getIDocumentSettingAccess();
+ bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+ }
+
+ if ( m_pFrame->IsInTab() && !bWordTableCell )
{
// For compatibility reasons, we disable Keep/Widows/Orphans
// inside splittable row frames:
@@ -397,15 +460,80 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
const SwTwips nChg = aRectFnSet.YDiff( nTmpY, nDocPrtTop + nOldHeight );
- // below the Widows-threshold...
- if( rLine.GetLineNr() >= m_nWidLines )
+ // hyphenation-keep: truncate a hyphenated line at the end of
+ // the column, page or spread (but not more)
+ int nExtraWidLines = 0;
+ if( rLine.GetLineNr() >= m_nWidLines && pMaster->HasPara() &&
+ ( rLine.GetLineNr() == m_nWidLines || !rLine.GetCurr()->IsEndHyph() ) )
+ {
+ SwParaPortion *pMasterPara = pMaster->GetPara();
+ const SwAttrSet& rSet = pFrame->GetTextNodeForParaProps()->GetSwAttrSet();
+ const SvxHyphenZoneItem &rAttr = rSet.GetHyphenZone();
+
+ bool bKeep = rAttr.IsHyphen() && rAttr.IsKeep() && rAttr.GetKeepType();
+
+ // if PAGE or SPREAD, allow hyphenation at bottom of the non-last columns
+ if( bKeep && pFrame->IsInSct() && (
+ rAttr.GetKeepType() == css::text::ParagraphHyphenationKeepType::SPREAD ||
+ rAttr.GetKeepType() == css::text::ParagraphHyphenationKeepType::PAGE ) )
+ {
+ const SwSectionFrame* const pSct = pFrame->FindSctFrame();
+ // multi-column section
+ if ( pSct->Lower()->IsColumnFrame() && pSct->Lower()->GetNext() )
+ {
+ const SwFrame *pCol = pFrame->FindColFrame();
+ // and not in the last column
+ if (pCol && !pCol->GetNext())
+ {
+ bKeep = false;
+ }
+ }
+ }
+
+ // if SPREAD, allow hyphenation at bottom of left pages
+ if ( bKeep && rAttr.GetKeepType() == css::text::ParagraphHyphenationKeepType::SPREAD &&
+ pFrame->FindPageFrame()->OnRightPage() )
+ {
+ bKeep = false;
+ }
+
+ if ( bKeep && pMasterPara && pMasterPara->GetNext() )
+ {
+ SwLineLayout * pNext = pMasterPara->GetNext();
+ SwLineLayout * pCurr = pNext;
+ SwLineLayout * pPrev = pNext;
+ while ( pNext->GetNext() )
+ {
+ pPrev = pCurr;
+ pCurr = pNext;
+ pNext = pNext->GetNext();
+ }
+ // hyphenated line, but not the last remaining one
+ if ( pNext->IsEndHyph() && !pNext->IsLastHyph() )
+ {
+ nExtraWidLines = rLine.GetLineNr() - m_nWidLines + 1;
+ // set remaining line to "last remaining hyphenated line"
+ // to avoid truncating multiple hyphenated lines instead
+ // of a single one
+ if ( pCurr->IsEndHyph() )
+ pCurr->SetLastHyph( true );
+ // also unset the line before the remaining one
+ // TODO: check also the line after the truncated line?
+ if ( pPrev->IsLastHyph() )
+ pPrev->SetLastHyph( false );
+ }
+ }
+ }
+
+ // below the Widows-threshold..., with an extra hyphenated line
+ if( rLine.GetLineNr() >= m_nWidLines + nExtraWidLines )
{
// Follow to Master I
// If the Follow *grows*, there is the chance for the Master to
// receive lines, that it was forced to hand over to the Follow lately:
// Prepare(Need); check that below nChg!
// (0W, 2O, 2M, 2F) + 1F = 3M, 2F
- if( rLine.GetLineNr() > m_nWidLines && pFrame->IsJustWidow() )
+ if( rLine.GetLineNr() > m_nWidLines + nExtraWidLines && pFrame->IsJustWidow() )
{
// If the Master is locked, it has probably just donated a line
// to us, we don't return that just because we turned it into
@@ -415,7 +543,7 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
const SwTwips nTmpRstHeight = aRectFnSet.BottomDist( pMaster->getFrameArea(),
aRectFnSet.GetPrtBottom(*pMaster->GetUpper()) );
if ( nTmpRstHeight >=
- SwTwips(rLine.GetInfo().GetParaPortion()->Height() ) )
+ rLine.GetInfo().GetParaPortion()->Height() )
{
pMaster->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
pMaster->InvalidateSize_();
@@ -437,7 +565,7 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
{
SwTwips nTmpRstHeight = aRectFnSet.BottomDist( pMaster->getFrameArea(),
aRectFnSet.GetPrtBottom(*pMaster->GetUpper()) );
- if( nTmpRstHeight >= SwTwips(rLine.GetInfo().GetParaPortion()->Height() ) )
+ if( nTmpRstHeight >= rLine.GetInfo().GetParaPortion()->Height() )
{
pMaster->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
pMaster->InvalidateSize_();
@@ -461,14 +589,14 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
// could result in multiple lines for us.
// Therefore, the CalcFollow() remains in control until the Follow got all
// necessary lines.
- sal_uInt16 nNeed = 1; // was: nWidLines - rLine.GetLineNr();
+ sal_Int32 nNeed = 1; // was: nWidLines - rLine.GetLineNr();
// Special case: Master cannot give lines to follow
// i#91421
if ( !pMaster->GetIndPrev() )
{
pMaster->ChgThisLines();
- sal_uLong nLines = pMaster->GetThisLines();
+ sal_Int32 nLines = pMaster->GetThisLines();
if(nLines == 0 && pMaster->HasPara())
{
const SwParaPortion *pMasterPara = pMaster->GetPara();
@@ -477,20 +605,68 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
}
if( nLines <= nNeed )
return false;
+
+ if (pFrame->IsInTab())
+ {
+ const SwFrame* pRow = pFrame;
+ while (pRow && !pRow->IsRowFrame())
+ {
+ pRow = pRow->GetUpper();
+ }
+
+ if (pRow && pRow->HasFixSize())
+ {
+ // This is a follow frame and our side is fixed.
+ const SwAttrSet& rSet = pFrame->GetTextNodeForParaProps()->GetSwAttrSet();
+ const SvxOrphansItem& rOrph = rSet.GetOrphans();
+ if (nLines <= static_cast<sal_Int32>(rOrph.GetValue()))
+ {
+ // If the master gives us a line as part of widow control, then its orphan
+ // control will move everything to the follow, which is worse than having no
+ // widow / orphan control at all. Don't send a Widows prepare hint, in this
+ // case.
+ return true;
+ }
+ }
+ }
}
pMaster->Prepare( PrepareHint::Widows, static_cast<void*>(&nNeed) );
return true;
}
-bool WidowsAndOrphans::WouldFit( SwTextMargin &rLine, SwTwips &rMaxHeight, bool bTst )
+namespace sw {
+
+auto FindNonFlyPortion(SwLineLayout const& rLine) -> bool
+{
+ for (SwLinePortion const* pPortion = rLine.GetFirstPortion();
+ pPortion; pPortion = pPortion->GetNextPortion())
+ {
+ switch (pPortion->GetWhichPor())
+ {
+ case PortionType::Fly:
+ case PortionType::Glue:
+ case PortionType::Margin:
+ break;
+ default:
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+};
+
+} // namespace sw
+
+bool WidowsAndOrphans::WouldFit( SwTextMargin &rLine, SwTwips &rMaxHeight, bool bTst, bool bMoveBwd )
{
// Here it does not matter, if pFrame is swapped or not.
// IsInside() takes care of itself
// We expect that rLine is set to the last line
OSL_ENSURE( !rLine.GetNext(), "WouldFit: aLine::Bottom missed!" );
- sal_uInt16 nLineCnt = rLine.GetLineNr();
+ sal_Int32 nLineCnt = rLine.GetLineNr();
// First satisfy the Orphans-rule and the wish for initials ...
const sal_uInt16 nMinLines = std::max( GetOrphansLines(), rLine.GetDropLines() );
@@ -500,11 +676,26 @@ bool WidowsAndOrphans::WouldFit( SwTextMargin &rLine, SwTwips &rMaxHeight, bool
rLine.Top();
SwTwips nLineSum = rLine.GetLineHeight();
- while( nMinLines > rLine.GetLineNr() )
+ // tdf#146500 for MoveBwd(), want at least 1 line with non-fly
+ bool hasNonFly(!bMoveBwd);
+ if (!hasNonFly)
+ {
+ hasNonFly = ::sw::FindNonFlyPortion(*rLine.GetCurr());
+ }
+ while (nMinLines > rLine.GetLineNr() || !hasNonFly)
{
if( !rLine.NextLine() )
- return false;
+ {
+ if (nMinLines > rLine.GetLineNr())
+ return false;
+ else
+ break;
+ }
nLineSum += rLine.GetLineHeight();
+ if (!hasNonFly)
+ {
+ hasNonFly = ::sw::FindNonFlyPortion(*rLine.GetCurr());
+ }
}
// We do not fit
diff --git a/sw/source/core/text/widorp.hxx b/sw/source/core/text/widorp.hxx
index c24bd5273f52..996a7fc913bc 100644
--- a/sw/source/core/text/widorp.hxx
+++ b/sw/source/core/text/widorp.hxx
@@ -18,11 +18,11 @@
*/
#pragma once
-class SwTextFrame;
-
#include <swtypes.hxx>
#include "itrtxt.hxx"
+class SwTextFrame;
+
class SwTextFrameBreak
{
private:
@@ -63,7 +63,7 @@ public:
void ClrOrphLines(){ m_nOrphLines = 0; }
bool FindBreak( SwTextFrame *pFrame, SwTextMargin &rLine, bool bHasToFit );
- bool WouldFit( SwTextMargin &rLine, SwTwips &rMaxHeight, bool bTest );
+ bool WouldFit( SwTextMargin &rLine, SwTwips &rMaxHeight, bool bTest, bool bMoveBwd );
// i#16128 - This method is named this way to avoid confusion with
// base class method <SwTextFrameBreak::IsBreakNow>, which isn't virtual.
bool IsBreakNowWidAndOrp( SwTextMargin &rLine )
@@ -79,4 +79,10 @@ public:
}
};
+namespace sw {
+
+auto FindNonFlyPortion(SwLineLayout const& rLine) -> bool;
+
+} // namespace sw
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/wrong.cxx b/sw/source/core/text/wrong.cxx
index 5a70c7be9507..00be8d5fac4f 100644
--- a/sw/source/core/text/wrong.cxx
+++ b/sw/source/core/text/wrong.cxx
@@ -22,25 +22,26 @@
#include <SwGrammarMarkUp.hxx>
#include <ndtxt.hxx>
#include <txtfrm.hxx>
+#include <utility>
#include <osl/diagnose.h>
-SwWrongArea::SwWrongArea( const OUString& rType, WrongListType listType,
+SwWrongArea::SwWrongArea( OUString aType, WrongListType listType,
css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag,
sal_Int32 nPos,
sal_Int32 nLen)
-: maType(rType), mnPos(nPos), mnLen(nLen), mpSubList(nullptr)
+: maType(std::move(aType)), mxPropertyBag(xPropertyBag), mnPos(nPos), mnLen(nLen), mpSubList(nullptr)
{
mColor = getWrongAreaColor(listType, xPropertyBag);
mLineType = getWrongAreaLineType(listType, xPropertyBag);
}
-SwWrongArea::SwWrongArea( const OUString& rType,
+SwWrongArea::SwWrongArea( OUString aType,
css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag,
sal_Int32 nPos,
sal_Int32 nLen,
SwWrongList* pSubList)
-: maType(rType), mnPos(nPos), mnLen(nLen), mpSubList(pSubList), mLineType(WRONGAREA_NONE)
+: maType(std::move(aType)), mxPropertyBag(xPropertyBag), mnPos(nPos), mnLen(nLen), mpSubList(pSubList), mLineType(WRONGAREA_NONE)
{
if (pSubList != nullptr)
{
@@ -203,7 +204,7 @@ sal_uInt16 SwWrongList::GetWrongPos( sal_Int32 nValue ) const
return (rST.mnPos <= nValue && nValue < rST.mnPos + rST.mnLen)
|| (rST.mnPos > nValue);
});
- return static_cast<sal_uInt16>(std::distance(maList.begin(), aIter));
+ return o3tl::narrowing<sal_uInt16>(std::distance(maList.begin(), aIter));
}
--nMax;
@@ -442,9 +443,9 @@ bool SwWrongList::InvalidateWrong( )
return false;
}
-SwWrongList* SwWrongList::SplitList( sal_Int32 nSplitPos )
+std::unique_ptr<SwWrongList> SwWrongList::SplitList( sal_Int32 nSplitPos )
{
- SwWrongList *pRet = nullptr;
+ std::unique_ptr<SwWrongList> pRet;
sal_uInt16 nLst = 0;
while( nLst < Count() && Pos( nLst ) < nSplitPos )
++nLst;
@@ -462,9 +463,9 @@ SwWrongList* SwWrongList::SplitList( sal_Int32 nSplitPos )
if( nLst )
{
if( WRONGLIST_GRAMMAR == GetWrongListType() )
- pRet = new SwGrammarMarkUp();
+ pRet.reset(new SwGrammarMarkUp());
else
- pRet = new SwWrongList( GetWrongListType() );
+ pRet.reset(new SwWrongList( GetWrongListType() ));
pRet->Insert(0, maList.begin(), ( nLst >= maList.size() ? maList.end() : maList.begin() + nLst ) );
pRet->SetInvalid( GetBeginInv(), GetEndInv() );
pRet->Invalidate_( nSplitPos ? nSplitPos - 1 : nSplitPos, nSplitPos );
@@ -559,7 +560,7 @@ void SwWrongList::Remove(sal_uInt16 nIdx, sal_uInt16 nLen )
i1 += nIdx;
std::vector<SwWrongArea>::iterator i2 = i1;
- if ( nIdx + nLen >= static_cast<sal_uInt16>(maList.size()) )
+ if ( nIdx + nLen >= o3tl::narrowing<sal_uInt16>(maList.size()) )
i2 = maList.end(); // robust
else
i2 += nLen;
@@ -602,10 +603,10 @@ void SwWrongList::RemoveEntry( sal_Int32 nBegin, sal_Int32 nEnd ) {
return (rST.mnPos != nBegin) || ((rST.mnPos + rST.mnLen) != nEnd);
});
}
- auto nDel = static_cast<sal_uInt16>(std::distance(aDelIter, aIter));
+ auto nDel = o3tl::narrowing<sal_uInt16>(std::distance(aDelIter, aIter));
if( nDel )
{
- auto nDelPos = static_cast<sal_uInt16>(std::distance(maList.cbegin(), aDelIter));
+ auto nDelPos = o3tl::narrowing<sal_uInt16>(std::distance(maList.cbegin(), aDelIter));
Remove( nDelPos, nDel );
}
}
diff --git a/sw/source/core/text/xmldump.cxx b/sw/source/core/text/xmldump.cxx
index fd3d0ef4d62e..86d736a0c8b9 100644
--- a/sw/source/core/text/xmldump.cxx
+++ b/sw/source/core/text/xmldump.cxx
@@ -26,273 +26,134 @@
#include <libxml/xmlwriter.h>
#include <SwPortionHandler.hxx>
#include <view.hxx>
+#include <flyfrms.hxx>
#include <svx/svdobj.hxx>
-namespace {
+#include "porlay.hxx"
-class XmlPortionDumper:public SwPortionHandler
+const char* sw::PortionTypeToString(PortionType nType)
{
- private:
- xmlTextWriterPtr m_Writer;
- TextFrameIndex m_Ofs;
- const OUString& m_rText;
- OUString m_aLine;
-
- static const char* getTypeName(PortionType nType)
- {
- switch (nType)
- {
- case PortionType::NONE:
- return "PortionType::NONE";
- case PortionType::FlyCnt:
- return "PortionType::FlyCnt";
-
- case PortionType::Hole:
- return "PortionType::Hole";
- case PortionType::TempEnd:
- return "PortionType::TempEnd";
- case PortionType::Break:
- return "PortionType::Break";
- case PortionType::Kern:
- return "PortionType::Kern";
- case PortionType::Arrow:
- return "PortionType::Arrow";
- case PortionType::Multi:
- return "PortionType::Multi";
- case PortionType::HiddenText:
- return "PortionType::HiddenText";
- case PortionType::ControlChar:
- return "PortionType::ControlChar";
- case PortionType::Bookmark:
- return "PortionType::Bookmark";
-
- case PortionType::Text:
- return "PortionType::Text";
- case PortionType::Lay:
- return "PortionType::Lay";
- case PortionType::Para:
- return "PortionType::Para";
- case PortionType::Hanging:
- return "PortionType::Hanging";
-
- case PortionType::Drop:
- return "PortionType::Drop";
- case PortionType::Tox:
- return "PortionType::Tox";
- case PortionType::IsoTox:
- return "PortionType::IsoTox";
- case PortionType::Ref:
- return "PortionType::Ref";
- case PortionType::IsoRef:
- return "PortionType::IsoRef";
- case PortionType::Meta:
- return "PortionType::Meta";
- case PortionType::FieldMark:
- return "PortionType::FieldMark";
- case PortionType::FieldFormCheckbox:
- return "PortionType::FieldFormCheckbox";
- case PortionType::InputField:
- return "PortionType::InputField";
-
- case PortionType::Expand:
- return "PortionType::Expand";
- case PortionType::Blank:
- return "PortionType::Blank";
- case PortionType::PostIts:
- return "PortionType::PostIts";
-
- case PortionType::Hyphen:
- return "PortionType::Hyphen";
- case PortionType::HyphenStr:
- return "PortionType::HyphenStr";
- case PortionType::SoftHyphen:
- return "PortionType::SoftHyphen";
- case PortionType::SoftHyphenStr:
- return "PortionType::SoftHyphenStr";
- case PortionType::SoftHyphenComp:
- return "PortionType::SoftHyphenComp";
-
- case PortionType::Field:
- return "PortionType::Field";
- case PortionType::Hidden:
- return "PortionType::Hidden";
- case PortionType::QuoVadis:
- return "PortionType::QuoVadis";
- case PortionType::ErgoSum:
- return "PortionType::ErgoSum";
- case PortionType::Combined:
- return "PortionType::Combined";
- case PortionType::Footnote:
- return "PortionType::Footnote";
-
- case PortionType::FootnoteNum:
- return "PortionType::FootnoteNum";
- case PortionType::Number:
- return "PortionType::Number";
- case PortionType::Bullet:
- return "PortionType::Bullet";
- case PortionType::GrfNum:
- return "PortionType::GrfNum";
-
- case PortionType::Glue:
- return "PortionType::Glue";
-
- case PortionType::Margin:
- return "PortionType::Margin";
-
- case PortionType::Fix:
- return "PortionType::Fix";
- case PortionType::Fly:
- return "PortionType::Fly";
-
- case PortionType::Table:
- return "PortionType::Table";
-
- case PortionType::TabRight:
- return "PortionType::TabRight";
- case PortionType::TabCenter:
- return "PortionType::TabCenter";
- case PortionType::TabDecimal:
- return "PortionType::TabDecimal";
-
- case PortionType::TabLeft:
- return "PortionType::TabLeft";
- default:
- return "Unknown";
- }
- }
-
- public:
- explicit XmlPortionDumper(xmlTextWriterPtr some_writer, const OUString& rText)
- : m_Writer(some_writer)
- , m_Ofs(0)
- , m_rText(rText)
- {
- }
-
- /**
- @param nLength
- length of this portion in the model string
- @param rText
- text which is painted on-screen
- */
- virtual void Text( TextFrameIndex nLength,
- PortionType nType,
- sal_Int32 nHeight,
- sal_Int32 nWidth) override
- {
- (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("Text"));
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nLength"), "%i",
- static_cast<int>(static_cast<sal_Int32>(nLength)));
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nType"), "%s",
- getTypeName(nType));
- if (nHeight > 0)
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nHeight"), "%i",
- static_cast<int>(nHeight));
- if (nWidth > 0)
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nWidth"), "%i",
- static_cast<int>(nWidth));
- if (nLength > TextFrameIndex(0))
- (void)xmlTextWriterWriteAttribute(
- m_Writer, BAD_CAST("Portion"),
- BAD_CAST(m_rText.copy(sal_Int32(m_Ofs), sal_Int32(nLength)).toUtf8().getStr()));
-
- (void)xmlTextWriterEndElement(m_Writer);
- m_aLine += m_rText.subView(sal_Int32(m_Ofs), sal_Int32(nLength));
- m_Ofs += nLength;
- }
-
- /**
- @param nLength
- length of this portion in the model string
- @param rText
- text which is painted on-screen
- @param nType
- type of this portion
- @param nHeight
- font size of the painted text
- */
- virtual void Special( TextFrameIndex nLength,
- const OUString & rText,
- PortionType nType,
- sal_Int32 nHeight,
- sal_Int32 nWidth,
- const SwFont* pFont ) override
- {
- (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("Special"));
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nLength"), "%i",
- static_cast<int>(static_cast<sal_Int32>(nLength)));
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nType"), "%s",
- getTypeName(nType));
- OString sText8 = OUStringToOString( rText, RTL_TEXTENCODING_UTF8 );
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("rText"), "%s", sText8.getStr());
-
- if (nHeight > 0)
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nHeight"), "%i",
- static_cast<int>(nHeight));
-
- if (nWidth > 0)
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nWidth"), "%i",
- static_cast<int>(nWidth));
-
- if (pFont)
- pFont->dumpAsXml(m_Writer);
-
- (void)xmlTextWriterEndElement(m_Writer);
- m_aLine += rText;
- m_Ofs += nLength;
- }
-
- virtual void LineBreak( sal_Int32 nWidth ) override
- {
- (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("LineBreak"));
- if (nWidth > 0)
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nWidth"), "%i",
- static_cast<int>(nWidth));
- if (!m_aLine.isEmpty())
- {
- (void)xmlTextWriterWriteAttribute(m_Writer, BAD_CAST("Line"),
- BAD_CAST(m_aLine.toUtf8().getStr()));
- m_aLine.clear();
- }
- (void)xmlTextWriterEndElement(m_Writer);
- }
-
- /**
- * @param nLength
- * number of 'model string' characters to be skipped
- */
- virtual void Skip( TextFrameIndex nLength ) override
- {
- (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("Skip"));
- (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nLength"), "%i",
- static_cast<int>(static_cast<sal_Int32>(nLength)));
- (void)xmlTextWriterEndElement(m_Writer);
- m_Ofs += nLength;
- }
-
- virtual void Finish( ) override
+ switch (nType)
{
- (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("Finish"));
- (void)xmlTextWriterEndElement(m_Writer);
- }
-
-};
-
- xmlTextWriterPtr lcl_createDefaultWriter()
- {
- xmlTextWriterPtr writer = xmlNewTextWriterFilename( "layout.xml", 0 );
- xmlTextWriterSetIndent(writer,1);
- (void)xmlTextWriterSetIndentString(writer, BAD_CAST(" "));
- (void)xmlTextWriterStartDocument( writer, nullptr, nullptr, nullptr );
- return writer;
- }
-
- void lcl_freeWriter( xmlTextWriterPtr writer )
- {
- (void)xmlTextWriterEndDocument( writer );
- xmlFreeTextWriter( writer );
- }
+ case PortionType::NONE:
+ return "PortionType::NONE";
+ case PortionType::FlyCnt:
+ return "PortionType::FlyCnt";
+
+ case PortionType::Hole:
+ return "PortionType::Hole";
+ case PortionType::TempEnd:
+ return "PortionType::TempEnd";
+ case PortionType::Break:
+ return "PortionType::Break";
+ case PortionType::Kern:
+ return "PortionType::Kern";
+ case PortionType::Arrow:
+ return "PortionType::Arrow";
+ case PortionType::Multi:
+ return "PortionType::Multi";
+ case PortionType::HiddenText:
+ return "PortionType::HiddenText";
+ case PortionType::ControlChar:
+ return "PortionType::ControlChar";
+ case PortionType::Bookmark:
+ return "PortionType::Bookmark";
+
+ case PortionType::Text:
+ return "PortionType::Text";
+ case PortionType::Lay:
+ return "PortionType::Lay";
+ case PortionType::Para:
+ return "PortionType::Para";
+ case PortionType::Hanging:
+ return "PortionType::Hanging";
+
+ case PortionType::Drop:
+ return "PortionType::Drop";
+ case PortionType::Tox:
+ return "PortionType::Tox";
+ case PortionType::IsoTox:
+ return "PortionType::IsoTox";
+ case PortionType::Ref:
+ return "PortionType::Ref";
+ case PortionType::IsoRef:
+ return "PortionType::IsoRef";
+ case PortionType::Meta:
+ return "PortionType::Meta";
+ case PortionType::ContentControl:
+ return "PortionType::ContentControl";
+ case PortionType::FieldMark:
+ return "PortionType::FieldMark";
+ case PortionType::FieldFormCheckbox:
+ return "PortionType::FieldFormCheckbox";
+ case PortionType::InputField:
+ return "PortionType::InputField";
+
+ case PortionType::Expand:
+ return "PortionType::Expand";
+ case PortionType::Blank:
+ return "PortionType::Blank";
+ case PortionType::PostIts:
+ return "PortionType::PostIts";
+
+ case PortionType::Hyphen:
+ return "PortionType::Hyphen";
+ case PortionType::HyphenStr:
+ return "PortionType::HyphenStr";
+ case PortionType::SoftHyphen:
+ return "PortionType::SoftHyphen";
+ case PortionType::SoftHyphenStr:
+ return "PortionType::SoftHyphenStr";
+ case PortionType::SoftHyphenComp:
+ return "PortionType::SoftHyphenComp";
+
+ case PortionType::Field:
+ return "PortionType::Field";
+ case PortionType::Hidden:
+ return "PortionType::Hidden";
+ case PortionType::QuoVadis:
+ return "PortionType::QuoVadis";
+ case PortionType::ErgoSum:
+ return "PortionType::ErgoSum";
+ case PortionType::Combined:
+ return "PortionType::Combined";
+ case PortionType::Footnote:
+ return "PortionType::Footnote";
+
+ case PortionType::FootnoteNum:
+ return "PortionType::FootnoteNum";
+ case PortionType::Number:
+ return "PortionType::Number";
+ case PortionType::Bullet:
+ return "PortionType::Bullet";
+ case PortionType::GrfNum:
+ return "PortionType::GrfNum";
+
+ case PortionType::Glue:
+ return "PortionType::Glue";
+
+ case PortionType::Margin:
+ return "PortionType::Margin";
+
+ case PortionType::Fix:
+ return "PortionType::Fix";
+ case PortionType::Fly:
+ return "PortionType::Fly";
+
+ case PortionType::Tab:
+ return "PortionType::Tab";
+
+ case PortionType::TabRight:
+ return "PortionType::TabRight";
+ case PortionType::TabCenter:
+ return "PortionType::TabCenter";
+ case PortionType::TabDecimal:
+ return "PortionType::TabDecimal";
+
+ case PortionType::TabLeft:
+ return "PortionType::TabLeft";
+ }
+ return "Unknown";
}
void SwFrame::dumpTopMostAsXml(xmlTextWriterPtr writer) const
@@ -306,194 +167,6 @@ void SwFrame::dumpTopMostAsXml(xmlTextWriterPtr writer) const
pFrame->dumpAsXml(writer);
}
-void SwFrame::dumpAsXml( xmlTextWriterPtr writer ) const
-{
- bool bCreateWriter = ( nullptr == writer );
- if ( bCreateWriter )
- writer = lcl_createDefaultWriter();
-
- const char *name = nullptr;
-
- switch ( GetType( ) )
- {
- case SwFrameType::Root:
- name = "root";
- break;
- case SwFrameType::Page:
- name = "page";
- break;
- case SwFrameType::Column:
- name = "column";
- break;
- case SwFrameType::Header:
- name = "header";
- break;
- case SwFrameType::Footer:
- name = "footer";
- break;
- case SwFrameType::FtnCont:
- name = "ftncont";
- break;
- case SwFrameType::Ftn:
- name = "ftn";
- break;
- case SwFrameType::Body:
- name = "body";
- break;
- case SwFrameType::Fly:
- name = "fly";
- break;
- case SwFrameType::Section:
- name = "section";
- break;
- case SwFrameType::Tab:
- name = "tab";
- break;
- case SwFrameType::Row:
- name = "row";
- break;
- case SwFrameType::Cell:
- name = "cell";
- break;
- case SwFrameType::Txt:
- name = "txt";
- break;
- case SwFrameType::NoTxt:
- name = "notxt";
- break;
- default: break;
- }
-
- if ( name != nullptr )
- {
- (void)xmlTextWriterStartElement( writer, reinterpret_cast<const xmlChar *>(name) );
-
- dumpAsXmlAttributes( writer );
-
- if (IsRootFrame())
- {
- const SwRootFrame* pRootFrame = static_cast<const SwRootFrame*>(this);
- (void)xmlTextWriterStartElement(writer, BAD_CAST("sfxViewShells"));
- SwView* pView = static_cast<SwView*>(SfxViewShell::GetFirst(true, checkSfxViewShell<SwView>));
- while (pView)
- {
- if (pRootFrame->GetCurrShell()->GetSfxViewShell() && pView->GetObjectShell() == pRootFrame->GetCurrShell()->GetSfxViewShell()->GetObjectShell())
- pView->dumpAsXml(writer);
- pView = static_cast<SwView*>(SfxViewShell::GetNext(*pView, true, checkSfxViewShell<SwView>));
- }
- (void)xmlTextWriterEndElement(writer);
- }
-
- if (IsPageFrame())
- {
- const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>(this);
- (void)xmlTextWriterStartElement(writer, BAD_CAST("page_status"));
- (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyLayout"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidFlyLayout()).getStr()));
- (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyContent"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidFlyContent()).getStr()));
- (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyInCnt"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidFlyInCnt()).getStr()));
- (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidLayout"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidLayout()).getStr()));
- (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidContent"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidContent()).getStr()));
- (void)xmlTextWriterEndElement(writer);
- (void)xmlTextWriterStartElement(writer, BAD_CAST("page_info"));
- (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("phyNum"), "%d", pPageFrame->GetPhyPageNum());
- (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("virtNum"), "%d", pPageFrame->GetVirtPageNum());
- OUString aFormatName = pPageFrame->GetPageDesc()->GetName();
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("pageDesc"), "%s", BAD_CAST(OUStringToOString(aFormatName, RTL_TEXTENCODING_UTF8).getStr()));
- (void)xmlTextWriterEndElement(writer);
- if (auto const* pObjs = pPageFrame->GetSortedObjs())
- {
- (void)xmlTextWriterStartElement(writer, BAD_CAST("sorted_objs"));
- for (SwAnchoredObject const*const pObj : *pObjs)
- { // just print pointer, full details will be printed on its anchor frame
- // this nonsense is needed because of multiple inheritance
- if (SwFlyFrame const*const pFly = dynamic_cast<SwFlyFrame const*>(pObj))
- {
- (void)xmlTextWriterStartElement(writer, BAD_CAST("fly"));
- (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pFly);
- }
- else
- {
- (void)xmlTextWriterStartElement(writer, BAD_CAST(pObj->getElementName()));
- (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pObj);
- }
- (void)xmlTextWriterEndElement(writer);
- }
- (void)xmlTextWriterEndElement(writer);
- }
- }
-
- if (IsTextFrame())
- {
- const SwTextFrame *pTextFrame = static_cast<const SwTextFrame *>(this);
- sw::MergedPara const*const pMerged(pTextFrame->GetMergedPara());
- if (pMerged)
- {
- (void)xmlTextWriterStartElement( writer, BAD_CAST( "merged" ) );
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "paraPropsNodeIndex" ), "%" SAL_PRIuUINTPTR, pMerged->pParaPropsNode->GetIndex() );
- for (auto const& e : pMerged->extents)
- {
- (void)xmlTextWriterStartElement( writer, BAD_CAST( "extent" ) );
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIuUINTPTR, e.pNode->GetIndex() );
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "start" ), "%" SAL_PRIdINT32, e.nStart );
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "end" ), "%" SAL_PRIdINT32, e.nEnd );
- (void)xmlTextWriterEndElement( writer );
- }
- (void)xmlTextWriterEndElement( writer );
- }
- }
-
- if (IsCellFrame())
- {
- SwCellFrame const* pCellFrame(static_cast<SwCellFrame const*>(this));
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "rowspan" ), "%ld", pCellFrame->GetLayoutRowSpan() );
- }
-
- (void)xmlTextWriterStartElement( writer, BAD_CAST( "infos" ) );
- dumpInfosAsXml( writer );
- (void)xmlTextWriterEndElement( writer );
-
- // Dump Anchored objects if any
- const SwSortedObjs* pAnchored = GetDrawObjs();
- if ( pAnchored && pAnchored->size() > 0 )
- {
- (void)xmlTextWriterStartElement( writer, BAD_CAST( "anchored" ) );
-
- for (SwAnchoredObject* pObject : *pAnchored)
- {
- pObject->dumpAsXml( writer );
- }
-
- (void)xmlTextWriterEndElement( writer );
- }
-
- // Dump the children
- if ( IsTextFrame( ) )
- {
- const SwTextFrame *pTextFrame = static_cast<const SwTextFrame *>(this);
- OUString aText = pTextFrame->GetText( );
- for ( int i = 0; i < 32; i++ )
- {
- aText = aText.replace( i, '*' );
- }
- OString aText8 =OUStringToOString( aText,
- RTL_TEXTENCODING_UTF8 );
- (void)xmlTextWriterWriteString( writer,
- reinterpret_cast<const xmlChar *>(aText8.getStr( )) );
- XmlPortionDumper pdumper( writer, aText );
- pTextFrame->VisitPortions( pdumper );
-
- }
- else
- {
- dumpChildrenAsXml( writer );
- }
- (void)xmlTextWriterEndElement( writer );
- }
-
- if ( bCreateWriter )
- lcl_freeWriter( writer );
-}
-
void SwFrame::dumpInfosAsXml( xmlTextWriterPtr writer ) const
{
// output the Frame
@@ -511,10 +184,6 @@ void SwFrame::dumpInfosAsXml( xmlTextWriterPtr writer ) const
(void)xmlTextWriterEndElement( writer );
}
-// Hack: somehow conversion from "..." to va_list does
-// bomb on two string literals in the format.
-const char* const TMP_FORMAT = "%" SAL_PRIuUINTPTR;
-
void SwFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const
{
(void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "ptr" ), "%p", this );
@@ -528,120 +197,6 @@ void SwFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const
(void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "upper" ), "%" SAL_PRIuUINT32, GetUpper()->GetFrameId() );
if ( GetLower( ) )
(void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "lower" ), "%" SAL_PRIuUINT32, GetLower()->GetFrameId() );
- if (IsFootnoteFrame())
- {
- SwFootnoteFrame const*const pFF(static_cast<SwFootnoteFrame const*>(this));
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("ref"), "%" SAL_PRIuUINT32, pFF->GetRef()->GetFrameId() );
- if (pFF->GetMaster())
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("master"), "%" SAL_PRIuUINT32, pFF->GetMaster()->GetFrameId() );
- if (pFF->GetFollow())
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFF->GetFollow()->GetFrameId() );
- }
- if (IsSctFrame())
- {
- SwSectionFrame const*const pFrame(static_cast<SwSectionFrame const*>(this));
- SwSectionNode const*const pNode(pFrame->GetSection() ? pFrame->GetSection()->GetFormat()->GetSectionNode() : nullptr);
- (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("sectionNodeIndex"), TMP_FORMAT, pNode ? pNode->GetIndex() : -1);
- }
- if ( IsTextFrame( ) )
- {
- const SwTextFrame *pTextFrame = static_cast<const SwTextFrame *>(this);
- const SwTextNode *pTextNode = pTextFrame->GetTextNodeFirst();
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), TMP_FORMAT, pTextNode->GetIndex() );
-
- OString aMode = "Horizontal";
- if (IsVertLRBT())
- {
- aMode = "VertBTLR";
- }
- else if (IsVertLR())
- {
- aMode = "VertLR";
- }
- else if (IsVertical())
- {
- aMode = "Vertical";
- }
- (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("WritingMode"), BAD_CAST(aMode.getStr()));
- }
- if (IsHeaderFrame() || IsFooterFrame())
- {
- const SwHeadFootFrame *pHeadFootFrame = static_cast<const SwHeadFootFrame*>(this);
- OUString aFormatName = pHeadFootFrame->GetFormat()->GetName();
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "fmtName" ), "%s", BAD_CAST(OUStringToOString(aFormatName, RTL_TEXTENCODING_UTF8).getStr()));
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "fmtPtr" ), "%p", pHeadFootFrame->GetFormat());
- }
-}
-
-void SwFrame::dumpChildrenAsXml( xmlTextWriterPtr writer ) const
-{
- const SwFrame *pFrame = GetLower( );
- for ( ; pFrame != nullptr; pFrame = pFrame->GetNext( ) )
- {
- pFrame->dumpAsXml( writer );
- }
-}
-
-void SwAnchoredObject::dumpAsXml( xmlTextWriterPtr writer ) const
-{
- bool bCreateWriter = ( nullptr == writer );
- if ( bCreateWriter )
- writer = lcl_createDefaultWriter();
-
- (void)xmlTextWriterStartElement( writer, BAD_CAST( getElementName() ) );
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "ptr" ), "%p", this );
-
- (void)xmlTextWriterStartElement( writer, BAD_CAST( "bounds" ) );
- // don't call GetObjBoundRect(), it modifies the layout
- SwRect(GetDrawObj()->GetLastBoundRect()).dumpAsXmlAttributes(writer);
- (void)xmlTextWriterEndElement( writer );
-
- if (const SdrObject* pObject = GetDrawObj())
- pObject->dumpAsXml(writer);
-
- (void)xmlTextWriterEndElement( writer );
-
- if ( bCreateWriter )
- lcl_freeWriter( writer );
-}
-
-void SwFont::dumpAsXml(xmlTextWriterPtr writer) const
-{
- (void)xmlTextWriterStartElement(writer, BAD_CAST("SwFont"));
- (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", this);
- // do not use Color::AsRGBHexString() as that omits the transparency
- (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("color"), "%08" SAL_PRIxUINT32, sal_uInt32(GetColor()));
- (void)xmlTextWriterEndElement(writer);
-}
-
-void SwTextFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const
-{
- SwFrame::dumpAsXmlAttributes( writer );
- if ( HasFollow() )
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
-
- if (m_pPrecede != nullptr)
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwTextFrame*>(m_pPrecede)->GetFrameId() );
-}
-
-void SwSectionFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const
-{
- SwFrame::dumpAsXmlAttributes( writer );
- if ( HasFollow() )
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
-
- if (m_pPrecede != nullptr)
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwSectionFrame*>( m_pPrecede )->GetFrameId() );
-}
-
-void SwTabFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const
-{
- SwFrame::dumpAsXmlAttributes( writer );
- if ( HasFollow() )
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
-
- if (m_pPrecede != nullptr)
- (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwTabFrame*>( m_pPrecede )->GetFrameId() );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */