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.cxx1746
-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.cxx130
-rw-r--r--sw/source/core/text/frmcrsr.cxx95
-rw-r--r--sw/source/core/text/frmform.cxx624
-rw-r--r--sw/source/core/text/frminf.cxx11
-rw-r--r--sw/source/core/text/frmpaint.cxx204
-rw-r--r--sw/source/core/text/guess.cxx478
-rw-r--r--sw/source/core/text/guess.hxx28
-rw-r--r--sw/source/core/text/inftxt.cxx844
-rw-r--r--sw/source/core/text/inftxt.hxx172
-rw-r--r--sw/source/core/text/itradj.cxx334
-rw-r--r--sw/source/core/text/itratr.cxx479
-rw-r--r--sw/source/core/text/itratr.hxx16
-rw-r--r--sw/source/core/text/itrcrsr.cxx449
-rw-r--r--sw/source/core/text/itrform2.cxx866
-rw-r--r--sw/source/core/text/itrform2.hxx6
-rw-r--r--sw/source/core/text/itrpaint.cxx156
-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.hxx40
-rw-r--r--sw/source/core/text/noteurl.cxx37
-rw-r--r--sw/source/core/text/pordrop.hxx35
-rw-r--r--sw/source/core/text/porexp.cxx82
-rw-r--r--sw/source/core/text/porexp.hxx13
-rw-r--r--sw/source/core/text/porfld.cxx296
-rw-r--r--sw/source/core/text/porfld.hxx76
-rw-r--r--sw/source/core/text/porfly.cxx102
-rw-r--r--sw/source/core/text/porfly.hxx4
-rw-r--r--sw/source/core/text/porftn.hxx12
-rw-r--r--sw/source/core/text/porglue.cxx47
-rw-r--r--sw/source/core/text/porglue.hxx25
-rw-r--r--sw/source/core/text/porhyph.hxx7
-rw-r--r--sw/source/core/text/porlay.cxx1627
-rw-r--r--sw/source/core/text/porlay.hxx53
-rw-r--r--sw/source/core/text/porlin.cxx98
-rw-r--r--sw/source/core/text/porlin.hxx92
-rw-r--r--sw/source/core/text/pormulti.cxx429
-rw-r--r--sw/source/core/text/pormulti.hxx13
-rw-r--r--sw/source/core/text/porref.cxx4
-rw-r--r--sw/source/core/text/porref.hxx4
-rw-r--r--sw/source/core/text/porrst.cxx444
-rw-r--r--sw/source/core/text/porrst.hxx66
-rw-r--r--sw/source/core/text/portab.hxx26
-rw-r--r--sw/source/core/text/portox.cxx4
-rw-r--r--sw/source/core/text/portox.hxx4
-rw-r--r--sw/source/core/text/portxt.cxx462
-rw-r--r--sw/source/core/text/portxt.hxx24
-rw-r--r--sw/source/core/text/possiz.hxx54
-rw-r--r--sw/source/core/text/redlnitr.cxx351
-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.cxx91
-rw-r--r--sw/source/core/text/txtfld.cxx413
-rw-r--r--sw/source/core/text/txtfly.cxx402
-rw-r--r--sw/source/core/text/txtfrm.cxx1359
-rw-r--r--sw/source/core/text/txtftn.cxx141
-rw-r--r--sw/source/core/text/txthyph.cxx45
-rw-r--r--sw/source/core/text/txtinit.cxx2
-rw-r--r--sw/source/core/text/txtpaint.cxx7
-rw-r--r--sw/source/core/text/txtpaint.hxx15
-rw-r--r--sw/source/core/text/txttab.cxx166
-rw-r--r--sw/source/core/text/widorp.cxx318
-rw-r--r--sw/source/core/text/widorp.hxx18
-rw-r--r--sw/source/core/text/wrong.cxx27
-rw-r--r--sw/source/core/text/xmldump.cxx691
67 files changed, 9970 insertions, 5010 deletions
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx
index 088f21c00b10..62052080d479 100644
--- a/sw/source/core/text/EnhancedPDFExportHelper.cxx
+++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx
@@ -17,22 +17,25 @@
* 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>
#include <vcl/outdev.hxx>
#include <vcl/pdfextoutdevdata.hxx>
+#include <vcl/pdf/PDFNote.hxx>
#include <tools/multisel.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/lrspitem.hxx>
#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 +68,38 @@
#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 <PostItMgr.hxx>
+#include <AnnotationWin.hxx>
+#include <names.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 +130,65 @@ 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::vector< IdMapEntry > NoteIdMap;
+typedef std::map< const SwTable*, TableColumnsMapEntry > TableColumnsMap;
+typedef std::map< const SwTable*, sal_Int32 > TableCaptionsMap;
+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;
+ TableCaptionsMap m_TableCaptionsMap;
+ LinkIdMap m_LinkIdMap;
+ NoteIdMap m_NoteIdMap;
+ 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;
+constexpr OUString constTitleStyleName = u"Title"_ustr;
+constexpr OUString constEmphasisStyleName = u"Emphasis"_ustr;
+constexpr OUString constStrongEmphasisStyleName = u"Strong Emphasis"_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 +197,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,12 +216,13 @@ 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";
+constexpr OUStringLiteral aAnnotString = u"Annot";
// returns true if first paragraph in cell frame has 'table heading' style
bool lcl_IsHeadlineCell( const SwCellFrame& rCellFrame )
@@ -178,14 +235,101 @@ bool lcl_IsHeadlineCell( const SwCellFrame& rCellFrame )
SwTextNode const*const pTextNode = static_cast<const SwTextFrame*>(pCnt)->GetTextNodeForParaProps();
const SwFormat* pTextFormat = pTextNode->GetFormatColl();
- OUString sStyleName;
+ ProgName sStyleName;
SwStyleNameMapper::FillProgName( pTextFormat->GetName(), sStyleName, SwGetPoolIdFromName::TxtColl );
- bRet = sStyleName == aTableHeadingName;
+ bRet = sStyleName.toString() == 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);
+ TableStyleName const& rStyleName(pTable->GetTableStyleName());
+ if (!rStyleName.isEmpty())
+ {
+ if (SwTableAutoFormat const*const pTableAF =
+ pTable->GetFrameFormat()->GetDoc().GetTableStyles().FindAutoFormat(rStyleName))
+ {
+ bRet |= pTableAF->HasHeaderRow();
+ }
+ }
}
return bRet;
}
+// returns true if the frame is a caption
+bool lcl_IsCaptionFrame(const SwFrame& rFrame)
+{
+ if (!rFrame.IsTextFrame())
+ return false;
+
+ SwTextFrame const& rTextFrame(*static_cast<const SwTextFrame*>(&rFrame));
+ const SwTextNode* const pTextNd(rTextFrame.GetTextNodeForParaProps());
+ if (!pTextNd)
+ return false;
+
+ const SwFormat* pTextFormat = pTextNd->GetFormatColl();
+ const SwFormat* pParentTextFormat = pTextFormat ? pTextFormat->DerivedFrom() : nullptr;
+
+ ProgName sParentStyleName;
+ if (pParentTextFormat)
+ SwStyleNameMapper::FillProgName(pParentTextFormat->GetName(), sParentStyleName,
+ SwGetPoolIdFromName::TxtColl);
+
+ return sParentStyleName == aCaption;
+}
+
+const SwTabFrame* lcl_FindTableForCaption(const SwFrame& rFrame)
+{
+ const SwTabFrame* pTabFrame = nullptr;
+ bool bPrevFrame = false;
+
+ // It is possible to add multiple captions to a table,
+ // both above and below, either simultaneously or separately.
+ // Start by checking the next frame, and if we don't find a table frame
+ // or if the next frame is not a caption, we return to the current caption
+ // and perform the same operation backwards using the previous frames.
+ const SwFrame* pRetFrame = rFrame.GetNext();
+ if (!pRetFrame)
+ {
+ bPrevFrame = true;
+ pRetFrame = rFrame.GetPrev();
+ }
+
+ while (pRetFrame)
+ {
+ if (pRetFrame->IsTabFrame())
+ {
+ pTabFrame = static_cast<const SwTabFrame*>(pRetFrame);
+ break;
+ }
+
+ // Check if the next or the previous frame is a caption frame
+ bool bIsCaption = lcl_IsCaptionFrame(*pRetFrame);
+ if (bIsCaption && pRetFrame->GetNext())
+ {
+ pRetFrame = !bPrevFrame ? pRetFrame->GetNext() : pRetFrame->GetPrev();
+ }
+ else if (!bPrevFrame && rFrame.GetPrev())
+ {
+ // If no table was found while checking the GetNext() frames,
+ // jump back to the current caption and
+ // start checking the GetPrev() frames.
+ bPrevFrame = true;
+ pRetFrame = rFrame.GetPrev();
+ }
+ else
+ // This part handles the case
+ // when the table has been deleted,
+ // but the caption has not.
+ break;
+ }
+
+ return pTabFrame;
+}
+
// List all frames for which the NonStructElement tag is set:
bool lcl_IsInNonStructEnv( const SwFrame& rFrame )
{
@@ -208,25 +352,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 +420,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 +428,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,
@@ -309,7 +491,7 @@ SwTaggedPDFHelper::SwTaggedPDFHelper( const Num_Info* pNumInfo,
else if ( mpPorInfo )
BeginInlineStructureElements();
else
- BeginTag( vcl::PDFWriter::NonStructElement, OUString() );
+ BeginTag( vcl::pdf::StructElement::NonStructElement, OUString() );
#if OSL_DEBUG_LEVEL > 1
nCurrentStruct = mpPDFExtOutDevData->GetCurrentStructureElement();
@@ -336,10 +518,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 +544,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 +565,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.contains(pKey)
+ || 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 +597,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 +605,63 @@ 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::pdf::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::pdf::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() ) ||
+ rFrame.IsTabFrame() )
+ {
+ pKey = lcl_GetKeyFromFrame(rFrame);
+
+ if (pKey)
+ {
+ FrameTagSet& rFrameTagSet(mpPDFExtOutDevData->GetSwPDFState()->m_FrameTagSet);
+ assert(!rFrameTagSet.contains(pKey));
+ 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 +672,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 )
+ if (vcl::pdf::StructElement::List == eType)
{
- NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap();
+ NumListIdMap& rNumListIdMap(mpPDFExtOutDevData->GetSwPDFState()->m_NumListIdMap);
rNumListIdMap[ pNodeNum ] = nId;
}
- else if ( vcl::PDFWriter::LIBody == eType )
+ else if (vcl::pdf::StructElement::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,8 +700,25 @@ 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 )
+void SwTaggedPDFHelper::SetAttributes(vcl::pdf::StructElement eType)
{
sal_Int32 nVal;
@@ -510,16 +743,21 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
bool bHeight = false;
bool bBox = false;
bool bRowSpan = false;
+ bool bAltText = false;
// Check which attributes to set:
- switch ( eType )
+ switch (eType)
{
- case vcl::PDFWriter::Document :
+ case vcl::pdf::StructElement::Document:
bWritingMode = true;
break;
- case vcl::PDFWriter::Table :
+ case vcl::pdf::StructElement::Note:
+ bPlacement = true;
+ break;
+
+ case vcl::pdf::StructElement::Table:
bPlacement =
bWritingMode =
bSpaceBefore =
@@ -531,13 +769,15 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
bBox = true;
break;
- case vcl::PDFWriter::TableRow :
+ case vcl::pdf::StructElement::TableRow:
bPlacement =
bWritingMode = true;
break;
- case vcl::PDFWriter::TableHeader :
- case vcl::PDFWriter::TableData :
+ case vcl::pdf::StructElement::TableHeader:
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Scope, vcl::PDFWriter::Column);
+ [[fallthrough]];
+ case vcl::pdf::StructElement::TableData:
bPlacement =
bWritingMode =
bWidth =
@@ -545,17 +785,22 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
bRowSpan = true;
break;
- case vcl::PDFWriter::H1 :
- case vcl::PDFWriter::H2 :
- case vcl::PDFWriter::H3 :
- case vcl::PDFWriter::H4 :
- case vcl::PDFWriter::H5 :
- case vcl::PDFWriter::H6 :
- case vcl::PDFWriter::Paragraph :
- case vcl::PDFWriter::Heading :
- case vcl::PDFWriter::Caption :
- case vcl::PDFWriter::BlockQuote :
-
+ case vcl::pdf::StructElement::Caption:
+ if (pFrame->IsSctFrame())
+ {
+ break;
+ }
+ [[fallthrough]];
+ case vcl::pdf::StructElement::H1:
+ case vcl::pdf::StructElement::H2:
+ case vcl::pdf::StructElement::H3:
+ case vcl::pdf::StructElement::H4:
+ case vcl::pdf::StructElement::H5:
+ case vcl::pdf::StructElement::H6:
+ case vcl::pdf::StructElement::Paragraph:
+ case vcl::pdf::StructElement::Heading:
+ case vcl::pdf::StructElement::BlockQuote:
+ case vcl::pdf::StructElement::Title:
bPlacement =
bWritingMode =
bSpaceBefore =
@@ -566,13 +811,35 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
bTextAlign = true;
break;
- case vcl::PDFWriter::Formula :
- case vcl::PDFWriter::Figure :
+ case vcl::pdf::StructElement::Formula:
+ case vcl::pdf::StructElement::Figure:
+ bAltText =
bPlacement =
bWidth =
bHeight =
bBox = true;
break;
+
+ case vcl::pdf::StructElement::Division:
+ if (pFrame->IsFlyFrame()) // this can be something else too
+ {
+ bAltText = true;
+ bBox = true;
+ }
+ break;
+
+ case vcl::pdf::StructElement::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;
}
@@ -581,10 +848,20 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
if ( bPlacement )
{
- eVal = vcl::PDFWriter::TableHeader == eType ||
- vcl::PDFWriter::TableData == eType ?
- vcl::PDFWriter::Inline :
- vcl::PDFWriter::Block;
+ bool bIsFigureInline = false;
+ if (vcl::pdf::StructElement::Figure == eType)
+ {
+ const SwFrame* pKeyFrame = static_cast<const SwFlyFrame&>(*pFrame).GetAnchorFrame();
+ if (const SwLayoutFrame* pUpperFrame = pKeyFrame->GetUpper())
+ if (pUpperFrame->GetType() == SwFrameType::Body)
+ bIsFigureInline = true;
+ }
+
+ eVal = vcl::pdf::StructElement::TableHeader == eType
+ || vcl::pdf::StructElement::TableData == eType
+ || bIsFigureInline
+ ? vcl::PDFWriter::Inline
+ : vcl::PDFWriter::Block;
mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::Placement, eVal );
}
@@ -632,9 +909,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.ResolveTextFirstLineOffset({});
if ( 0 != nVal )
mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::TextIndent, nVal );
}
@@ -658,9 +935,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() : u" - "_ustr);
+ OUString const altText(rFly.GetObjTitle() + sep + rFly.GetObjDescription());
+ if (!altText.isEmpty())
+ {
+ mpPDFExtOutDevData->SetAlternateText(altText);
+ }
+ }
if ( bWidth )
{
@@ -677,10 +970,10 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
if ( bBox )
{
// BBox only for non-split tables:
- if ( vcl::PDFWriter::Table != eType ||
+ if (vcl::pdf::StructElement::Table != eType ||
( pFrame->IsTabFrame() &&
!static_cast<const SwTabFrame*>(pFrame)->IsFollow() &&
- !static_cast<const SwTabFrame*>(pFrame)->HasFollow() ) )
+ !static_cast<const SwTabFrame*>(pFrame)->HasFollow() ))
{
mpPDFExtOutDevData->SetStructureBoundingBox(pFrame->getFrameArea().SVRect());
}
@@ -688,9 +981,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 +994,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 +1010,12 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
}
}
}
+
+ if (mpFrameInfo->m_isLink)
+ {
+ SwRect const aRect(mpFrameInfo->mrFrame.getFrameArea());
+ LinkLinkLink(*mpPDFExtOutDevData, aRect);
+ }
}
/*
@@ -731,15 +1030,16 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
bool bBaselineShift = false;
bool bTextDecorationType = false;
bool bLinkAttribute = false;
+ bool bAnnotAttribute = false;
bool bLanguage = false;
// Check which attributes to set:
- switch ( eType )
+ switch (eType)
{
- case vcl::PDFWriter::Span :
- case vcl::PDFWriter::Quote :
- case vcl::PDFWriter::Code :
+ case vcl::pdf::StructElement::Span:
+ case vcl::pdf::StructElement::Quote:
+ case vcl::pdf::StructElement::Code:
if( PortionType::HyphenStr == pPor->GetWhichPor() || PortionType::SoftHyphenStr == pPor->GetWhichPor() ||
PortionType::Hyphen == pPor->GetWhichPor() || PortionType::SoftHyphen == pPor->GetWhichPor() )
bActualText = true;
@@ -751,13 +1051,64 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
}
break;
- case vcl::PDFWriter::Link :
+ case vcl::pdf::StructElement::Link:
+ case vcl::pdf::StructElement::BibEntry:
bTextDecorationType =
bBaselineShift =
bLinkAttribute =
bLanguage = true;
break;
+ case vcl::pdf::StructElement::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;
+
+ case vcl::pdf::StructElement::Annot:
+ bAnnotAttribute =
+ bLanguage = true;
+ break;
+
default:
break;
}
@@ -802,7 +1153,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,19 +1161,73 @@ 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);
+ }
+
+ if (bAnnotAttribute)
+ {
+ SwRect aPorRect;
+ rInf.CalcRect(*pPor, &aPorRect);
+ const NoteIdMap& rNoteIdMap(mpPDFExtOutDevData->GetSwPDFState()->m_NoteIdMap);
+ const Point aCenter = aPorRect.Center();
+ auto aIter = std::find_if(rNoteIdMap.begin(), rNoteIdMap.end(),
+ [&aCenter](const IdMapEntry& rEntry)
+ { return rEntry.first.Contains(aCenter); });
+ if (aIter != rNoteIdMap.end())
{
- sal_Int32 nLinkId = (*aIter).second;
- mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::LinkAnnotation, nLinkId );
+ sal_Int32 nNoteId = (*aIter).second;
+ mpPDFExtOutDevData->SetStructureAttributeNumerical(vcl::PDFWriter::NoteAnnotation,
+ nNoteId);
}
}
}
+ else if (mpNumInfo && eType == vcl::pdf::StructElement::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())
+ {
+ 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);
+ }
}
void SwTaggedPDFHelper::BeginNumberedListStructureElements()
@@ -832,14 +1237,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 +1285,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 +1325,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 +1356,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();
}
@@ -953,12 +1367,17 @@ void SwTaggedPDFHelper::BeginNumberedListStructureElements()
const bool bNewItemTag = bNewListTag || pTextNd->IsCountedInList(); // If the text node is not counted, we do not start a new list item:
if ( bNewListTag )
- BeginTag( vcl::PDFWriter::List, aListString );
+ BeginTag(vcl::pdf::StructElement::List, aListString);
if ( bNewItemTag )
{
- BeginTag( vcl::PDFWriter::ListItem, aListItemString );
- BeginTag( vcl::PDFWriter::LIBody, aListBodyString );
+ BeginTag(vcl::pdf::StructElement::ListItem, aListItemString);
+ assert(rTextFrame.GetPara());
+ // check whether to open LBody now or delay until after Lbl
+ if (!rTextFrame.GetPara()->HasNumberingPortion(SwParaPortion::OnlyNumbering))
+ {
+ BeginTag(vcl::pdf::StructElement::LIBody, aListBodyString);
+ }
}
}
@@ -968,7 +1387,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.
@@ -989,7 +1408,7 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
// Document: Document
- nPDFType = vcl::PDFWriter::Document;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Document);
aPDFType = aDocumentString;
break;
@@ -998,24 +1417,24 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
// Header, Footer: NonStructElement
- nPDFType = vcl::PDFWriter::NonStructElement;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::NonStructElement);
break;
- case SwFrameType::FtnCont :
+ case SwFrameType::FootnoteContainer:
// Footnote container: Division
- nPDFType = vcl::PDFWriter::Division;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Division);
aPDFType = aDivString;
break;
- case SwFrameType::Ftn :
+ case SwFrameType::Footnote:
// Footnote frame: Note
// Note: vcl::PDFWriter::Note is actually a ILSE. Nevertheless
// we treat it like a grouping element!
- nPDFType = vcl::PDFWriter::Note;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Note);
aPDFType = aNoteString;
break;
@@ -1026,26 +1445,54 @@ 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.contains(pSection))
+ {
+ // 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 = sal_uInt16(vcl::pdf::StructElement::Caption);
+ aPDFType = aCaptionString;
+ }
+ else if (SectionType::ToxContent == pSection->GetType())
{
const SwTOXBase* pTOXBase = pSection->GetTOXBase();
if ( pTOXBase )
{
if ( TOX_INDEX == pTOXBase->GetType() )
{
- nPDFType = vcl::PDFWriter::Index;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Index);
aPDFType = aIndexString;
}
else
{
- nPDFType = vcl::PDFWriter::TOC;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::TOC);
aPDFType = aTOCString;
}
}
}
else if ( SectionType::Content == pSection->GetType() )
{
- nPDFType = vcl::PDFWriter::Section;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Section);
aPDFType = aSectString;
}
}
@@ -1057,14 +1504,24 @@ 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::pdf::StructElement::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;
- OUString sStyleName;
- OUString sParentStyleName;
+ ProgName sStyleName;
+ ProgName sParentStyleName;
if ( pTextFormat)
SwStyleNameMapper::FillProgName( pTextFormat->GetName(), sStyleName, SwGetPoolIdFromName::TxtColl );
@@ -1074,14 +1531,22 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
// This is the default. If the paragraph could not be mapped to
// any of the standard pdf tags, we write a user defined tag
// <stylename> with role = P
- nPDFType = vcl::PDFWriter::Paragraph;
- aPDFType = sStyleName;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Paragraph);
+ aPDFType = sStyleName.toString();
+
+ // Title
+
+ if (sStyleName == constTitleStyleName)
+ {
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Title);
+ aPDFType = constTitleStyleName;
+ }
// Quotations: BlockQuote
if (sStyleName == aQuotations)
{
- nPDFType = vcl::PDFWriter::BlockQuote;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::BlockQuote);
aPDFType = aBlockQuoteString;
}
@@ -1089,7 +1554,7 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
else if (sStyleName == aCaption)
{
- nPDFType = vcl::PDFWriter::Caption;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Caption);
aPDFType = aCaptionString;
}
@@ -1097,27 +1562,63 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
else if (sParentStyleName == aCaption)
{
- nPDFType = vcl::PDFWriter::Caption;
- aPDFType = sStyleName + aCaptionString;
+ OUString sTableCaption = sStyleName.toString() + aCaptionString;
+
+ if (!pFrame->IsInFly()) // Table caption
+ {
+ TableCaptionsMap& rTableCaptionsMap(
+ mpPDFExtOutDevData->GetSwPDFState()->m_TableCaptionsMap);
+
+ const SwTabFrame* pTabFrame = lcl_FindTableForCaption(*pFrame);
+ if (pTabFrame)
+ {
+ const SwTable* pTable = pTabFrame->GetTable();
+ if (rTableCaptionsMap.contains(pTable))
+ {
+ // Reopen Caption tag:
+ // - if the table has an above and below caption
+ // - if the table has multiple above or below captions
+ m_nRestoreCurrentTag
+ = mpPDFExtOutDevData->GetCurrentStructureElement();
+
+ sal_Int32 const nCaptionId = rTableCaptionsMap[pTable];
+ mpPDFExtOutDevData->SetCurrentStructureElement(nCaptionId);
+ }
+ else
+ {
+ OpenTagImpl(pTable);
+
+ // Open Caption tag
+ sal_Int32 const nId = BeginTagImpl(
+ nullptr, vcl::pdf::StructElement::Caption, sTableCaption);
+
+ rTableCaptionsMap[pTable] = nId;
+ }
+ }
+ aPDFType = "Standard";
+ }
+ else // Figure caption
+ {
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Caption);
+ aPDFType = sTableCaption;
+ }
}
// Heading: H
else if (sStyleName == aHeading)
{
- nPDFType = vcl::PDFWriter::Heading;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Heading);
aPDFType = aHString;
}
// 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 +1636,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>(sal_uInt16(vcl::pdf::StructElement::H1) + nRealLevel);
}
// Section: TOCI
@@ -1154,7 +1676,7 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
if ( pTOXBase && TOX_INDEX != pTOXBase->GetType() )
{
// Special case: Open additional TOCI tag:
- BeginTag( vcl::PDFWriter::TOCI, aTOCIString );
+ BeginTagImpl(nullptr, vcl::pdf::StructElement::TOCI, aTOCIString);
}
}
}
@@ -1165,7 +1687,7 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
// TabFrame: Table
- nPDFType = vcl::PDFWriter::Table;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Table);
aPDFType = aTableString;
{
@@ -1173,7 +1695,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() )
@@ -1219,12 +1741,12 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
if ( !static_cast<const SwRowFrame*>(pFrame)->IsRepeatedHeadline() )
{
- nPDFType = vcl::PDFWriter::TableRow;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::TableRow);
aPDFType = aTRString;
}
else
{
- nPDFType = vcl::PDFWriter::NonStructElement;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::NonStructElement);
}
break;
@@ -1236,12 +1758,12 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
const SwTabFrame* pTable = static_cast<const SwCellFrame*>(pFrame)->FindTabFrame();
if ( pTable->IsInHeadline( *pFrame ) || lcl_IsHeadlineCell( *static_cast<const SwCellFrame*>(pFrame) ) )
{
- nPDFType = vcl::PDFWriter::TableHeader;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::TableHeader);
aPDFType = aTHString;
}
else
{
- nPDFType = vcl::PDFWriter::TableData;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::TableData);
aPDFType = aTDString;
}
}
@@ -1255,9 +1777,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 = sal_uInt16(vcl::pdf::StructElement::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 = sal_uInt16(vcl::pdf::StructElement::NonStructElement);
+ }
+ else if (pFly->Lower() && pFly->Lower()->IsNoTextFrame())
{
bool bFormula = false;
@@ -1274,18 +1807,18 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
}
if ( bFormula )
{
- nPDFType = vcl::PDFWriter::Formula;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Formula);
aPDFType = aFormulaString;
}
else
{
- nPDFType = vcl::PDFWriter::Figure;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Figure);
aPDFType = aFigureString;
}
}
else
{
- nPDFType = vcl::PDFWriter::Division;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Division);
aPDFType = aDivString;
}
}
@@ -1296,12 +1829,26 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
if ( USHRT_MAX != nPDFType )
{
- BeginTag( static_cast<vcl::PDFWriter::StructElement>(nPDFType), aPDFType );
+ BeginTag(vcl::pdf::StructElement(nPDFType), aPDFType );
}
}
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 +1858,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,63 +1966,120 @@ 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);
+
+ ProgName 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.toString(), pInetFormatAttr));
+
sal_uInt16 nPDFType = USHRT_MAX;
OUString aPDFType;
switch ( pPor->GetWhichPor() )
{
+ case PortionType::PostIts:
+ if (!mpPDFExtOutDevData->GetSwPDFState()->m_NoteIdMap.empty())
+ {
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Annot);
+ aPDFType = aAnnotString;
+ }
+ break;
+
case PortionType::Hyphen :
case PortionType::SoftHyphen :
// Check for alternative spelling:
case PortionType::HyphenStr :
case PortionType::SoftHyphenStr :
- nPDFType = vcl::PDFWriter::Span;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Span);
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 = sal_uInt16(vcl::pdf::StructElement::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;
+ }
+ }
+ // Emphasis
+ else if (sStyleName == constEmphasisStyleName)
+ {
+ if (!isContinueSpan)
+ {
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Emphasis);
+ aPDFType = constEmphasisStyleName;
+ CreateCurrentSpan(rInf, sStyleName.toString());
+ }
+ }
+ // Strong
+ else if (sStyleName == constStrongEmphasisStyleName)
+ {
+ if (!isContinueSpan)
+ {
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Strong);
+ aPDFType = constStrongEmphasisStyleName;
+ CreateCurrentSpan(rInf, sStyleName.toString());
+ }
}
// Check for Quote/Code character style:
else if (sStyleName == aQuotation)
{
- nPDFType = vcl::PDFWriter::Quote;
- aPDFType = aQuoteString;
+ if (!isContinueSpan)
+ {
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Quote);
+ aPDFType = aQuoteString;
+ CreateCurrentSpan(rInf, sStyleName.toString());
+ }
}
else if (sStyleName == aSourceText)
{
- nPDFType = vcl::PDFWriter::Code;
- aPDFType = aCodeString;
+ if (!isContinueSpan)
+ {
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Code);
+ aPDFType = aCodeString;
+ CreateCurrentSpan(rInf, sStyleName.toString());
+ }
}
- 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() ||
@@ -1389,18 +2090,19 @@ void SwTaggedPDFHelper::BeginInlineStructureElements()
nCurrentLanguage != nDefaultLang ||
!sStyleName.isEmpty())
{
- nPDFType = vcl::PDFWriter::Span;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Span);
if (!sStyleName.isEmpty())
- aPDFType = sStyleName;
+ aPDFType = sStyleName.toString();
else
aPDFType = aSpanString;
+ CreateCurrentSpan(rInf, sStyleName.toString());
}
}
}
break;
case PortionType::Footnote :
- nPDFType = vcl::PDFWriter::Link;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Link);
aPDFType = aLinkString;
break;
@@ -1416,30 +2118,96 @@ void SwTaggedPDFHelper::BeginInlineStructureElements()
const SwField* pField = pHint->GetFormatField().GetField();
if ( SwFieldIds::GetRef == pField->Which() )
{
- nPDFType = vcl::PDFWriter::Link;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Link);
aPDFType = aLinkString;
}
else if ( SwFieldIds::TableOfAuthorities == pField->Which() )
{
- nPDFType = vcl::PDFWriter::BibEntry;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::BibEntry);
aPDFType = aBibEntryString;
}
}
}
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 = sal_uInt16(vcl::pdf::StructElement::Ruby);
+ aPDFType = "Ruby";
+ break;
+ case 1:
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::RT);
+ aPDFType = "RT";
+ break;
+ case 2:
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::RB);
+ aPDFType = "RB";
+ break;
+ }
+ }
+ else if (pMulti->IsDouble())
+ {
+ EndCurrentAll();
+ switch (mpPorInfo->m_Mode)
+ {
+ case 0:
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::Warichu);
+ aPDFType = "Warichu";
+ break;
+ case 1:
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::WP);
+ aPDFType = "WP";
+ break;
+ case 2:
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::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 = sal_uInt16(vcl::pdf::StructElement::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 = sal_uInt16(vcl::pdf::StructElement::LILabel);
+ aPDFType = aListLabelString;
+ }
+ break;
+
+ case PortionType::Tab :
case PortionType::TabRight :
case PortionType::TabCenter :
case PortionType::TabDecimal :
- nPDFType = vcl::PDFWriter::NonStructElement;
+ nPDFType = sal_uInt16(vcl::pdf::StructElement::NonStructElement);
break;
default: break;
}
if ( USHRT_MAX != nPDFType )
{
- BeginTag( static_cast<vcl::PDFWriter::StructElement>(nPDFType), aPDFType );
+ BeginTag( static_cast<vcl::pdf::StructElement>(nPDFType), aPDFType );
}
}
@@ -1481,27 +2249,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,14 +2274,26 @@ 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;
+ }
+ return MapSwRectToPDFRect(pCurrPage, rRectangle);
+}
+
+double SwEnhancedPDFExportHelper::GetSwRectToPDFRectScale()
+{
+ return 0.75;
+}
+
+tools::Rectangle SwEnhancedPDFExportHelper::MapSwRectToPDFRect(const SwPageFrame* pCurrPage,
+ const tools::Rectangle& rRectangle)
+{
//the page has been scaled by 75% and vertically centered, so adjust these
//rectangles equivalently
tools::Rectangle aRect(rRectangle);
Size aRectSize(aRect.GetSize());
- double fScale = 0.75;
+ double fScale = GetSwRectToPDFRectScale();
aRectSize.setWidth( aRectSize.Width() * fScale );
aRectSize.setHeight( aRectSize.Height() * fScale );
tools::Long nOrigHeight = pCurrPage->getFrameArea().Height();
@@ -1531,7 +2306,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 +2316,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 +2336,8 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
if ( !mbEditEngineOnly )
{
+ assert(pPDFExtOutDevData->GetSwPDFState() == nullptr);
+ pPDFExtOutDevData->SetSwPDFState(new SwEnhancedPDFState(eLanguageDefault));
// POSTITS
@@ -1592,15 +2369,38 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
const Color* pColor;
pNumFormatter->GetOutputString(aDateDiff.GetDate(), nFormat, sDate, &pColor);
- vcl::PDFNote aNote;
+ vcl::pdf::PDFNote aNote;
// The title should consist of the author and the date:
- aNote.Title = pField->GetPar1() + ", " + sDate + ", " + (pField->GetResolved() ? SwResId(STR_RESOLVED) : "");
+ aNote.maTitle = pField->GetPar1() + ", " + sDate + ", " + (pField->GetResolved() ? SwResId(STR_RESOLVED) : u""_ustr);
// Guess what the contents contains...
- aNote.Contents = pField->GetText();
+ aNote.maContents = pField->GetText();
+
+ tools::Rectangle aPopupRect(0, 0);
+ SwPostItMgr* pPostItMgr = pDoc->GetEditShell()->GetPostItMgr();
+ for (auto it = pPostItMgr->begin(); it != pPostItMgr->end(); ++it)
+ {
+ sw::annotation::SwAnnotationWin* pWin = it->get()->mpPostIt;
+ if (pWin)
+ {
+ const SwRect& aAnnotRect = pWin->GetAnchorRect();
+ if (aAnnotRect.Contains(rNoteRect))
+ {
+ Point aPt(pDoc->GetEditShell()->GetWin()->PixelToLogic(pWin->GetPosPixel()));
+ Size aSize(pDoc->GetEditShell()->GetWin()->PixelToLogic(pWin->GetSizePixel()));
+ aPopupRect = tools::Rectangle(aPt, aSize);
+ }
+ }
+ }
// Link Export
tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rNoteRect.SVRect()));
- pPDFExtOutDevData->CreateNote(aRect, aNote, aNotePageNum);
+ sal_Int32 nNoteId = pPDFExtOutDevData->CreateNote(aRect, aNote, aPopupRect, aNotePageNum);
+
+ if (mrPrintData.GetPrintPostIts() != SwPostItMode::InMargins)
+ {
+ const IdMapEntry aNoteEntry(aRect, nNoteId);
+ pPDFExtOutDevData->GetSwPDFState()->m_NoteIdMap.push_back(aNoteEntry);
+ }
}
mrSh.SwCursorShell::ClearMark();
}
@@ -1623,10 +2423,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,28 +2435,28 @@ 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 altText(p->rINetAttr.GetINetFormat().GetName());
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();
- if (! JumpToSwMark( &mrSh, aURL ))
+ if (! JumpToSwMark( &mrSh, SwMarkName(aURL) ))
{
continue; // target deleted
}
@@ -1678,11 +2478,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 +2498,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,29 +2523,28 @@ 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();
- if (! JumpToSwMark( &mrSh, aURL ))
+ if (! JumpToSwMark( &mrSh, SwMarkName(aURL) ))
{
continue; // target deleted
}
@@ -1767,11 +2565,11 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
}
}
- if ( !bIntern || -1 != nDestId )
+ if ( !bInternal || -1 != nDestId )
{
Point aNullPt;
const SwRect aLinkRect = pFrameFormat->FindLayoutRect( false, &aNullPt );
-
+ UIName const linkName(pItem->GetName());
// Link PageNums
std::vector<sal_Int32> aLinkPageNums = CalcOutputPageNums( aLinkRect );
@@ -1780,10 +2578,14 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
{
tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, aLinkRect.SVRect()));
const sal_Int32 nLinkId =
- pPDFExtOutDevData->CreateLink(aRect, aLinkPageNum);
+ pPDFExtOutDevData->CreateLink(aRect, linkName.toString(), 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 +2594,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, linkName.toString());
}
}
}
@@ -1808,7 +2610,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,20 +2619,31 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
if (xShape->getShapeType() == "com.sun.star.drawing.MediaShape")
{
uno::Reference<beans::XPropertySet> xShapePropSet(xShape, uno::UNO_QUERY);
+ OUString title;
+ xShapePropSet->getPropertyValue(u"Title"_ustr) >>= title;
+ OUString description;
+ xShapePropSet->getPropertyValue(u"Description"_ustr) >>= 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;
+ xShapePropSet->getPropertyValue(u"MediaURL"_ustr) >>= aMediaURL;
if (!aMediaURL.isEmpty())
{
+ OUString const mimeType(xShapePropSet->getPropertyValue(u"MediaMimeType"_ustr).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.
OUString aTempFileURL;
- xShapePropSet->getPropertyValue("PrivateTempFileURL") >>= aTempFileURL;
+ xShapePropSet->getPropertyValue(u"PrivateTempFileURL"_ustr) >>= aTempFileURL;
pPDFExtOutDevData->SetScreenStream(nScreenId, aTempFileURL);
}
else
@@ -1858,19 +2671,18 @@ 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();
// Destination Rectangle
const SwGetRefField* pField = static_cast<SwGetRefField*>(pFormatField->GetField());
- const OUString& rRefName = pField->GetSetRefName();
- mrSh.GotoRefMark( rRefName, pField->GetSubType(), pField->GetSeqNo() );
+ const SwMarkName& rRefName = pField->GetSetRefName();
+ 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 +2697,7 @@ 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 );
// Create links for all selected rectangles:
const size_t nNumOfRects = aTmp.size();
@@ -1903,11 +2714,11 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// Link Export
aRect = SwRectToPDFRect(pCurrPage, rLinkRect.SVRect());
const sal_Int32 nLinkId =
- pPDFExtOutDevData->CreateLink(aRect, aLinkPageNum);
+ pPDFExtOutDevData->CreateLink(aRect, rRefName.toString(), 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 +2726,7 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// #i44368# Links in Header/Footer
if ( bHeaderFooter )
{
- MakeHeaderFooterLinks( *pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, "", true );
+ MakeHeaderFooterLinks(*pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, u""_ustr, true, rRefName.toString());
}
}
}
@@ -1935,12 +2746,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 +2761,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 +2779,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, numStrRef, 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, numStrSymbol, 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 );
}
}
}
@@ -2013,13 +2845,21 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
std::stack< StackEntry > aOutlineStack;
aOutlineStack.push( StackEntry( -1, -1 ) ); // push default value
+ // outlines inside flys (text frames) collected before the normal
+ // outlines by GetOutLineNds(), so store them with page/position data
+ // to insert later on the right page and position:
+ // tuple< nDestPageNum, rDestRect, nLevel, rEntry, nDestId >
+ typedef std::tuple< sal_Int32, SwRect, sal_Int32, const OUString, sal_Int32 > FlyEntry;
+ std::vector< FlyEntry > aFlyVector;
+ sal_Int32 nStartFly = 0; // first not processed item in aFlyVector
+
const SwOutlineNodes::size_type nOutlineCount =
mrSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
for ( SwOutlineNodes::size_type i = 0; i < nOutlineCount; ++i )
{
// Check if outline is hidden
const SwTextNode* pTNd = mrSh.GetNodes().GetOutLineNds()[ i ]->GetTextNode();
- OSL_ENSURE( nullptr != pTNd, "Enhanced pdf export - text node is missing" );
+ assert(pTNd && "Enhanced pdf export - text node is missing");
if ( pTNd->IsHidden() ||
!sw::IsParaPropsNode(*mrSh.GetLayout(), *pTNd) ||
@@ -2027,6 +2867,14 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
pTNd->GetText().isEmpty())
continue;
+ // Check if outline is inside a text frame
+ bool bFlyOutline = pTNd->GetFlyFormat();
+
+ // save outline stack to use for postponed fly outlines
+ std::stack< StackEntry > aSavedOutlineStack;
+ if ( !aFlyVector.empty() && !bFlyOutline )
+ aSavedOutlineStack = aOutlineStack;
+
// Get parent id from stack:
const sal_Int8 nLevel = static_cast<sal_Int8>(mrSh.getIDocumentOutlineNodesAccess()->getOutlineLevel( i ));
sal_Int8 nLevelOnTopOfStack = aOutlineStack.top().first;
@@ -2056,17 +2904,72 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
// Outline entry text
- const OUString& rEntry = mrSh.getIDocumentOutlineNodesAccess()->getOutlineText(
+ const OUString aEntry = mrSh.getIDocumentOutlineNodesAccess()->getOutlineText(
i, mrSh.GetLayout(), true, false, false );
+ // postpone fly outlines
+ if ( bFlyOutline )
+ {
+ aFlyVector.push_back(
+ FlyEntry(nDestPageNum, rDestRect, nLevel, aEntry, nDestId) );
+ continue;
+ }
+
+ // create new outline items from postponed fly outlines, if they are before
+ // the recent not fly outline (and after the already created fly outlines)
+ for (size_t j = nStartFly; j < aFlyVector.size(); ++j)
+ {
+ if ( std::get<0>(aFlyVector[j]) < nDestPageNum ||
+ ( std::get<0>(aFlyVector[j]) == nDestPageNum &&
+ std::get<1>(aFlyVector[j]).Pos().Y() < rDestRect.Pos().Y() ) )
+ {
+ sal_Int32 nFlyLevel = std::get<2>(aFlyVector[j]);
+ sal_Int8 nLevelOnTopOfSavedStack = aSavedOutlineStack.top().first;
+ while ( nLevelOnTopOfSavedStack >= nFlyLevel &&
+ nLevelOnTopOfSavedStack != -1 )
+ {
+ aSavedOutlineStack.pop();
+ nLevelOnTopOfSavedStack = aSavedOutlineStack.top().first;
+ }
+ const sal_Int32 nFlyParent = aSavedOutlineStack.top().second;
+ const sal_Int32 nId = pPDFExtOutDevData->CreateOutlineItem( nFlyParent,
+ std::get<3>(aFlyVector[j]),
+ std::get<4>(aFlyVector[j]) );
+ // Push current level and outline id on saved stack:
+ aSavedOutlineStack.push( StackEntry( nFlyLevel, nId ) );
+ ++nStartFly;
+ }
+ else
+ break;
+ }
+
// Create a new outline item:
const sal_Int32 nOutlineId =
- pPDFExtOutDevData->CreateOutlineItem( nParent, rEntry, nDestId );
+ pPDFExtOutDevData->CreateOutlineItem( nParent, aEntry, nDestId );
// Push current level and nOutlineId on stack:
aOutlineStack.push( StackEntry( nLevel, nOutlineId ) );
}
}
+
+ // create remaining fly outlines
+ for (size_t j = nStartFly; j < aFlyVector.size(); ++j)
+ {
+ sal_Int32 nLevel = std::get<2>(aFlyVector[j]);
+ sal_Int8 nLevelOnTopOfStack = aOutlineStack.top().first;
+ while ( nLevelOnTopOfStack >= nLevel &&
+ nLevelOnTopOfStack != -1 )
+ {
+ aOutlineStack.pop();
+ nLevelOnTopOfStack = aOutlineStack.top().first;
+ }
+ const sal_Int32 nParent = aOutlineStack.top().second;
+
+ const sal_Int32 nOutlineId = pPDFExtOutDevData->CreateOutlineItem( nParent,
+ std::get<3>(aFlyVector[j]),
+ std::get<4>(aFlyVector[j]) );
+ aOutlineStack.push( StackEntry( std::get<2>(aFlyVector[j]), nOutlineId ) );
+ }
}
if( pPDFExtOutDevData->GetIsExportNamedDestinations() )
@@ -2077,14 +2980,14 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
// 1. a name for the destination, formed from the standard OOo bookmark name
// 2. the destination, obtained from where the bookmark destination lies
IDocumentMarkAccess* const pMarkAccess = mrSh.GetDoc()->getIDocumentMarkAccess();
- for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getBookmarksBegin();
+ for(auto ppMark = pMarkAccess->getBookmarksBegin();
ppMark != pMarkAccess->getBookmarksEnd();
++ppMark)
{
//get the name
- const ::sw::mark::IMark* pBkmk = *ppMark;
+ const ::sw::mark::MarkBase* pBkmk = *ppMark;
mrSh.SwCursorShell::ClearMark();
- const OUString& sBkName = pBkmk->GetName();
+ const SwMarkName& sBkName = pBkmk->GetName();
//jump to it
if (! JumpToSwMark( &mrSh, sBkName ))
@@ -2105,7 +3008,7 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
if ( -1 != nDestPageNum )
{
tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rDestRect.SVRect()));
- pPDFExtOutDevData->CreateNamedDest(sBkName, aRect, nDestPageNum);
+ pPDFExtOutDevData->CreateNamedDest(sBkName.toString(), aRect, nDestPageNum);
}
}
mrSh.SwCursorShell::ClearMark();
@@ -2121,11 +3024,11 @@ 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 );
+ JumpToSwMark( &mrSh, SwMarkName(aBookmarkName) );
// Destination Rectangle
const SwRect& rDestRect = mrSh.GetCharRect();
@@ -2157,6 +3060,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 +3079,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 +3144,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 aURL = rAuthorityField.GetAbsoluteURL();
+ if (aURL.getLength() == 0)
+ {
+ continue;
+ }
+
+ const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
+ if (!lcl_TryMoveToNonHiddenField(mrSh, rTextNode, *pFormatField))
+ {
+ continue;
+ }
- // Select the field.
- mrSh.SwCursorShell::SetMark();
- mrSh.SwCursorShell::Right(1, CRSR_SKIP_CHARS);
+ OUString const content(rAuthorityField.GetAuthority(mrSh.GetLayout()));
- // Create the links.
- for (const auto& rLinkRect : *mrSh.SwCursorShell::GetCursor_())
- {
- for (const auto& rLinkPageNum : CalcOutputPageNums(rLinkRect))
+ // 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, aURL);
+ }
}
+ 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;
- mrSh.SwCursorShell::ClearMark();
+ 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;
+
+ const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
+ if (!lcl_TryMoveToNonHiddenField(mrSh, rTextNode, *pFormatField))
+ {
+ continue;
+ }
+
+ OUString const content(rAuthorityField.GetAuthority(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 +3310,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 +3319,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 );
+ // 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);
-
- // 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..6330857deb18 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 46
-#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..1af5e50d8550 100644
--- a/sw/source/core/text/atrstck.cxx
+++ b/sw/source/core/text/atrstck.cxx
@@ -42,6 +42,7 @@
#include <editeng/twolinesitem.hxx>
#include <editeng/charhiddenitem.hxx>
#include <editeng/boxitem.hxx>
+#include <editeng/nhypitem.hxx>
#include <editeng/shaditem.hxx>
#include <viewopt.hxx>
#include <charfmt.hxx>
@@ -60,9 +61,10 @@
* stack, the top most attribute on the stack is valid. Because some
* kinds of attributes have to be pushed to the same stacks we map their
* ids to stack ids
- * Attention: The first NUM_DEFAULT_VALUES ( defined in swfntcch.hxx )
+ * Attention: Stacks for character attributes (RES_CHRATR_*)
* are stored in the defaultitem-cache, if you add one, you have to increase
- * NUM_DEFAULT_VALUES.
+ * NUM_DEFAULT_VALUES (defined in swfntcch.hxx), and ensure your new stack id
+ * is below this number.
* Also adjust NUM_ATTRIBUTE_STACKS in atrhndl.hxx.
*/
const sal_uInt8 StackPos[ RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN + 1 ] =
@@ -112,17 +114,19 @@ const sal_uInt8 StackPos[ RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN + 1 ] =
38, // RES_CHRATR_HIGHLIGHT, // 42
0, // RES_CHRATR_GRABBAG, // 43
0, // RES_CHRATR_BIDIRTL, // 44
- 0, // RES_CHRATR_IDCTHINT, // 45
- 39, // RES_TXTATR_REFMARK, // 46
- 40, // RES_TXTATR_TOXMARK, // 47
- 41, // RES_TXTATR_META, // 48
- 41, // RES_TXTATR_METAFIELD, // 49
- 0, // RES_TXTATR_AUTOFMT, // 50
- 0, // RES_TXTATR_INETFMT // 51
- 0, // RES_TXTATR_CHARFMT, // 52
- 42, // RES_TXTATR_CJK_RUBY, // 53
- 0, // RES_TXTATR_UNKNOWN_CONTAINER, // 54
- 43, // RES_TXTATR_INPUTFIELD // 55
+ 0, // RES_CHRATR_UNUSED3, // 45
+ 39, // RES_CHRATR_SCRIPT_HINT, // 46
+ 40, // RES_TXTATR_REFMARK, // 47
+ 41, // RES_TXTATR_TOXMARK, // 48
+ 42, // RES_TXTATR_META, // 49
+ 42, // RES_TXTATR_METAFIELD, // 50
+ 0, // RES_TXTATR_AUTOFMT, // 51
+ 0, // RES_TXTATR_INETFMT // 52
+ 0, // RES_TXTATR_CHARFMT, // 53
+ 43, // RES_TXTATR_CJK_RUBY, // 54
+ 0, // RES_TXTATR_UNKNOWN_CONTAINER, // 55
+ 44, // RES_TXTATR_INPUTFIELD // 56
+ 45, // RES_TXTATR_CONTENTCONTROL // 57
};
namespace CharFormat
@@ -220,9 +224,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 +240,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 +250,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 +325,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( )
@@ -344,15 +343,19 @@ void SwAttrHandler::PushAndChg( const SwTextAttr& rAttr, SwFont& rFnt )
// they have to be pushed to each stack they belong to
if ( RES_TXTATR_INETFMT == rAttr.Which() ||
RES_TXTATR_CHARFMT == rAttr.Which() ||
+ RES_PARATR_LIST_AUTOFMT == rAttr.Which() ||
RES_TXTATR_AUTOFMT == rAttr.Which() )
{
- const SfxItemSet* pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
+ const SfxItemSet* pSet = rAttr.Which() == RES_PARATR_LIST_AUTOFMT
+ ? rAttr.GetAttr().StaticWhichCast(RES_PARATR_LIST_AUTOFMT).GetStyleHandle().get()
+ : CharFormat::GetItemSet( rAttr.GetAttr() );
if ( !pSet ) return;
+ bool const inParent{rAttr.Which() != RES_TXTATR_AUTOFMT && rAttr.Which() != RES_PARATR_LIST_AUTOFMT};
for ( sal_uInt16 i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++)
{
const SfxPoolItem* pItem;
- bool bRet = SfxItemState::SET == pSet->GetItemState( i, rAttr.Which() != RES_TXTATR_AUTOFMT, &pItem );
+ bool bRet = SfxItemState::SET == pSet->GetItemState(i, inParent, &pItem);
if ( bRet )
{
@@ -371,6 +374,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 +447,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 +494,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 +538,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 +552,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 +567,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
@@ -615,8 +636,16 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush )
CharFormat::GetItem( *pTopAt, RES_CHRATR_HIDDEN ) :
m_pDefaultArray[ nStackPos ];
+ const sal_uInt16 nStackPos2 = StackPos[ RES_CHRATR_NOHYPHEN ];
+ const SwTextAttr* pTopAt2 = GetTop(nStackPos2);
+
+ const SfxPoolItem* pTmpItem2 = pTopAt2 ?
+ CharFormat::GetItem( *pTopAt2, RES_CHRATR_NOHYPHEN ) :
+ m_pDefaultArray[ nStackPos2 ];
+
if ((m_pShell && !m_pShell->GetWin()) ||
- (pTmpItem && !pTmpItem->StaticWhichCast(RES_CHRATR_HIDDEN).GetValue()) )
+ (pTmpItem && !pTmpItem->StaticWhichCast(RES_CHRATR_HIDDEN).GetValue()) ||
+ (pTmpItem2 && !pTmpItem2->StaticWhichCast(RES_CHRATR_NOHYPHEN).GetValue()) )
{
rFnt.SetUnderline( rItem.StaticWhichCast(RES_CHRATR_UNDERLINE).GetLineStyle() );
rFnt.SetUnderColor( rItem.StaticWhichCast(RES_CHRATR_UNDERLINE).GetColor() );
@@ -671,6 +700,19 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush )
case RES_CHRATR_HIGHLIGHT :
rFnt.SetHighlightColor( rItem.StaticWhichCast(RES_CHRATR_HIGHLIGHT).GetColor() );
break;
+ case RES_CHRATR_NOHYPHEN :
+ if ( m_pShell && m_pShell->GetWin() &&
+ m_pShell->GetViewOptions()->IsViewMetaChars() )
+ {
+ if ( rItem.StaticWhichCast(RES_CHRATR_NOHYPHEN).GetValue() )
+ {
+ rFnt.SetUnderline( LINESTYLE_DOTTED );
+ rFnt.SetUnderColor( COL_LIGHTGRAY );
+ }
+ else
+ ActivateTop( rFnt, RES_CHRATR_UNDERLINE );
+ }
+ break;
case RES_CHRATR_CJK_FONT :
{
auto& rFontItem = rItem.StaticWhichCast(RES_CHRATR_CJK_FONT);
@@ -751,7 +793,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 +828,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 +857,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 +880,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..a2545c300abf 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:
@@ -216,7 +216,7 @@ bool SwTextFrame::GetCharRect( SwRect& rOrig, const SwPosition &rPos,
Point aPnt1 = pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos();
SwTextNode const*const pTextNd(GetTextNodeForParaProps());
short nFirstOffset;
- pTextNd->GetFirstLineOfsWithNum( nFirstOffset );
+ pTextNd->GetFirstLineOfsWithNum(nFirstOffset, {});
Point aPnt2;
if ( aRectFnSet.IsVert() )
@@ -339,7 +339,7 @@ bool SwTextFrame::GetCharRect( SwRect& rOrig, const SwPosition &rPos,
if( bRet )
{
SwPageFrame *pPage = pFrame->FindPageFrame();
- OSL_ENSURE( pPage, "Text escaped from page?" );
+ assert(pPage && "Text escaped from page?");
const SwTwips nOrigTop = aRectFnSet.GetTop(rOrig);
const SwTwips nPageTop = aRectFnSet.GetTop(pPage->getFrameArea());
const SwTwips nPageBott = aRectFnSet.GetBottom(pPage->getFrameArea());
@@ -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() );
@@ -697,6 +697,48 @@ bool SwTextFrame::LeftMargin(SwPaM *pPam) const
return true;
}
+bool SwTextFrame::IsInHyphenatedWord(SwPaM *pPam, bool bSelection) const
+{
+ assert(GetMergedPara() || &pPam->GetPointNode() == static_cast<SwContentNode const*>(GetDep()));
+
+ SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(),
+ SwTextCursor::IsRightMargin() );
+ pFrame->GetFormatted();
+ if (!IsEmpty())
+ {
+ SwTextSizeInfo aInf( pFrame );
+ SwTextCursor aLine( pFrame, &aInf );
+ TextFrameIndex const nCursorPos(MapModelToViewPos(*pPam->GetPoint()));
+ aLine.CharCursorToLine(nCursorPos);
+ if ( aLine.GetCurr()->IsEndHyph() )
+ {
+ TextFrameIndex nPos(aLine.GetStart() + aLine.GetCurr()->GetLen());
+ while( nPos > nCursorPos && ' ' != aInf.GetText()[sal_Int32(nPos) - 1] )
+ --nPos;
+ if ( nPos == nCursorPos && ( bSelection ||
+ // without selection, the cursor must be inside the word, not before that
+ // to apply the character formatting, as usual
+ ( nPos > aLine.GetStart() && ' ' != aInf.GetText()[sal_Int32(nPos) - 1] ) ) )
+ return true;
+ }
+ // the hyphenated word starts in the previous line
+ if ( aLine.GetStart() > TextFrameIndex(0) )
+ {
+ TextFrameIndex nPos(aLine.GetStart());
+ aLine.CharCursorToLine(nPos - TextFrameIndex(1));
+ if ( aLine.GetCurr()->IsEndHyph() )
+ {
+ while( nPos < nCursorPos && ' ' != aInf.GetText()[sal_Int32(nPos)] )
+ ++nPos;
+ if ( nPos == nCursorPos &&
+ ( bSelection || ' ' != aInf.GetText()[sal_Int32(nPos)] ) )
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
/*
* To the line end: That's the position before the last char of the line.
* Exception: In the last line, it should be able to place the cursor after
@@ -705,7 +747,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 +806,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 +873,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 +1193,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 +1234,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 +1242,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 +1361,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 +1460,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 +1473,19 @@ 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() +
- GetTextNodeForParaProps()->GetLeftMarginWithNum();
- SwTwips nRight = rFill.Right() - rLRSpace.GetRight();
+
+ SwTwips nLeft = rFill.Left() + rTextLeftMargin.ResolveLeft(rFirstLine, /*metrics*/ {})
+ + GetTextNodeForParaProps()->GetLeftMarginWithNum();
+ SwTwips nRight = rFill.Right() - rRightMargin.ResolveRight({});
SwTwips nCenter = ( nLeft + nRight ) / 2;
rRect.Left( nLeft );
if( SwFillMode::Margin == rFill.Mode() )
@@ -1475,7 +1520,7 @@ void SwTextFrame::FillCursorPos( SwFillData& rFill ) const
SwTwips nSpace = 0;
if( SwFillMode::Tab != rFill.Mode() )
{
- SwDrawTextInfo aDrawInf( pSh, *pOut, " ", 0, 2 );
+ SwDrawTextInfo aDrawInf( pSh, *pOut, u" "_ustr, 0, 2 );
nSpace = pFnt->GetTextSize_( aDrawInf ).Width()/2;
}
if( rFill.X() >= nRight )
@@ -1510,15 +1555,15 @@ void SwTextFrame::FillCursorPos( SwFillData& rFill ) const
}
else if( rFill.X() > nLeft )
{
- SwTwips nTextLeft = rFill.Left() + rLRSpace.GetTextLeft() +
- GetTextNodeForParaProps()->GetLeftMarginWithNum(true);
+ SwTwips nTextLeft = rFill.Left() + rTextLeftMargin.ResolveTextLeft({})
+ + GetTextNodeForParaProps()->GetLeftMarginWithNum(true);
rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTextLeft;
SwTwips nLeftTab;
SwTwips nRightTab = nLeft;
sal_uInt16 nSpaceCnt = 0;
sal_uInt16 nSpaceOnlyCnt = 0;
- sal_uInt16 nTabCnt = 0;
sal_uInt16 nIdx = 0;
+ int nTabCnt = 0;
do
{
nLeftTab = nRightTab;
@@ -1536,7 +1581,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..17e55bf5ba48 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,10 @@
#include <editeng/tstpitem.hxx>
#include <redline.hxx>
#include <comphelper/lok.hxx>
+#include <flyfrms.hxx>
+#include <frmtool.hxx>
+#include <layouter.hxx>
+#include <fmtsrnd.hxx>
// Tolerance in formatting and text output
#define SLOPPY_TWIPS 5
@@ -139,9 +145,15 @@ void SwTextFrame::ValidateBodyFrame()
SwSwapIfSwapped swap( this );
// See comment in ValidateFrame()
- if ( !IsInFly() && !IsInTab() &&
- !( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() ) )
- ValidateBodyFrame_( GetUpper() );
+ if ( !IsInFly() && !IsInTab())
+ {
+ if (SwSectionFrame* pSctFrame = FindSctFrame())
+ {
+ SwFrame* pLower = pSctFrame->Lower();
+ if (pLower && !pLower->IsColumnFrame())
+ ValidateBodyFrame_( GetUpper() );
+ }
+ }
}
bool SwTextFrame::GetDropRect_( SwRect &rRect ) const
@@ -212,7 +224,7 @@ bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
if( pPara )
{
pPara->GetReformat() = SwCharRange();
- pPara->GetDelta() = 0;
+ pPara->SetDelta(0);
}
}
@@ -310,7 +322,7 @@ bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
if( pPara )
{
pPara->GetReformat() = SwCharRange();
- pPara->GetDelta() = 0;
+ pPara->SetDelta(0);
}
}
@@ -323,7 +335,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 +349,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 +399,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 +508,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()
@@ -466,14 +518,16 @@ void SwTextFrame::AdjustFrame( const SwTwips nChgHght, bool bHasToFit )
// We can get a bit of space in table cells, because there could be some
// left through a vertical alignment to the top.
// Assure that first lower in upper is the current one or is valid.
- if ( IsInTab() &&
- ( GetUpper()->Lower() == this ||
- GetUpper()->Lower()->isFrameAreaDefinitionValid() ) )
+ if (IsInTab())
{
- tools::Long nAdd = aRectFnSet.YDiff( aRectFnSet.GetTop(GetUpper()->Lower()->getFrameArea()),
- aRectFnSet.GetPrtTop(*GetUpper()) );
- OSL_ENSURE( nAdd >= 0, "Ey" );
- nRstHeight += nAdd;
+ SwFrame* pLower = GetUpper()->Lower();
+ if ( pLower == this || (pLower && pLower->isFrameAreaDefinitionValid()) )
+ {
+ tools::Long nAdd = aRectFnSet.YDiff( aRectFnSet.GetTop(pLower->getFrameArea()),
+ aRectFnSet.GetPrtTop(*GetUpper()) );
+ OSL_ENSURE( nAdd >= 0, "Ey" );
+ nRstHeight += nAdd;
+ }
}
// nRstHeight < 0 means that the TextFrame is located completely outside of its Upper.
@@ -517,9 +571,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 +584,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 +602,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:
@@ -561,24 +612,32 @@ css::uno::Sequence< css::style::TabStop > SwTextFrame::GetTabStopInfo( SwTwips C
// If it's 0, the FollowFrame is deleted.
void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
const TextFrameIndex nOffset, const TextFrameIndex nEnd,
- const sal_uInt8 nMode )
+ const bool bDontJoin)
{
SwFrameSwapper aSwapper( this, false );
// 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 )
+ 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,21 +650,47 @@ 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 ( nMode )
+ 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 (bDontJoin)
GetFollow()->ManipOfst(TextFrameIndex(0));
if ( CalcFollow( nNewOfst ) ) // CalcFollow only at the end, we do a SetOffset there
@@ -666,16 +751,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 +789,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 +850,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 +860,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();
}
@@ -816,15 +908,18 @@ bool SwTextFrame::CalcPreps()
}
else if ( aRectFnSet.IsVert() )
{
+ // Replicate the same overflow behavior that is used for horizontal portions.
+ SwTwips const nTmp = sw::WIDOW_MAGIC - (getFrameArea().Left() + 10000);
+ SwTwips nDiff = nTmp - getFrameArea().Width();
+
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
- aFrm.Width( aFrm.Width() + aFrm.Left() );
- aFrm.Left( 0 );
+ aFrm.Width(nTmp);
}
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
- aPrt.Width( aPrt.Width() + getFrameArea().Left() );
+ aPrt.Width(aPrt.Width() + nDiff);
}
SetWidow( true );
@@ -987,7 +1082,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 +1091,107 @@ void SwTextFrame::ChangeOffset( SwTextFrame* pFrame, TextFrameIndex nNew )
MoveFlyInCnt( pFrame, nNew, TextFrameIndex(COMPLETE_STRING) );
}
+static bool isFirstVisibleFrameInPageBody(const SwTextFrame* pFrame)
+{
+ const SwFrame* pBodyFrame = pFrame->FindBodyFrame();
+ while (pBodyFrame && !pBodyFrame->IsPageBodyFrame())
+ pBodyFrame = pBodyFrame->GetUpper()->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 == pBodyFrame)
+ 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());
+}
+
+namespace
+{
+/// Determines if pFrame has at least one anchored object which is positioned against the page frame
+/// and uses all space available for body text.
+bool HasFullPageFly(const SwTextFrame* pFrame)
+{
+ const SwFrame* pBodyFrame = pFrame->FindBodyFrame();
+ if (!pBodyFrame)
+ {
+ // Inside a fly frame, not interesting.
+ return false;
+ }
+
+ const SwRect& rBodyFrameArea = pBodyFrame->getFrameArea();
+ const SwSortedObjs* pDrawObjs = pFrame->GetDrawObjs();
+ if (!pDrawObjs)
+ {
+ return false;
+ }
+
+ for (SwAnchoredObject* pDrawObj : *pDrawObjs)
+ {
+ SwFrameFormat* pFrameFormat = pDrawObj->GetFrameFormat();
+ if (pFrameFormat->GetHoriOrient().GetRelationOrient() != text::RelOrientation::PAGE_FRAME)
+ {
+ continue;
+ }
+
+ if (pFrameFormat->GetVertOrient().GetRelationOrient() != text::RelOrientation::PAGE_FRAME)
+ {
+ continue;
+ }
+
+ if (pFrameFormat->GetSurround().GetValue() != text::WrapTextMode::WrapTextMode_NONE)
+ {
+ // Not a case where the request is to wrap the content around the object, ignore.
+ continue;
+ }
+
+ if (pDrawObj->GetObjRectWithSpaces().Contains(rBodyFrameArea))
+ {
+ // Wrap is requested, but the object uses all available space: this is a full page
+ // object.
+ return true;
+ }
+ }
+
+ return false;
+}
+}
+
void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
WidowsAndOrphans &rFrameBreak,
TextFrameIndex const nStrLen,
@@ -1013,36 +1209,81 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
// Call base class method <SwTextFrameBreak::IsBreakNow(..)>
// instead of method <WidowsAndOrphans::IsBreakNow(..)> to get a break,
// even if due to widow rule no enough lines exists.
- sal_uInt8 nNew = ( !GetFollow() &&
+ bool createNew = ( !GetFollow() &&
nEnd < nStrLen &&
( rLine.IsStop() ||
( bHasToFit
? ( rLine.GetLineNr() > 1 &&
!rFrameBreak.IsInside( rLine ) )
- : rFrameBreak.IsBreakNow( rLine ) ) ) )
- ? 1 : 0;
+ : rFrameBreak.IsBreakNow( rLine ) ) ) );
+
+ SwTextFormatInfo& rInf = rLine.GetInfo();
+ bool bEmptyWithSplitFly = false;
+ if (!createNew && !nStrLen && !rInf.GetTextFly().IsOn() && IsEmptyWithSplitFly())
+ {
+ // Empty paragraph, so IsBreakNow() is not called, but we should split the fly portion and
+ // the paragraph marker.
+ createNew = true;
+ 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;
+ (*GetDrawObjs())[0]->GetFrameFormat()->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
- // Still try split text frame if we have columns.
- if (FindColFrame())
- bOnlyContainsAsCharAnchoredObj = false;
+ if (bLoneAsCharAnchoredObj)
+ {
+ // 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 (!isFirstVisibleFrameInPageBody(this))
+ bLoneAsCharAnchoredObj = false;
+ else
+ createNew = false;
+ }
+ else if (createNew)
+ {
+ 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))
+ createNew = false;
+ }
+ 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); tdf#136040: still try split text
+ // frame if we have columns.
+ if (pBodyFrame && !FindColFrame() && isFirstVisibleFrameInPageBody(this)
+ && !hasFly(this) && !hasAtPageFly(pBodyFrame))
+ createNew = false;
+ }
+ }
- if ( nNew && bOnlyContainsAsCharAnchoredObj )
+ if (createNew && nEnd == TextFrameIndex(0) && !bEmptyWithSplitFly && HasFullPageFly(this))
{
- nNew = 0;
+ // We intended to split at start, due to an anchored object which would use all space on the
+ // current page. It makes no sense to split & move all text of the frame forward: the
+ // current page would be empty and we would move back later anyway.
+ createNew = false;
}
- if ( nNew )
+ if (createNew)
{
SplitFrame( nEnd );
}
-
- const SwFrame *pBodyFrame = FindBodyFrame();
+ bool dontJoin = createNew;
const tools::Long nBodyHeight = pBodyFrame ? ( IsVertical() ?
pBodyFrame->getFrameArea().Width() :
@@ -1052,12 +1293,12 @@ 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() )
{
rLine.TruncLines( true );
- nNew = 1;
+ dontJoin = true;
}
// FindBreak truncates the last line
@@ -1067,10 +1308,14 @@ 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 )
+ if (dontJoin && nOld < nEnd)
RemoveFootnote( nOld, nEnd - nOld );
ChangeOffset( GetFollow(), nEnd );
if( !bDelta )
@@ -1082,6 +1327,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() )
{
@@ -1096,7 +1342,7 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
GetFollow()->IsFieldFollow() ||
(nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule()))
{
- nNew |= 3;
+ dontJoin = true;
}
else if (FindTabFrame() && nEnd > TextFrameIndex(0) &&
rLine.GetInfo().GetChar(nEnd - TextFrameIndex(1)) == CH_BREAK)
@@ -1105,24 +1351,49 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
// ends with a hard line break. Don't join the follow just
// because the follow would have no content, we may still need it
// for the paragraph mark.
- nNew |= 1;
+ dontJoin = true;
+ }
+ // 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 (dontJoin && 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 );
- nNew |= 3;
+ dontJoin = true;
}
}
// If the remaining height changed e.g by RemoveFootnote() we need to
@@ -1142,7 +1413,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;
}
@@ -1163,13 +1434,13 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
AdjustFrame( nChg, bHasToFit );
if( HasFollow() || IsInFootnote() )
- AdjustFollow_( rLine, nEnd, nStrLen, nNew );
+ AdjustFollow_(rLine, nEnd, nStrLen, dontJoin);
pPara->SetPrepMustFit( false );
}
// 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 +1448,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;
@@ -1267,17 +1538,25 @@ bool SwTextFrame::FormatLine( SwTextFormatter &rLine, const bool bPrev )
rRepaint.SetRightOfst( nRght );
// Finally we enlarge the repaint rectangle if we found an underscore
- // within our line. 40 Twips should be enough
- const bool bHasUnderscore =
- ( rLine.GetInfo().GetUnderScorePos() < nNewStart );
- if ( bHasUnderscore || rLine.GetCurr()->HasUnderscore() )
- rRepaint.Bottom( rRepaint.Bottom() + 40 );
-
- const_cast<SwLineLayout*>(rLine.GetCurr())->SetUnderscore( bHasUnderscore );
+ // or another glyph extending beyond the line height within the line.
+ auto nBaseAscent = pNew->GetAscent();
+ auto nMaxExtraAscent
+ = std::max({ SwTwips{ 0 }, rLine.GetInfo().GetExtraAscent() - nBaseAscent,
+ rLine.GetCurr()->GetExtraAscent() });
+ rRepaint.Top(rRepaint.Top() - nMaxExtraAscent);
+ const_cast<SwLineLayout*>(rLine.GetCurr())->SetExtraAscent(nMaxExtraAscent);
+
+ auto nBaseDescent = pNew->Height() - pNew->GetAscent();
+ auto nMaxExtraDescent
+ = std::max({ SwTwips{ 0 }, rLine.GetInfo().GetExtraDescent() - nBaseDescent,
+ rLine.GetCurr()->GetExtraDescent() });
+ rRepaint.Bottom(rRepaint.Bottom() + nMaxExtraDescent);
+ const_cast<SwLineLayout*>(rLine.GetCurr())->SetExtraDescent(nMaxExtraDescent);
}
// 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 +1875,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 +1984,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 +2020,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();
@@ -1744,10 +2054,58 @@ void SwTextFrame::Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pP
}
}
+namespace
+{
+class SwTextFrameFormatScopeGuard
+{
+private:
+ VclPtr<OutputDevice> m_pOut = nullptr;
+ VclPtr<OutputDevice> m_pRef = nullptr;
+
+public:
+ SwTextFrameFormatScopeGuard(OutputDevice* pOut, SwTextFrame* pFrame)
+ : m_pOut(pOut)
+ {
+ auto pVsh = pFrame->getRootFrame()->GetCurrShell();
+ if (pVsh)
+ {
+ m_pRef = &pVsh->GetRefDev();
+ }
+
+ if (m_pOut)
+ {
+ m_pOut->Push(vcl::PushFlags::ALL);
+ }
+
+ if (m_pRef)
+ {
+ m_pRef->Push(vcl::PushFlags::ALL);
+ }
+ }
+
+ ~SwTextFrameFormatScopeGuard()
+ {
+ if (m_pRef)
+ {
+ m_pRef->Pop();
+ }
+
+ if (m_pOut)
+ {
+ m_pOut->Pop();
+ }
+ }
+};
+}
+
// We calculate the text frame's size and send a notification.
// Shrink() or Grow() to adjust the frame's size to the changed required space.
void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs * )
{
+ // tdf#92091: SwTextFrame::Format is re-entrant, but may change VCL global state.
+ // The previous state is saved here and restored after returning.
+ SwTextFrameFormatScopeGuard stSg{ pRenderContext, this };
+
SwRectFnSet aRectFnSet(this);
CalcAdditionalFirstLineOffset();
@@ -1757,9 +2115,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 +2124,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 +2141,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 +2261,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 +2275,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 +2365,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 +2494,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..386f40942f5f 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)
@@ -174,14 +172,15 @@ void SwTextFrameInfo::GetSpaces(
// in the selection
if( aLine.GetNext() )
{
- nPos = aLine.GetTextEnd();
+ TextFrameIndex const nEndPos{aLine.GetTextEnd()};
- if( nPos < aLine.GetEnd() )
+ // if only whitespace in line, nEndPos < nPos !
+ if (nPos < nEndPos && nEndPos < aLine.GetEnd())
{
TextFrameIndex const nOff( !bWithLineBreak && CH_BREAK ==
aLine.GetInfo().GetChar(aLine.GetEnd() - TextFrameIndex(1))
? 1 : 0 );
- AddRange( rRanges, nPos, aLine.GetEnd() - nPos - nOff );
+ AddRange(rRanges, nEndPos, aLine.GetEnd() - nEndPos - nOff);
}
}
}
diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx
index 6f1d1fc12ad1..22f00a9b6391 100644
--- a/sw/source/core/text/frmpaint.cxx
+++ b/sw/source/core/text/frmpaint.cxx
@@ -43,6 +43,8 @@
#include <tabfrm.hxx>
#include <numrule.hxx>
#include <wrong.hxx>
+#include <vcl/lineinfo.hxx>
+#include <officecfg/Office/Writer.hxx>
#include <EnhancedPDFExportHelper.hxx>
@@ -68,7 +70,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,14 +84,21 @@ 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());
}
void PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax, bool bRed, const OUString* pRedlineText = nullptr );
- void PaintRedline( SwTwips nY, tools::Long nMax );
+ void PaintRedline( SwTwips nY, tools::Long nMax, sal_Int16 nWordSpacing = 0 );
};
}
@@ -128,7 +137,7 @@ SwExtraPainter::SwExtraPainter( const SwTextFrame *pFrame, SwViewShell *pVwSh,
m_nDivider = !m_rLineInf.GetDivider().isEmpty() ? m_rLineInf.GetDividerCountBy() : 0;
m_nX = pFrame->getFrameArea().Left();
SwCharFormat* pFormat = m_rLineInf.GetCharFormat( const_cast<IDocumentStylePoolAccess&>(pFrame->GetDoc().getIDocumentStylePoolAccess()) );
- OSL_ENSURE( pFormat, "PaintExtraData without CharFormat" );
+ assert(pFormat && "PaintExtraData without CharFormat");
m_pFnt.reset( new SwFont(&pFormat->GetAttrSet(), &pFrame->GetDoc().getIDocumentSettingAccess()) );
m_pFnt->Invalidate();
m_pFnt->ChgPhysFnt( m_pSh, *m_pSh->GetOut() );
@@ -170,7 +179,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;
@@ -199,7 +208,7 @@ void SwExtraPainter::PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax,
if ( pRedlineText )
{
- m_pFnt->SetColor(NON_PRINTING_CHARACTER_COLOR);
+ m_pFnt->SetColor(m_pSh->GetViewOptions()->GetNonPrintingCharacterColor());
// don't strike out text in Insertions In Margin mode
if ( !m_pSh->GetViewOptions()->IsShowChangesInMargin2() )
m_pFnt->SetStrikeout( STRIKEOUT_SINGLE );
@@ -248,7 +257,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;
@@ -272,7 +281,8 @@ void SwExtraPainter::PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax,
}
}
-void SwExtraPainter::PaintRedline( SwTwips nY, tools::Long nMax )
+// paint redline or word spacing indicator
+void SwExtraPainter::PaintRedline( SwTwips nY, tools::Long nMax, sal_Int16 nWordSpacing )
{
Point aStart( m_nRedX, nY );
Point aEnd( m_nRedX, nY + nMax );
@@ -280,24 +290,27 @@ 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;
m_aClip.ChgClip( m_aRect, m_pTextFrame );
}
}
- const Color aOldCol( m_pSh->GetOut()->GetLineColor() );
- m_pSh->GetOut()->SetLineColor( SW_MOD()->GetRedlineMarkColor() );
+ m_pSh->GetOut()->Push(vcl::PushFlags::LINECOLOR);
+ m_pSh->GetOut()->SetLineColor(nWordSpacing ? COL_LIGHTRED : SwModule::get()->GetRedlineMarkColor());
- if ( m_pTextFrame->IsVertical() )
+ if ( nWordSpacing )
{
- m_pTextFrame->SwitchHorizontalToVertical( aStart );
- m_pTextFrame->SwitchHorizontalToVertical( aEnd );
- }
+ LineInfo aLineInfo;
+ aLineInfo.SetStyle(LineStyle::Solid);
+ aLineInfo.SetWidth( nWordSpacing * 2540/1440 );
- m_pSh->GetOut()->DrawLine( aStart, aEnd );
- m_pSh->GetOut()->SetLineColor( aOldCol );
+ m_pSh->GetOut()->DrawLine( aStart, aEnd, aLineInfo );
+ }
+ else
+ m_pSh->GetOut()->DrawLine( aStart, aEnd );
+ m_pSh->GetOut()->Pop();
}
void SwTextFrame::PaintExtraData( const SwRect &rRect ) const
@@ -305,26 +318,31 @@ void SwTextFrame::PaintExtraData( const SwRect &rRect ) const
if( getFrameArea().Top() > rRect.Bottom() || getFrameArea().Bottom() < rRect.Top() )
return;
+ PaintOutlineContentVisibilityButton();
+
SwDoc const& rDoc(GetDoc());
const IDocumentRedlineAccess& rIDRA = rDoc.getIDocumentRedlineAccess();
const SwLineNumberInfo &rLineInf = rDoc.GetLineNumberInfo();
const SwFormatLineNumber &rLineNum = GetAttrSet()->GetLineNumber();
bool bLineNum = !IsInTab() && rLineInf.IsPaintLineNumbers() &&
( !IsInFly() || rLineInf.IsCountInFlys() ) && rLineNum.IsCount();
- sal_Int16 eHor = static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos());
+ sal_Int16 eHor = static_cast<sal_Int16>(SwModule::get()->GetRedlineMarkPos());
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ bool bWordSpacingIndicator = officecfg::Office::Writer::Content::Display::ShowWordSpacingIndicator::get()
+ && pSh->GetViewOptions()->IsViewMetaChars();
if (eHor != text::HoriOrientation::NONE
+ && !bWordSpacingIndicator
&& (!IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
|| getRootFrame()->IsHideRedlines()))
{
eHor = text::HoriOrientation::NONE;
}
bool bRedLine = eHor != text::HoriOrientation::NONE;
- if ( !bLineNum && !bRedLine )
+ if ( !bLineNum && !bRedLine && !bWordSpacingIndicator )
return;
if( IsLocked() || IsHiddenNow() || !getFramePrintArea().Height() )
return;
- SwViewShell *pSh = getRootFrame()->GetCurrShell();
SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
SwRect rOldRect( rRect );
@@ -369,59 +387,47 @@ void SwTextFrame::PaintExtraData( const SwRect &rRect ) const
tools::Long nBottom = rRect.Bottom();
- bool bNoPrtLine = 0 == GetMinPrtLine();
- if( !bNoPrtLine )
- {
- while ( aLine.Y() < GetMinPrtLine() )
- {
- if( ( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() )
- && !aLine.GetCurr()->IsDummy() )
- aExtra.IncLineNr();
- if( !aLine.Next() )
- break;
- }
- bNoPrtLine = aLine.Y() >= GetMinPrtLine();
- }
const bool bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin();
- if( bNoPrtLine )
+ do
{
- do
+ if( bNoDummy || !aLine.GetCurr()->IsDummy() )
{
- if( bNoDummy || !aLine.GetCurr()->IsDummy() )
+ SwTwips nExtraSpaceSize = aLine.GetCurr()->GetFirstPortion()->ExtraSpaceSize();
+ if ( nExtraSpaceSize && bWordSpacingIndicator )
+ aExtra.PaintRedline( aLine.Y(), aLine.GetLineHeight(), nExtraSpaceSize );
+
+ bool bRed = bRedLine && aLine.GetCurr()->HasRedline();
+ if( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() )
{
- bool bRed = bRedLine && aLine.GetCurr()->HasRedline();
- if( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() )
+ bool bRedInMargin = bIsShowChangesInMargin && bRed;
+ bool bNum = bLineNum && ( aExtra.HasNumber() || aExtra.HasDivider() );
+ if( bRedInMargin || bNum )
{
- bool bRedInMargin = bIsShowChangesInMargin && bRed;
- bool bNum = bLineNum && ( aExtra.HasNumber() || aExtra.HasDivider() );
- if( bRedInMargin || bNum )
+ SwTwips nTmpHeight, nTmpAscent;
+ aLine.CalcAscentAndHeight( nTmpAscent, nTmpHeight );
+ if ( bRedInMargin )
{
- sal_uInt16 nTmpHeight, nTmpAscent;
- aLine.CalcAscentAndHeight( nTmpAscent, nTmpHeight );
- if ( bRedInMargin )
+ const OUString* pRedlineText = aLine.GetCurr()->GetRedlineText();
+ if( !pRedlineText->isEmpty() )
{
- const OUString* pRedlineText = aLine.GetCurr()->GetRedlineText();
- if( !pRedlineText->isEmpty() )
- {
- aExtra.PaintExtra( aLine.Y(), nTmpAscent,
- nTmpHeight, bRed, pRedlineText );
- bRed = false;
- bNum = false;
- }
- }
- if ( bNum )
- {
- aExtra.PaintExtra( aLine.Y(), nTmpAscent, nTmpHeight, bRed );
+ aExtra.PaintExtra( aLine.Y(), nTmpAscent,
+ nTmpHeight, bRed, pRedlineText );
bRed = false;
+ bNum = false;
}
}
- aExtra.IncLineNr();
+ if ( bNum )
+ {
+ aExtra.PaintExtra( aLine.Y(), nTmpAscent, nTmpHeight, bRed );
+ bRed = false;
+ }
}
- if( bRed )
- aExtra.PaintRedline( aLine.Y(), aLine.GetLineHeight() );
+ aExtra.IncLineNr();
}
- } while( aLine.Next() && aLine.Y() <= nBottom );
- }
+ if( bRed )
+ aExtra.PaintRedline( aLine.Y(), aLine.GetLineHeight() );
+ }
+ } while( aLine.Next() && aLine.Y() <= nBottom );
}
else
{
@@ -492,6 +498,8 @@ SwRect SwTextFrame::GetPaintSwRect()
bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const
{
+ PaintParagraphStylesHighlighting();
+
SwViewShell *pSh = getRootFrame()->GetCurrShell();
if( pSh && ( pSh->GetViewOptions()->IsParagraph() || bInitFont ) )
{
@@ -530,13 +538,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();
}
}
@@ -561,11 +569,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.0 < rFirstLine.GetTextFirstLineOffset().m_dValue)
+ {
+ aPos.AdjustX(rFirstLine.ResolveTextFirstLineOffset({}));
+ }
std::unique_ptr<SwSaveClip, o3tl::default_delete<SwSaveClip>> xClip;
if( IsUndersized() )
@@ -592,7 +602,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 );
@@ -616,7 +627,7 @@ bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const
pFnt->SetUnderline( LINESTYLE_NONE );
}
- pFnt->SetColor(NON_PRINTING_CHARACTER_COLOR);
+ pFnt->SetColor(pSh->GetViewOptions()->GetNonPrintingCharacterColor());
pFnt->DrawText_( aDrawInf );
}
}
@@ -628,19 +639,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, PaintFrameMode) 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;
@@ -667,6 +672,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));
@@ -740,21 +770,11 @@ void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
aLine.TwipsToLine( rRect.Top() + 1 );
tools::Long nBottom = rRect.Bottom();
- bool bNoPrtLine = 0 == GetMinPrtLine();
- if( !bNoPrtLine )
+ do
{
- while ( aLine.Y() < GetMinPrtLine() && aLine.Next() )
- ;
- bNoPrtLine = aLine.Y() >= GetMinPrtLine();
- }
- if( bNoPrtLine )
- {
- do
- {
- aLine.DrawTextLine( rRect, aClip, IsUndersized() );
+ aLine.DrawTextLine(rRect, aClip, IsUndersized(), oTaggedLabel, oTaggedParagraph, isPDFTaggingEnabled);
- } while( aLine.Next() && aLine.Y() <= nBottom );
- }
+ } while( aLine.Next() && aLine.Y() <= nBottom );
// Once is enough:
if( aLine.IsPaintDrop() )
@@ -764,10 +784,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..694a74361553 100644
--- a/sw/source/core/text/guess.cxx
+++ b/sw/source/core/text/guess.cxx
@@ -27,10 +27,13 @@
#include <com/sun/star/i18n/BreakType.hpp>
#include <com/sun/star/i18n/WordType.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/text/ParagraphHyphenationKeepType.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 +45,120 @@ 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 SwTextGuess::maybeAdjustPositionsForBlockAdjust(tools::Long& rMaxSizeDiff,
+ SwTwips& rExtraAscent, SwTwips& rExtraDescent,
+ const SwTextFormatInfo& rInf, const SwScriptInfo& rSI,
+ sal_uInt16 maxComp,
+ std::optional<SwLinePortionLayoutContext> nLayoutContext)
+{
+ 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(m_nCutPos); !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(m_nCutPos, 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
+ m_nBreakStart = m_nCutPos = newCutPos;
+ m_nBreakPos = breakPos;
+ // throw away old m_xHyphWord because the current break pos is now between words
+ m_xHyphWord = nullptr;
+
+ rInf.GetTextSize(&rSI, rInf.GetIdx(), breakPos - rInf.GetIdx(), nLayoutContext, maxComp,
+ m_nBreakWidth, rMaxSizeDiff, rExtraAscent, rExtraDescent,
+ rInf.GetCachedVclData().get());
+ rInf.GetTextSize(&rSI, breakPos, m_nBreakStart - breakPos, nLayoutContext, maxComp,
+ m_nExtraBlankWidth, rMaxSizeDiff, rExtraAscent, rExtraDescent,
+ 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,
+ sal_uInt16 nPropWordSpacing, sal_Int16 nSpaceWidth )
{
m_nCutPos = rInf.GetIdx();
@@ -61,50 +171,36 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
OSL_ENSURE( nPorHeight, "+SwTextGuess::Guess: no height" );
- sal_uInt16 nMaxSizeDiff;
+ tools::Long nMaxSizeDiff;
+ SwTwips nExtraAscent = 0;
+ SwTwips nExtraDescent = 0;
const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
- sal_uInt16 nMaxComp = ( SwFontScript::CJK == rInf.GetFont()->GetActual() ) &&
- rSI.CountCompChg() &&
- ! rInf.IsMulti() &&
- ! rPor.InFieldGrp() &&
- ! rPor.IsDropPortion() ?
- 10000 :
- 0 ;
+ const sal_uInt16 nMaxComp = rPor.GetMaxComp(rInf);
SwTwips nLineWidth = rInf.GetLineWidth();
TextFrameIndex nMaxLen = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx();
- const SvxAdjust& rAdjust = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust().GetAdjust();
+ SvxAdjustItem aAdjustItem = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
+ const SvxAdjust aAdjust = aAdjustItem.GetAdjust();
+ // Maximum word spacing allows bigger spaces to limit hyphenation,
+ // implement it based on the hyphenation zone: calculate a hyphenation zone
+ // from maximum word spacing and space count of the line
+ SwTwips nWordSpacingMaximumZone = 0;
- // tdf#104668 space chars at the end should be cut if the compatibility option is enabled
- // for LTR mode only
- if ( !rInf.GetTextFrame()->IsRightToLeft() )
+ if ( nSpacesInLine )
{
- if (rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
- DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
+ SwTwips nExtraSpace = nSpacesInLine * nSpaceWidth/10.0 * (1.0 - nPropWordSpacing / 100.0);
+ nLineWidth += nExtraSpace;
+ // convert maximum word spacing to hyphenation zone, if defined
+ if ( nPropWordSpacing == aAdjustItem.GetPropWordSpacing() )
{
- 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;
- }
- }
+ SwTwips nMaxDif = aAdjustItem.GetPropWordSpacingMaximum() - nPropWordSpacing;
+ nWordSpacingMaximumZone = nSpacesInLine * nSpaceWidth/10.0 * nMaxDif / 100.0;
}
+
+ rInf.SetExtraSpace(nExtraSpace);
}
if ( rInf.GetLen() < nMaxLen )
@@ -123,7 +219,7 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
{
SwTextGridItem const*const pGrid(
GetGridItem(rInf.GetTextFrame()->FindPageFrame()));
- bAddItalic = !pGrid || GRID_LINES_CHARS != pGrid->GetGridType();
+ bAddItalic = !pGrid || SwTextGrid::LinesAndChars != pGrid->GetGridType();
}
// do not add extra italic value for an isolated blank:
@@ -163,31 +259,57 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
( bUnbreakableNumberings && rPor.IsNumberPortion() ) )
{
// call GetTextSize with maximum compression (for kanas)
- rInf.GetTextSize( &rSI, rInf.GetIdx(), nMaxLen,
- nMaxComp, m_nBreakWidth, nMaxSizeDiff );
+ rInf.GetTextSize(&rSI, rInf.GetIdx(), nMaxLen, rInf.GetLayoutContext(), nMaxComp,
+ m_nBreakWidth, nMaxSizeDiff, nExtraAscent, nExtraDescent);
if ( ( m_nBreakWidth <= nLineWidth ) || ( bUnbreakableNumberings && rPor.IsNumberPortion() ) )
{
// portion fits to line
m_nCutPos = rInf.GetIdx() + nMaxLen;
+ bool bRet = rPor.InFieldGrp()
+ || maybeAdjustPositionsForBlockAdjust(
+ nMaxSizeDiff, nExtraAscent, nExtraDescent, rInf,
+ rSI, nMaxComp, rInf.GetLayoutContext());
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 )
rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
+ rInf.SetExtraAscent(nExtraAscent);
+ rInf.SetExtraDescent(nExtraDescent);
+
m_nBreakWidth += nLeftRightBorderSpace;
- return true;
+ return bRet;
}
}
- bool bHyph = rInf.IsHyphenate() && !rInf.IsHyphForbud();
+ bool bHyph = rInf.IsHyphenate() && !rInf.IsHyphForbud() &&
+ // disable hyphenation at minimum word spacing
+ // (and at weighted word spacing between minimum and desired word spacing)
+ !( nPropWordSpacing < aAdjustItem.GetPropWordSpacing() );
+
+ // disable hyphenation according to hyphenation-keep and hyphenation-keep-type,
+ // or modify hyphenation according to hyphenation-zone-column/page/spread (see widorp.cxx)
+ sal_Int16 nEndZone = 0;
+ if ( bHyph &&
+ rInf.GetTextFrame()->GetNoHyphOffset() != TextFrameIndex(COMPLETE_STRING) && // ) // &&
+ // when there is a portion in the last line, rInf.GetIdx() > GetNoHyphOffset()
+ rInf.GetTextFrame()->GetNoHyphOffset() <= rInf.GetIdx() )
+ {
+ nEndZone = rInf.GetTextFrame()->GetNoHyphEndZone();
+ // disable hyphenation in the last line, when hyphenation-keep-line is enabled
+ // and hyphenation-keep, too (i.e. when end zone is not defined),
+ // also when the end zone is bigger, than the line width
+ if ( nEndZone < 0 || nEndZone >= nLineWidth )
+ bHyph = false;
+ }
TextFrameIndex nHyphPos(0);
// nCutPos is the first character not fitting to the current line
@@ -195,8 +317,181 @@ 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;
+ sal_Int32 nParaZone = -1;
+ const css::beans::PropertyValues & rHyphValues = rInf.GetHyphValues();
+ assert( rHyphValues.getLength() > 10 && rHyphValues[5].Name == UPN_HYPH_ZONE && rHyphValues[10].Name == UPN_HYPH_ZONE_ALWAYS );
+ // hyphenation zone (distance from the line end in twips)
+ sal_Int16 nTextHyphenZone = 0;
+ sal_Int16 nTextHyphenZoneAlways = 0;
+ rHyphValues[5].Value >>= nTextHyphenZone;
+
+ // maximum word spacing can result bigger hyphenation zone,
+ // if there are enough spaces in the line: apply that
+ if ( nWordSpacingMaximumZone > nTextHyphenZone )
+ nTextHyphenZone = nWordSpacingMaximumZone;
+
+ rHyphValues[10].Value >>= nTextHyphenZoneAlways;
+ if ( nTextHyphenZone || nTextHyphenZoneAlways || nEndZone )
+ {
+ nHyphZone = nTextHyphenZone >= nLineWidth
+ ? 0
+ : sal_Int32(rInf.GetTextBreak( nLineWidth - (nEndZone ? nEndZone : nTextHyphenZone),
+ nMaxLen, nMaxComp, rInf.GetCachedVclData().get() ));
+ }
m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, nHyphPos, rInf.GetCachedVclData().get() );
+ if ( !nEndZone && nTextHyphenZoneAlways &&
+ // if paragraph end zone is not different from the hyphenation zone, skip its handling
+ nTextHyphenZoneAlways != nTextHyphenZone &&
+ // end of the paragraph
+ rInf.GetText().getLength() - sal_Int32(nHyphZone) <
+ sal_Int32(m_nCutPos) - sal_Int32(rInf.GetLineStart() ) )
+ {
+ nParaZone = nTextHyphenZoneAlways >= nLineWidth
+ ? 0
+ : sal_Int32(rInf.GetTextBreak( nLineWidth - nTextHyphenZoneAlways,
+ nMaxLen, nMaxComp, rInf.GetCachedVclData().get() ));
+ }
+
+ // don't try to hyphenate in the hyphenation zone or in the paragraph end zone
+ // maximum end zone is the lower non-negative text break position
+ // (-1 means that no hyphenation zone is defined)
+ sal_Int32 nMaxZone = nParaZone > -1 && nParaZone < nHyphZone
+ ? nParaZone
+ : nHyphZone;
+ if ( nMaxZone != -1 && TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
+ {
+ sal_Int32 nZonePos = sal_Int32(m_nCutPos);
+ // disable hyphenation, if there is a space within the hyphenation or end zones
+ // 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.GetLineStart()) <= nZonePos && nMaxZone <= nZonePos; --nZonePos )
+ {
+ sal_Unicode cChar = rInf.GetText()[nZonePos];
+ if ( cChar == CH_BLANK || cChar == CH_FULL_BLANK || cChar == CH_SIX_PER_EM )
+ {
+ // no column/page/spread/end zone (!nEndZone),
+ // but is it good for a paragraph end zone?
+ if ( nParaZone != -1 && nParaZone <= nZonePos &&
+ // still end of the paragraph, i.e. still more characters in the original
+ // last full line, then in the planned last paragraph line
+ // FIXME: guarantee, that last not full line won't become a full line
+ rInf.GetText().getLength() - sal_Int32(nZonePos) <
+ sal_Int32(m_nCutPos) - sal_Int32(rInf.GetLineStart() ) )
+ {
+ bHyph = false;
+ }
+ // otherwise disable hyphenation, if there is a space in the hyphenation zone
+ else if ( nHyphZone != 1 && nHyphZone <= nZonePos )
+ {
+ bHyph = false;
+ }
+ // set applied end zone
+ if ( !bHyph && nEndZone )
+ rInf.GetTextFrame()->SetNoHyphOffset(TextFrameIndex(COMPLETE_STRING));
+ }
+ }
+ }
+
+ 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 of the line
+ sal_Int32 nLastWord = rInf.GetText().getLength() - 1;
+ bool bDoNotHyphenateLastLine = false; // don't hyphenate last full line of the paragraph
+ bool bHyphenationNoLastWord = false; // do not hyphenate the last word of the paragraph
+ assert( rHyphValues.getLength() > 3 && rHyphValues[3].Name == UPN_HYPH_NO_LAST_WORD );
+ assert( rHyphValues.getLength() > 6 && rHyphValues[6].Name == UPN_HYPH_KEEP_TYPE );
+ assert( rHyphValues.getLength() > 8 && rHyphValues[8].Name == UPN_HYPH_KEEP );
+ rHyphValues[3].Value >>= bHyphenationNoLastWord;
+ rHyphValues[8].Value >>= bDoNotHyphenateLastLine;
+ if ( bDoNotHyphenateLastLine )
+ {
+ sal_Int16 nKeepType = css::text::ParagraphHyphenationKeepType::COLUMN;
+ rHyphValues[6].Value >>= nKeepType;
+ if ( nKeepType == css::text::ParagraphHyphenationKeepType::ALWAYS )
+ {
+ if ( TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
+ nLastWord = sal_Int32(m_nCutPos);
+ }
+ else
+ bDoNotHyphenateLastLine = false;
+ }
+
+ if ( bHyphenationNoLastWord || bDoNotHyphenateLastLine )
+ {
+ // 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 line
+ if ( ( bHyphenationNoLastWord || bDoNotHyphenateLastLine ) &&
+ 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.GetLineStart()) < nLastWord &&
+ // if the case of bDoNotHyphenateLastLine == true, skip hyphenation
+ // only if the character length of the very last line of the paragraph
+ // would be still less, than the length of the recent last but one line
+ // with hyphenation, i.e. don't skip hyphenation, if the last paragraph
+ // line is already near full.
+ ( !bDoNotHyphenateLastLine ||
+ // FIXME: character count is not fail-safe: remaining characters
+ // can exceed the line, resulting two last full paragraph lines
+ // with disabled hyphenation.
+ rInf.GetText().getLength() - sal_Int32(nLastWord) <
+ sal_Int32(m_nCutPos) - sal_Int32(rInf.GetLineStart() ) ) )
+ {
+ m_nCutPos = TextFrameIndex(nLastWord);
+ }
+ }
+
if ( !nHyphPos && rInf.GetIdx() )
nHyphPos = rInf.GetIdx() - TextFrameIndex(1);
}
@@ -207,9 +502,9 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
#if OSL_DEBUG_LEVEL > 1
if ( TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
{
- sal_uInt16 nMinSize;
- rInf.GetTextSize( &rSI, rInf.GetIdx(), m_nCutPos - rInf.GetIdx(),
- nMaxComp, nMinSize, nMaxSizeDiff );
+ SwTwips nMinSize;
+ rInf.GetTextSize(&rSI, rInf.GetIdx(), m_nCutPos - rInf.GetIdx(), std::nullopt, nMaxComp,
+ nMinSize, nMaxSizeDiff, nExtraAscent, nExtraDescent);
OSL_ENSURE( nMinSize <= nLineWidth, "What a Guess!!!" );
}
#endif
@@ -219,23 +514,31 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
{
// second check if everything fits to line
m_nCutPos = m_nBreakPos = rInf.GetIdx() + nMaxLen - TextFrameIndex(1);
- rInf.GetTextSize( &rSI, rInf.GetIdx(), nMaxLen, nMaxComp,
- m_nBreakWidth, nMaxSizeDiff );
+ rInf.GetTextSize(&rSI, rInf.GetIdx(), nMaxLen, rInf.GetLayoutContext(), nMaxComp,
+ m_nBreakWidth, nMaxSizeDiff, nExtraAscent, nExtraDescent);
// The following comparison should always give true, otherwise
// there likely has been a pixel rounding error in GetTextBreak
if ( m_nBreakWidth <= nLineWidth )
{
+ bool bRet = rPor.InFieldGrp()
+ || maybeAdjustPositionsForBlockAdjust(
+ nMaxSizeDiff, nExtraAscent, nExtraDescent, rInf,
+ rSI, nMaxComp, rInf.GetLayoutContext());
+
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 )
rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
+ rInf.SetExtraAscent(nExtraAscent);
+ rInf.SetExtraDescent(nExtraDescent);
+
m_nBreakWidth += nLeftRightBorderSpace;
- return true;
+ return bRet;
}
}
@@ -250,36 +553,13 @@ 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();
+ // throw away old m_xHyphWord when m_nBreakStart changes
+ m_xHyphWord = nullptr;
}
else
{
@@ -339,12 +619,12 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
// We have to switch the current language if we have a script
// change at nCutPos. Otherwise LATIN punctuation would never
// be allowed to be hanging punctuation.
- // NEVER call GetLang if the string has been modified!!!
+ // NEVER call GetLangOfChar if the string has been modified!!!
LanguageType aLang = rInf.GetFont()->GetLanguage();
// If we are inside a field portion, we use a temporary string which
// differs from the string at the textnode. Therefore we are not allowed
- // to call the GetLang function.
+ // to call the GetLangOfChar function.
if ( m_nCutPos && ! rPor.InFieldGrp() )
{
const CharClass& rCC = GetAppCharClass();
@@ -423,6 +703,27 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
m_nBreakStart = m_nBreakPos;
bHyph = BreakType::HYPHENATION == aResult.breakType;
+ if (bHyph)
+ {
+ LanguageType aNoHyphLang;
+ if (rPor.InFieldGrp())
+ {
+ // If we are inside a field portion, we use a temporary string which
+ // differs from the string at the textnode. Therefore we are not allowed
+ // to call the GetLangOfChar function.
+ aNoHyphLang = LANGUAGE_DONTKNOW;
+ }
+ else
+ {
+ // allow hyphenation of the word only if it's not disabled by character formatting
+ aNoHyphLang = rInf.GetTextFrame()->GetLangOfChar(
+ TextFrameIndex( sal_Int32(m_nBreakPos) +
+ aResult.rHyphenatedWord->getHyphenationPos() ),
+ 1, true, /*bNoneIfNoHyphenation=*/true );
+ }
+ // allow hyphenation of the word only if it's not disabled by character formatting
+ bHyph = aNoHyphLang != LANGUAGE_NONE;
+ }
if (bHyph && m_nBreakPos != TextFrameIndex(COMPLETE_STRING))
{
@@ -465,7 +766,7 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
m_nBreakPos = rInf.GetIdx() - TextFrameIndex(1);
}
- if( rAdjust != SvxAdjust::Left )
+ if( aAdjust != SvxAdjust::Left )
{
// Delete any blanks at the end of a line, but be careful:
// If a field has been expanded, we do not want to delete any
@@ -494,10 +795,10 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
if (m_nBreakPos > m_nCutPos && m_nBreakPos != TextFrameIndex(COMPLETE_STRING))
{
const TextFrameIndex nHangingLen = m_nBreakPos - m_nCutPos;
- SwPosSize aTmpSize = rInf.GetTextSize( &rSI, m_nCutPos, nHangingLen );
+ SwPositiveSize 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();
}
@@ -535,19 +836,30 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
if( nPorLen )
{
- rInf.GetTextSize( &rSI, rInf.GetIdx(), nPorLen,
- nMaxComp, m_nBreakWidth, nMaxSizeDiff,
- rInf.GetCachedVclData().get() );
+ rInf.GetTextSize(&rSI, rInf.GetIdx(), nPorLen, std::nullopt, nMaxComp, m_nBreakWidth,
+ nMaxSizeDiff, nExtraAscent, nExtraDescent, rInf.GetCachedVclData().get());
+ rInf.SetBreakWidth(m_nBreakWidth);
// save maximum width for later use
if ( nMaxSizeDiff )
rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
+ rInf.SetExtraAscent(nExtraAscent);
+ rInf.SetExtraDescent(nExtraDescent);
+
m_nBreakWidth += nItalic + nLeftRightBorderSpace;
}
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, std::nullopt,
+ nMaxComp, m_nExtraBlankWidth, nMaxSizeDiff, nExtraAscent, nExtraDescent,
+ 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..5c1d0d387b16 100644
--- a/sw/source/core/text/guess.hxx
+++ b/sw/source/core/text/guess.hxx
@@ -32,32 +32,38 @@ class SwTextGuess
{
css::uno::Reference< css::linguistic2::XHyphenatedWord > m_xHyphWord;
std::unique_ptr<SwHangingPortion> m_pHanging; // for hanging punctuation
- TextFrameIndex m_nCutPos; // this character doesn't fit
- TextFrameIndex m_nBreakStart; // start index of word containing line break
- TextFrameIndex m_nBreakPos; // start index of break position
- TextFrameIndex m_nFieldDiff; // absolute positions can be wrong if we
+ TextFrameIndex m_nCutPos{ 0 }; // this character doesn't fit
+ TextFrameIndex m_nBreakStart{ 0 }; // start index of word containing line break
+ TextFrameIndex m_nBreakPos{ 0 }; // start index of break position
+ TextFrameIndex m_nFieldDiff{ 0 }; // absolute positions can be wrong if we
// a field in the text has been expanded
- sal_uInt16 m_nBreakWidth; // width of the broken portion
+ SwTwips m_nBreakWidth{ 0 }; // width of the broken portion
+ SwTwips m_nExtraBlankWidth{ 0 }; // width of spaces after the break
public:
- SwTextGuess(): m_nCutPos(0), m_nBreakStart(0),
- m_nBreakPos(0), m_nFieldDiff(0), m_nBreakWidth(0)
- { }
+ SwTextGuess() = default;
// 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,
+ sal_uInt16 nPropWordSpacing = 100, sal_Int16 nSpaceWidth = 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; }
+ SwTwips BreakWidth() const { return m_nBreakWidth; }
+ SwTwips ExtraBlankWidth() const { return m_nExtraBlankWidth; }
TextFrameIndex CutPos() const { return m_nCutPos; }
TextFrameIndex BreakStart() const { return m_nBreakStart; }
TextFrameIndex BreakPos() const {return m_nBreakPos; }
TextFrameIndex FieldDiff() const {return m_nFieldDiff; }
const css::uno::Reference< css::linguistic2::XHyphenatedWord >& HyphWord() const
{ return m_xHyphWord; }
+private:
+ bool maybeAdjustPositionsForBlockAdjust(tools::Long& rMaxSizeDiff,
+ SwTwips& rExtraAscent, SwTwips& rExtraDescent,
+ const SwTextFormatInfo& rInf, const SwScriptInfo& rSI,
+ sal_uInt16 maxComp,
+ std::optional<SwLinePortionLayoutContext> nLayoutContext);
};
-
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index bb63a36ae157..7c85cc95a8a8 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -21,15 +21,17 @@
#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>
+#include <sfx2/StylePreviewRenderer.hxx>
#include <sal/log.hxx>
#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,19 +40,22 @@
#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 <fmteiro.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentDeviceAccess.hxx>
#include <IDocumentMarkAccess.hxx>
#include <paratr.hxx>
+#include <sectfrm.hxx>
#include <rootfrm.hxx>
#include "inftxt.hxx"
#include <noteurl.hxx>
+#include "porfly.hxx"
#include "porftn.hxx"
#include "porrst.hxx"
#include "itratr.hxx"
@@ -66,13 +71,25 @@
#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 <names.hxx>
+#include <com/sun/star/awt/FontSlant.hpp>
using namespace ::com::sun::star;
using namespace ::com::sun::star::linguistic2;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
-#define CHAR_UNDERSCORE u'_'
#define CHAR_LEFT_ARROW u'\x25C0'
#define CHAR_RIGHT_ARROW u'\x25B6'
#define CHAR_TAB u'\x2192'
@@ -99,7 +116,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 +124,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 +141,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;
}
}
@@ -137,7 +154,7 @@ void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet,
m_pSpace = &rAttrSet.GetLineSpacing();
m_nVertAlign = rAttrSet.GetParaVertAlign().GetValue();
- m_nDefTabStop = USHRT_MAX;
+ m_nDefTabStop = std::numeric_limits<SwTwips>::max();
}
void SwTextInfo::CtorInitTextInfo( SwTextFrame *pFrame )
@@ -191,6 +208,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)
@@ -206,6 +224,8 @@ SwTextSizeInfo::SwTextSizeInfo()
, m_bForbiddenChars(false)
, m_bSnapToGrid(false)
, m_nDirection(0)
+, m_nExtraSpace(0)
+, m_nBreakWidth(0)
{}
SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew )
@@ -221,6 +241,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() ),
@@ -235,7 +256,9 @@ SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew )
m_bScriptSpace( rNew.HasScriptSpace() ),
m_bForbiddenChars( rNew.HasForbiddenChars() ),
m_bSnapToGrid( rNew.SnapToGrid() ),
- m_nDirection( rNew.GetDirection() )
+ m_nDirection( rNew.GetDirection() ),
+ m_nExtraSpace( rNew.GetExtraSpace() ),
+ m_nBreakWidth( rNew.GetBreakWidth() )
{
#if OSL_DEBUG_LEVEL > 0
ChkOutDev( *this );
@@ -247,6 +270,8 @@ void SwTextSizeInfo::CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextF
{
m_pKanaComp = nullptr;
m_nKanaIdx = 0;
+ m_nExtraSpace = 0;
+ m_nBreakWidth = 0;
m_pFrame = pFrame;
CtorInitTextInfo( m_pFrame );
SwDoc const& rDoc(m_pFrame->GetDoc());
@@ -280,14 +305,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;
}
@@ -295,7 +320,7 @@ void SwTextSizeInfo::CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextF
m_pOpt = m_pVsh ?
m_pVsh->GetViewOptions() :
- SW_MOD()->GetViewOption(rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)); // Options from Module, due to StarONE
+ SwModule::get()->GetViewOption(rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)); // Options from Module, due to StarONE
// bURLNotify is set if MakeGraphic prepares it
// TODO: Unwind
@@ -309,7 +334,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 +357,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() ),
@@ -346,7 +372,9 @@ SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew, const OUString* pTex
m_bScriptSpace( rNew.HasScriptSpace() ),
m_bForbiddenChars( rNew.HasForbiddenChars() ),
m_bSnapToGrid( rNew.SnapToGrid() ),
- m_nDirection( rNew.GetDirection() )
+ m_nDirection( rNew.GetDirection() ),
+ m_nExtraSpace( rNew.GetExtraSpace() ),
+ m_nBreakWidth( rNew.GetBreakWidth() )
{
#if OSL_DEBUG_LEVEL > 0
ChkOutDev( *this );
@@ -379,21 +407,23 @@ void SwTextSizeInfo::NoteAnimation() const
"SwTextSizeInfo::NoteAnimation() changed m_pOut" );
}
-SwPosSize SwTextSizeInfo::GetTextSize( OutputDevice* pOutDev,
+SwPositiveSize SwTextSizeInfo::GetTextSize( OutputDevice* pOutDev,
const SwScriptInfo* pSI,
const OUString& rText,
const TextFrameIndex nIndex,
const TextFrameIndex nLength) const
{
- SwDrawTextInfo aDrawInf( m_pVsh, *pOutDev, pSI, rText, nIndex, nLength );
+ SwDrawTextInfo aDrawInf(m_pVsh, *pOutDev, pSI, rText, nIndex, nLength,
+ /*layout context*/ std::nullopt);
aDrawInf.SetFrame( m_pFrame );
aDrawInf.SetFont( m_pFnt );
aDrawInf.SetSnapToGrid( SnapToGrid() );
aDrawInf.SetKanaComp( 0 );
- return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
+ return SwPositiveSize(m_pFnt->GetTextSize_( aDrawInf ));
}
-SwPosSize SwTextSizeInfo::GetTextSize() const
+SwPositiveSize
+SwTextSizeInfo::GetTextSize(std::optional<SwLinePortionLayoutContext> nLayoutContext) const
{
const SwScriptInfo& rSI =
const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
@@ -406,41 +436,47 @@ SwPosSize SwTextSizeInfo::GetTextSize() const
GetKanaComp() :
0 ;
- SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rSI, *m_pText, m_nIdx, m_nLen );
+ SwDrawTextInfo aDrawInf(m_pVsh, *m_pOut, &rSI, *m_pText, m_nIdx, m_nLen, nLayoutContext);
+ aDrawInf.SetMeasureLen( m_nMeasureLen );
aDrawInf.SetFrame( m_pFrame );
aDrawInf.SetFont( m_pFnt );
aDrawInf.SetSnapToGrid( SnapToGrid() );
aDrawInf.SetKanaComp( nComp );
- return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
+ return SwPositiveSize(m_pFnt->GetTextSize_( aDrawInf ));
}
-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
+void SwTextSizeInfo::GetTextSize(const SwScriptInfo* pSI, const TextFrameIndex nIndex,
+ const TextFrameIndex nLength,
+ std::optional<SwLinePortionLayoutContext> nLayoutContext,
+ const sal_uInt16 nComp, SwTwips& nMinSize,
+ tools::Long& nMaxSizeDiff, SwTwips& nExtraAscent,
+ SwTwips& nExtraDescent,
+ vcl::text::TextLayoutCache const* const pCache) const
{
- SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, pSI, *m_pText, nIndex, nLength,
- 0, false, pCache);
+ SwDrawTextInfo aDrawInf(m_pVsh, *m_pOut, pSI, *m_pText, nIndex, nLength, nLayoutContext, 0,
+ false, pCache);
aDrawInf.SetFrame( m_pFrame );
aDrawInf.SetFont( m_pFnt );
aDrawInf.SetSnapToGrid( SnapToGrid() );
aDrawInf.SetKanaComp( nComp );
- SwPosSize aSize( m_pFnt->GetTextSize_( aDrawInf ) );
- nMaxSizeDiff = static_cast<sal_uInt16>(aDrawInf.GetKanaDiff());
+ SwPositiveSize aSize( m_pFnt->GetTextSize_( aDrawInf ) );
+ nMaxSizeDiff = aDrawInf.GetKanaDiff();
+ nExtraAscent = aDrawInf.GetExtraAscent();
+ nExtraDescent = aDrawInf.GetExtraDescent();
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();
OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
- SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
- *m_pText, GetIdx(), nMaxLen, 0, false, pCache );
+ SwDrawTextInfo aDrawInf(m_pVsh, *m_pOut, &rScriptInfo, *m_pText, GetIdx(), nMaxLen,
+ /*layout context*/ std::nullopt, 0, false, pCache);
aDrawInf.SetFrame( m_pFrame );
aDrawInf.SetFont( m_pFnt );
aDrawInf.SetSnapToGrid( SnapToGrid() );
@@ -454,14 +490,14 @@ 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();
OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
- SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
- *m_pText, GetIdx(), nMaxLen, 0, false, pCache );
+ SwDrawTextInfo aDrawInf(m_pVsh, *m_pOut, &rScriptInfo, *m_pText, GetIdx(), nMaxLen,
+ /*layout context*/ std::nullopt, 0, false, pCache);
aDrawInf.SetFrame( m_pFrame );
aDrawInf.SetFont( m_pFnt );
aDrawInf.SetSnapToGrid( SnapToGrid() );
@@ -521,39 +557,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
{
/**
@@ -562,7 +565,7 @@ namespace
*/
class SwTransparentTextGuard
{
- ScopedVclPtrInstance<VirtualDevice> m_aContentVDev;
+ ScopedVclPtrInstance<VirtualDevice> m_aContentVDev { DeviceFormat::WITH_ALPHA };
GDIMetaFile m_aContentMetafile;
MapMode m_aNewMapMode;
SwRect m_aPorRect;
@@ -604,7 +607,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);
@@ -619,6 +622,35 @@ SwTransparentTextGuard::~SwTransparentTextGuard()
}
}
+static bool lcl_IsFrameReadonly(SwTextFrame* pFrame)
+{
+ const SwFlyFrame* pFly;
+ const SwSection* pSection;
+
+ if( pFrame && pFrame->IsInFly())
+ {
+ pFly = pFrame->FindFlyFrame();
+ if (pFly->GetFormat()->GetEditInReadonly().GetValue())
+ {
+ const SwFrame* pLower = pFly->Lower();
+ if (pLower && !pLower->IsNoTextFrame())
+ {
+ return false;
+ }
+ }
+ }
+ // edit in readonly sections
+ else if ( pFrame && pFrame->IsInSct() &&
+ nullptr != ( pSection = pFrame->FindSctFrame()->GetSection() ) &&
+ pSection->IsEditInReadonlyFlag() )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPor,
TextFrameIndex const nStart, TextFrameIndex const nLength,
const bool bKern, const bool bWrong,
@@ -642,18 +674,25 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPo
bool bCfgIsAutoGrammar = false;
SvtLinguConfig().GetProperty( UPN_IS_GRAMMAR_AUTO ) >>= bCfgIsAutoGrammar;
const bool bBullet = OnWin() && GetOpt().IsBlank() && IsNoSymbol();
- const bool bTmpWrong = bWrong && OnWin() && GetOpt().IsOnlineSpell();
+ bool bTmpWrong = bWrong && OnWin() && GetOpt().IsOnlineSpell();
+ SfxObjectShell* pObjShell = m_pFrame->GetDoc().GetDocShell();
+ if (bTmpWrong && pObjShell)
+ {
+ if (pObjShell->IsReadOnly() && lcl_IsFrameReadonly(m_pFrame))
+ bTmpWrong = false;
+ }
+
const bool bTmpGrammarCheck = bGrammarCheck && OnWin() && bCfgIsAutoGrammar && GetOpt().IsOnlineSpell();
const bool bTmpSmart = bSmartTag && OnWin() && !GetOpt().IsPagePreview() && SwSmartTagMgr::Get().IsSmartTagsEnabled();
OSL_ENSURE( GetParaPortion(), "No paragraph!");
- SwDrawTextInfo aDrawInf( m_pFrame->getRootFrame()->GetCurrShell(), *m_pOut, pSI, rText, nStart, nLength,
- rPor.Width(), bBullet );
+ SwDrawTextInfo aDrawInf(m_pFrame->getRootFrame()->GetCurrShell(), *m_pOut, pSI, rText, nStart,
+ nLength, rPor.GetLayoutContext(), rPor.Width(), bBullet);
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 +720,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 +755,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() )
@@ -954,8 +995,8 @@ 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()) );
+ SwTwips nOldWidth = rPor.Width();
+ const_cast<SwLinePortion&>(rPor).Width(aFontSize.Width());
rTextPaintInfo.DrawText( aTmp, rPor );
const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
rNonConstTextPaintInfo.SetFont( const_cast<SwFont*>(pOldFnt) );
@@ -988,7 +1029,7 @@ void SwTextPaintInfo::DrawTab( const SwLinePortion &rPor ) const
const sal_Unicode cChar = GetTextFrame()->IsRightToLeft() ? CHAR_TAB_RTL : CHAR_TAB;
const sal_uInt8 nOptions = DRAW_SPECIAL_OPTIONS_CENTER | DRAW_SPECIAL_OPTIONS_ROTATE;
- lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
+ lcl_DrawSpecial( *this, rPor, aRect, SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor(), cChar, nOptions );
}
void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const
@@ -996,7 +1037,14 @@ void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const
if( !OnWin() )
return;
- sal_uInt16 nOldWidth = rPor.Width();
+ SwLineBreakClear eClear = SwLineBreakClear::NONE;
+ if (rPor.IsBreakPortion())
+ {
+ const auto& rBreakPortion = static_cast<const SwBreakPortion&>(rPor);
+ eClear = rBreakPortion.GetClear();
+ }
+
+ SwTwips nOldWidth = rPor.Width();
const_cast<SwLinePortion&>(rPor).Width( LINE_BREAK_WIDTH );
SwRect aRect;
@@ -1008,7 +1056,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, SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor(), cChar, nOptions );
+
+ if (eClear != SwLineBreakClear::NONE)
+ {
+ // Paint indicator if this clear is left/right/all.
+ m_pOut->Push(vcl::PushFlags::LINECOLOR);
+ m_pOut->SetLineColor(SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor());
+ 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 );
@@ -1054,7 +1119,7 @@ void SwTextPaintInfo::DrawPostIts( bool bScript ) const
Size aSize;
Point aTmp;
- const sal_uInt16 nPostItsWidth = SwViewOption::GetPostItsWidth( GetOut() );
+ const SwTwips nPostItsWidth = SwViewOption::GetPostItsWidth(GetOut());
const sal_uInt16 nFontHeight = m_pFnt->GetHeight( m_pVsh, *GetOut() );
const sal_uInt16 nFontAscent = m_pFnt->GetAscent( m_pVsh, *GetOut() );
@@ -1089,8 +1154,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 +1165,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 +1192,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 +1203,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();
@@ -1161,8 +1224,8 @@ void SwTextPaintInfo::DrawBackBrush( const SwLinePortion &rPor ) const
if(aIntersect.HasArea())
{
SwPosition const aPosition(m_pFrame->MapViewToModelPos(GetIdx()));
- const ::sw::mark::IMark* pFieldmark =
- m_pFrame->GetDoc().getIDocumentMarkAccess()->getFieldmarkFor(aPosition);
+ const ::sw::mark::MarkBase* pFieldmark =
+ m_pFrame->GetDoc().getIDocumentMarkAccess()->getInnerFieldmarkFor(aPosition);
bool bIsStartMark = (TextFrameIndex(1) == GetLen()
&& CH_TXT_ATR_FIELDSTART == GetText()[sal_Int32(GetIdx())]);
if(pFieldmark) {
@@ -1171,12 +1234,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,175 +1271,312 @@ 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() )
+ pTmpOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+
+ if (aFillColor == COL_TRANSPARENT)
+ pTmpOut->SetFillColor();
+ else
+ pTmpOut->SetFillColor(aFillColor);
+ pTmpOut->SetLineColor();
+
+ DrawRect( aIntersect, false );
+
+ pTmpOut->Pop();
+}
+
+void SwTextPaintInfo::DrawBorder( const SwLinePortion &rPor ) const
+{
+ SwRect aDrawArea;
+ CalcRect( rPor, &aDrawArea );
+ if ( aDrawArea.HasArea() )
{
- 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;
+ PaintCharacterBorder(*m_pFnt, aDrawArea, GetTextFrame()->IsVertical(),
+ GetTextFrame()->IsVertLRBT(), rPor.GetJoinBorderWithPrev(),
+ rPor.GetJoinBorderWithNext());
+ }
+}
- 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 );
+namespace {
- drawcontinue:
+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;
+}
+}
- if ( !draw )
- return;
+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;
- if ( !full )
- {
- pPos = const_cast<SwLinePortion *>(&rPor);
- nIdx = GetIdx();
+ if (!pView->IsSpotlightCharStyles() && !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(u"CharStyleName"_ustr) >>= sCurrentCharStyle;
- nLen = pPos->GetLen();
- for (TextFrameIndex i = nIdx + nLen - TextFrameIndex(1);
- i >= nIdx; --i)
+ 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())
+ {
+ UIName sCharStyleDisplayName = SwStyleNameMapper::GetUIName(ProgName(sCurrentCharStyle),
+ SwGetPoolIdFromName::ChrFmt);
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // For simplicity in kit mode, we render in the document "all styles" that exist
+ if (const SwCharFormat* pCharFormat = pFrame->GetDoc().FindCharFormatByName(sCharStyleDisplayName))
+ {
+ // Do this so these are stable across views regardless of an individual
+ // user's selection mode in the style panel.
+ sCSNumberOrDF = OUString::number(pFrame->GetDoc().GetCharFormats()->GetPos(pCharFormat));
+ aFillColor = ColorHash(sCharStyleDisplayName.toString());
+ }
+ }
+ else
+ {
+ if (!sCharStyleDisplayName.isEmpty())
+ {
+ StylesSpotlightColorMap& rCharStylesColorMap = pView->GetStylesSpotlightCharColorMap();
+ auto it = rCharStylesColorMap.find(sCharStyleDisplayName.toString());
+ if (it != rCharStylesColorMap.end())
{
- 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();
+ sCSNumberOrDF = OUString::number(it->second.second);
+ aFillColor = it->second.first;
+ }
+ }
+ }
+ }
+ // 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;
- const_cast<SwLinePortion&>(rPor).Width( nNewWidth );
- CalcRect( rPor, nullptr, &aIntersect, true );
- const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
+ if (!rMap.hasPropertyByName(rPropName))
+ continue;
- if ( !aIntersect.HasArea() )
- {
- return;
- }
+ if (std::find(aHiddenProperties.begin(), aHiddenProperties.end(), rPropName)
+ != aHiddenProperties.end())
+ continue;
- break;
- }
+ 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);
- pTmpOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
+ // 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);
- pTmpOut->SetFillColor(aFillColor);
- pTmpOut->SetLineColor();
+ // 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));
- DrawRect( aIntersect, false );
+ vcl::Font aFont(pTmpOut->GetFont());
+ aFont.SetOrientation(Degree10(0));
+ pTmpOut->SetFont(aFont);
- pTmpOut->Pop();
-}
+ pTmpOut->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::TextOriginLeft);
+ //pTmpOut->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::BiDiStrong);
-void SwTextPaintInfo::DrawBorder( const SwLinePortion &rPor ) const
-{
- SwRect aDrawArea;
- CalcRect( rPor, &aDrawArea );
- if ( aDrawArea.HasArea() )
- {
- PaintCharacterBorder(*m_pFnt, aDrawArea, GetTextFrame()->IsVertical(),
- GetTextFrame()->IsVertLRBT(), rPor.GetJoinBorderWithPrev(),
- rPor.GetJoinBorderWithNext());
+ 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,
+ bool bKeepLine, sal_Int16 nCompoundMinLeading, sal_Int16 nTextHyphZoneAlways )
{
sal_Int32 nLen = rVals.getLength();
if (0 == nLen) // yet to be initialized?
{
- rVals.realloc( 3 );
+ rVals.realloc( 11 );
PropertyValue *pVal = rVals.getArray();
pVal[0].Name = UPN_HYPH_MIN_LEADING;
@@ -1390,13 +1590,53 @@ 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;
+
+ pVal[9].Name = UPN_HYPH_KEEP_LINE;
+ pVal[9].Handle = UPH_HYPH_KEEP_LINE;
+ pVal[9].Value <<= bKeepLine;
+
+ pVal[10].Name = UPN_HYPH_ZONE_ALWAYS;
+ pVal[10].Handle = UPH_HYPH_ZONE_ALWAYS;
+ pVal[10].Value <<= nTextHyphZoneAlways;
}
- else if (3 == nLen) // already initialized once?
+ else if (11 == 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;
+ pVal[9].Value <<= bKeepLine;
+ pVal[10].Value <<= nTextHyphZoneAlways;
}
else {
OSL_FAIL( "unexpected size of sequence" );
@@ -1405,7 +1645,7 @@ static void lcl_InitHyphValues( PropertyValues &rVals,
const PropertyValues & SwTextFormatInfo::GetHyphValues() const
{
- OSL_ENSURE( 3 == m_aHyphVals.getLength(),
+ OSL_ENSURE( 11 == m_aHyphVals.getLength(),
"hyphenation values not yet initialized" );
return m_aHyphVals;
}
@@ -1423,8 +1663,19 @@ 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 sal_Int16 nTextHyphZoneAlways = rAttr.GetTextHyphenZoneAlways();
+ const bool bKeep = rAttr.IsKeep();
+ const sal_Int16 nKeepType = rAttr.GetKeepType();
+ const bool bKeepLine = rAttr.IsKeepLine();
+ const sal_Int16 nCompoundMinimalLeading = std::max(rAttr.GetCompoundMinLead(), sal_uInt8(2));
+ lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing,
+ bNoCapsHyphenation, bNoLastWordHyphenation,
+ nMinimalWordLength, nTextHyphZone, bKeep, nKeepType,
+ bKeepLine, nCompoundMinimalLeading, nTextHyphZoneAlways );
}
return bAuto;
}
@@ -1450,19 +1701,20 @@ void SwTextFormatInfo::CtorInitTextFormatInfo( OutputDevice* pRenderContext, SwT
m_nFirst = 0;
m_nRealWidth = 0;
m_nForcedLeftMargin = 0;
+ m_nExtraAscent = 0;
+ m_nExtraDescent = 0;
m_pRest = nullptr;
m_nLineHeight = 0;
m_nLineNetHeight = 0;
SetLineStart(TextFrameIndex(0));
- SvtCTLOptions::TextNumerals const nTextNumerals(
- SW_MOD()->GetCTLOptions().GetCTLTextNumerals());
+ SvtCTLOptions::TextNumerals const nTextNumerals(SwModule::get()->GetCTLTextNumerals());
// cannot cache for NUMERALS_CONTEXT because we need to know the string
// for the whole paragraph now
if (nTextNumerals != SvtCTLOptions::NUMERALS_CONTEXT)
{
// set digit mode to what will be used later to get same results
- SwDigitModeModifier const m(*m_pRef, LANGUAGE_NONE /*dummy*/);
+ SwDigitModeModifier const m(*m_pRef, LANGUAGE_NONE /*dummy*/, nTextNumerals);
assert(m_pRef->GetDigitLanguage() != LANGUAGE_NONE);
SetCachedVclData(OutputDevice::CreateTextLayoutCache(*m_pText));
}
@@ -1483,7 +1735,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();
@@ -1499,9 +1753,9 @@ bool SwTextFormatInfo::IsHyphenate() const
if (pShell)
{
pShell->AppendInfoBarWhenReady(
- "hyphenationmissing", SwResId(STR_HYPH_MISSING),
+ u"hyphenationmissing"_ustr, 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);
}
}
@@ -1546,8 +1800,9 @@ void SwTextFormatInfo::Init()
m_cTabDecimal = 0;
m_nWidth = m_nRealWidth;
m_nForcedLeftMargin = 0;
+ m_nExtraAscent = 0;
+ m_nExtraDescent = 0;
m_nSoftHyphPos = TextFrameIndex(0);
- m_nUnderScorePos = TextFrameIndex(COMPLETE_STRING);
m_nLastBookmarkPos = TextFrameIndex(-1);
m_cHookChar = 0;
SetIdx(TextFrameIndex(0));
@@ -1580,15 +1835,16 @@ SwTextFormatInfo::SwTextFormatInfo( const SwTextFormatInfo& rInf,
m_pLastTab(nullptr),
m_nSoftHyphPos(TextFrameIndex(0)),
m_nLineStart(rInf.GetIdx()),
- m_nUnderScorePos(TextFrameIndex(COMPLETE_STRING)),
m_nLeft(rInf.m_nLeft),
m_nRight(rInf.m_nRight),
m_nFirst(rInf.m_nLeft),
- m_nRealWidth(sal_uInt16(nActWidth)),
+ m_nRealWidth(nActWidth),
m_nWidth(m_nRealWidth),
m_nLineHeight(0),
m_nLineNetHeight(0),
m_nForcedLeftMargin(0),
+ m_nExtraAscent(0),
+ m_nExtraDescent(0),
m_bFull(false),
m_bFootnoteDone(true),
m_bErgoDone(true),
@@ -1616,9 +1872,40 @@ 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();
+ const SwTwips nHeight = pCurr->GetRealHeight();
for( SwLinePortion *pPor = pCurr->GetNextPortion(); pPor; pPor = pPor->GetNextPortion() )
{
if( pPor->IsFootnotePortion() && nHeight > static_cast<SwFootnotePortion*>(pPor)->Orig() )
@@ -1668,11 +1955,6 @@ TextFrameIndex SwTextFormatInfo::ScanPortionEnd(TextFrameIndex const nStart,
m_cHookChar = cPos;
return i;
- case CHAR_UNDERSCORE:
- if (TextFrameIndex(COMPLETE_STRING) == m_nUnderScorePos)
- m_nUnderScorePos = i;
- break;
-
default:
if ( cTabDec )
{
@@ -1758,7 +2040,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 +2073,17 @@ 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();
+ }
+ else
+ { // tdf#158658 Put content after tab into margin like Word.
+ // Try to limit the paragraph to 55.87cm, it's max tab pos in Word UI.
+ nLineWidth = o3tl::toTwips(558, o3tl::Length::mm) - X();
+ }
return nLineWidth;
}
@@ -1803,6 +2098,7 @@ SwTextSlot::SwTextSlot(
, m_pOldGrammarCheckList(nullptr)
, nIdx(0)
, nLen(0)
+ , nMeasureLen(0)
, pInf(nullptr)
{
if( rCh.isEmpty() )
@@ -1822,11 +2118,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 +2200,7 @@ SwTextSlot::~SwTextSlot()
pInf->SetText( *pOldText );
pInf->SetIdx( nIdx );
pInf->SetLen( nLen );
+ pInf->SetMeasureLen( nMeasureLen );
// ST2
// Restore old smart tag list
@@ -1928,7 +2229,8 @@ SwFontSave::SwFontSave(const SwTextSizeInfo &rInf, SwFont *pNew,
( ! pNew->GetBackColor() && pFnt->GetBackColor() ) ||
( pNew->GetBackColor() && ! pFnt->GetBackColor() ) ||
( pNew->GetBackColor() && pFnt->GetBackColor() &&
- ( *pNew->GetBackColor() != *pFnt->GetBackColor() ) ) )
+ ( *pNew->GetBackColor() != *pFnt->GetBackColor() ) )
+ || !pNew->GetActualFont().SvxFontSubsetEquals(pFnt->GetActualFont()))
{
pNew->SetTransparent( true );
pNew->SetAlign( ALIGN_BASELINE );
@@ -1988,4 +2290,30 @@ bool SwTextFormatInfo::CheckCurrentPosBookmark()
}
}
+sal_Int32 SwTextFormatInfo::GetLineSpaceCount(TextFrameIndex nBreakPos)
+{
+ if ( sal_Int32(nBreakPos) >= GetText().getLength() )
+ return 0;
+
+ sal_Int32 nSpaces = 0;
+ sal_Int32 nInlineSpaces = -1;
+ for (sal_Int32 i = sal_Int32(GetLineStart()); i < sal_Int32(nBreakPos); ++i)
+ {
+ sal_Unicode cChar = GetText()[i];
+ if ( cChar == CH_BLANK )
+ ++nSpaces;
+ else
+ {
+ if ( nInlineSpaces == -1 )
+ {
+ nInlineSpaces = 0;
+ nSpaces = 0;
+ }
+ else
+ nInlineSpaces = nSpaces;
+ }
+ }
+ return nInlineSpaces == -1 ? 0: nInlineSpaces;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx
index bb245d5b031f..4792ce8399d1 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>
@@ -36,8 +37,6 @@ namespace com::sun::star::linguistic2 { class XHyphenatedWord; }
class SvxBrushItem;
class SvxLineSpacingItem;
-class SvxTabStop;
-class SvxTabStopItem;
class SwFlyPortion;
class SwFormatDrop;
class SwLinePortion;
@@ -60,25 +59,24 @@ 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;
+ SwTwips m_nDefTabStop;
bool m_bListTabStopIncluded;
tools::Long m_nListTabStopPosition;
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
+ SwTwips GetDefTabStop() const { return m_nDefTabStop; }
+ void SetDefTabStop(SwTwips nNew) const
{ const_cast<SwLineInfo*>(this)->m_nDefTabStop = nNew; }
// vertical alignment
@@ -126,7 +124,7 @@ public:
class SwTextSizeInfo : public SwTextInfo
{
private:
- typedef std::map< SwLinePortion const *, sal_uInt16 > SwTextPortionMap;
+ typedef std::map<SwLinePortion const*, SwTwips> SwTextPortionMap;
protected:
// during formatting, a small database is built, mapping portion pointers
@@ -145,7 +143,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 +152,8 @@ protected:
const OUString *m_pText;
TextFrameIndex m_nIdx;
TextFrameIndex m_nLen;
+ TextFrameIndex m_nMeasureLen;
+ std::optional<SwLinePortionLayoutContext> m_nLayoutContext;
sal_uInt16 m_nKanaIdx;
bool m_bOnWin : 1;
bool m_bNotEOL : 1;
@@ -173,6 +173,8 @@ protected:
bool m_bForbiddenChars : 1; // Forbidden start/endline characters
bool m_bSnapToGrid : 1; // paragraph snaps to grid
sal_uInt8 m_nDirection : 2; // writing direction: 0/90/180/270 degree
+ SwTwips m_nExtraSpace; // extra space before shrinking = nSpacesInLine * (nSpaceWidth/0.8 - nSpaceWidth)
+ SwTwips m_nBreakWidth; // break width to calculate space width at justification
protected:
void CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame,
@@ -182,12 +184,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; }
@@ -244,36 +246,48 @@ public:
sal_uInt16 GetTextHeight() const;
- SwPosSize GetTextSize( OutputDevice* pOut, const SwScriptInfo* pSI,
+ SwPositiveSize GetTextSize( OutputDevice* pOut, const SwScriptInfo* pSI,
const OUString& rText, TextFrameIndex nIdx,
TextFrameIndex nLen ) const;
- SwPosSize GetTextSize() const;
- void GetTextSize( const SwScriptInfo* pSI, TextFrameIndex nIdx,
- TextFrameIndex nLen, const sal_uInt16 nComp,
- sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
- vcl::TextLayoutCache const* = nullptr) const;
- inline SwPosSize GetTextSize(const SwScriptInfo* pSI, TextFrameIndex nIdx,
+ SwPositiveSize GetTextSize(std::optional<SwLinePortionLayoutContext> nLayoutContext
+ = std::nullopt) const;
+ void GetTextSize(const SwScriptInfo* pSI, TextFrameIndex nIdx, TextFrameIndex nLen,
+ std::optional<SwLinePortionLayoutContext> nLayoutContext,
+ const sal_uInt16 nComp, SwTwips& nMinSize, tools::Long& nMaxSizeDiff,
+ SwTwips& nExtraAscent, SwTwips& nExtraDescent,
+ vcl::text::TextLayoutCache const* = nullptr) const;
+ inline SwPositiveSize GetTextSize(const SwScriptInfo* pSI, TextFrameIndex nIdx,
TextFrameIndex nLen) const;
- inline SwPosSize GetTextSize( const OUString &rText ) const;
+ inline SwPositiveSize GetTextSize( const OUString &rText ) const;
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; }
+ const std::optional<SwLinePortionLayoutContext> & GetLayoutContext() const { return m_nLayoutContext; }
+
+ void SetLayoutContext(std::optional<SwLinePortionLayoutContext> nNew)
+ {
+ m_nLayoutContext = nNew;
+ }
+
// No Bullets for the symbol font!
bool IsNoSymbol() const
{ return RTL_TEXTENCODING_SYMBOL != m_pFnt->GetCharSet( m_pFnt->GetActual() ); }
@@ -286,16 +300,23 @@ public:
bool HasHint(TextFrameIndex nPos) const;
+ // extra space before shrinking = nSpacesInLine * (nSpaceWidth/0.8 - nSpaceWidth)
+ void SetExtraSpace(SwTwips nVal) { m_nExtraSpace = nVal; }
+ SwTwips GetExtraSpace() const { return m_nExtraSpace; }
+ // set break width to calculate space width later
+ void SetBreakWidth(SwTwips nVal) { m_nBreakWidth = nVal; }
+ SwTwips GetBreakWidth() const { return m_nBreakWidth; }
+
// If Kana Compression is enabled, a minimum and maximum portion width
// is calculated. We format lines with minimal size and share remaining
// space among compressed kanas.
// During formatting, the maximum values of compressible portions are
// stored in m_aMaxWidth and discarded after a line has been formatted.
- void SetMaxWidthDiff( const SwLinePortion *nKey, sal_uInt16 nVal )
+ void SetMaxWidthDiff(const SwLinePortion* nKey, SwTwips nVal)
{
m_aMaxWidth.insert( std::make_pair( nKey, nVal ) );
};
- sal_uInt16 GetMaxWidthDiff( const SwLinePortion *nKey )
+ SwTwips GetMaxWidthDiff(const SwLinePortion* nKey)
{
SwTextPortionMap::iterator it = m_aMaxWidth.find( nKey );
@@ -325,11 +346,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 +375,7 @@ class SwTextPaintInfo : public SwTextSizeInfo
const bool bGrammarCheck = false );
SwTextPaintInfo &operator=(const SwTextPaintInfo&) = delete;
+ void NotifyURL_(const SwLinePortion& rPor) const;
protected:
SwTextPaintInfo()
@@ -399,8 +421,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 +434,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 +467,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; }
@@ -469,7 +501,6 @@ class SwTextFormatInfo : public SwTextPaintInfo
TextFrameIndex m_nSoftHyphPos; ///< SoftHyphPos for Hyphenation
TextFrameIndex m_nLineStart; ///< Current line start in rText
- TextFrameIndex m_nUnderScorePos; ///< enlarge repaint if underscore has been found
TextFrameIndex m_nLastBookmarkPos; ///< need to check for bookmarks at every portion
// #i34348# Changed type from sal_uInt16 to SwTwips
SwTwips m_nLeft; // Left margin
@@ -477,11 +508,13 @@ class SwTextFormatInfo : public SwTextPaintInfo
SwTwips m_nFirst; // EZE
/// First or left margin, depending on context.
SwTwips m_nLeftMargin = 0;
- sal_uInt16 m_nRealWidth; // "real" line width
- sal_uInt16 m_nWidth; // "virtual" line width
- sal_uInt16 m_nLineHeight; // Final height after CalcLine
- sal_uInt16 m_nLineNetHeight; // line height without spacing
- sal_uInt16 m_nForcedLeftMargin; // Shift of left margin due to frame
+ SwTwips m_nRealWidth; // "real" line width
+ SwTwips m_nWidth; // "virtual" line width
+ SwTwips m_nLineHeight; // Final height after CalcLine
+ SwTwips m_nLineNetHeight; // line height without spacing
+ SwTwips m_nForcedLeftMargin; // Shift of left margin due to frame
+ SwTwips m_nExtraAscent = 0; // Enlarge clipping area for glyphs above the line height
+ SwTwips m_nExtraDescent = 0; // Enlarge clipping area for glyphs below the line height
bool m_bFull : 1; // Line is full
bool m_bFootnoteDone : 1; // Footnote already formatted
@@ -509,6 +542,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 );
@@ -524,8 +567,8 @@ public:
SwTextFormatInfo( const SwTextFormatInfo& rInf, SwLineLayout& rLay,
SwTwips nActWidth );
- sal_uInt16 Width() const { return m_nWidth; }
- void Width( const sal_uInt16 nNew ) { m_nWidth = nNew; }
+ SwTwips Width() const { return m_nWidth; }
+ void Width(const SwTwips nNew) { m_nWidth = nNew; }
void Init();
/**
@@ -545,10 +588,10 @@ public:
SwTwips First() const { return m_nFirst; }
void First( const SwTwips nNew ) { m_nFirst = nNew; }
void LeftMargin( const SwTwips nNew) { m_nLeftMargin = nNew; }
- sal_uInt16 RealWidth() const { return m_nRealWidth; }
- void RealWidth( const sal_uInt16 nNew ) { m_nRealWidth = nNew; }
- sal_uInt16 ForcedLeftMargin() const { return m_nForcedLeftMargin; }
- void ForcedLeftMargin( const sal_uInt16 nN ) { m_nForcedLeftMargin = nN; }
+ SwTwips RealWidth() const { return m_nRealWidth; }
+ void RealWidth(const SwTwips nNew) { m_nRealWidth = nNew; }
+ SwTwips ForcedLeftMargin() const { return m_nForcedLeftMargin; }
+ void ForcedLeftMargin(const SwTwips nN) { m_nForcedLeftMargin = nN; }
sal_uInt8 &MaxHyph() { return m_nMaxHyph; }
const sal_uInt8 &MaxHyph() const { return m_nMaxHyph; }
@@ -558,7 +601,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,15 +628,22 @@ 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; }
// these are used during fly calculation
- sal_uInt16 GetLineHeight() const { return m_nLineHeight; }
- void SetLineHeight( const sal_uInt16 nNew ) { m_nLineHeight = nNew; }
- sal_uInt16 GetLineNetHeight() const { return m_nLineNetHeight; }
- void SetLineNetHeight( const sal_uInt16 nNew ) { m_nLineNetHeight = nNew; }
+ SwTwips GetLineHeight() const { return m_nLineHeight; }
+ void SetLineHeight(const SwTwips nNew) { m_nLineHeight = nNew; }
+ SwTwips GetLineNetHeight() const { return m_nLineNetHeight; }
+ void SetLineNetHeight(const SwTwips nNew) { m_nLineNetHeight = nNew; }
const SwLinePortion *GetUnderflow() const { return m_pUnderflow; }
SwLinePortion *GetUnderflow() { return m_pUnderflow; }
@@ -637,8 +687,12 @@ public:
// Should the hyphenate helper be discarded?
bool IsHyphenate() const;
- TextFrameIndex GetUnderScorePos() const { return m_nUnderScorePos; }
- void SetUnderScorePos(TextFrameIndex const nNew) { m_nUnderScorePos = nNew; }
+
+ SwTwips GetExtraAscent() const { return m_nExtraAscent; }
+ void SetExtraAscent(SwTwips nNew) { m_nExtraAscent = std::max(m_nExtraAscent, nNew); }
+
+ SwTwips GetExtraDescent() const { return m_nExtraDescent; }
+ void SetExtraDescent(SwTwips nNew) { m_nExtraDescent = std::max(m_nExtraDescent, nNew); }
// Calls HyphenateWord() of Hyphenator
css::uno::Reference< css::linguistic2::XHyphenatedWord >
@@ -661,6 +715,9 @@ public:
void SetTabOverflow( bool bOverflow ) { m_bTabOverflow = bOverflow; }
bool IsTabOverflow() const { return m_bTabOverflow; }
+ // get line space count between line start and break position
+ // by stripping also terminating spaces
+ sal_Int32 GetLineSpaceCount(TextFrameIndex nBreakPos);
};
/**
@@ -673,7 +730,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 +738,7 @@ class SwTextSlot final
std::unique_ptr<sw::WrongListIterator> m_pTempIter;
TextFrameIndex nIdx;
TextFrameIndex nLen;
+ TextFrameIndex nMeasureLen;
bool bOn;
SwTextSizeInfo *pInf;
@@ -715,12 +773,18 @@ inline sal_uInt16 SwTextSizeInfo::GetTextHeight() const
return const_cast<SwFont*>(GetFont())->GetHeight( m_pVsh, *GetOut() );
}
-inline SwPosSize SwTextSizeInfo::GetTextSize( const OUString &rText ) const
+inline sal_uInt16 SwTextSizeInfo::GetHangingBaseline() const
+{
+ assert(GetOut());
+ return const_cast<SwFont*>(GetFont())->GetHangingBaseline( m_pVsh, *GetOut() );
+}
+
+inline SwPositiveSize SwTextSizeInfo::GetTextSize( const OUString &rText ) const
{
return GetTextSize(m_pOut, nullptr, rText, TextFrameIndex(0), TextFrameIndex(rText.getLength()));
}
-inline SwPosSize SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI,
+inline SwPositiveSize SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI,
TextFrameIndex const nNewIdx,
TextFrameIndex const nNewLen) const
{
diff --git a/sw/source/core/text/itradj.cxx b/sw/source/core/text/itradj.cxx
index a5944e49e357..c8abfee4868a 100644
--- a/sw/source/core/text/itradj.cxx
+++ b/sw/source/core/text/itradj.cxx
@@ -20,6 +20,9 @@
#include <sal/config.h>
#include <o3tl/safeint.hxx>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <swscanner.hxx>
+#include <i18nutil/kashida.hxx>
#include <IDocumentSettingAccess.hxx>
#include <doc.hxx>
@@ -45,8 +48,34 @@ void SwTextAdjuster::FormatBlock( )
const SwLinePortion *pFly = nullptr;
bool bSkip = !IsLastBlock() &&
+ // don't skip, if the last paragraph line needs space shrinking
+ m_pCurr->ExtraShrunkWidth() <= m_pCurr->Width() &&
m_nStart + m_pCurr->GetLen() >= TextFrameIndex(GetInfo().GetText().getLength());
+ // tdf#162725 if the last line is longer, than the paragraph width,
+ // it contains shrinking spaces: don't skip block format here
+ if( bSkip )
+ {
+ // sum width of the text portions to calculate the line width without shrinking
+ tools::Long nBreakWidth = 0;
+ const SwLinePortion *pPos = m_pCurr->GetNextPortion();
+ while( pPos && bSkip )
+ {
+ if( !pPos->InGlueGrp() &&
+ // don't calculate with the terminating space,
+ // otherwise it would result justified line mistakenly
+ ( pPos->GetNextPortion() || !pPos->IsHolePortion() ) )
+ {
+ nBreakWidth += pPos->Width();
+ }
+
+ if( nBreakWidth > m_pCurr->Width() )
+ bSkip = false;
+
+ pPos = pPos->GetNextPortion();
+ }
+ }
+
// Multi-line fields are tricky, because we need to check whether there are
// any other text portions in the paragraph.
if( bSkip )
@@ -110,138 +139,101 @@ void SwTextAdjuster::FormatBlock( )
GetInfo().GetParaPortion()->GetRepaint().SetOffset(0);
}
-static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr,
- sal_Int32& rKashidas, TextFrameIndex& nGluePortion)
+static bool lcl_ComputeKashidaPositions(SwTextSizeInfo& rInf, SwTextIter& rItr,
+ TextFrameIndex& nGluePortion,
+ const tools::Long nGluePortionWidth,
+ SwLineLayout* pCurrLine, TextFrameIndex nLineBaseIndex)
{
// i60594 validate Kashida justification
TextFrameIndex nIdx = rItr.GetStart();
TextFrameIndex nEnd = rItr.GetEnd();
- // Note on calling KashidaJustify():
- // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
- // 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() );
-
- if (rKashidas <= 0) // nothing to do
- return true;
-
- // kashida positions found in SwScriptInfo are not necessarily valid in every font
- // if two characters are replaced by a ligature glyph, there will be no place for a kashida
- std::vector<TextFrameIndex> aKashidaPos;
- rSI.GetKashidaPositions(nIdx, rItr.GetLength(), aKashidaPos);
- assert(aKashidaPos.size() >= o3tl::make_unsigned(rKashidas));
- std::vector<TextFrameIndex> aKashidaPosDropped(aKashidaPos.size());
- sal_Int32 nKashidaIdx = 0;
- while ( rKashidas && nIdx < nEnd )
+ std::vector<TextFrameIndex> aKashidaPositions;
+ std::vector<tools::Long> aKashidaWidths;
+ tools::Long nMaxKashidaWidth = 0;
+
+ // Parse the text, and apply the kashida insertion rules
+ std::function<LanguageType(sal_Int32, sal_Int32, bool)> const pGetLangOfChar(
+ [&rInf](sal_Int32 const nBegin, sal_uInt16 const nScript, bool const bNoChar)
+ { return rInf.GetTextFrame()->GetLangOfChar(TextFrameIndex{ nBegin }, nScript, bNoChar); });
+ SwScanner aScanner(pGetLangOfChar, rInf.GetText(), nullptr, ModelToViewHelper(),
+ i18n::WordType::DICTIONARY_WORD, sal_Int32(nIdx), sal_Int32(nEnd));
+
+ std::vector<bool> aValidPositions;
+ while (aScanner.NextWord())
{
- rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
- TextFrameIndex nNext = rItr.GetNextAttr();
-
- // is there also a script change before?
- // if there is, nNext should point to the script change
- TextFrameIndex const nNextScript = rSI.NextScriptChg( nIdx );
- if( nNextScript < nNext )
- nNext = nNextScript;
-
- if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
- nNext = nEnd;
- sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( nullptr, nullptr, nIdx, nNext - nIdx );
- if (nKashidasInAttr > 0)
+ const OUString& rWord = aScanner.GetWord();
+
+ // Fetch the set of valid positions from VCL, where possible
+ if (SwScriptInfo::IsKashidaScriptText(rInf.GetText(), TextFrameIndex{ aScanner.GetBegin() },
+ TextFrameIndex{ aScanner.GetLen() }))
{
+ aValidPositions.clear();
+
+ rItr.SeekAndChgAttrIter(TextFrameIndex{ aScanner.GetBegin() }, rInf.GetRefDev());
+
// Kashida glyph looks suspicious, skip Kashida justification
- if ( rInf.GetOut()->GetMinKashida() <= 0 )
- {
- return false;
- }
+ auto nFontMinKashida = rInf.GetRefDev()->GetMinKashida();
+ if (nFontMinKashida <= 0)
+ continue;
- sal_Int32 nKashidasDropped = 0;
- if ( !SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
- {
- nKashidasDropped = nKashidasInAttr;
- rKashidas -= nKashidasDropped;
- }
- else
+ vcl::text::ComplexTextLayoutFlags nOldLayout = rInf.GetRefDev()->GetLayoutMode();
+ rInf.GetRefDev()->SetLayoutMode(nOldLayout
+ | vcl::text::ComplexTextLayoutFlags::BiDiRtl);
+
+ rInf.GetRefDev()->GetWordKashidaPositions(rWord, &aValidPositions);
+
+ rInf.GetRefDev()->SetLayoutMode(nOldLayout);
+
+ auto stKashidaPos = i18nutil::GetWordKashidaPosition(rWord, aValidPositions);
+ if (stKashidaPos.has_value())
{
- ComplexTextLayoutFlags nOldLayout = rInf.GetOut()->GetLayoutMode();
- rInf.GetOut()->SetLayoutMode ( nOldLayout | ComplexTextLayoutFlags::BiDiRtl );
- nKashidasDropped = rInf.GetOut()->ValidateKashidas(
- rInf.GetText(), sal_Int32(nIdx), sal_Int32(nNext - nIdx),
- nKashidasInAttr,
- reinterpret_cast<sal_Int32*>(aKashidaPos.data() + nKashidaIdx),
- reinterpret_cast<sal_Int32*>(aKashidaPosDropped.data()));
- rInf.GetOut()->SetLayoutMode ( nOldLayout );
- if ( nKashidasDropped )
+ TextFrameIndex nNewKashidaPos{ aScanner.GetBegin() + stKashidaPos->nIndex };
+
+ // tdf#164098: The above algorithm can return out-of-range kashida positions. This
+ // can happen if, for example, a single word is split across multiple lines, and
+ // the best kashida candidate position is on the first line.
+ if (nNewKashidaPos >= nIdx && nNewKashidaPos < nEnd)
{
- rSI.MarkKashidasInvalid(nKashidasDropped, aKashidaPosDropped.data());
- rKashidas -= nKashidasDropped;
- nGluePortion -= TextFrameIndex(nKashidasDropped);
+ aKashidaPositions.push_back(nNewKashidaPos - nLineBaseIndex);
+ aKashidaWidths.push_back(nFontMinKashida);
+ nMaxKashidaWidth = std::max(nMaxKashidaWidth, nFontMinKashida);
}
}
- nKashidaIdx += nKashidasInAttr;
}
- nIdx = nNext;
}
- // return false if all kashidas have been eliminated
- return (rKashidas > 0);
-}
+ nGluePortion += TextFrameIndex{ aKashidaPositions.size() };
-static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr, sal_Int32& rKashidas,
- TextFrameIndex& nGluePortion, const tools::Long nGluePortionWidth, tools::Long& nSpaceAdd )
-{
- // check kashida width
- // if width is smaller than minimal kashida width allowed by fonts in the current line
- // drop one kashida after the other until kashida width is OK
- while (rKashidas)
+ // The line may not have enough extra space for all possible kashida.
+ // Remove them from the beginning of the line to the end.
+ std::reverse(aKashidaPositions.begin(), aKashidaPositions.end());
+ std::reverse(aKashidaWidths.begin(), aKashidaWidths.end());
+
+ while (nGluePortion && !aKashidaPositions.empty())
{
- bool bAddSpaceChanged = false;
- TextFrameIndex nIdx = rItr.GetStart();
- TextFrameIndex nEnd = rItr.GetEnd();
- while ( nIdx < nEnd )
+ tools::Long nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
+ if (nSpaceAdd / SPACING_PRECISION_FACTOR >= nMaxKashidaWidth)
{
- rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
- TextFrameIndex nNext = rItr.GetNextAttr();
-
- // is there also a script change before?
- // if there is, nNext should point to the script change
- TextFrameIndex const nNextScript = rSI.NextScriptChg( nIdx );
- if( nNextScript < nNext )
- nNext = nNextScript;
+ break;
+ }
- if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
- nNext = nEnd;
- sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( nullptr, nullptr, nIdx, nNext - nIdx );
+ aKashidaPositions.pop_back();
+ aKashidaWidths.pop_back();
- tools::Long nFontMinKashida = rInf.GetOut()->GetMinKashida();
- if ( nFontMinKashida && nKashidasInAttr > 0 && SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
- {
- sal_Int32 nKashidasDropped = 0;
- while ( rKashidas && nGluePortion && nKashidasInAttr > 0 &&
- nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida )
- {
- --nGluePortion;
- --rKashidas;
- --nKashidasInAttr;
- ++nKashidasDropped;
- if( !rKashidas || !nGluePortion ) // nothing left, return false to
- return false; // do regular blank justification
-
- nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
- bAddSpaceChanged = true;
- }
- if( nKashidasDropped )
- rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx );
- }
- if ( bAddSpaceChanged )
- break; // start all over again
- nIdx = nNext;
+ nMaxKashidaWidth = 0;
+ if (!aKashidaWidths.empty())
+ {
+ nMaxKashidaWidth = *std::max_element(aKashidaWidths.begin(), aKashidaWidths.end());
}
- if ( !bAddSpaceChanged )
- break; // everything was OK
+
+ --nGluePortion;
}
- return true;
+
+ std::reverse(aKashidaPositions.begin(), aKashidaPositions.end());
+ pCurrLine->SetKashida(std::move(aKashidaPositions));
+
+ return !aKashidaWidths.empty();
}
// CalcNewBlock() must only be called _after_ CalcLine()!
@@ -264,20 +256,15 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
SwTextSizeInfo aInf ( GetTextFrame() );
SwTextIter aItr ( GetTextFrame(), &aInf );
- if ( rSI.CountKashida() )
+ TextFrameIndex nLineBase{ 0 };
+ if (rSI.ParagraphContainsKashidaScript())
{
while (aItr.GetCurr() != pCurrent && aItr.GetNext())
aItr.Next();
- if( bSkipKashida )
- {
- rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength());
- }
- else
- {
- rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() );
- rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() );
- }
+ nLineBase = aItr.GetStart();
+ rSI.ReplaceKashidaPositions({});
+ pCurrent->SetKashida({});
}
// Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
@@ -290,6 +277,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 )
{
@@ -348,18 +337,16 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
const tools::Long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
SPACING_PRECISION_FACTOR;
- sal_Int32 nKashidas = 0;
- if( nGluePortion && rSI.CountKashida() && !bSkipKashida )
+ if (rSI.ParagraphContainsKashidaScript() && !bSkipKashida)
{
- // kashida positions found in SwScriptInfo are not necessarily valid in every font
- // if two characters are replaced by a ligature glyph, there will be no place for a kashida
- if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion ))
+ if (!lcl_ComputeKashidaPositions(aInf, aItr, nGluePortion, nGluePortionWidth,
+ pCurrent, nLineBase))
{
- // all kashida positions are invalid
+ // no kashidas left
// do regular blank justification
pCurrent->FinishSpaceAdd();
- GetInfo().SetIdx( m_nStart );
- CalcNewBlock( pCurrent, pStopAt, nReal, true );
+ GetInfo().SetIdx(m_nStart);
+ CalcNewBlock(pCurrent, pStopAt, nReal, true);
return;
}
}
@@ -367,22 +354,22 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
if( nGluePortion )
{
tools::Long nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
-
- // i60594
- if( rSI.CountKashida() && !bSkipKashida )
- {
- if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd ))
- {
- // no kashidas left
- // do regular blank justification
- pCurrent->FinishSpaceAdd();
- GetInfo().SetIdx( m_nStart );
- CalcNewBlock( pCurrent, pStopAt, nReal, true );
- return;
- }
- }
-
- pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
+ // shrink, if not shrunk line width exceed the set line width
+ // i.e. if pCurrent->ExtraShrunkWidth() > 0
+ // tdf#163720 but at hyphenated lines, still nBreakWidth contains the correct
+ // not shrunk line width (ExtraShrunkWidth + hyphen length), so use that
+ if ( pCurrent->ExtraShrunkWidth() > nBreakWidth )
+ nBreakWidth = pCurrent->ExtraShrunkWidth();
+ // 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
+ : ( nSpaceAdd < 0 )
+ // shrink, if portions exceed the line width available before an image
+ ? -nSpaceAdd + LONG_MAX/2
+ : 0;
+
+ pCurrent->SetLLSpaceAdd( nSpaceSub ? nSpaceSub : nSpaceAdd, nSpaceIdx );
pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
}
else if (IsOneBlock() && nCharCnt > TextFrameIndex(1))
@@ -399,6 +386,10 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
else
++nGluePortion;
}
+ else
+ {
+ nBreakWidth += pPos->Width();
+ }
GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
if ( pPos == pStopAt )
{
@@ -407,6 +398,33 @@ void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
}
pPos = pPos->GetNextPortion();
}
+
+ // tdf#164140: Rebuild kashida position indices after line adjustment
+ if (rSI.ParagraphContainsKashidaScript())
+ {
+ std::vector<TextFrameIndex> aKashidaPositions;
+
+ SwTextSizeInfo aKashInf(GetTextFrame());
+ SwTextIter aKashItr(GetTextFrame(), &aKashInf);
+
+ while (true)
+ {
+ const SwLineLayout* pCurrLine = aKashItr.GetCurr();
+ for (const auto& nPos : pCurrLine->GetKashida())
+ {
+ aKashidaPositions.push_back(nPos + aKashItr.GetStart());
+ }
+
+ if (!aKashItr.GetNextLine())
+ {
+ break;
+ }
+
+ aKashItr.NextLine();
+ }
+
+ rSI.ReplaceKashidaPositions(std::move(aKashidaPositions));
+ }
}
SwTwips SwTextAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
@@ -434,7 +452,7 @@ SwTwips SwTextAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
{
// 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() )
@@ -488,7 +506,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 +527,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 +542,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
@@ -545,10 +563,10 @@ SwMarginPortion *SwTextAdjuster::CalcRightMargin( SwLineLayout *pCurrent,
SwTwips nReal )
{
tools::Long nRealWidth;
- const sal_uInt16 nRealHeight = GetLineHeight();
- const sal_uInt16 nLineHeight = pCurrent->Height();
+ const SwTwips nRealHeight = GetLineHeight();
+ const SwTwips nLineHeight = pCurrent->Height();
- sal_uInt16 nPrtWidth = pCurrent->PrtWidth();
+ SwTwips nPrtWidth = pCurrent->PrtWidth();
SwLinePortion *pLast = pCurrent->FindLastPortion();
if( GetInfo().IsMulti() )
@@ -579,7 +597,7 @@ SwMarginPortion *SwTextAdjuster::CalcRightMargin( SwLineLayout *pCurrent,
pLast->Append( pRight );
if( tools::Long( nPrtWidth )< nRealWidth )
- pRight->PrtWidth( sal_uInt16( nRealWidth - nPrtWidth ) );
+ pRight->PrtWidth(nRealWidth - nPrtWidth);
// pCurrent->Width() is set to the real size, because we attach the
// MarginPortions.
@@ -588,7 +606,7 @@ SwMarginPortion *SwTextAdjuster::CalcRightMargin( SwLineLayout *pCurrent,
// implicitly. GetLeftMarginAdjust() and IsJustified() think they have a
// line filled with chars.
- pCurrent->PrtWidth( sal_uInt16( nRealWidth ) );
+ pCurrent->PrtWidth(nRealWidth);
return pRight;
}
@@ -702,7 +720,7 @@ SwFlyPortion *SwTextAdjuster::CalcFlyPortion( const tools::Long nRealWidth,
{
SwTextFly aTextFly( GetTextFrame() );
- const sal_uInt16 nCurrWidth = m_pCurr->PrtWidth();
+ const SwTwips nCurrWidth = m_pCurr->PrtWidth();
SwFlyPortion *pFlyPortion = nullptr;
SwRect aLineVert( rCurrRect );
@@ -734,7 +752,7 @@ SwFlyPortion *SwTextAdjuster::CalcFlyPortion( const tools::Long nRealWidth,
aLocal.Width( nRealWidth - aLocal.Left() );
GetInfo().GetParaPortion()->SetFly();
pFlyPortion = new SwFlyPortion( aLocal );
- pFlyPortion->Height( sal_uInt16( rCurrRect.Height() ) );
+ pFlyPortion->Height(rCurrRect.Height());
// The Width could be smaller than the FixWidth, thus:
pFlyPortion->AdjFixWidth();
}
@@ -747,7 +765,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 +798,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 +849,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..4fb47e279bba 100644
--- a/sw/source/core/text/itratr.cxx
+++ b/sw/source/core/text/itratr.cxx
@@ -23,6 +23,7 @@
#include <hintids.hxx>
#include <editeng/charscaleitem.hxx>
+#include <editeng/cmapitem.hxx>
#include <svl/itemiter.hxx>
#include <svx/svdobj.hxx>
#include <vcl/svapp.hxx>
@@ -32,11 +33,13 @@
#include <fmtflcnt.hxx>
#include <fmtcntnt.hxx>
#include <fmtftn.hxx>
+#include <fmtpdsc.hxx>
#include <frmatr.hxx>
#include <frmfmt.hxx>
#include <fmtfld.hxx>
#include <doc.hxx>
#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
#include <txatbase.hxx>
#include <viewsh.hxx>
#include <rootfrm.hxx>
@@ -57,6 +60,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;
@@ -150,9 +157,7 @@ SwTextAttr *SwAttrIter::GetAttr(TextFrameIndex const nPosition) const
bool SwAttrIter::SeekAndChgAttrIter(TextFrameIndex const nNewPos, OutputDevice* pOut)
{
- std::pair<SwTextNode const*, sal_Int32> const pos( m_pMergedPara
- ? sw::MapViewToModel(*m_pMergedPara, nNewPos)
- : std::make_pair(m_pTextNode, sal_Int32(nNewPos)));
+ std::pair<SwTextNode const*, sal_Int32> const pos{SeekNewPos(nNewPos, nullptr)};
bool bChg = m_nStartIndex && pos.first == m_pTextNode && pos.second == m_nPosition
? m_pFont->IsFntChg()
: Seek( nNewPos );
@@ -260,6 +265,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 +273,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 +284,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 +293,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))
{
@@ -295,15 +301,90 @@ void SwAttrIter::SeekFwd(const sal_Int32 nOldPos, const sal_Int32 nNewPos)
if ( pTextAttr->GetAnyEnd() > nNewPos ) Chg( pTextAttr );
m_nStartIndex++;
}
+}
+
+void SwAttrIter::SeekToEnd()
+{
+ if (m_pTextNode->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH))
+ {
+ SfxItemPool & rPool{const_cast<SwAttrPool&>(m_pTextNode->GetDoc().GetAttrPool())};
+ SwFormatAutoFormat const& rListAutoFormat{m_pTextNode->GetAttr(RES_PARATR_LIST_AUTOFMT)};
+ std::shared_ptr<SfxItemSet> const pSet{rListAutoFormat.GetStyleHandle()};
+ if (!pSet)
+ {
+ return;
+ }
+ if (pSet->HasItem(RES_TXTATR_CHARFMT))
+ {
+ SwFormatCharFormat const& rCharFormat{pSet->Get(RES_TXTATR_CHARFMT)};
+ m_pEndCharFormatAttr.reset(new SwTextAttrEnd{
+ SfxPoolItemHolder{rPool, &rCharFormat}, -1, -1});
+ Chg(m_pEndCharFormatAttr.get());
+ }
+ // note: RES_TXTATR_CHARFMT should be cleared here but it looks like
+ // SwAttrHandler only looks at RES_CHRATR_* anyway
+ m_pEndAutoFormatAttr.reset(new SwTextAttrEnd{
+ SfxPoolItemHolder{rPool, &rListAutoFormat}, -1, -1});
+ Chg(m_pEndAutoFormatAttr.get());
+ }
+}
+
+std::pair<SwTextNode const*, sal_Int32>
+SwAttrIter::SeekNewPos(TextFrameIndex const nNewPos, bool *const o_pIsToEnd)
+{
+ std::pair<SwTextNode const*, sal_Int32> newPos{ m_pMergedPara
+ ? sw::MapViewToModel(*m_pMergedPara, nNewPos)
+ : std::make_pair(m_pTextNode, sal_Int32(nNewPos))};
+
+ bool isToEnd{false};
+ if (m_pMergedPara)
+ {
+ if (m_pMergedPara->extents.empty())
+ {
+ isToEnd = true;
+ assert(m_pMergedPara->pLastNode == newPos.first);
+ }
+ else
+ {
+ auto const& rLast{m_pMergedPara->extents.back()};
+ isToEnd = rLast.pNode == newPos.first && rLast.nEnd == newPos.second;
+ // for text formatting: use *last* node if all text is hidden
+ if (isToEnd
+ && m_pMergedPara->pLastNode != newPos.first // implies there is hidden text
+ && m_pViewShell->GetLayout()->GetParagraphBreakMode() == sw::ParagraphBreakMode::Hidden
+ && m_pTextNode->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH))
+ {
+ TextFrameIndex nHiddenStart(COMPLETE_STRING);
+ TextFrameIndex nHiddenEnd(0);
+ m_pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex(0), nHiddenStart, nHiddenEnd);
+ if (TextFrameIndex(0) == nHiddenStart
+ && TextFrameIndex(m_pMergedPara->mergedText.getLength()) <= nHiddenEnd)
+ {
+ newPos.first = m_pMergedPara->pLastNode;
+ newPos.second = m_pMergedPara->pLastNode->Len();
+ }
+ }
+ }
+ }
+ else
+ {
+ isToEnd = newPos.second == m_pTextNode->Len();
+ }
+ if (o_pIsToEnd)
+ {
+ *o_pIsToEnd = isToEnd;
+ }
+ return newPos;
}
bool SwAttrIter::Seek(TextFrameIndex const nNewPos)
{
// note: nNewPos isn't necessarily an index returned from GetNextAttr
- std::pair<SwTextNode const*, sal_Int32> const newPos( m_pMergedPara
- ? sw::MapViewToModel(*m_pMergedPara, nNewPos)
- : std::make_pair(m_pTextNode, sal_Int32(nNewPos)));
+ bool isToEnd{false};
+ std::pair<SwTextNode const*, sal_Int32> const newPos{SeekNewPos(nNewPos, &isToEnd)};
if ( m_pRedline && m_pRedline->ExtOn() )
m_pRedline->LeaveExtend(*m_pFont, newPos.first->GetIndex(), newPos.second);
@@ -329,7 +410,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.
@@ -363,7 +443,8 @@ bool SwAttrIter::Seek(TextFrameIndex const nNewPos)
m_pMergedPara->mergedText, nullptr, nullptr);
}
}
- if (m_pMergedPara || m_pTextNode->GetpSwpHints())
+ // also reset it if the RES_PARATR_LIST_AUTOFMT has been applied!
+ if (m_pMergedPara || m_pTextNode->GetpSwpHints() || m_pEndAutoFormatAttr)
{
if( m_pRedline )
m_pRedline->Clear( nullptr );
@@ -371,6 +452,8 @@ bool SwAttrIter::Seek(TextFrameIndex const nNewPos)
// reset font to its original state
m_aAttrHandler.Reset();
m_aAttrHandler.ResetFont( *m_pFont );
+ m_pEndCharFormatAttr.reset();
+ m_pEndAutoFormatAttr.reset();
if( m_nPropFont )
m_pFont->SetProportion( m_nPropFont );
@@ -419,6 +502,11 @@ bool SwAttrIter::Seek(TextFrameIndex const nNewPos)
}
}
+ if (isToEnd && !m_pEndAutoFormatAttr)
+ {
+ SeekToEnd();
+ }
+
m_pFont->SetActual( m_pScriptInfo->WhichFont(nNewPos) );
if( m_pRedline )
@@ -443,7 +531,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 +547,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 +558,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 +597,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 +628,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 +643,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 +664,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 +675,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 +688,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 +719,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 +728,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 +743,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 +833,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
@@ -778,6 +868,93 @@ TextFrameIndex SwAttrIter::GetNextAttr() const
}
}
+namespace
+{
+class FormatBreakTracker
+{
+private:
+ std::optional<SvxCaseMap> m_nCaseMap;
+
+ bool m_bNeedsBreak = false;
+
+ void SetCaseMap(SvxCaseMap nValue)
+ {
+ if (m_nCaseMap != nValue)
+ m_bNeedsBreak = true;
+
+ m_nCaseMap = nValue;
+ }
+
+public:
+ void HandleItemSet(const SfxItemSet& rSet)
+ {
+ if (const SvxCaseMapItem* pItem = rSet.GetItem(RES_CHRATR_CASEMAP))
+ SetCaseMap(pItem->GetCaseMap());
+ }
+
+ void Reset() { m_bNeedsBreak = false; }
+
+ bool NeedsBreak() const { return m_bNeedsBreak; }
+};
+
+bool HasFormatBreakAttribute(FormatBreakTracker* pTracker, const SwTextAttr* pAttr)
+{
+ pTracker->Reset();
+
+ switch (pAttr->Which())
+ {
+ case RES_TXTATR_AUTOFMT:
+ case RES_TXTATR_CHARFMT:
+ {
+ const SfxItemSet& rSet((pAttr->Which() == RES_TXTATR_CHARFMT)
+ ? static_cast<SfxItemSet const&>(
+ pAttr->GetCharFormat().GetCharFormat()->GetAttrSet())
+ : *pAttr->GetAutoFormat().GetStyleHandle());
+
+ pTracker->HandleItemSet(rSet);
+ }
+ break;
+ }
+
+ if (pAttr->IsFormatIgnoreStart() || pAttr->IsFormatIgnoreEnd())
+ pTracker->Reset();
+
+ return pTracker->NeedsBreak();
+}
+}
+
+TextFrameIndex SwAttrIter::GetNextLayoutBreakAttr() const
+{
+ size_t nStartIndex(m_nStartIndex);
+ SwTextNode const* pTextNode(m_pTextNode);
+
+ sal_Int32 nNext = std::numeric_limits<sal_Int32>::max();
+
+ auto* pHints = pTextNode->GetpSwpHints();
+ if (!pHints)
+ {
+ return TextFrameIndex{ nNext };
+ }
+
+ FormatBreakTracker stTracker;
+ stTracker.HandleItemSet(pTextNode->GetSwAttrSet());
+
+ for (size_t i = 0; i < pHints->Count(); ++i)
+ {
+ SwTextAttr* const pAttr(pHints->Get(i));
+ if (HasFormatBreakAttribute(&stTracker, pAttr))
+ {
+ if (i >= nStartIndex)
+ {
+ nNext = pAttr->GetStart();
+ break;
+ }
+ }
+ }
+
+ return TextFrameIndex{ nNext };
+}
+
namespace {
class SwMinMaxArgs
@@ -869,7 +1046,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 +1055,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 +1065,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;
@@ -899,9 +1076,9 @@ static void lcl_MinMaxNode( SwFrameFormat* pNd, SwMinMaxNodeArgs* pIn )
if( !bIsDrawFrameFormat )
{
// Does the frame contain a table at the start or the end?
- const SwNodes& rNodes = pNd->GetDoc()->GetNodes();
+ 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 )
{
@@ -946,14 +1123,14 @@ static void lcl_MinMaxNode( SwFrameFormat* pNd, SwMinMaxNodeArgs* pIn )
}
const SvxLRSpaceItem &rLR = pNd->GetLRSpace();
- nMin += rLR.GetLeft();
- nMin += rLR.GetRight();
- nMax += rLR.GetLeft();
- nMax += rLR.GetRight();
+ nMin += rLR.ResolveLeft({});
+ nMin += rLR.ResolveRight({});
+ nMax += rLR.ResolveLeft({});
+ nMax += rLR.ResolveRight({});
if( css::text::WrapTextMode_THROUGH == pNd->GetSurround().GetSurround() )
{
- pIn->Minimum( nMin );
+ rIn.Minimum( nMin );
return;
}
@@ -966,33 +1143,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 +1180,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,28 +1197,29 @@ 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.ResolveTextLeft({}) + GetLeftMarginWithNum(true);
short nFLOffs;
// For enumerations a negative first line indentation is probably filled already
- if( !GetFirstLineOfsWithNum( nFLOffs ) || nFLOffs > nLROffset )
+ if (!GetFirstLineOfsWithNum(nFLOffs, {}) || nFLOffs > nLROffset)
nLROffset = nFLOffs;
SwMinMaxNodeArgs aNodeArgs;
aNodeArgs.m_nMinWidth = 0;
aNodeArgs.m_nMaxWidth = 0;
aNodeArgs.m_nLeftRest = nLROffset;
- aNodeArgs.m_nRightRest = rSpace.GetRight();
+ aNodeArgs.m_nRightRest = rRightMargin.ResolveRight({});
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 +1229,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.ResolveRight({}) - aNodeArgs.m_nRightRest);
aNodeArgs.m_nRightRest -= aNodeArgs.m_nRightDiff;
if (aNodeArgs.m_nRightRest < 0)
aNodeArgs.m_nMaxWidth -= aNodeArgs.m_nRightRest;
@@ -1173,8 +1351,8 @@ void SwTextNode::GetMinMaxSize( sal_uLong nIndex, sal_uLong& rMin, sal_uLong &rM
else
nCurrentWidth = pFrameFormat->GetFrameSize().GetWidth();
}
- nCurrentWidth += rLR.GetLeft();
- nCurrentWidth += rLR.GetRight();
+ nCurrentWidth += rLR.ResolveLeft({});
+ nCurrentWidth += rLR.ResolveRight({});
aArg.m_nWordAdd = nOldWidth + nOldAdd;
aArg.m_nWordWidth = nCurrentWidth;
aArg.m_nRowWidth += nCurrentWidth;
@@ -1224,7 +1402,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.ResolveRight({});
rAbsMin += nLROffset;
rAbsMin += nAdd;
@@ -1301,7 +1479,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 +1610,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 +1620,151 @@ 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;
+}
+
+bool SwTextFrame::HasSplitFlyDrawObjs() const
+{
+ return !GetSplitFlyDrawObjs().empty();
+}
+
+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 (SvxBreak const eBreak = GetBreakItem().GetBreak();
+ eBreak == SvxBreak::ColumnBefore || eBreak == SvxBreak::ColumnBoth
+ || eBreak == SvxBreak::PageBefore || eBreak == SvxBreak::PageBoth
+ || GetPageDescItem().GetPageDesc() != nullptr)
+ {
+ return false;
+ }
+
+ SwRectFnSet fnUpper(GetUpper());
+ if (fnUpper.YDiff(fnUpper.GetBottom(getFrameArea()), fnUpper.GetPrtBottom(*GetUpper())) <= 0)
+ {
+ 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 +1785,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..d721ca46c826 100644
--- a/sw/source/core/text/itratr.hxx
+++ b/sw/source/core/text/itratr.hxx
@@ -21,18 +21,21 @@
#include <o3tl/deleter.hxx>
#include "atrhndl.hxx"
#include <swfont.hxx>
+#include <txatbase.hxx>
namespace sw { struct MergedPara; }
-class SwTextAttr;
class SwTextNode;
class SwRedlineItr;
class SwViewShell;
class SwTextFrame;
-class SwAttrIter
+class SAL_DLLPUBLIC_RTTI SwAttrIter
{
friend class SwFontSave;
protected:
+ struct Destr{ void operator()(SwTextAttr *const pAttr) { SwTextAttr::Destroy(pAttr); } };
+ ::std::unique_ptr<SwTextAttr, Destr> m_pEndCharFormatAttr;
+ ::std::unique_ptr<SwTextAttr, Destr> m_pEndAutoFormatAttr;
SwAttrHandler m_aAttrHandler;
SwViewShell *m_pViewShell;
@@ -57,18 +60,20 @@ private:
const SwTextNode* m_pTextNode;
sw::MergedPara const* m_pMergedPara;
+ std::pair<SwTextNode const*, sal_Int32> SeekNewPos(TextFrameIndex nNewPos, bool * o_pIsToEnd);
void SeekFwd(sal_Int32 nOldPos, sal_Int32 nNewPos);
+ void SeekToEnd();
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,12 +81,13 @@ 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
// char position.
TextFrameIndex GetNextAttr() const;
+ TextFrameIndex GetNextLayoutBreakAttr() const;
/// Enables the attributes used at char pos nPos in the logical font
bool Seek(TextFrameIndex nPos);
// Creates the font at the specified position via Seek() and checks
diff --git a/sw/source/core/text/itrcrsr.cxx b/sw/source/core/text/itrcrsr.cxx
index f7c6380cb3bb..1a8c500ef92f 100644
--- a/sw/source/core/text/itrcrsr.cxx
+++ b/sw/source/core/text/itrcrsr.cxx
@@ -54,7 +54,7 @@ static void lcl_GetCharRectInsideField( SwTextSizeInfo& rInf, SwRect& rOrig,
const SwCursorMoveState& rCMS,
const SwLinePortion& rPor )
{
- OSL_ENSURE( rCMS.m_pSpecialPos, "Information about special pos missing" );
+ assert(rCMS.m_pSpecialPos && "Information about special pos missing");
if ( rPor.InFieldGrp() && !static_cast<const SwFieldPortion&>(rPor).GetExp().isEmpty() )
{
@@ -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,17 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
GetInfo().SetFont( GetFnt() );
const SwTextNode *const pNode = m_pFrame->GetTextNodeForParaProps();
- const SvxLRSpaceItem &rSpace = pNode->GetSwAttrSet().GetLRSpace();
+ auto stMetrics = GetFnt()->GetFontUnitMetrics();
+
+ SvxFirstLineIndentItem const& rFirstLine(pNode->GetSwAttrSet().GetFirstLineIndent());
+ SvxTextLeftMarginItem const& rTextLeftMargin(pNode->GetSwAttrSet().GetTextLeftMargin());
+ SvxRightMarginItem const& rRightMargin(pNode->GetSwAttrSet().GetRightMargin());
+
// #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.
@@ -184,16 +189,13 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
if ( m_pFrame->IsRightToLeft() )
{
// this calculation is identical this the calculation for L2R layout - see below
- mnLeft = m_pFrame->getFrameArea().Left() +
- m_pFrame->getFramePrintArea().Left() +
- nLMWithNum -
- pNode->GetLeftMarginWithNum() -
- // #i95907#
- // #i111284#
- // rSpace.GetLeft() + rSpace.GetTextLeft();
- ( bListLevelIndentsApplicableAndLabelAlignmentActive
- ? 0
- : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
+ mnLeft = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + nLMWithNum
+ - pNode->GetLeftMarginWithNum() -
+ // #i95907#
+ // #i111284#
+ // rSpace.GetLeft() + rSpace.GetTextLeft();
+ (rTextLeftMargin.ResolveLeft(rFirstLine, stMetrics)
+ - rTextLeftMargin.ResolveTextLeft(stMetrics));
}
else
{
@@ -203,32 +205,37 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
!pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
{
// this calculation is identical this the calculation for R2L layout - see above
- mnLeft = m_pFrame->getFrameArea().Left() +
- m_pFrame->getFramePrintArea().Left() +
- nLMWithNum -
- pNode->GetLeftMarginWithNum() -
- // #i95907#
- // #i111284#
- ( bListLevelIndentsApplicableAndLabelAlignmentActive
- ? 0
- : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
+ mnLeft = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left()
+ + nLMWithNum - pNode->GetLeftMarginWithNum() -
+ // #i95907#
+ // #i111284#
+ (rTextLeftMargin.ResolveLeft(rFirstLine, stMetrics)
+ - rTextLeftMargin.ResolveTextLeft(stMetrics));
}
else
{
- mnLeft = m_pFrame->getFrameArea().Left() +
- std::max( tools::Long( rSpace.GetTextLeft() + nLMWithNum ),
- m_pFrame->getFramePrintArea().Left() );
+ mnLeft
+ = m_pFrame->getFrameArea().Left()
+ + std::max(tools::Long(rTextLeftMargin.ResolveTextLeft(stMetrics) + nLMWithNum),
+ m_pFrame->getFramePrintArea().Left());
}
}
mnRight = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + m_pFrame->getFramePrintArea().Width();
- if( mnLeft >= mnRight &&
- // #i53066# Omit adjustment of nLeft for numbered
- // paras inside cells inside new documents:
- ( pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ||
- !m_pFrame->IsInTab() ||
- ( !nLMWithNum && (!bLabelAlignmentActive || bListLevelIndentsApplicable) ) ) )
+ // tdf#163913: Apply font-relative adjustment to the margins
+ mnLeft += rTextLeftMargin.ResolveLeftVariablePart(rFirstLine, stMetrics);
+ mnRight -= rRightMargin.ResolveRightVariablePart(stMetrics);
+
+ if (mnLeft >= mnRight &&
+ // #i53066# Omit adjustment of nLeft for numbered
+ // paras inside cells inside new documents:
+ (pNode->getIDocumentSettingAccess()->get(
+ DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING)
+ || !m_pFrame->IsInTab()
+ || (bListLevelIndentsApplicable
+ && nLMWithNum == rTextLeftMargin.ResolveTextLeft(stMetrics))
+ || (!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
@@ -241,8 +248,7 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
{
short nFLOfst = 0;
tools::Long nFirstLineOfs = 0;
- if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
- rSpace.IsAutoFirst() )
+ if (!pNode->GetFirstLineOfsWithNum(nFLOfst, stMetrics) && rFirstLine.IsAutoFirst())
{
nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
LanguageType const aLang = m_pFrame->GetLangOfChar(
@@ -250,49 +256,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" );
}
}
}
@@ -319,9 +330,10 @@ void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *p
}
else
{
- mnFirst = m_pFrame->getFrameArea().Left() +
- std::max( rSpace.GetTextLeft() + nLMWithNum+ nFirstLineOfs,
- m_pFrame->getFramePrintArea().Left() );
+ mnFirst = m_pFrame->getFrameArea().Left()
+ + std::max(rTextLeftMargin.ResolveTextLeft(stMetrics) + nLMWithNum
+ + nFirstLineOfs,
+ m_pFrame->getFramePrintArea().Left());
}
// Note: <SwTextFrame::GetAdditionalFirstLineOffset()> returns a negative
@@ -396,6 +408,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 +472,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 +480,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 +523,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 +549,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();
@@ -609,7 +653,12 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
{
if ( pPor->InSpaceGrp() && nSpaceAdd )
- nX += pPor->PrtWidth() +
+ // tdf#163042 In the case of shrunk lines with a single portion,
+ // adjust the line width to show the cursor in the correct position
+ nX += ( ( std::abs( m_pCurr->Width() - pPor->PrtWidth() ) <= 1 &&
+ m_pCurr->ExtraShrunkWidth() > 0 )
+ ? m_pCurr->ExtraShrunkWidth()
+ : pPor->PrtWidth() ) +
pPor->CalcSpacing( nSpaceAdd, aInf );
else
{
@@ -670,7 +719,6 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
{
if( pPor->IsMultiPortion() )
{
- nTmpAscent = AdjustBaseLine( *m_pCurr, pPor );
GetInfo().SetMulti( true );
pOrig->Pos().AdjustY(nTmpAscent - nPorAscent );
@@ -882,9 +930,22 @@ 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();
+ TextFrameIndex nMaxLen = TextFrameIndex(aInf.GetText().getLength()) - aInf.GetIdx();
+ aInf.SetLen( std::min(nMaxLen, pPor->GetLen()) );
pPor->SetLen( nOfst - aInf.GetIdx() );
- aInf.SetLen( pPor->GetLen() );
+ 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 +961,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 +1001,7 @@ void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
{
nX -= GetInfo().GetFont()->GetRightBorderSpace();
}
- }
+ }
}
bWidth = false;
break;
@@ -957,7 +1023,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 +1143,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 +1269,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 +1292,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 +1312,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())
{
@@ -1291,6 +1345,46 @@ static bool ConsiderNextPortionForCursorOffset(const SwLinePortion* pPor, sal_uI
return true;
}
+static auto SearchLine(SwLineLayout const*const pLineOfFoundPor,
+ SwLinePortion const*const pFoundPor,
+ int & rLines, std::vector<SwFieldPortion const*> & rPortions,
+ SwLineLayout const*const pLine) -> bool
+{
+ for (SwLinePortion const* pLP = pLine; pLP; pLP = pLP->GetNextPortion())
+ {
+ if (pLP == pFoundPor)
+ {
+ return true;
+ }
+ if (pLP->InFieldGrp())
+ {
+ SwFieldPortion const* pField(static_cast<SwFieldPortion const*>(pLP));
+ if (!pField->IsFollow())
+ {
+ rLines = 0;
+ rPortions.clear();
+ }
+ if (pLine == pLineOfFoundPor)
+ {
+ rPortions.emplace_back(pField);
+ }
+ }
+ else if (pLP->IsMultiPortion())
+ {
+ SwMultiPortion const*const pMulti(static_cast<SwMultiPortion const*>(pLP));
+ for (SwLineLayout const* pMLine = &pMulti->GetRoot();
+ pMLine; pMLine = pMLine->GetNext())
+ {
+ if (SearchLine(pLineOfFoundPor, pFoundPor, rLines, rPortions, pMLine))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return (pLine == pLineOfFoundPor);
+}
+
// Return: Offset in String
TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, const Point &rPoint,
bool bChgNode, SwCursorMoveState* pCMS ) const
@@ -1313,21 +1407,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();
@@ -1339,14 +1429,18 @@ 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();
+ // tdf#16342 In the case of shrunk lines with a single portion,
+ // adjust the line width to move the cursor to the click position
+ SwTwips nWidth =
+ ( std::abs( m_pCurr->Width() - pPor->Width() ) <= 1 && m_pCurr->ExtraShrunkWidth() > 0 )
+ ? m_pCurr->ExtraShrunkWidth()
+ : 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 +1464,7 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
}
}
- sal_uInt16 nWidth30;
+ SwTwips nWidth30;
if ( pPor->IsPostItsPortion() )
nWidth30 = 0;
else
@@ -1380,9 +1474,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 +1483,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 +1525,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 +1576,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,10 +1596,18 @@ 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())
{
+ if (pPor->IsBreakPortion())
+ {
+ return nCurrStart;
+ }
if ( nWidth )
{
// no quick return for as-character frames, we want to peek inside
@@ -1553,37 +1654,39 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
return nCurrStart;
}
}
- else
+ else if (pPor->IsPostItsPortion())
{
- if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() ||
- pPor->InToxRefGrp() )
+ if (SwPostItsPortion* pPostItsPortion = dynamic_cast<SwPostItsPortion*>(pPor))
{
- SwPostItsPortion* pPostItsPortion = pPor->IsPostItsPortion() ? dynamic_cast<SwPostItsPortion*>(pPor) : nullptr;
- if (pPostItsPortion)
+ if (!pPostItsPortion->IsScript()) // tdf#141079
{
- if (!pPostItsPortion->IsScript()) // tdf#141079
- {
- // Offset would be nCurrStart + nLength below, do the same for post-it portions.
- nCurrStart += pPor->GetLen();
- }
+ // Offset would be nCurrStart + nLength below, do the same for post-it portions.
+ nCurrStart += pPor->GetLen();
}
- return nCurrStart;
}
- if ( pPor->InFieldGrp() )
+ return nCurrStart;
+ }
+ else if (pPor->InToxRefGrp())
+ {
+ return nCurrStart;
+ }
+ else if (pPor->InFieldGrp())
+ {
+ if (bRightOver && !static_cast<SwFieldPortion*>(pPor)->HasFollow())
{
- if( bRightOver && !static_cast<SwFieldPortion*>(pPor)->HasFollow() )
- {
- nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
- }
- return nCurrStart;
+ nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
}
+ return nCurrStart;
}
}
// 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 +1728,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 +1743,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 +1756,20 @@ 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!" );
- SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
- *aSizeInf.GetOut(),
- &pPara->GetScriptInfo(),
- aSizeInf.GetText(),
- aSizeInf.GetIdx(),
- pPor->GetLen() );
+ // 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(), nSafeLen, aSizeInf.GetLayoutContext());
// Drop portion works like a multi portion, just its parts are not portions
if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
@@ -1689,12 +1793,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 +1833,27 @@ 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())
+ {
+ if (SearchLine(m_pCurr, pPor, nLines, portions, pLine))
+ {
+ break;
+ }
+ ++nLines;
+ }
+ for (SwFieldPortion const* pField : portions)
+ {
+ pCMS->m_pSpecialPos->nCharOfst += pField->GetExp().getLength();
+ }
+ pCMS->m_pSpecialPos->nLineOfst = nLines;
+ }
nLength = TextFrameIndex(0);
}
@@ -1736,11 +1861,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 +1875,13 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
// (BugId: 9692 + Change in feshview)
SwFlyInContentFrame *pTmp = pFlyPor->GetFlyFrame();
SwFrame* pLower = pTmp->GetLower();
- bool bChgNodeInner = pLower
- && (pLower->IsTextFrame() || pLower->IsLayoutFrame());
+ // 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->IsNoTextFrame()
+ && (!pCMS || pCMS->m_eState != CursorMoveState::SetOnlyText)));
Point aTmpPoint( rPoint );
if ( m_pFrame->IsRightToLeft() )
@@ -1765,7 +1890,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 +1942,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 +1956,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 +2049,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..2d19e046430f 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,20 @@
#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 <EnhancedPDFExportHelper.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 <unotxdoc.hxx>
using namespace ::com::sun::star;
@@ -126,7 +140,7 @@ void SwTextFormatter::Insert( SwLineLayout *pLay )
m_pCurr = pLay;
}
-sal_uInt16 SwTextFormatter::GetFrameRstHeight() const
+SwTwips SwTextFormatter::GetFrameRstHeight() const
{
// We want the rest height relative to the page.
// If we're in a table, then pFrame->GetUpper() is not the page.
@@ -140,7 +154,17 @@ sal_uInt16 SwTextFormatter::GetFrameRstHeight() const
if( 0 > nHeight )
return m_pCurr->Height();
else
- return sal_uInt16( nHeight );
+ return nHeight;
+}
+
+bool SwTextFormatter::ClearIfIsFirstOfBorderMerge(const SwLinePortion* pPortion)
+{
+ if (pPortion == m_pFirstOfBorderMerge)
+ {
+ m_pFirstOfBorderMerge = nullptr;
+ return true;
+ }
+ return false;
}
SwLinePortion *SwTextFormatter::Underflow( SwTextFormatInfo &rInf )
@@ -155,7 +179,6 @@ SwLinePortion *SwTextFormatter::Underflow( SwTextFormatInfo &rInf )
// Can be seen in 8081.sdw, if you enter text in the first line
TextFrameIndex const nSoftHyphPos = rInf.GetSoftHyphPos();
- TextFrameIndex const nUnderScorePos = rInf.GetUnderScorePos();
// Save flys and set to 0, or else segmentation fault
// Not ClearFly(rInf) !
@@ -168,7 +191,6 @@ SwLinePortion *SwTextFormatter::Underflow( SwTextFormatInfo &rInf )
// Truncate()
rInf.SetUnderflow(nullptr);
rInf.SetSoftHyphPos( nSoftHyphPos );
- rInf.SetUnderScorePos( nUnderScorePos );
rInf.SetPaintOfst( GetLeftMargin() );
// We look for the portion with the under-flow position
@@ -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,13 +406,51 @@ 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()));
- const bool bHasGrid = pGrid && rInf.SnapToGrid() &&
- GRID_LINES_CHARS == pGrid->GetGridType();
+ // tdf#149089: For compatibility with MSO grid layout, do not insert kern portions to
+ // align successive portions to the char grid when MS_WORD_COMP_GRID_METRICS is set.
+ // See also tdf#161145.
+ // tdf#139418: However, in testing, this only seems to apply to horizontal text.
+ const bool bUseGridKernPors = GetTextFrame()->IsVertical()
+ || !GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::MS_WORD_COMP_GRID_METRICS);
+ const bool bHasGrid = pGrid && rInf.SnapToGrid()
+ && SwTextGrid::LinesAndChars == pGrid->GetGridType() && bUseGridKernPors;
const SwDoc & rDoc = rInf.GetTextFrame()->GetDoc();
const sal_uInt16 nGridWidth = bHasGrid ? GetGridWidth(*pGrid, rDoc) : 0;
@@ -424,7 +483,7 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
{
SwFontScript nNxtActual = rInf.GetFont()->GetActual();
SwFontScript nLstActual = nNxtActual;
- sal_uInt16 nLstHeight = static_cast<sal_uInt16>(rInf.GetFont()->GetHeight());
+ tools::Long nLstHeight = rInf.GetFont()->GetHeight();
bool bAllowBehind = false;
const CharClass& rCC = GetAppCharClass();
@@ -467,7 +526,7 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
if ( pTmpFnt )
{
nLstActual = pTmpFnt->GetActual();
- nLstHeight = static_cast<sal_uInt16>(pTmpFnt->GetHeight());
+ nLstHeight = pTmpFnt->GetHeight();
}
}
}
@@ -482,18 +541,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 +598,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 +685,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 +706,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();
@@ -658,7 +728,7 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
// calculate size
SwLinePortion* pTmpPor = pGridKernPortion->GetNextPortion();
- sal_uInt16 nSumWidth = pPor->Width();
+ SwTwips nSumWidth = pPor->Width();
while ( pTmpPor )
{
nSumWidth = nSumWidth + pTmpPor->Width();
@@ -670,15 +740,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 +777,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 +859,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 +868,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,8 +897,9 @@ 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() );
+ pPor->Height(rInf.GetTextHeight());
bCalc = true;
}
else
@@ -842,32 +923,290 @@ 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;
+ bool bTextFound = false;
+ for (const auto& rItem : pContentControl->GetListItems())
+ {
+ pListWidget->Entries.push_back(rItem.m_aDisplayText);
+ if (rItem.m_aDisplayText == aText)
+ {
+ pListWidget->SelectedEntries.push_back(nIndex);
+ bTextFound = true;
+ }
+ ++nIndex;
+ }
+ if (!aText.isEmpty() && !bTextFound)
+ {
+ // The selected entry has to be an index, if there is no index for it, insert one at
+ // the start.
+ pListWidget->Entries.insert(pListWidget->Entries.begin(), aText);
+ pListWidget->SelectedEntries.push_back(0);
+ }
+ 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;
+ }
+
+ bool bShrinkPageForPostIts = pPDFExtOutDevData->GetIsExportNotesInMargin()
+ && sw_GetPostIts(rDoc.getIDocumentFieldsAccess(), nullptr);
+ const SwFont* pFont = rInf.GetFont();
+ if (pFont)
+ {
+ pDescriptor->TextFont = pFont->GetActualFont();
+ if (bShrinkPageForPostIts)
+ {
+ // Page area is scaled down so we have space for comments. Scale down the font height
+ // for the content of the widgets, too.
+ double fScale = SwEnhancedPDFExportHelper::GetSwRectToPDFRectScale();
+ pDescriptor->TextFont.SetFontHeight(pDescriptor->TextFont.GetFontHeight() * fScale);
+ }
+
+ // Need to transport the color explicitly, so it's applied to both already filled in and
+ // future content.
+ pDescriptor->TextColor = pFont->GetColor();
+ }
+
+ // 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);
+
+ // PDF spec 12.5.2 Annotation Dictionaries says the default border with is 1pt wide, increase
+ // the rectangle to compensate for that, otherwise the text will be cut off at the end.
+ aLocation.AddTop(-20);
+ aLocation.AddBottom(20);
+ aLocation.AddLeft(-20);
+ aLocation.AddRight(20);
+
+ tools::Rectangle aRect = aLocation.SVRect();
+ if (bShrinkPageForPostIts)
+ {
+ // Map the rectangle of the form widget, similar to how it's done for e.g. hyperlinks.
+ const SwPageFrame* pPageFrame = pTextFrame->FindPageFrame();
+ if (pPageFrame)
+ {
+ aRect = SwEnhancedPDFExportHelper::MapSwRectToPDFRect(pPageFrame, aRect);
+ }
+ }
+ pDescriptor->Location = aRect;
+
+ pPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::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)
+ OUString ExpandFieldmark(Fieldmark* pBM)
{
- const IFieldmark::parameter_map_t* const pParameters = pBM->GetParameters();
+ if (pBM->GetFieldname() == ODF_FORMCHECKBOX)
+ {
+ ::sw::mark::CheckboxFieldmark const*const pCheckboxFm(
+ dynamic_cast<CheckboxFieldmark const*>(pBM));
+ assert(pCheckboxFm);
+ return pCheckboxFm->IsChecked()
+ ? u"\u2612"_ustr
+ : u"\u2610"_ustr;
+ }
+ assert(pBM->GetFieldname() == ODF_FORMDROPDOWN);
+ const Fieldmark::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 Fieldmark::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 Fieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
if (pListEntries != pParameters->end())
{
uno::Sequence< OUString > vListEntries;
@@ -876,7 +1215,6 @@ namespace sw::mark {
return vListEntries[nCurrentIdx];
}
- static constexpr OUStringLiteral vEnSpaces = u"\u2002\u2002\u2002\u2002\u2002";
return vEnSpaces;
}
}
@@ -892,7 +1230,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 +1243,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 +1256,68 @@ 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);
+
+ if (const SwDocShell* pShell = rDoc.GetDocShell())
+ {
+ rtl::Reference<SwXTextDocument> xDocumentMetadataAccess(pShell->GetBaseModel());
+
+ 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 +1325,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::Fieldmark *pBM = pFrame->GetDoc().getIDocumentMarkAccess()->getInnerFieldmarkFor(aPosition);
if(pBM != nullptr && pBM->GetFieldname( ) == ODF_FORMDATE)
{
if (ch == CH_TXT_ATR_FIELDSTART)
@@ -970,10 +1368,14 @@ 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;
+ if (pBM && pBM->GetFieldname() == ODF_FORMTEXT)
+ pPor->SetFieldmarkText(true);
+ }
}
}
}
@@ -993,25 +1395,23 @@ 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()));
-
+ // until next layout-breaking attribute change:
+ const TextFrameIndex nNextLayoutBreakAttr = GetNextLayoutBreakAttr();
// 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);
+
+ auto nNextContext = std::min({ nNextChg, nNextLayoutBreakAttr, nNextScript, nNextDir });
+ nNextChg = std::min({ nNextChg, nNextAttr, nNextScript, nNextDir, nNextHidden, nNextBookmark });
// Turbo boost:
// We assume that font characters are not larger than twice
@@ -1030,13 +1430,15 @@ 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 = std::min(nNextChg, nExpect);
+ nNextContext = std::min(nNextContext, nExpect);
+ }
// we keep an invariant during method calls:
// there are no portion ending characters like hard spaces
@@ -1056,6 +1458,56 @@ SwTextPortion *SwTextFormatter::NewTextPortion( SwTextFormatInfo &rInf )
pPor->SetLen( nNextChg - rInf.GetIdx() );
rInf.SetLen( pPor->GetLen() );
+
+ // Generate a new layout context for the text portion. This is necessary
+ // for the first text portion in a paragraph, or for any successive
+ // portions that are outside of the bounds of the previous context.
+ if (!rInf.GetLayoutContext().has_value()
+ || rInf.GetLayoutContext()->m_nBegin < rInf.GetLineStart().get()
+ || rInf.GetLayoutContext()->m_nEnd < nNextChg.get())
+ {
+ // The layout context must terminate at special characters
+ sal_Int32 nEnd = rInf.GetIdx().get();
+ for (; nEnd < nNextContext.get(); ++nEnd)
+ {
+ bool bAtEnd = false;
+ switch (rInf.GetText()[nEnd])
+ {
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_TAB:
+ case CH_TXTATR_NEWLINE:
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ case CHAR_SOFTHYPHEN:
+ bAtEnd = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (bAtEnd)
+ {
+ break;
+ }
+ }
+
+ std::optional<SwLinePortionLayoutContext> nNewContext;
+ if (rInf.GetIdx().get() != nEnd)
+ {
+ nNewContext = SwLinePortionLayoutContext{ rInf.GetIdx().get(), nEnd };
+ }
+
+ rInf.SetLayoutContext(nNewContext);
+ }
+
+ pPor->SetLayoutContext(rInf.GetLayoutContext());
+
return pPor;
}
@@ -1196,32 +1648,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 +1689,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 +1776,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 +1835,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 +1876,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 SwTwips nWidthOfPortionsUpToDecimalPosition = rInf.X() - pLastTabPortion->GetFix();
static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition );
rInf.SetTabDecimal( 0 );
}
@@ -1494,11 +1939,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 +2017,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 +2062,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 +2070,7 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
FormatReset( GetInfo() );
GetInfo().SetNumDone( bOldNumDone );
+ GetInfo().SetFootnoteDone(bOldFootnoteDone);
GetInfo().SetArrowDone( bOldArrowDone );
GetInfo().SetErgoDone( bOldErgoDone );
@@ -1636,6 +2082,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 +2109,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,8 +2128,17 @@ 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();
+ GetInfo().SetExtraSpace(0);
// delete old rest
if ( GetInfo().GetRest() )
@@ -1690,6 +2153,7 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
m_pCurr->SetLen(TextFrameIndex(0));
m_pCurr->Width(0);
+ m_pCurr->ExtraShrunkWidth(0);
m_pCurr->Truncate();
}
}
@@ -1700,7 +2164,7 @@ TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
// the SwLineLayout is wider as well.
if (GetInfo().GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_OVER_MARGIN))
{
- sal_uInt16 nSum = 0;
+ SwTwips nSum = 0;
SwLinePortion* pPor = m_pCurr->GetFirstPortion();
while (pPor)
@@ -1772,21 +2236,63 @@ 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()));
if ( pGrid && GetInfo().SnapToGrid() )
{
+ // tdf#88752: Grid base height is ignored for table rows in compat mode
+ if (m_pFrame->IsInTab()
+ && m_pFrame->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::MS_WORD_COMP_GRID_METRICS))
+ {
+ m_pCurr->SetRealHeight(nLineHeight);
+ return;
+ }
+
const sal_uInt16 nGridWidth = pGrid->GetBaseHeight();
const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight();
const bool bRubyTop = ! pGrid->GetRubyTextBelow();
nLineHeight = nGridWidth + nRubyHeight;
- const sal_uInt16 nAmpRatio = (m_pCurr->Height() + nLineHeight - 1)/nLineHeight;
+ const auto nAmpRatio = (m_pCurr->Height() + nLineHeight - 1) / nLineHeight;
nLineHeight *= nAmpRatio;
- const sal_uInt16 nAsc = m_pCurr->GetAscent() +
+ // tdf#164871: Handle all types of line spacing in grid layout.
+ // The prop/auto rule was originally implemented with #99106#, but other spacing
+ // types were not implemented (perhaps unintentionally). These implementations
+ // differ from the below non-grid implementations, so cannot be reused.
+ const SvxLineSpacingItem* pSpace = m_aLineInf.GetLineSpacing();
+ if (pSpace)
+ {
+ switch (pSpace->GetLineSpaceRule())
+ {
+ case SvxLineSpaceRule::Min:
+ // tdf#164871: MSO idiosyncratically disables the grid extra space
+ // when the minimum height is 0. While strange, certain documents
+ // require this and it seems harmless to emulate.
+ if (pSpace->GetLineHeight() == 0)
+ {
+ nLineHeight = m_pCurr->Height() + nRubyHeight;
+ }
+
+ if (nLineHeight < pSpace->GetLineHeight())
+ {
+ nLineHeight = pSpace->GetLineHeight();
+ }
+ break;
+
+ case SvxLineSpaceRule::Fix:
+ nLineHeight = pSpace->GetLineHeight();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ const SwTwips nAsc = m_pCurr->GetAscent() +
( bRubyTop ?
( nLineHeight - m_pCurr->Height() + nRubyHeight ) / 2 :
( nLineHeight - m_pCurr->Height() - nRubyHeight ) / 2 );
@@ -1796,7 +2302,6 @@ void SwTextFormatter::CalcRealHeight( bool bNewLine )
m_pInf->GetParaPortion()->SetFixLineHeight();
// we ignore any line spacing options except from ...
- const SvxLineSpacingItem* pSpace = m_aLineInf.GetLineSpacing();
if ( ! IsParaLine() && pSpace &&
SvxInterLineSpaceRule::Prop == pSpace->GetInterLineSpaceRule() )
{
@@ -1806,7 +2311,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,8 +2346,8 @@ void SwTextFormatter::CalcRealHeight( bool bNewLine )
nTmp /= 100;
if( !nTmp )
++nTmp;
- nLineHeight = static_cast<sal_uInt16>(nTmp);
- sal_uInt16 nAsc = ( 4 * nLineHeight ) / 5; // 80%
+ nLineHeight = nTmp;
+ SwTwips nAsc = (4 * nLineHeight) / 5; // 80%
#if 0
// could do clipping here (like Word does)
// but at 0.5 its unreadable either way...
@@ -1866,7 +2371,7 @@ void SwTextFormatter::CalcRealHeight( bool bNewLine )
case SvxLineSpaceRule::Fix:
{
nLineHeight = pSpace->GetLineHeight();
- const sal_uInt16 nAsc = ( 4 * nLineHeight ) / 5; // 80%
+ const SwTwips nAsc = (4 * nLineHeight) / 5; // 80%
if( nAsc < m_pCurr->GetAscent() ||
nLineHeight - nAsc < m_pCurr->Height() - m_pCurr->GetAscent() )
m_pCurr->SetClipping( true );
@@ -1892,6 +2397,8 @@ void SwTextFormatter::CalcRealHeight( bool bNewLine )
if( nTmp < 50 )
nTmp = nTmp ? 50 : 100;
+ bool bPropLineShrinks = (nTmp < 100);
+
// extend line height by (nPropLineSpace - 100) percent of the font height
nTmp -= 100;
nTmp *= m_pCurr->GetTextHeight();
@@ -1899,7 +2406,20 @@ void SwTextFormatter::CalcRealHeight( bool bNewLine )
nTmp += nLineHeight;
if (nTmp < 1)
nTmp = 1;
- nLineHeight = static_cast<sal_uInt16>(nTmp);
+ nLineHeight = nTmp;
+
+ // tdf#146081: The height and ascent of the first line may have been
+ // adjusted above. In order to have consistent line spacing when rendering,
+ // the same adjustments must be made to the following lines.
+ if (bPropLineShrinks
+ && GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE))
+ {
+ SwTwips nAsc = (4 * nLineHeight) / 5; // 80%
+ m_pCurr->SetAscent(nAsc);
+ m_pCurr->Height(nLineHeight, false);
+ m_pInf->GetParaPortion()->SetFixLineHeight();
+ }
break;
}
case SvxInterLineSpaceRule::Fix:
@@ -1941,8 +2461,8 @@ void SwTextFormatter::FeedInf( SwTextFormatInfo &rInf ) const
rInf.First( FirstLeft() );
rInf.LeftMargin(GetLeftMargin());
- rInf.RealWidth( sal_uInt16(rInf.Right() - GetLeftMargin()) );
- rInf.Width( rInf.RealWidth() );
+ rInf.RealWidth(rInf.Right() - GetLeftMargin());
+ rInf.Width(std::max(rInf.RealWidth(), SwTwips(0)));
if( const_cast<SwTextFormatter*>(this)->GetRedln() )
{
const_cast<SwTextFormatter*>(this)->GetRedln()->Clear( const_cast<SwTextFormatter*>(this)->GetFnt() );
@@ -2099,8 +2619,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() )
{
@@ -2252,7 +2772,7 @@ bool SwTextFormatter::ChkFlyUnderflow( SwTextFormatInfo &rInf ) const
{
// First we check, whether a fly overlaps with the line.
// = GetLineHeight()
- const sal_uInt16 nHeight = GetCurr()->GetRealHeight();
+ const SwTwips nHeight = GetCurr()->GetRealHeight();
SwRect aLine( GetLeftMargin(), Y(), rInf.RealWidth(), nHeight );
SwRect aLineVert( aLine );
@@ -2285,7 +2805,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 +2822,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 +2898,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 +2950,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 +3013,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 +3045,7 @@ void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
if( bForced )
{
m_pCurr->SetForcedLeftMargin();
- rInf.ForcedLeftMargin( static_cast<sal_uInt16>(aInter.Width()) );
+ rInf.ForcedLeftMargin(aInter.Width());
}
if( bFullLine )
@@ -2481,7 +3061,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 +3074,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 +3091,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 +3131,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 +3150,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 +3172,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 +3182,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() ||
@@ -2726,8 +3310,8 @@ void SwTextFormatter::MergeCharacterBorder( SwLinePortion& rPortion, SwLinePorti
{
// Calculate maximum height and ascent
SwLinePortion* pActPor = m_pFirstOfBorderMerge;
- sal_uInt16 nMaxAscent = 0;
- sal_uInt16 nMaxHeight = 0;
+ SwTwips nMaxAscent = 0;
+ SwTwips nMaxHeight = 0;
bool bReachCurrent = false;
while( pActPor )
{
@@ -2767,6 +3351,16 @@ void SwTextFormatter::MergeCharacterBorder( SwLinePortion& rPortion, SwLinePorti
Seek(rInf.GetIdx());
}
+namespace sw {
+ bool IsShowHiddenChars(SwViewShell const*const pViewShell)
+ {
+ SwViewOption const*const pOpt{pViewShell ? pViewShell->GetViewOptions() : nullptr};
+ const bool bShowInDocView{pViewShell && pViewShell->GetWin() && pOpt->IsShowHiddenChar()};
+ const bool bShowForPrinting{pViewShell && pOpt->IsShowHiddenChar(true) && pOpt->IsPrinting()};
+ return (bShowInDocView || bShowForPrinting);
+ }
+}
+
namespace {
// calculates and sets optimal repaint offset for the current line
tools::Long lcl_CalcOptRepaint( SwTextFormatter &rThis,
diff --git a/sw/source/core/text/itrform2.hxx b/sw/source/core/text/itrform2.hxx
index bd986e4be324..18ee69f2fac4 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;
@@ -186,7 +186,7 @@ public:
void Insert( SwLineLayout *pLine );
// The remaining height to the page border
- sal_uInt16 GetFrameRstHeight() const;
+ SwTwips GetFrameRstHeight() const;
// How wide would you be without any bounds (Flys etc.)?
SwTwips CalcFitToContent_( );
@@ -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..6e9d48a3a8f3 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,15 +126,14 @@ 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() );
-// sal_uInt16 nFntAscent = GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), GetInfo().GetOut() );
-#endif
-
// maybe catch-up adjustment
GetAdjusted();
+ AddExtraBlankWidth();
GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
GetInfo().ResetSpaceIdx();
GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
@@ -140,13 +147,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 +200,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
@@ -195,7 +217,7 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
GetInfo().GetPos().Y() + nTmpHeight > rPaint.Top() + rPaint.Height() )
{
bClip = false;
- rClip.ChgClip( rPaint, m_pFrame, m_pCurr->HasUnderscore() );
+ rClip.ChgClip(rPaint, m_pFrame, m_pCurr->GetExtraAscent(), m_pCurr->GetExtraDescent());
}
#if OSL_DEBUG_LEVEL > 1
static bool bClipAlways = false;
@@ -228,7 +250,7 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
// tdf#117448 at small fixed line height, enlarge clipping area in table cells
// to show previously clipped text content on the area of paragraph margins
if ( rFrame.IsInTab() )
- rClip.ChgClip( aLineRect, m_pFrame, false, rFrame.GetTopMargin(), rFrame.GetBottomMargin() );
+ rClip.ChgClip(aLineRect, m_pFrame, rFrame.GetTopMargin(), rFrame.GetBottomMargin());
else
rClip.ChgClip( aLineRect, m_pFrame );
bClip = false;
@@ -243,7 +265,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 );
@@ -350,7 +372,7 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
GetInfo().X() + pPor->Width() + ( pPor->Height() / 2 ) > nMaxRight )
{
bClip = false;
- rClip.ChgClip( rPaint, m_pFrame, m_pCurr->HasUnderscore() );
+ rClip.ChgClip(rPaint, m_pFrame, m_pCurr->GetExtraAscent(), m_pCurr->GetExtraDescent());
}
// Portions, which lay "below" the text like post-its
@@ -385,9 +407,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 +430,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 +456,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,13 +487,51 @@ 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
delete GetInfo().GetUnderFnt();
GetInfo().SetUnderFnt( nullptr );
- // paint remaining stuff
+ // paint remaining stuff, e.g. the line ending symbols, pilcrow (¶) and the line break
if( bDrawInWindow )
{
// If special vertical alignment is enabled, GetInfo().Y() is the
@@ -455,6 +564,12 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
GetInfo().Y( GetInfo().GetPos().Y()
+ AdjustBaseLine( *m_pCurr, &aEnd ) );
GetInfo().X( GetInfo().X() +
+ // tdf#163042 In the case of shrunk lines with a single portion, adjust
+ // the line width (if needed, i.e. if the shrunk line doesn't end in a space)
+ // to show the terminating pilcrow at the correct position, and not before that
+ ( ( !( pEndTempl->GetNextPortion() && pEndTempl->GetNextPortion()->IsHolePortion() ) &&
+ std::abs( m_pCurr->Width() - m_pCurr->GetFirstPortion()->Width() ) <= 1 && m_pCurr->ExtraShrunkWidth() > 0 )
+ ? m_pCurr->ExtraShrunkWidth() - m_pCurr->Width() : 0 ) +
( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 ) );
aEnd.Paint( GetInfo() );
GetInfo().Y( nOldY );
@@ -543,12 +658,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..eb3a9a859854 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
@@ -144,9 +144,9 @@ private:
SwTwips mnLeft;
SwTwips mnRight;
SwTwips mnFirst;
- sal_uInt16 mnDropLeft;
- sal_uInt16 mnDropHeight;
- sal_uInt16 mnDropDescent;
+ SwTwips mnDropLeft;
+ SwTwips mnDropHeight;
+ SwTwips mnDropDescent;
sal_uInt16 mnDropLines;
SvxAdjust mnAdjust;
// #i91133#
@@ -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)
@@ -188,8 +188,7 @@ public:
bool IsLastBlock() const { return m_bLastBlock; }
bool IsLastCenter() const { return m_bLastCenter; }
SvxAdjust GetAdjust() const { return mnAdjust; }
- sal_uInt16 GetLineWidth() const
- { return sal_uInt16( Right() - GetLeftMargin() + 1 ); }
+ SwTwips GetLineWidth() const { return Right() - GetLeftMargin() + 1; }
SwTwips GetLeftMin() const { return std::min(mnFirst, mnLeft); }
bool HasNegFirst() const { return mnFirst < mnLeft; }
@@ -201,11 +200,11 @@ public:
// DropCaps
sal_uInt16 GetDropLines() const { return mnDropLines; }
void SetDropLines( const sal_uInt16 nNew ) { mnDropLines = nNew; }
- sal_uInt16 GetDropLeft() const { return mnDropLeft; }
- sal_uInt16 GetDropHeight() const { return mnDropHeight; }
- void SetDropHeight( const sal_uInt16 nNew ) { mnDropHeight = nNew; }
- sal_uInt16 GetDropDescent() const { return mnDropDescent; }
- void SetDropDescent( const sal_uInt16 nNew ) { mnDropDescent = nNew; }
+ SwTwips GetDropLeft() const { return mnDropLeft; }
+ SwTwips GetDropHeight() const { return mnDropHeight; }
+ void SetDropHeight(const SwTwips nNew) { mnDropHeight = nNew; }
+ SwTwips GetDropDescent() const { return mnDropDescent; }
+ void SetDropDescent(const SwTwips nNew) { mnDropDescent = nNew; }
void DropInit();
// Returns the TextPos for start and end of the current line without whitespace
@@ -270,6 +269,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 +287,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 +335,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..eb5a2aa01892 100644
--- a/sw/source/core/text/pordrop.hxx
+++ b/sw/source/core/text/pordrop.hxx
@@ -38,7 +38,7 @@ class SwDropPortionPart
std::unique_ptr<SwDropPortionPart> m_pFollow;
std::unique_ptr<SwFont> m_pFnt;
TextFrameIndex m_nLen;
- sal_uInt16 m_nWidth;
+ SwTwips m_nWidth;
bool m_bJoinBorderWithNext;
bool m_bJoinBorderWithPrev;
@@ -51,8 +51,8 @@ public:
void SetFollow( std::unique_ptr<SwDropPortionPart> pNew ) { m_pFollow = std::move(pNew); };
SwFont& GetFont() const { return *m_pFnt; }
TextFrameIndex GetLen() const { return m_nLen; }
- sal_uInt16 GetWidth() const { return m_nWidth; }
- void SetWidth( sal_uInt16 nNew ) { m_nWidth = nNew; }
+ SwTwips GetWidth() const { return m_nWidth; }
+ void SetWidth(SwTwips nNew) { m_nWidth = nNew; }
bool GetJoinBorderWithPrev() const { return m_bJoinBorderWithPrev; }
bool GetJoinBorderWithNext() const { return m_bJoinBorderWithNext; }
@@ -60,38 +60,39 @@ 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;
std::unique_ptr<SwDropPortionPart> m_pPart; // due to script/attribute changes
sal_uInt16 m_nLines; // Line count
- sal_uInt16 m_nDropHeight; // Height
- sal_uInt16 m_nDropDescent; // Distance to the next line
- sal_uInt16 m_nDistance; // Distance to the text
- sal_uInt16 m_nFix; // Fixed position
- short m_nY; // Y Offset
+ SwTwips m_nDropHeight; // Height
+ SwTwips m_nDropDescent; // Distance to the next line
+ SwTwips m_nDistance; // Distance to the text
+ SwTwips m_nFix; // Fixed position
+ SwTwips m_nY; // Y Offset
bool FormatText( SwTextFormatInfo &rInf );
void PaintText( const SwTextPaintInfo &rInf ) const;
public:
SwDropPortion( const sal_uInt16 nLineCnt,
- const sal_uInt16 nDropHeight,
- const sal_uInt16 nDropDescent,
- const sal_uInt16 nDistance );
+ const SwTwips nDropHeight,
+ const SwTwips nDropDescent,
+ const SwTwips nDistance );
virtual ~SwDropPortion() override;
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
void PaintDrop( const SwTextPaintInfo &rInf ) const;
virtual bool Format( SwTextFormatInfo &rInf ) override;
- virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
- virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual SwPositiveSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(SwTwips nOfst) const override;
sal_uInt16 GetLines() const { return m_nLines; }
- sal_uInt16 GetDistance() const { return m_nDistance; }
- sal_uInt16 GetDropHeight() const { return m_nDropHeight; }
- sal_uInt16 GetDropDescent() const { return m_nDropDescent; }
- sal_uInt16 GetDropLeft() const { return Width() + m_nFix; }
+ SwTwips GetDistance() const { return m_nDistance; }
+ SwTwips GetDropHeight() const { return m_nDropHeight; }
+ SwTwips GetDropDescent() const { return m_nDropDescent; }
+ SwTwips GetDropLeft() const { return Width() + m_nFix; }
SwDropPortionPart* GetPart() const { return m_pPart.get(); }
void SetPart( std::unique_ptr<SwDropPortionPart> pNew ) { m_pPart = std::move(pNew); }
diff --git a/sw/source/core/text/porexp.cxx b/sw/source/core/text/porexp.cxx
index 29f470c1490c..afdf71da21c0 100644
--- a/sw/source/core/text/porexp.cxx
+++ b/sw/source/core/text/porexp.cxx
@@ -18,11 +18,12 @@
*/
#include <viewopt.hxx>
+#include <IDocumentSettingAccess.hxx>
#include <SwPortionHandler.hxx>
#include "inftxt.hxx"
#include "porexp.hxx"
-TextFrameIndex SwExpandPortion::GetModelPositionForViewPoint(const sal_uInt16 nOfst) const
+TextFrameIndex SwExpandPortion::GetModelPositionForViewPoint(const SwTwips nOfst) const
{ return SwLinePortion::GetModelPositionForViewPoint( nOfst ); }
bool SwExpandPortion::GetExpText( const SwTextSizeInfo&, OUString &rText ) const
@@ -38,7 +39,17 @@ void SwExpandPortion::HandlePortion( SwPortionHandler& rPH ) const
rPH.Special( GetLen(), OUString(), GetWhichPor() );
}
-SwPosSize SwExpandPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+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);
+}
+
+SwPositiveSize SwExpandPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
{
SwTextSlot aDiffText( &rInf, this, false, false );
return rInf.GetTextSize();
@@ -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,51 @@ 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 (rInf.GetOpt().IsViewMetaChars() && rInf.GetOpt().IsHardBlank())
+ {
+ // Draw tilde or degree sign
+ OUString aMarker = (m_cChar == CHAR_HARDBLANK ?
+ rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess()
+ .get(DocumentSettingId::USE_VARIABLE_WIDTH_NBSP)
+ ? u"~"_ustr
+ : u"°"_ustr
+ : u"-"_ustr); //CHAR_HARDHYPHEN
+
+ SwPositiveSize 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(SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor());
+ 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 +261,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 )
{
@@ -223,7 +289,7 @@ void SwPostItsPortion::Paint( const SwTextPaintInfo &rInf ) const
rInf.DrawPostIts( IsScript() );
}
-sal_uInt16 SwPostItsPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+SwTwips SwPostItsPortion::GetViewWidth(const SwTextSizeInfo& rInf) const
{
// Unbelievable: PostIts are always visible
return rInf.OnWin() ? SwViewOption::GetPostItsWidth( rInf.GetOut() ) : 0;
diff --git a/sw/source/core/text/porexp.hxx b/sw/source/core/text/porexp.hxx
index 9c7be5be5aaf..baf45c7cf2dd 100644
--- a/sw/source/core/text/porexp.hxx
+++ b/sw/source/core/text/porexp.hxx
@@ -26,15 +26,19 @@ class SwExpandPortion : public SwTextPortion
public:
SwExpandPortion() { SetWhichPor( PortionType::Expand ); }
virtual bool Format( SwTextFormatInfo &rInf ) override;
- virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(SwTwips nOfst) const override;
virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
- virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual SwPositiveSize 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;
};
+/// 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
@@ -63,7 +70,7 @@ public:
explicit SwPostItsPortion( bool bScrpt );
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
- virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
+ virtual SwTwips GetViewWidth(const SwTextSizeInfo& rInf) const override;
virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
bool IsScript() const { return m_bScript; }
};
diff --git a/sw/source/core/text/porfld.cxx b/sw/source/core/text/porfld.cxx
index fdb2e4442916..ad4fa9290310 100644
--- a/sw/source/core/text/porfld.cxx
+++ b/sw/source/core/text/porfld.cxx
@@ -21,10 +21,16 @@
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
-#include <vcl/graph.hxx>
+#include <utility>
+
+#include <comphelper/string.hxx>
#include <editeng/brushitem.hxx>
+#include <o3tl/deleter.hxx>
+#include <vcl/graph.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 +48,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 +65,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;
@@ -66,21 +73,20 @@ SwFieldPortion *SwFieldPortion::Clone( const OUString &rExpand ) const
void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField )
{
- OSL_ENSURE( pField, "TakeNextOffset: Missing Source" );
+ assert(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 +106,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() ) );
@@ -115,12 +119,12 @@ SwFieldPortion::~SwFieldPortion()
m_pFont.reset();
}
-sal_uInt16 SwFieldPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+SwTwips 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 +141,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 +160,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 +173,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 +182,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 +204,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 );
@@ -202,7 +224,7 @@ void SwFieldPortion::CheckScript( const SwTextSizeInfo &rInf )
return;
SwFontScript nActual = m_pFont ? m_pFont->GetActual() : rInf.GetFont()->GetActual();
- sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, 0 );
+ sal_Int16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, 0 );
sal_Int32 nChg = 0;
if( i18n::ScriptType::WEAK == nScript )
{
@@ -316,10 +338,6 @@ bool SwFieldPortion::Format( SwTextFormatInfo &rInf )
}
rInf.SetLen( nFullLen );
- if (TextFrameIndex(COMPLETE_STRING) != rInf.GetUnderScorePos() &&
- rInf.GetUnderScorePos() > rInf.GetIdx() )
- rInf.SetUnderScorePos( rInf.GetIdx() );
-
if( m_pFont )
m_pFont->AllocFontCacheId( rInf.GetVsh(), m_pFont->GetActual() );
@@ -381,25 +399,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;
+ }
+ };
+ if (IsHook(nNew, true))
+ {
+ if (nNew == CH_BREAK)
+ {
+ bFull = true;
}
- default: ;
+ aNew = aNew.copy(1);
+ ++nNextOfst;
}
// Even if there is no more text left for a follow field,
@@ -410,8 +440,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 +472,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 +485,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,20 +493,30 @@ 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
+SwPositiveSize SwFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
{
SwFontSave aSave( rInf, m_pFont.get() );
- SwPosSize aSize( SwExpandPortion::GetTextSize( rInf ) );
+ SwPositiveSize aSize( SwExpandPortion::GetTextSize( rInf ) );
return aSize;
}
@@ -498,13 +547,12 @@ bool SwHiddenPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText )
SwNumberPortion::SwNumberPortion( const OUString &rExpand,
std::unique_ptr<SwFont> pFont,
const bool bLft,
- const bool bCntr,
- const sal_uInt16 nMinDst,
+ const bool bCntr, const SwTwips 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 );
@@ -512,7 +560,7 @@ SwNumberPortion::SwNumberPortion( const OUString &rExpand,
SetCenter( bCntr );
}
-TextFrameIndex SwNumberPortion::GetModelPositionForViewPoint(const sal_uInt16) const
+TextFrameIndex SwNumberPortion::GetModelPositionForViewPoint(const SwTwips) const
{
return TextFrameIndex(0);
}
@@ -550,15 +598,20 @@ 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()
- - rInf.First()
- + rInf.ForcedLeftMargin();
+ + rInf.GetTextFrame()
+ ->GetTextNodeForParaProps()
+ ->GetSwAttrSet()
+ .GetFirstLineIndent()
+ .ResolveTextFirstLineOffset({})
+ - rInf.First() + rInf.ForcedLeftMargin();
}
else
{
@@ -595,10 +648,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;
}
@@ -636,9 +689,9 @@ void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
}
// calculate the width of the number portion, including follows
- const sal_uInt16 nOldWidth = Width();
- sal_uInt16 nSumWidth = 0;
- sal_uInt16 nOffset = 0;
+ const SwTwips nOldWidth = Width();
+ SwTwips nSumWidth = 0;
+ SwTwips nOffset = 0;
const SwLinePortion* pTmp = this;
while ( pTmp && pTmp->InNumberGrp() )
@@ -686,7 +739,7 @@ void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
// logical const: reset width
SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
bPaintSpace = bPaintSpace && m_nFixWidth < nOldWidth;
- sal_uInt16 nSpaceOffs = m_nFixWidth;
+ SwTwips nSpaceOffs = m_nFixWidth;
pThis->Width( m_nFixWidth );
if( ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
@@ -702,7 +755,7 @@ void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
if( IsCenter() )
{
/* #110778# a / 2 * 2 == a is not a tautology */
- sal_uInt16 nTmpOffset = nOffset;
+ SwTwips nTmpOffset = nOffset;
nOffset /= 2;
if( nOffset < m_nMinDist )
nOffset = nTmpOffset - m_nMinDist;
@@ -729,7 +782,7 @@ void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
pThis->Width( nOldWidth - nSpaceOffs + 12 );
{
- SwTextSlot aDiffText( &aInf, this, true, false, " " );
+ SwTextSlot aDiffText( &aInf, this, true, false, u" "_ustr );
aInf.DrawText( *this, aInf.GetLen(), true );
}
}
@@ -742,7 +795,7 @@ SwBulletPortion::SwBulletPortion( const sal_UCS4 cBullet,
std::unique_ptr<SwFont> pFont,
const bool bLft,
const bool bCntr,
- const sal_uInt16 nMinDst,
+ const SwTwips nMinDst,
const bool bLabelAlignmentPosAndSpaceModeActive )
: SwNumberPortion( OUString(&cBullet, 1) + rBulletFollowedBy,
std::move(pFont), bLft, bCntr, nMinDst,
@@ -757,7 +810,7 @@ SwGrfNumPortion::SwGrfNumPortion(
const OUString& rGraphicFollowedBy,
const SvxBrushItem* pGrfBrush, OUString const & referer,
const SwFormatVertOrient* pGrfOrient, const Size& rGrfSize,
- const bool bLft, const bool bCntr, const sal_uInt16 nMinDst,
+ const bool bLft, const bool bCntr, const SwTwips nMinDst,
const bool bLabelAlignmentPosAndSpaceModeActive ) :
SwNumberPortion( rGraphicFollowedBy, nullptr, bLft, bCntr, nMinDst,
bLabelAlignmentPosAndSpaceModeActive ),
@@ -785,14 +838,14 @@ 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) );
+ Height(m_nGrfHeight);
m_bNoPaint = false;
}
-SwGrfNumPortion::~SwGrfNumPortion()
+void SwGrfNumPortion::ImplDestroy()
{
if ( IsAnimated() )
{
@@ -803,6 +856,11 @@ SwGrfNumPortion::~SwGrfNumPortion()
m_pBrush.reset();
}
+SwGrfNumPortion::~SwGrfNumPortion()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
void SwGrfNumPortion::StopAnimation( const OutputDevice* pOut )
{
if ( IsAnimated() )
@@ -817,7 +875,7 @@ bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
{
SetHide( false );
// Width( nFixWidth );
- sal_uInt16 nFollowedByWidth( 0 );
+ SwTwips nFollowedByWidth(0);
if ( mbLabelAlignmentPosAndSpaceModeActive )
{
SwFieldPortion::Format( rInf );
@@ -834,7 +892,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 +927,7 @@ bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
}
if( Width() < nDiff )
- Width( sal_uInt16(nDiff) );
+ Width( nDiff );
return bFull;
}
@@ -900,7 +958,7 @@ void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
if( m_nFixWidth < Width() && !bTmpLeft )
{
- sal_uInt16 nOffset = Width() - m_nFixWidth;
+ SwTwips nOffset = Width() - m_nFixWidth;
if( nOffset < m_nMinDist )
nOffset = 0;
else
@@ -935,7 +993,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 +1017,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 +1053,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 +1124,7 @@ void SwTextFrame::StopAnimation( const OutputDevice* pOut )
*/
SwCombinedPortion::SwCombinedPortion( const OUString &rText )
: SwFieldPortion( rText )
+ , m_aWidth{ 0, 0, 0 }
, m_nUpPos(0)
, m_nLowPos(0)
, m_nProportion(55)
@@ -1166,7 +1229,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);
+ 2 * rInf.GetOut()->GetFontMetric().GetFontSize().Width() / 3;
}
}
}
@@ -1178,7 +1241,7 @@ bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
m_nProportion = 55;
// In nMainAscent/Descent we store the ascent and descent
// of the original surrounding font
- sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
+ SwTwips nMaxDescent, nMaxAscent, nMaxWidth;
sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
nMainDescent = nMainDescent - nMainAscent;
@@ -1213,7 +1276,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] = aSize.Width();
if( i == nTop ) // enter the second line
{
m_nLowPos = nMaxDescent;
@@ -1228,7 +1291,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 )
@@ -1257,8 +1320,8 @@ bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
Height( nMainAscent + nMainDescent );
// We calculate the x positions of the characters in both lines...
- sal_uInt16 nTopDiff = 0;
- sal_uInt16 nBotDiff = 0;
+ SwTwips nTopDiff = 0;
+ SwTwips nBotDiff = 0;
if( nMaxWidth > Width() )
{
nTopDiff = ( nMaxWidth - Width() ) / 2;
@@ -1291,7 +1354,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();
@@ -1304,7 +1367,7 @@ bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
return bFull;
}
-sal_uInt16 SwCombinedPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+SwTwips SwCombinedPortion::GetViewWidth(const SwTextSizeInfo& rInf) const
{
if( !GetLen() ) // for the dummy part at the end of the line, where
return 0; // the combined portion doesn't fit.
@@ -1350,4 +1413,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::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::pdf::StructElement::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..da9624ffd854 100644
--- a/sw/source/core/text/porfld.hxx
+++ b/sw/source/core/text/porfld.hxx
@@ -30,10 +30,12 @@
class SvxBrushItem;
class SwFormatVertOrient;
+enum class SwJumpEditFormat;
class SwFieldPortion : public SwExpandPortion
{
friend class SwTextFormatter;
+
protected:
OUString m_aExpand; // The expanded field
std::unique_ptr<SwFont> m_pFont; // For multi-line fields
@@ -41,7 +43,7 @@ protected:
TextFrameIndex m_nNextScriptChg;
TextFrameIndex m_nFieldLen; //< Length of field text, 1 for normal fields, any number for input fields
// TODO ^ do we need this as member or is base class len enough?
- sal_uInt16 m_nViewWidth; // Screen width for empty fields
+ SwTwips m_nViewWidth; // Screen width for empty fields
bool m_bFollow : 1; // 2nd or later part of a field
bool m_bLeft : 1; // Used by SwNumberPortion
bool m_bHide : 1; // Used by SwNumberPortion
@@ -50,8 +52,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 +61,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; }
@@ -77,7 +78,7 @@ public:
// Empty fields are also allowed
virtual SwLinePortion *Compress() override;
- virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
+ virtual SwTwips GetViewWidth(const SwTextSizeInfo& rInf) const override;
bool IsFollow() const { return m_bFollow; }
void SetFollow( bool bNew ) { m_bFollow = bNew; }
@@ -103,10 +104,15 @@ public:
virtual SwFieldPortion *Clone( const OUString &rExpand ) const;
// Extra GetTextSize because of pFnt
- virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual SwPositiveSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
// 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;
};
/**
@@ -128,19 +134,18 @@ public:
class SwNumberPortion : public SwFieldPortion
{
protected:
- sal_uInt16 m_nFixWidth; // See Glues
- sal_uInt16 m_nMinDist; // Minimal distance to the text
+ SwTwips m_nFixWidth; // See Glues
+ SwTwips m_nMinDist; // Minimal distance to the text
bool mbLabelAlignmentPosAndSpaceModeActive;
public:
SwNumberPortion( const OUString &rExpand,
std::unique_ptr<SwFont> pFnt,
const bool bLeft,
- const bool bCenter,
- const sal_uInt16 nMinDst,
+ const bool bCenter, const SwTwips nMinDst,
const bool bLabelAlignmentPosAndSpaceModeActive );
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
- virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(SwTwips nOfst) const override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
// Field cloner for SplitGlue
@@ -156,7 +161,7 @@ public:
std::unique_ptr<SwFont> pFnt,
const bool bLeft,
const bool bCenter,
- const sal_uInt16 nMinDst,
+ const SwTwips nMinDst,
const bool bLabelAlignmentPosAndSpaceModeActive );
};
@@ -175,7 +180,7 @@ public:
const Size& rGrfSize,
const bool bLeft,
const bool bCenter,
- const sal_uInt16 nMinDst,
+ const SwTwips nMinDst,
const bool bLabelAlignmentPosAndSpaceModeActive );
virtual ~SwGrfNumPortion() override;
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
@@ -194,6 +199,8 @@ public:
SwTwips GetRelPos() const { return m_nYPos; }
SwTwips GetGrfHeight() const { return m_nGrfHeight; }
sal_Int16 GetOrient() const { return m_eOrient; }
+private:
+ void ImplDestroy();
};
/**
@@ -206,25 +213,25 @@ 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
+ SwTwips m_aPos[6]; // up to six X positions
+ o3tl::enumarray<SwFontScript, SwTwips> 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
+ SwTwips m_nUpPos; // the Y position of the upper baseline
+ SwTwips m_nLowPos; // the Y position of the lower baseline
sal_uInt8 m_nProportion; // relative font height
public:
explicit SwCombinedPortion( const OUString &rExpand );
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
- virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
+ virtual SwTwips GetViewWidth(const SwTextSizeInfo& rInf) const override;
};
-namespace sw::mark { class IFieldmark; }
+namespace sw::mark { class Fieldmark; }
class SwFieldFormDropDownPortion : public SwFieldPortion
{
public:
- explicit SwFieldFormDropDownPortion(sw::mark::IFieldmark *pFieldMark, const OUString &rExpand)
+ explicit SwFieldFormDropDownPortion(sw::mark::Fieldmark *pFieldMark, const OUString &rExpand)
: SwFieldPortion(rExpand)
, m_pFieldMark(pFieldMark)
{
@@ -235,14 +242,14 @@ public:
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
private:
- sw::mark::IFieldmark* m_pFieldMark;
+ sw::mark::Fieldmark* m_pFieldMark;
};
class SwFieldFormDatePortion : public SwFieldPortion
{
public:
- explicit SwFieldFormDatePortion(sw::mark::IFieldmark *pFieldMark, bool bStart)
- : SwFieldPortion("")
+ explicit SwFieldFormDatePortion(sw::mark::Fieldmark *pFieldMark, bool bStart)
+ : SwFieldPortion(u""_ustr)
, m_pFieldMark(pFieldMark)
, m_bStart(bStart)
{
@@ -253,8 +260,29 @@ public:
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
private:
- sw::mark::IFieldmark* m_pFieldMark;
+ sw::mark::Fieldmark* m_pFieldMark;
bool m_bStart;
};
+class SwJumpFieldPortion final : public SwFieldPortion
+{
+public:
+ explicit SwJumpFieldPortion(OUString aExpand, OUString aHelp, std::unique_ptr<SwFont> pFont,
+ SwJumpEditFormat 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:
+ SwJumpEditFormat 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..b0202f7fecca 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(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 SwTwips nNewWidth = 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() :
@@ -153,30 +164,32 @@ void SwTextFrame::MoveFlyInCnt(SwTextFrame *pNew,
if ( nullptr == pObjs )
return;
- for ( size_t i = 0; GetDrawObjs() && i < pObjs->size(); ++i )
+ size_t i = 0;
+ while (GetDrawObjs() && i < pObjs->size())
{
// 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 );
}
- else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) != nullptr )
+ else
{
RemoveDrawObj( *pAnchoredObj );
pNew->AppendDrawObj( *pAnchoredObj );
}
- --i;
+ continue; // pObjs shrunk
}
}
+ ++i;
}
}
@@ -211,8 +224,8 @@ void sw::FlyContentPortion::Paint(const SwTextPaintInfo& rInf) const
rInf.GetTextFrame()->SwitchHorizontalToVertical(aRepaintRect);
if(!((m_pFly->IsCompletePaint() ||
- m_pFly->getFrameArea().IsOver(aRepaintRect)) &&
- SwFlyFrame::IsPaint(m_pFly->GetVirtDrawObj(), m_pFly->getRootFrame()->GetCurrShell())))
+ m_pFly->getFrameArea().Overlaps(aRepaintRect)) &&
+ SwFlyFrame::IsPaint(m_pFly->GetVirtDrawObj(), *m_pFly->getRootFrame()->GetCurrShell())))
return;
SwRect aRect(m_pFly->getFrameArea());
@@ -226,7 +239,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 +249,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 +284,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 +374,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 +405,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() + 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..b22deb6ca4a2 100644
--- a/sw/source/core/text/porftn.hxx
+++ b/sw/source/core/text/porftn.hxx
@@ -30,18 +30,18 @@ class SwTextFootnote;
class SwFootnotePortion : public SwFieldPortion
{
SwTextFootnote *m_pFootnote;
- sal_uInt16 m_nOrigHeight;
+ SwTwips m_nOrigHeight;
// #i98418#
bool mbPreferredScriptTypeSet;
SwFontScript mnPreferredScriptType;
public:
SwFootnotePortion( const OUString &rExpand, SwTextFootnote *pFootnote,
- sal_uInt16 nOrig = USHRT_MAX );
- sal_uInt16& Orig() { return m_nOrigHeight; }
+ SwTwips nOrig = std::numeric_limits<SwTwips>::max());
+ SwTwips& Orig() { return m_nOrigHeight; }
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
- virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual SwPositiveSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
// #i98418#
@@ -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;
@@ -94,7 +94,7 @@ class SwErgoSumPortion : public SwFieldPortion
{
public:
SwErgoSumPortion( const OUString &rExp, std::u16string_view rStr );
- virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(SwTwips nOfst) const override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
// Field cloner for SplitGlue
diff --git a/sw/source/core/text/porglue.cxx b/sw/source/core/text/porglue.cxx
index 7c09ded23a2f..cd5eaf2643b5 100644
--- a/sw/source/core/text/porglue.cxx
+++ b/sw/source/core/text/porglue.cxx
@@ -25,14 +25,14 @@
#include "porfly.hxx"
#include <comphelper/string.hxx>
-SwGluePortion::SwGluePortion( const sal_uInt16 nInitFixWidth )
+SwGluePortion::SwGluePortion(const SwTwips nInitFixWidth)
: m_nFixWidth( nInitFixWidth )
{
PrtWidth( m_nFixWidth );
SetWhichPor( PortionType::Glue );
}
-TextFrameIndex SwGluePortion::GetModelPositionForViewPoint(const sal_uInt16 nOfst) const
+TextFrameIndex SwGluePortion::GetModelPositionForViewPoint(const SwTwips nOfst) const
{
// FIXME why nOfst > GetLen() ? is that supposed to be > Width() ?
if( !GetLen() || nOfst > sal_Int32(GetLen()) || !Width() )
@@ -41,12 +41,12 @@ TextFrameIndex SwGluePortion::GetModelPositionForViewPoint(const sal_uInt16 nOfs
return TextFrameIndex(nOfst / (Width() / sal_Int32(GetLen())));
}
-SwPosSize SwGluePortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+SwPositiveSize SwGluePortion::GetTextSize( const SwTextSizeInfo &rInf ) const
{
if (TextFrameIndex(1) >= GetLen() || rInf.GetLen() > GetLen() || !Width() || !GetLen())
- return SwPosSize(*this);
+ return SwPositiveSize(*this);
else
- return SwPosSize((Width() / sal_Int32(GetLen())) * sal_Int32(rInf.GetLen()), Height());
+ return SwPositiveSize((Width() / sal_Int32(GetLen())) * sal_Int32(rInf.GetLen()), Height());
}
bool SwGluePortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
@@ -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);
@@ -87,7 +88,7 @@ void SwGluePortion::Paint( const SwTextPaintInfo &rInf ) const
if (TextFrameIndex(1) == GetLen())
{
OUString aBullet( CH_BULLET );
- SwPosSize aBulletSize( rInf.GetTextSize( aBullet ) );
+ SwPositiveSize aBulletSize( rInf.GetTextSize( aBullet ) );
Point aPos( rInf.GetPos() );
aPos.AdjustX((Width()/2) - (aBulletSize.Width()/2) );
SwTextPaintInfo aInf( rInf, &aBullet );
@@ -128,13 +129,25 @@ 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!
*/
SwFixPortion::SwFixPortion( const SwRect &rRect )
- :SwGluePortion( sal_uInt16(rRect.Width()) ), m_nFix( sal_uInt16(rRect.Left()) )
+ :SwGluePortion(rRect.Width()), m_nFix(rRect.Left())
{
- Height( sal_uInt16(rRect.Height()) );
+ Height(rRect.Height());
SetWhichPor( PortionType::Fix );
}
@@ -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..bf72ac571151 100644
--- a/sw/source/core/text/porglue.hxx
+++ b/sw/source/core/text/porglue.hxx
@@ -24,36 +24,43 @@
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:
- sal_uInt16 m_nFixWidth;
+ SwTwips m_nFixWidth;
public:
- explicit SwGluePortion( const sal_uInt16 nInitFixWidth );
+ explicit SwGluePortion(const SwTwips nInitFixWidth);
void Join( SwGluePortion *pVictim );
inline tools::Long GetPrtGlue() const;
- sal_uInt16 GetFixWidth() const { return m_nFixWidth; }
- void SetFixWidth( const sal_uInt16 nNew ) { m_nFixWidth = nNew; }
+ SwTwips GetFixWidth() const { return m_nFixWidth; }
+ void SetFixWidth(const SwTwips nNew) { m_nFixWidth = nNew; }
void MoveGlue( SwGluePortion *pTarget, const tools::Long nPrtGlue );
inline void MoveAllGlue( SwGluePortion *pTarget );
inline void MoveHalfGlue( SwGluePortion *pTarget );
inline void AdjFixWidth();
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
- virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
- virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(SwTwips nOfst) const override;
+ virtual SwPositiveSize 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
{
- sal_uInt16 m_nFix; // The width offset in the line
+ SwTwips m_nFix; // The width offset in the line
public:
explicit SwFixPortion( const SwRect &rFlyRect );
SwFixPortion();
- void SetFix( const sal_uInt16 nNewFix ) { m_nFix = nNewFix; }
- sal_uInt16 GetFix() const { return m_nFix; }
+ void SetFix(const SwTwips nNewFix) { m_nFix = nNewFix; }
+ SwTwips 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..a8340089add1 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
@@ -57,7 +60,7 @@ public:
class SwSoftHyphPortion : public SwHyphPortion
{
bool m_bExpand;
- sal_uInt16 m_nViewWidth;
+ SwTwips m_nViewWidth;
public:
SwSoftHyphPortion();
@@ -69,7 +72,7 @@ public:
void SetExpand( const bool bNew ) { m_bExpand = bNew; }
bool IsExpand() const { return m_bExpand; }
- virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
+ virtual SwTwips GetViewWidth(const SwTextSizeInfo& rInf) const override;
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx
index 3d26e990c50c..e321a16160dc 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,140 +41,51 @@
#include <optional>
#include <editeng/adjustitem.hxx>
#include <editeng/charhiddenitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/scripthintitem.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>
+#include <i18nutil/kashida.hxx>
+#include <i18nutil/scriptchangescanner.hxx>
+#include <unotxdoc.hxx>
-#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.
-static bool isBehChar(sal_Unicode cCh)
-{
- bool bRet = false;
- switch (u_getIntPropertyValue(cCh, UCHAR_JOINING_GROUP))
- {
- 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:
- case U_JG_BURUSHASKI_YEH_BARREE:
- bRet = true;
- break;
- default:
- bRet = false;
- break;
- }
-
- return bRet;
-}
-
-// Yeh and charters that behave like Yeh in final form.
-static bool isYehChar(sal_Unicode cCh)
-{
- bool bRet = false;
- switch (u_getIntPropertyValue(cCh, UCHAR_JOINING_GROUP))
- {
- case U_JG_YEH:
- case U_JG_FARSI_YEH:
- case U_JG_YEH_BARREE:
- case U_JG_BURUSHASKI_YEH_BARREE:
- case U_JG_YEH_WITH_TAIL:
- bRet = true;
- break;
- default:
- bRet = false;
- break;
- }
-
- return bRet;
-}
-
-static bool isTransparentChar ( sal_Unicode cCh )
-{
- return u_getIntPropertyValue( cCh, UCHAR_JOINING_TYPE ) == U_JT_TRANSPARENT;
-}
-
-// Checks if cCh + cNectCh builds a ligature (used for Kashidas)
-static bool lcl_IsLigature( sal_Unicode cCh, sal_Unicode cNextCh )
-{
- // Lam + Alef
- return ( isLamChar ( cCh ) && isAlefChar ( cNextCh ));
-}
-
-// Checks if cCh is connectable to cPrevCh (used for Kashidas)
-static bool lcl_ConnectToPrev( sal_Unicode cCh, sal_Unicode cPrevCh )
-{
- const int32_t nJoiningType = u_getIntPropertyValue( cPrevCh, UCHAR_JOINING_TYPE );
- bool bRet = nJoiningType != U_JT_RIGHT_JOINING && nJoiningType != U_JT_NON_JOINING;
-
- // check for ligatures cPrevChar + cChar
- if( bRet )
- bRet = !lcl_IsLigature( cPrevCh, cCh );
-
- return bRet;
-}
-
-static bool lcl_HasStrongLTR ( std::u16string_view rText, sal_Int32 nStart, sal_Int32 nEnd )
- {
- for( sal_Int32 nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
- {
- const UCharDirection nCharDir = u_charDirection ( rText[ nCharIdx ] );
- if ( nCharDir == U_LEFT_TO_RIGHT ||
- nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
- nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
- return true;
- }
- return false;
- }
+using namespace ::com::sun::star;
+using namespace i18n::ScriptType;
// This is (meant to be) functionally equivalent to 'delete m_pNext' where
// deleting a SwLineLayout recursively deletes the owned m_pNext SwLineLayout.
@@ -185,23 +96,20 @@ 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);
+ SwPositiveSize::Height(nNew);
if (bText)
m_nTextHeight = nNew;
}
@@ -318,30 +226,44 @@ void SwLineLayout::CreateSpaceAdd( const tools::Long nInit )
SetLLSpaceAdd( nInit, 0 );
}
-// Returns true if there are only blanks in [nStt, nEnd[
-static bool lcl_HasOnlyBlanks(std::u16string_view rText, TextFrameIndex nStt, TextFrameIndex 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 isFieldMarkPortion)
{
- 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 0x2003: // EM SPACE
+ case 0x2005: // FOUR-PER-EM SPACE
+ case 0x3000: // IDEOGRAPHIC SPACE
+ continue;
+ case 0x2002: // EN SPACE :
+ if (isFieldMarkPortion)
+ return false;
+ else
+ continue;
+ default:
+ return false;
}
}
- return bBlankOnly;
+ return true;
}
// Swapped out from FormatLine()
void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
{
- const sal_uInt16 nLineWidth = rInf.RealWidth();
+ const SwTwips nLineWidth = rInf.RealWidth();
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 );
@@ -361,6 +283,12 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
bool bHasBlankPortion = false;
bool bHasOnlyBlankPortions = true;
+ bool bHasTabPortions = false;
+ bool bHasNonBlankPortions = false;
+ SwTwips nTabPortionAscent = 0;
+ SwTwips nTabPortionHeight = 0;
+ SwTwips nSpacePortionAscent = 0;
+ SwTwips nSpacePortionHeight = 0;
bool bHasFlyPortion = false;
if( mpNextPortion )
@@ -374,7 +302,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 +317,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 +329,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,15 +341,41 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
AddPrtWidth( pPos->Width() );
// #i3952#
- if ( bIgnoreBlanksAndTabsForLineHeightCalculation )
+ if (bIgnoreBlanksAndTabsForLineHeightCalculation)
{
+ bHasTabPortions |= pPos->InTabGrp();
+ bool isSpacePortion = false;
if ( pPos->InTabGrp() || pPos->IsHolePortion() ||
( pPos->IsTextPortion() &&
- lcl_HasOnlyBlanks( rInf.GetText(), nPorSttIdx, nPorSttIdx + pPos->GetLen() ) ) )
+ (isSpacePortion = lcl_HasOnlyBlanks( rInf.GetText(), nPorSttIdx, nPorSttIdx + pPos->GetLen(),
+ pPos->IsFieldmarkText() ) ) ) )
{
pLast = pPos;
+ if (pPos->InTabGrp())
+ {
+ if (nTabPortionAscent < pPos->GetAscent())
+ {
+ nTabPortionAscent = pPos->GetAscent();
+ }
+ if (nTabPortionHeight < pPos->Height())
+ {
+ nTabPortionHeight = pPos->Height();
+ }
+ }
+ else if (isSpacePortion)
+ {
+ if (nSpacePortionAscent < pPos->GetAscent())
+ {
+ nSpacePortionAscent = pPos->GetAscent();
+ }
+ if (nSpacePortionHeight < pPos->Height())
+ {
+ nSpacePortionHeight = pPos->Height();
+ }
+ bHasBlankPortion = true;
+ }
+ bTmpDummy &= !pPos->InTabGrp();
pPos = pPos->GetNextPortion();
- bHasBlankPortion = true;
continue;
}
}
@@ -426,7 +383,10 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
// Ignore drop portion height
// tdf#130804 ... and bookmark portions
if ((pPos->IsDropPortion() && static_cast<SwDropPortion*>(pPos)->GetLines() > 1)
- || pPos->GetWhichPor() == PortionType::Bookmark)
+ || pPos->GetWhichPor() == PortionType::Bookmark
+ || (pPos->GetWhichPor() == PortionType::HiddenText
+ && rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::IGNORE_HIDDEN_CHARS_FOR_LINE_CALCULATION)))
{
pLast = pPos;
pPos = pPos->GetNextPortion();
@@ -434,11 +394,12 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
}
bHasOnlyBlankPortions = false;
+ bHasNonBlankPortions = true;
// 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 +412,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 +534,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 );
@@ -585,20 +564,70 @@ void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
// #i3952#
if ( bIgnoreBlanksAndTabsForLineHeightCalculation &&
- lcl_HasOnlyBlanks( rInf.GetText(), rInf.GetLineStart(), rInf.GetLineStart() + GetLen() ) )
+ lcl_HasOnlyBlanks(rInf.GetText(), rInf.GetLineStart(),
+ rInf.GetLineStart() + GetLen(),
+ false))
{
bHasBlankPortion = true;
}
+ else
+ {
+ bHasOnlyBlankPortions = false;
+ bHasNonBlankPortions = true;
+ }
}
- // #i3952#
- if ( bHasBlankPortion && bHasOnlyBlankPortions )
+ if (!rInf.IsNewLine()
+ && TextFrameIndex(rInf.GetText().getLength()) <= rInf.GetIdx()
+ && !bHasNonBlankPortions
+ && rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH))
+ {
+ // Word: for empty last line, line height is based on paragraph marker
+ // formatting, ignoring blanks/tabs
+ rLine.SeekAndChg(rInf);
+ SetAscent(rInf.GetAscent());
+ Height(rInf.GetTextHeight());
+ }
+ else if (bIgnoreBlanksAndTabsForLineHeightCalculation && !bHasNonBlankPortions &&
+ (bHasTabPortions || (bHasBlankPortion && (nSpacePortionAscent > 0 || nSpacePortionHeight > 0))))
+ {
+ //Word increases line height if _only_ spaces and|or tabstops are in a line
+ if (bHasTabPortions)
+ {
+ mnAscent = nTabPortionAscent;
+ Height(nTabPortionHeight, true);
+ }
+ else if (bHasBlankPortion)
+ {
+ if( mnAscent < nSpacePortionAscent )
+ mnAscent = nSpacePortionAscent;
+ if (Height() < nSpacePortionHeight)
+ Height(nSpacePortionHeight, true);
+ }
+ }
+ // #i3952# Whitespace does not increase line height
+ else 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 +659,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 +755,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,19 +778,28 @@ 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_bRest = m_bBlinking = m_bClipping = m_bContent = m_bRedline
- = m_bRedlineEnd = m_bForcedLeftMargin = m_bHanging = false;
+ 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;
}
SwLineLayout::SwLineLayout()
: m_pNext( nullptr ),
m_nRealHeight( 0 ),
- m_nTextHeight( 0 ),
- m_bUnderscore( false )
+ m_nTextHeight( 0 )
{
ResetFlags();
SetWhichPor( PortionType::Lay );
@@ -826,19 +868,103 @@ SwFontScript SwScriptInfo::WhichFont(sal_Int32 nIdx, OUString const& rText)
return lcl_ScriptToFont(nScript);
}
+static Color getBookmarkColor(const SwTextNode& rNode, sw::mark::Bookmark* 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, pBookmark);
+ if (const SwDocShell* pShell = rDoc.GetDocShell())
+ {
+ rtl::Reference<SwXTextDocument> xModel = pShell->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);
+
+ const uno::Reference<rdf::XRepository> xRepository =
+ xModel->getRDFRepository();
+ const uno::Reference<container::XEnumeration> xEnum(
+ xRepository->getStatements(css::uno::Reference<css::rdf::XResource>(xRef), 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 OUString getBookmarkType(const SwTextNode& rNode, sw::mark::Bookmark* pBookmark)
+{
+ // search ODF_PREFIX in metadata, otherwise use empty string;
+ OUString sRet;
+
+ try
+ {
+ SwDoc& rDoc = const_cast<SwDoc&>(rNode.GetDoc());
+ const rtl::Reference< SwXBookmark > xRef = SwXBookmark::CreateXBookmark(rDoc, pBookmark);
+ if (const SwDocShell* pShell = rDoc.GetDocShell())
+ {
+ static uno::Reference< uno::XComponentContext > xContext(
+ ::comphelper::getProcessComponentContext());
+
+ static uno::Reference< rdf::XURI > xODF_PREFIX(
+ rdf::URI::createKnown(xContext, rdf::URIs::RDF_TYPE), uno::UNO_SET_THROW);
+
+ rtl::Reference<SwXTextDocument> xDocumentMetadataAccess(
+ pShell->GetBaseModel());
+ const uno::Reference<rdf::XRepository> xRepository =
+ xDocumentMetadataAccess->getRDFRepository();
+ const uno::Reference<container::XEnumeration> xEnum(
+ xRepository->getStatements(css::uno::Reference<css::rdf::XResource>(xRef), xODF_PREFIX, 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() )
+ sRet = xObject->getValue();
+ }
+ }
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ }
+
+ return sRet;
+}
+
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::pair<sw::mark::Bookmark*, SwScriptInfo::MarkKind>> & rBookmarks,
+ std::vector<std::tuple<TextFrameIndex, SwScriptInfo::MarkKind, Color, SwMarkName, 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);
+ OUString sType = getBookmarkType(*pNode, it.first);
switch (it.second)
{
case SwScriptInfo::MarkKind::Start:
@@ -852,41 +978,40 @@ static void InitBookmarks(
// start of first[/end of last] extent, and the other one
// is outside this merged paragraph, is it deleted or not?
// 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);
+ auto [/*const SwPosition&*/ rStart, rEnd] = it.first->GetMarkStartEnd();
+ 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(), sType);
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(), sType);
break;
}
}
@@ -899,13 +1024,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(), sType);
}
}
break;
@@ -913,36 +1038,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(), sType);
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(), sType);
break;
}
else
@@ -957,26 +1082,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(), sType);
}
break;
}
@@ -990,6 +1115,10 @@ static void InitBookmarks(
break;
}
}
+ if (iter == end)
+ {
+ break; // remaining marks are hidden
+ }
}
}
@@ -1000,8 +1129,9 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
InitScriptInfo( rNode, pMerged, m_nDefaultDir == UBIDI_RTL );
}
-void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
- sw::MergedPara const*const pMerged, bool bRTL)
+// note: must not use pMerged->pParaPropsNode to avoid circular dependency
+void SwScriptInfo::InitScriptInfoHidden(const SwTextNode& rNode,
+ sw::MergedPara const*const pMerged)
{
assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
@@ -1016,18 +1146,30 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
SwTextNode const* pNode(nullptr);
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)
+ if (pMerged->extents.empty())
+ {
+ Range aRange(0, pMerged->pLastNode->Len() > 0 ? pMerged->pLastNode->Len() - 1 : 0);
+ MultiSelection aHiddenMulti(aRange);
+ CalcHiddenRanges(*pMerged->pLastNode, aHiddenMulti, nullptr);
+ if (aHiddenMulti.GetRangeCount() != 0)
+ {
+ m_HiddenChg.push_back(TextFrameIndex(0));
+ m_HiddenChg.push_back(TextFrameIndex(0));
+ }
+ }
+ else for (auto iter = pMerged->extents.begin();
+ iter != pMerged->extents.end(); oPrevIter = iter)
{
if (iter->pNode == pNode)
{
nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
+ ++iter;
continue; // skip extents at end of previous node
}
pNode = iter->pNode;
Range aRange( 0, pNode->Len() > 0 ? pNode->Len() - 1 : 0 );
MultiSelection aHiddenMulti( aRange );
- std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> bookmarks;
+ std::vector<std::pair<sw::mark::Bookmark*, MarkKind>> bookmarks;
CalcHiddenRanges(*pNode, aHiddenMulti, &bookmarks);
InitBookmarks(oPrevIter, iter, pMerged->extents.end(), nOffset, bookmarks, m_Bookmarks);
@@ -1037,74 +1179,159 @@ 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
{
Range aRange( 0, !rText.isEmpty() ? rText.getLength() - 1 : 0 );
MultiSelection aHiddenMulti( aRange );
- std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> bookmarks;
+ std::vector<std::pair<sw::mark::Bookmark*, MarkKind>> bookmarks;
CalcHiddenRanges(rNode, aHiddenMulti, &bookmarks);
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().toString().startsWith(
+ IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()))
+ {
+ continue;
+ }
+
+ // search for custom bookmark boundary mark color
+ Color c = getBookmarkColor(rNode, it.first);
+ OUString sType = getBookmarkType(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(), sType);
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(), sType);
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(), sType);
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) );
}
}
+}
+
+namespace
+{
+i18nutil::ScriptHintProvider lcl_FindScriptTypeHintSpans(const SwTextNode& rNode)
+{
+ i18nutil::ScriptHintProvider stProvider;
+
+ const SvxScriptHintItem* pItem = rNode.GetSwAttrSet().GetItemIfSet(RES_CHRATR_SCRIPT_HINT);
+ if (pItem)
+ {
+ stProvider.SetParagraphLevelHint(pItem->GetValue());
+ }
+
+ const SwpHints* pHints = rNode.GetpSwpHints();
+ if (pHints)
+ {
+ for (size_t nTmp = 0; nTmp < pHints->Count(); ++nTmp)
+ {
+ const SwTextAttr* pTextAttr = pHints->Get(nTmp);
+ const SvxScriptHintItem* pCharItem
+ = CharFormat::GetItem(*pTextAttr, RES_CHRATR_SCRIPT_HINT);
+ if (pCharItem)
+ {
+ const sal_Int32 nSt = pTextAttr->GetStart();
+ const sal_Int32 nEnd = *pTextAttr->End();
+ if (nEnd > nSt)
+ {
+ stProvider.AddHint(pCharItem->GetValue(), nSt, nEnd);
+ }
+ }
+ }
+ }
+
+ return stProvider;
+}
+}
+
+void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
+ sw::MergedPara const*const pMerged, bool bRTL)
+{
+ InitScriptInfoHidden(rNode, pMerged);
+
+ const OUString& rText(pMerged ? pMerged->mergedText : rNode.GetText());
// SCRIPT AND SCRIPT RELATED INFORMATION
@@ -1120,8 +1347,6 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
size_t nCnt = 0;
// counter for compression information arrays
size_t nCntComp = 0;
- // counter for kashida array
- size_t nCntKash = 0;
sal_Int16 nScript = i18n::ScriptType::LATIN;
@@ -1130,7 +1355,7 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
auto const& rParaItems((pMerged ? *pMerged->pParaPropsNode : rNode).GetSwAttrSet());
// justification type
- const bool bAdjustBlock = SvxAdjust::Block == rParaItems.GetAdjust().GetAdjust();
+ m_bAdjustBlock = (SvxAdjust::Block == rParaItems.GetAdjust().GetAdjust());
// FIND INVALID RANGES IN SCRIPT INFO ARRAYS:
@@ -1159,15 +1384,6 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
nCntComp++;
}
}
- if ( bAdjustBlock )
- {
- while( nCntKash < CountKashida() )
- {
- if ( nChg <= GetKashida( nCntKash ) )
- break;
- nCntKash++;
- }
- }
}
// ADJUST nChg VALUE:
@@ -1212,51 +1428,20 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
m_CompressionChanges.erase(m_CompressionChanges.begin() + nCntComp,
m_CompressionChanges.end());
- // get the start of the last kashida group
- TextFrameIndex nLastKashida = nChg;
- if( nCntKash && i18n::ScriptType::COMPLEX == nScript )
+ // Construct the script change scanner and advance it to the change range
+ auto stScriptHints = lcl_FindScriptTypeHintSpans(rNode);
+ auto pDirScanner = i18nutil::MakeDirectionChangeScanner(rText, m_nDefaultDir);
+ auto pScriptScanner = i18nutil::MakeScriptChangeScanner(
+ rText, SvtLanguageOptions::GetI18NScriptTypeOfLanguage(GetAppLanguage()), *pDirScanner,
+ stScriptHints);
+ while (!pScriptScanner->AtEnd())
{
- --nCntKash;
- nLastKashida = GetKashida( nCntKash );
- }
-
- // remove invalid entries from kashida array
- m_Kashida.erase(m_Kashida.begin() + nCntKash, m_Kashida.end());
-
- // TAKE CARE OF WEAK CHARACTERS: WE MUST FIND AN APPROPRIATE
- // SCRIPT FOR WEAK CHARACTERS AT THE BEGINNING OF A PARAGRAPH
-
- if (WEAK == g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)))
- {
- // If the beginning of the current group is weak, this means that
- // all of the characters in this group are weak. We have to assign
- // the scripts to these characters depending on the fonts which are
- // set for these characters to display them.
- TextFrameIndex nEnd(
- g_pBreakIt->GetBreakIter()->endOfScript(rText, sal_Int32(nChg), WEAK));
-
- if (nEnd > TextFrameIndex(rText.getLength()) || nEnd < TextFrameIndex(0))
- nEnd = TextFrameIndex(rText.getLength());
-
- nScript = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
-
- SAL_WARN_IF( i18n::ScriptType::LATIN != nScript &&
- i18n::ScriptType::ASIAN != nScript &&
- i18n::ScriptType::COMPLEX != nScript, "sw.core", "Wrong default language" );
-
- nChg = nEnd;
-
- // Get next script type or set to weak in order to exit
- sal_uInt8 nNextScript = (nEnd < TextFrameIndex(rText.getLength()))
- ? static_cast<sal_uInt8>(g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nEnd)))
- : sal_uInt8(WEAK);
-
- if ( nScript != nNextScript )
+ if (static_cast<sal_Int32>(nChg) < pScriptScanner->Peek().m_nEndIndex)
{
- m_ScriptChanges.emplace_back(nEnd, nScript);
- nCnt++;
- nScript = nNextScript;
+ break;
}
+
+ pScriptScanner->Advance();
}
// UPDATE THE SCRIPT INFO ARRAYS:
@@ -1264,60 +1449,12 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
while (nChg < TextFrameIndex(rText.getLength())
|| (m_ScriptChanges.empty() && rText.isEmpty()))
{
- SAL_WARN_IF( i18n::ScriptType::WEAK == nScript,
- "sw.core", "Inserting WEAK into SwScriptInfo structure" );
-
- TextFrameIndex nSearchStt = nChg;
- nChg = TextFrameIndex(g_pBreakIt->GetBreakIter()->endOfScript(
- rText, sal_Int32(nSearchStt), nScript));
-
- 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 );
- }
+ auto stChange = pScriptScanner->Peek();
+ pScriptScanner->Advance();
- // 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)))
- {
- 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
- {
- m_ScriptChanges.emplace_back(nChg, nScript);
- }
- }
- else
- {
- m_ScriptChanges.emplace_back(nChg, nScript);
- }
+ nScript = stChange.m_nScriptType;
+ nChg = TextFrameIndex{ stChange.m_nEndIndex };
+ m_ScriptChanges.emplace_back(nChg, nScript);
++nCnt;
// if current script is asian, we search for compressible characters
@@ -1340,6 +1477,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 +1485,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:
@@ -1389,315 +1529,35 @@ void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
}
}
}
-
- // we search for connecting opportunities (kashida)
- else if ( bAdjustBlock && i18n::ScriptType::COMPLEX == nScript )
+ else if (m_bAdjustBlock && i18n::ScriptType::COMPLEX == nScript)
{
- // sw_redlinehide: this is the only place that uses SwScanner with
- // frame text, so we convert to sal_Int32 here
- std::function<LanguageType (sal_Int32, sal_Int32, bool)> const pGetLangOfCharM(
- [&pMerged](sal_Int32 const nBegin, sal_uInt16 const script, bool const bNoChar)
- {
- std::pair<SwTextNode const*, sal_Int32> const pos(
- sw::MapViewToModel(*pMerged, TextFrameIndex(nBegin)));
- return pos.first->GetLang(pos.second, bNoChar ? 0 : 1, script);
- });
- std::function<LanguageType (sal_Int32, sal_Int32, bool)> const pGetLangOfChar1(
- [&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(),
- i18n::WordType::DICTIONARY_WORD,
- sal_Int32(nLastKashida), sal_Int32(nChg));
-
- // the search has to be performed on a per word base
- while ( aScanner.NextWord() )
+ if (SwScriptInfo::IsKashidaScriptText(
+ rText, TextFrameIndex{ stChange.m_nStartIndex },
+ TextFrameIndex{ stChange.m_nEndIndex - stChange.m_nStartIndex }))
{
- const OUString& rWord = aScanner.GetWord();
-
- sal_Int32 nIdx = 0;
- sal_Int32 nKashidaPos = -1;
- sal_Unicode cCh;
- sal_Unicode cPrevCh = 0;
-
- int nPriorityLevel = 7; // 0..6 = level found
- // 7 not found
-
- sal_Int32 nWordLen = rWord.getLength();
-
- // ignore trailing vowel chars
- while( nWordLen && isTransparentChar( rWord[ nWordLen - 1 ] ))
- --nWordLen;
-
- while (nIdx < nWordLen)
- {
- cCh = rWord[ nIdx ];
-
- // 1. Priority:
- // after user inserted kashida
- if ( 0x640 == cCh )
- {
- nKashidaPos = aScanner.GetBegin() + nIdx;
- nPriorityLevel = 0;
- }
-
- // 2. Priority:
- // after a Seen or Sad
- if (nPriorityLevel >= 1 && nIdx < nWordLen - 1)
- {
- if( isSeenOrSadChar( cCh )
- && (rWord[ nIdx+1 ] != 0x200C) ) // #i98410#: prevent ZWNJ expansion
- {
- nKashidaPos = aScanner.GetBegin() + nIdx;
- nPriorityLevel = 1;
- }
- }
-
- // 3. Priority:
- // before final form of Teh Marbuta, Heh, Dal
- if ( nPriorityLevel >= 2 && nIdx > 0 )
- {
- if ( isTehMarbutaChar ( cCh ) || // Teh Marbuta (right joining)
- isDalChar ( cCh ) || // Dal (right joining) final form may appear in the middle of word
- ( isHehChar ( cCh ) && nIdx == nWordLen - 1)) // Heh (dual joining) only at end of word
- {
-
- 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;
- nPriorityLevel = 2;
- }
- }
- }
-
- // 4. Priority:
- // before final form of Alef, Tah, Lam, Kaf or Gaf
- if ( nPriorityLevel >= 3 && nIdx > 0 )
- {
- if ( isAlefChar ( cCh ) || // Alef (right joining) final form may appear in the middle of word
- (( isLamChar ( cCh ) || // Lam,
- isTahChar ( cCh ) || // Tah,
- isKafChar ( cCh ) || // Kaf (all dual joining)
- isGafChar ( cCh ) )
- && nIdx == nWordLen - 1)) // only at end of word
- {
- 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;
- nPriorityLevel = 3;
- }
- }
- }
-
- // 5. Priority:
- // before medial Beh-like
- if ( nPriorityLevel >= 4 && nIdx > 0 && nIdx < nWordLen - 1 )
- {
- if ( isBehChar ( cCh ) )
- {
- // 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;
- nPriorityLevel = 4;
- }
- }
- }
- }
-
- // 6. Priority:
- // before the final form of Waw, Ain, Qaf and Feh
- if ( nPriorityLevel >= 5 && nIdx > 0 )
- {
- if ( isWawChar ( cCh ) || // Wav (right joining)
- // final form may appear in the middle of word
- (( isAinChar ( cCh ) || // Ain (dual joining)
- isQafChar ( cCh ) || // Qaf (dual joining)
- isFehChar ( cCh ) ) // Feh (dual joining)
- && nIdx == nWordLen - 1)) // only at end of word
- {
- 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;
- nPriorityLevel = 5;
- }
- }
- }
-
- // other connecting possibilities
- if ( nPriorityLevel >= 6 && nIdx > 0 )
- {
- // Reh, Zain
- if ( isRehChar ( cCh ) )
- {
- 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;
- nPriorityLevel = 6;
- }
- }
- }
-
- // Do not consider vowel marks when checking if a character
- // can be connected to previous character.
- if ( !isTransparentChar ( cCh) )
- cPrevCh = cCh;
-
- ++nIdx;
- } // end of current word
-
- if ( -1 != nKashidaPos )
- {
- m_Kashida.insert(m_Kashida.begin() + nCntKash, TextFrameIndex(nKashidaPos));
- nCntKash++;
- }
- } // end of kashida search
+ m_bParagraphContainsKashidaScript = true;
+ }
}
if (nChg < TextFrameIndex(rText.getLength()))
nScript = static_cast<sal_uInt8>(g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)));
nLastCompression = nChg;
- nLastKashida = nChg;
}
-#if OSL_DEBUG_LEVEL > 0
- // check kashida data
- TextFrameIndex nTmpKashidaPos(-1);
- bool bWrongKash = false;
- for (size_t i = 0; i < m_Kashida.size(); ++i)
- {
- TextFrameIndex nCurrKashidaPos = GetKashida( i );
- if ( nCurrKashidaPos <= nTmpKashidaPos )
- {
- bWrongKash = true;
- break;
- }
- nTmpKashidaPos = nCurrKashidaPos;
- }
- SAL_WARN_IF( bWrongKash, "sw.core", "Kashida array contains wrong data" );
-#endif
-
// remove invalid entries from direction information arrays
m_DirectionChanges.clear();
// Perform Unicode Bidi Algorithm for text direction information
+ pDirScanner->Reset();
+ while (!pDirScanner->AtEnd())
{
- UpdateBidiInfo( rText );
-
- // #i16354# Change script type for RTL text to CTL:
- // 1. All text in RTL runs will use the CTL font
- // #i89825# change the script type also to CTL (hennerdrewes)
- // 2. Text in embedded LTR runs that does not have any strong LTR characters (numbers!)
- for (size_t nDirIdx = 0; nDirIdx < m_DirectionChanges.size(); ++nDirIdx)
- {
- const sal_uInt8 nCurrDirType = GetDirType( nDirIdx );
- // nStart is start of RTL run:
- const TextFrameIndex nStart = nDirIdx > 0 ? GetDirChg(nDirIdx - 1) : TextFrameIndex(0);
- // nEnd is end of RTL run:
- const TextFrameIndex nEnd = GetDirChg( nDirIdx );
-
- if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run
- (nCurrDirType > UBIDI_LTR && // non-strong text in embedded LTR run
- !lcl_HasStrongLTR(rText, sal_Int32(nStart), sal_Int32(nEnd))))
- {
- // nScriptIdx points into the ScriptArrays:
- size_t nScriptIdx = 0;
-
- // Skip entries in ScriptArray which are not inside the RTL run:
- // Make nScriptIdx become the index of the script group with
- // 1. nStartPosOfGroup <= nStart and
- // 2. nEndPosOfGroup > nStart
- while ( GetScriptChg( nScriptIdx ) <= nStart )
- ++nScriptIdx;
-
- const TextFrameIndex nStartPosOfGroup = nScriptIdx
- ? GetScriptChg(nScriptIdx - 1)
- : TextFrameIndex(0);
- const sal_uInt8 nScriptTypeOfGroup = GetScriptType( nScriptIdx );
-
- SAL_WARN_IF( nStartPosOfGroup > nStart || GetScriptChg( nScriptIdx ) <= nStart,
- "sw.core", "Script override with CTL font trouble" );
-
- // Check if we have to insert a new script change at
- // position nStart. If nStartPosOfGroup < nStart,
- // we have to insert a new script change:
- if (nStart > TextFrameIndex(0) && nStartPosOfGroup < nStart)
- {
- m_ScriptChanges.insert(m_ScriptChanges.begin() + nScriptIdx,
- ScriptChangeInfo(nStart, nScriptTypeOfGroup) );
- ++nScriptIdx;
- }
-
- // Remove entries in ScriptArray which end inside the RTL run:
- while (nScriptIdx < m_ScriptChanges.size()
- && GetScriptChg(nScriptIdx) <= nEnd)
- {
- m_ScriptChanges.erase(m_ScriptChanges.begin() + nScriptIdx);
- }
-
- // Insert a new entry in ScriptArray for the end of the RTL run:
- m_ScriptChanges.insert(m_ScriptChanges.begin() + nScriptIdx,
- ScriptChangeInfo(nEnd, i18n::ScriptType::COMPLEX) );
-
-#if OSL_DEBUG_LEVEL > 1
- // Check that ScriptChangeInfos are in increasing order of
- // position and that we don't have "empty" changes.
- sal_uInt8 nLastTyp = i18n::ScriptType::WEAK;
- TextFrameIndex nLastPos = TextFrameIndex(0);
- for (const auto& rScriptChange : m_ScriptChanges)
- {
- SAL_WARN_IF( nLastTyp == rScriptChange.type ||
- nLastPos >= rScriptChange.position,
- "sw.core", "Heavy InitScriptType() confusion" );
- nLastPos = rScriptChange.position;
- nLastTyp = rScriptChange.type;
- }
-#endif
- }
- }
- }
-}
-
-void SwScriptInfo::UpdateBidiInfo( const OUString& rText )
-{
- // remove invalid entries from direction information arrays
- m_DirectionChanges.clear();
-
- // Bidi functions from icu 2.0
+ auto stDirChange = pDirScanner->Peek();
+ m_DirectionChanges.emplace_back(TextFrameIndex{ stDirChange.m_nEndIndex },
+ stDirChange.m_nLevel);
- UErrorCode nError = U_ZERO_ERROR;
- UBiDi* pBidi = ubidi_openSized( rText.getLength(), 0, &nError );
- nError = U_ZERO_ERROR;
-
- ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(rText.getStr()), rText.getLength(),
- m_nDefaultDir, nullptr, &nError );
- nError = U_ZERO_ERROR;
- int nCount = ubidi_countRuns( pBidi, &nError );
- int32_t nStart = 0;
- int32_t nEnd;
- UBiDiLevel nCurrDir;
- for ( int nIdx = 0; nIdx < nCount; ++nIdx )
- {
- ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
- m_DirectionChanges.emplace_back(TextFrameIndex(nEnd), nCurrDir);
- nStart = nEnd;
+ pDirScanner->Advance();
}
-
- ubidi_close( pBidi );
}
// returns the position of the next character which belongs to another script
@@ -1776,29 +1636,48 @@ 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, SwMarkName, OUString>>
+ SwScriptInfo::GetBookmarks(TextFrameIndex const nPos)
{
- MarkKind ret{0};
+ std::vector<std::tuple<SwScriptInfo::MarkKind, Color, SwMarkName, OUString>> aColors;
for (auto const& it : m_Bookmarks)
{
- if (nPos == it.first)
+ if (nPos == std::get<0>(it))
{
- ret |= it.second;
+ const SwMarkName& sName = std::get<3>(it);
+ // filter hidden bookmarks imported from OOXML
+ // TODO import them as hidden bookmarks
+ if ( !( sName.toString().startsWith("_Toc") || sName.toString().startsWith("_Ref") ) )
+ aColors.push_back(std::tuple<MarkKind, Color, SwMarkName,
+ OUString>(std::get<1>(it), std::get<2>(it), std::get<3>(it), std::get<4>(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, SwMarkName, OUString> const a,
+ std::tuple<MarkKind, Color, SwMarkName, 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 +1898,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
@@ -2054,8 +1933,8 @@ tools::Long SwScriptInfo::Compress(tools::Long* pKernArray, TextFrameIndex nIdx,
if( nIdx > nLen || nCompIdx >= nCompCount )
return 0;
- tools::Long nSub = 0;
- tools::Long nLast = nI ? pKernArray[ nI - 1 ] : 0;
+ double nSub = 0;
+ double nLast = nI ? rKernArray[ nI - 1 ] : 0;
do
{
const CompType nType = GetCompType( nCompIdx );
@@ -2067,7 +1946,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,10 +1957,10 @@ 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;
+ double nMove = 0;
if( SwScriptInfo::KANA != nType )
{
nLast /= 24000;
@@ -2101,10 +1980,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[nI - 1] += nMove;
+ rKernArray[nI] += -nSub;
+ ++nI;
++nIdx;
}
}
@@ -2123,111 +2003,27 @@ tools::Long SwScriptInfo::Compress(tools::Long* pKernArray, TextFrameIndex nIdx,
while( nIdx < nTmpChg )
{
- nLast = pKernArray[ nI ];
- pKernArray[ nI++ ] -= nSub;
+ nLast = rKernArray[ nI ];
+ rKernArray[nI] += -nSub;
+ ++nI;
++nIdx;
}
} while( nIdx < nLen );
return nSub;
}
-// Note on calling KashidaJustify():
-// Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
-// 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,
- TextFrameIndex const nStt,
- TextFrameIndex const nLen,
- tools::Long nSpaceAdd ) const
-{
- SAL_WARN_IF( !nLen, "sw.core", "Kashida justification without text?!" );
-
- if( !IsKashidaLine(nStt))
- return -1;
-
- // evaluate kashida information in collected in SwScriptInfo
-
- size_t nCntKash = 0;
- while( nCntKash < CountKashida() )
- {
- if ( nStt <= GetKashida( nCntKash ) )
- break;
- ++nCntKash;
- }
-
- const TextFrameIndex nEnd = nStt + nLen;
-
- size_t nCntKashEnd = nCntKash;
- while ( nCntKashEnd < CountKashida() )
- {
- if ( nEnd <= GetKashida( nCntKashEnd ) )
- break;
- ++nCntKashEnd;
- }
-
- size_t nActualKashCount = nCntKashEnd - nCntKash;
- for (size_t i = nCntKash; i < nCntKashEnd; ++i)
- {
- if ( nActualKashCount && !IsKashidaValid ( i ) )
- --nActualKashCount;
- }
-
- if ( !pKernArray )
- return nActualKashCount;
-
- // do nothing if there is no more kashida
- if ( nCntKash < CountKashida() )
- {
- // skip any invalid kashidas
- while (nCntKash < nCntKashEnd && !IsKashidaValid(nCntKash))
- ++nCntKash;
-
- TextFrameIndex nIdx = nCntKash < nCntKashEnd && IsKashidaValid(nCntKash)
- ? GetKashida(nCntKash)
- : nEnd;
- tools::Long nKashAdd = nSpaceAdd;
-
- while ( nIdx < nEnd )
- {
- TextFrameIndex nArrayPos = nIdx - nStt;
-
- // next kashida position
- ++nCntKash;
- while (nCntKash < nCntKashEnd && !IsKashidaValid(nCntKash))
- ++nCntKash;
-
- nIdx = nCntKash < nCntKashEnd && IsKashidaValid(nCntKash) ? GetKashida(nCntKash) : nEnd;
- if ( nIdx > nEnd )
- nIdx = nEnd;
-
- const TextFrameIndex nArrayEnd = nIdx - nStt;
-
- while ( nArrayPos < nArrayEnd )
- {
- pKernArray[ sal_Int32(nArrayPos) ] += nKashAdd;
- if ( pScrArray )
- pScrArray[ sal_Int32(nArrayPos) ] += nKashAdd;
- ++nArrayPos;
- }
- nKashAdd += nSpaceAdd;
- }
- }
-
- return 0;
-}
-
-// Checks if the current text is 'Arabic' text. Note that only the first
+// Checks if the text is in Arabic or Syriac. Note that only the first
// character has to be checked because a ctl portion only contains one
// script, see NewTextPortion
-bool SwScriptInfo::IsArabicText(const OUString& rText,
+bool SwScriptInfo::IsKashidaScriptText(const OUString& rText,
TextFrameIndex const nStt, TextFrameIndex const nLen)
{
using namespace ::com::sun::star::i18n;
static const ScriptTypeList typeList[] = {
- { UnicodeScript_kArabic, UnicodeScript_kArabic, sal_Int16(UnicodeScript_kArabic) }, // 11,
- { UnicodeScript_kScriptCount, UnicodeScript_kScriptCount, sal_Int16(UnicodeScript_kScriptCount) } // 88
+ { UnicodeScript_kArabic, UnicodeScript_kArabic, sal_Int16(UnicodeScript_kArabic) }, // 11,
+ { UnicodeScript_kSyriac, UnicodeScript_kSyriac, sal_Int16(UnicodeScript_kSyriac) }, // 12,
+ { UnicodeScript_kScriptCount, UnicodeScript_kScriptCount,
+ sal_Int16(UnicodeScript_kScriptCount) } // 88
};
// go forward if current position does not hold a regular character:
@@ -2253,155 +2049,38 @@ bool SwScriptInfo::IsArabicText(const OUString& rText,
{
const sal_Unicode cCh = rText[nIdx];
const sal_Int16 type = unicode::getUnicodeScriptType( cCh, typeList, sal_Int16(UnicodeScript_kScriptCount) );
- return type == sal_Int16(UnicodeScript_kArabic);
- }
- return false;
-}
-
-bool SwScriptInfo::IsKashidaValid(size_t const nKashPos) const
-{
- return m_KashidaInvalid.find(nKashPos) == m_KashidaInvalid.end();
-}
-
-void SwScriptInfo::ClearKashidaInvalid(size_t const nKashPos)
-{
- m_KashidaInvalid.erase(nKashPos);
-}
-
-// bMark == true:
-// marks the first valid kashida in the given text range as invalid
-// bMark == false:
-// clears all kashida invalid flags in the given text range
-bool SwScriptInfo::MarkOrClearKashidaInvalid(
- TextFrameIndex const nStt, TextFrameIndex const nLen,
- bool bMark, sal_Int32 nMarkCount)
-{
- size_t nCntKash = 0;
- while( nCntKash < CountKashida() )
- {
- if ( nStt <= GetKashida( nCntKash ) )
- break;
- nCntKash++;
- }
-
- const TextFrameIndex nEnd = nStt + nLen;
-
- while ( nCntKash < CountKashida() )
- {
- if ( nEnd <= GetKashida( nCntKash ) )
- break;
- if(bMark)
- {
- if ( MarkKashidaInvalid ( nCntKash ) )
- {
- --nMarkCount;
- if (!nMarkCount)
- return true;
- }
- }
- else
- {
- ClearKashidaInvalid ( nCntKash );
- }
- nCntKash++;
+ return type == sal_Int16(UnicodeScript_kArabic) || type == sal_Int16(UnicodeScript_kSyriac);
}
return false;
}
-bool SwScriptInfo::MarkKashidaInvalid(size_t const nKashPos)
-{
- return m_KashidaInvalid.insert(nKashPos).second;
-}
-
-// retrieve the kashida positions in the given text range
-void SwScriptInfo::GetKashidaPositions(
- TextFrameIndex const nStt, TextFrameIndex const nLen,
- std::vector<TextFrameIndex>& rKashidaPosition)
+tools::Long SwScriptInfo::CountKashidaPositions(TextFrameIndex nIdx, TextFrameIndex nEnd) const
{
- size_t nCntKash = 0;
- while( nCntKash < CountKashida() )
- {
- if ( nStt <= GetKashida( nCntKash ) )
- break;
- nCntKash++;
- }
-
- const TextFrameIndex nEnd = nStt + nLen;
-
- size_t nCntKashEnd = nCntKash;
- while ( nCntKashEnd < CountKashida() )
+ tools::Long nCount = 0;
+ for (const auto& nPos : m_Kashida)
{
- if ( nEnd <= GetKashida( nCntKashEnd ) )
+ if (nPos >= nEnd)
break;
- rKashidaPosition.push_back(GetKashida(nCntKashEnd));
- nCntKashEnd++;
- }
-}
-
-void SwScriptInfo::SetNoKashidaLine(TextFrameIndex const nStt, TextFrameIndex const nLen)
-{
- m_NoKashidaLine.push_back( nStt );
- m_NoKashidaLineEnd.push_back( nStt + nLen );
-}
-// determines if the line uses kashida justification
-bool SwScriptInfo::IsKashidaLine(TextFrameIndex const nCharIdx) const
-{
- for (size_t i = 0; i < m_NoKashidaLine.size(); ++i)
- {
- if (nCharIdx >= m_NoKashidaLine[i] && nCharIdx < m_NoKashidaLineEnd[i])
- return false;
+ if (nPos >= nIdx)
+ ++nCount;
}
- return true;
-}
-void SwScriptInfo::ClearNoKashidaLine(TextFrameIndex const nStt, TextFrameIndex const nLen)
-{
- size_t i = 0;
- while (i < m_NoKashidaLine.size())
- {
- if (nStt + nLen >= m_NoKashidaLine[i] && nStt < m_NoKashidaLineEnd[i])
- {
- m_NoKashidaLine.erase(m_NoKashidaLine.begin() + i);
- m_NoKashidaLineEnd.erase(m_NoKashidaLineEnd.begin() + i);
- }
- else
- ++i;
- }
+ return nCount;
}
-// mark the given character indices as invalid kashida positions
-void SwScriptInfo::MarkKashidasInvalid(sal_Int32 const nCnt,
- const TextFrameIndex* pKashidaPositions)
+void SwScriptInfo::ReplaceKashidaPositions(std::vector<TextFrameIndex> aKashidaPositions)
{
- SAL_WARN_IF( !pKashidaPositions || nCnt == 0, "sw.core", "Where are kashidas?" );
-
- size_t nCntKash = 0;
- sal_Int32 nKashidaPosIdx = 0;
-
- while (nCntKash < CountKashida() && nKashidaPosIdx < nCnt)
- {
- if ( pKashidaPositions [nKashidaPosIdx] > GetKashida( nCntKash ) )
- {
- ++nCntKash;
- continue;
- }
-
- if ( pKashidaPositions [nKashidaPosIdx] != GetKashida( nCntKash ) || !IsKashidaValid ( nCntKash ) )
- return; // something is wrong
-
- MarkKashidaInvalid ( nCntKash );
- nKashidaPosIdx++;
- }
+ m_Kashida = std::move(aKashidaPositions);
}
-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 +2090,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 +2106,8 @@ TextFrameIndex SwScriptInfo::ThaiJustify( const OUString& rText, tools::Long* pK
++nCnt;
}
- if ( pKernArray ) pKernArray[ nI ] += nSpaceSum;
- if ( pScrArray ) pScrArray[ nI ] += nSpaceSum;
+ if (pKernArray)
+ (*pKernArray)[nI] += nSpaceSum;
}
return nCnt;
@@ -2443,7 +2122,7 @@ SwScriptInfo* SwScriptInfo::GetScriptInfo( const SwTextNode& rTNd,
for( SwTextFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
{
- pScriptInfo = const_cast<SwScriptInfo*>(pLast->GetScriptInfo());
+ pScriptInfo = pLast->GetScriptInfo();
if ( pScriptInfo )
{
if (bAllowInvalid ||
@@ -2485,6 +2164,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 +2215,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,16 +2281,41 @@ 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)
+ std::vector<std::pair<sw::mark::Bookmark*, MarkKind>> *const pBookmarks)
{
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,10 +2341,12 @@ 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);
+ if (!pIndex->GetOwner() || pIndex->GetOwner()->GetOwnerType() != SwContentIndexOwnerType::Mark)
+ continue;
+ auto pMark = static_cast<sw::mark::MarkBase*>(pIndex->GetOwner());
+ sw::mark::Bookmark* pBookmark = dynamic_cast<sw::mark::Bookmark*>(pMark);
if (pBookmarks && pBookmark)
{
if (!pBookmark->IsExpanded())
@@ -2614,33 +2364,14 @@ 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();
+ auto [/*const SwPosition&*/ rMarkStartPos, rMarkEndPos] = pBookmark->GetMarkStartEnd();
+ const sal_Int32 nSt = rMarkStartPos.GetContentIndex();
+ const sal_Int32 nEnd = rMarkEndPos.GetContentIndex();
if( nEnd > nSt )
{
@@ -2666,7 +2397,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)
@@ -2688,7 +2419,7 @@ void SwScriptInfo::selectRedLineDeleted(const SwTextNode& rNode, MultiSelection
// Returns a MultiSection indicating the hidden ranges.
void SwScriptInfo::CalcHiddenRanges( const SwTextNode& rNode,
MultiSelection & rHiddenMulti,
- std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> *const pBookmarks)
+ std::vector<std::pair<sw::mark::Bookmark*, MarkKind>> *const pBookmarks)
{
selectHiddenTextProperty(rNode, rHiddenMulti, pBookmarks);
@@ -2734,12 +2465,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 +2488,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[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..afb8534de477 100644
--- a/sw/source/core/text/porlay.hxx
+++ b/sw/source/core/text/porlay.hxx
@@ -75,18 +75,22 @@ 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
+ std::vector<TextFrameIndex> m_aKashida;
+ 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
+ SwTwips m_nExtraAscent = 0;
+ SwTwips m_nExtraDescent = 0;
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;
@@ -96,7 +100,6 @@ private:
bool m_bRedlineEnd: 1; // Redlining for paragraph mark: tracked change at the end
bool m_bForcedLeftMargin : 1; // Left adjustment moved by the Fly
bool m_bHanging : 1; // Contains a hanging portion in the margin
- bool m_bUnderscore : 1;
enum RedlineType m_eRedlineEnd; // redline type of pilcrow and line break symbols
@@ -106,14 +109,14 @@ private:
void DeleteNext();
public:
- // From SwPosSize
- using SwPosSize::Height;
- virtual void Height(const sal_uInt16 nNew, const bool bText = true) override;
+ // From SwPositiveSize
+ using SwPositiveSize::Height;
+ 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 +126,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; }
@@ -143,8 +148,6 @@ public:
bool HasForcedLeftMargin() const { return m_bForcedLeftMargin; }
void SetHanging( const bool bNew ) { m_bHanging = bNew; }
bool IsHanging() const { return m_bHanging; }
- void SetUnderscore( const bool bNew ) { m_bUnderscore = bNew; }
- bool HasUnderscore() const { return m_bUnderscore; }
// Respecting empty dummy lines
void SetDummy( const bool bNew ) { m_bDummy = bNew; }
@@ -165,10 +168,16 @@ 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; }
+
+ void SetExtraAscent(SwTwips nNew) { m_nExtraAscent = nNew; }
+ SwTwips GetExtraAscent() const { return m_nExtraAscent; }
+
+ void SetExtraDescent(SwTwips nNew) { m_nExtraDescent = nNew; }
+ SwTwips GetExtraDescent() const { return m_nExtraDescent; }
// Creates the glue chain for short lines
SwMarginPortion *CalcLeftMargin();
@@ -202,6 +211,9 @@ public:
std::deque<sal_uInt16>* GetpKanaComp() const { return m_pKanaComp.get(); }
std::deque<sal_uInt16>& GetKanaComp() { return *m_pKanaComp; }
+ void SetKashida(std::vector<TextFrameIndex> aNew) { m_aKashida = std::move(aNew); }
+ std::span<const TextFrameIndex> GetKashida() const { return m_aKashida; }
+
/** determine ascent and descent for positioning of as-character anchored
object
@@ -240,6 +252,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 +280,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 +298,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 +331,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..f02fc0bae0f0 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
@@ -58,18 +59,17 @@ SwLinePortion *SwLinePortion::Compress()
return GetLen() || Width() ? this : nullptr;
}
-sal_uInt16 SwLinePortion::GetViewWidth( const SwTextSizeInfo & ) const
-{
- return 0;
-}
+SwTwips SwLinePortion::GetViewWidth(const SwTextSizeInfo&) const { return 0; }
SwLinePortion::SwLinePortion( ) :
mpNextPortion( nullptr ),
mnLineLength( 0 ),
mnAscent( 0 ),
+ mnHangingBaseline( 0 ),
mnWhichPor( PortionType::NONE ),
m_bJoinBorderWithPrev(false),
- m_bJoinBorderWithNext(false)
+ m_bJoinBorderWithNext(false),
+ m_bIsFieldmarkText(false)
{
}
@@ -79,22 +79,22 @@ void SwLinePortion::PrePaint( const SwTextPaintInfo& rInf,
OSL_ENSURE( rInf.OnWin(), "SwLinePortion::PrePaint: don't prepaint on a printer");
OSL_ENSURE( !Width(), "SwLinePortion::PrePaint: For Width()==0 only!");
- const sal_uInt16 nViewWidth = GetViewWidth( rInf );
+ const SwTwips nViewWidth = GetViewWidth(rInf);
if( ! nViewWidth )
return;
- const sal_uInt16 nHalfView = nViewWidth / 2;
- sal_uInt16 nLastWidth = pLast->Width();
+ const SwTwips nHalfView = nViewWidth / 2;
+ SwTwips 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;
+ SwTwips 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 :
@@ -107,22 +107,22 @@ void SwLinePortion::PrePaint( const SwTextPaintInfo& rInf,
switch (nDir.get())
{
case 0:
- nPos = sal_uInt16( rInf.X() );
+ nPos = rInf.X();
nPos += nLastWidth - nHalfView;
aInf.X( nPos );
break;
case 900:
- nPos = sal_uInt16( rInf.Y() );
+ nPos = rInf.Y();
nPos -= nLastWidth - nHalfView;
aInf.Y( nPos );
break;
case 1800:
- nPos = sal_uInt16( rInf.X() );
+ nPos = rInf.X();
nPos -= nLastWidth - nHalfView;
aInf.X( nPos );
break;
case 2700:
- nPos = sal_uInt16( rInf.Y() );
+ nPos = rInf.Y();
nPos += nLastWidth - nHalfView;
aInf.Y( nPos );
break;
@@ -138,12 +138,12 @@ void SwLinePortion::PrePaint( const SwTextPaintInfo& rInf,
void SwLinePortion::CalcTextSize( const SwTextSizeInfo &rInf )
{
if( GetLen() == rInf.GetLen() )
- *static_cast<SwPosSize*>(this) = GetTextSize( rInf );
+ *static_cast<SwPositiveSize*>(this) = GetTextSize( rInf );
else
{
SwTextSizeInfo aInf( rInf );
aInf.SetLen( GetLen() );
- *static_cast<SwPosSize*>(this) = GetTextSize( aInf );
+ *static_cast<SwPositiveSize*>(this) = GetTextSize( aInf );
}
}
@@ -201,6 +201,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;
@@ -219,7 +222,7 @@ SwLinePortion *SwLinePortion::FindPrevPortion( const SwLinePortion *pRoot )
return pPos;
}
-TextFrameIndex SwLinePortion::GetModelPositionForViewPoint(const sal_uInt16 nOfst) const
+TextFrameIndex SwLinePortion::GetModelPositionForViewPoint(const SwTwips nOfst) const
{
if( nOfst > ( PrtWidth() / 2 ) )
return GetLen();
@@ -227,11 +230,11 @@ TextFrameIndex SwLinePortion::GetModelPositionForViewPoint(const sal_uInt16 nOfs
return TextFrameIndex(0);
}
-SwPosSize SwLinePortion::GetTextSize( const SwTextSizeInfo & ) const
+SwPositiveSize SwLinePortion::GetTextSize( const SwTextSizeInfo & ) const
{
OSL_ENSURE( false, "SwLinePortion::GetTextSize: don't ask me about sizes, "
"I'm only a stupid SwLinePortion" );
- return SwPosSize();
+ return SwPositiveSize();
}
bool SwLinePortion::Format( SwTextFormatInfo &rInf )
@@ -246,7 +249,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 SwTwips nNewWidth = 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 +268,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 +294,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 +318,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..eb160830c1d4 100644
--- a/sw/source/core/text/porlin.hxx
+++ b/sw/source/core/text/porlin.hxx
@@ -18,48 +18,42 @@
*/
#pragma once
+#include <libxml/xmlwriter.h>
+
#include "possiz.hxx"
#include <txttypes.hxx>
#include <TextFrameIndex.hxx>
#include <rtl/ustring.hxx>
+#include <swporlayoutcontext.hxx>
class SwTextSizeInfo;
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 SwPositiveSize
{
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;
+ bool m_bIsFieldmarkText;
+ SwTwips m_nExtraBlankWidth = 0; // width of spaces after the break
+ SwTwips m_nExtraShrunkWidth = 0; // width of not shrunk line
+ SwTwips m_nExtraSpaceSize = 0; // extra space over normal space width
+
+ std::optional<SwLinePortionLayoutContext> m_nLayoutContext;
void Truncate_();
@@ -73,13 +67,26 @@ 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 ExtraShrunkWidth() const { return m_nExtraShrunkWidth; }
+ void ExtraShrunkWidth(const SwTwips nNew) { m_nExtraShrunkWidth = nNew; }
+ SwTwips ExtraSpaceSize() const { return m_nExtraSpaceSize; }
+ void ExtraSpaceSize(const SwTwips nNew) { m_nExtraSpaceSize = nNew; }
+ SwTwips GetHangingBaseline() const { return mnHangingBaseline; }
+ void SetHangingBaseline( const SwTwips nNewBaseline ) { mnHangingBaseline = nNewBaseline; }
+ const std::optional<SwLinePortionLayoutContext> & GetLayoutContext() const { return m_nLayoutContext; }
+ void SetLayoutContext(std::optional<SwLinePortionLayoutContext> nNew)
+ {
+ m_nLayoutContext = nNew;
+ }
// Insert methods
virtual SwLinePortion *Insert( SwLinePortion *pPortion );
@@ -140,9 +147,8 @@ public:
SwLinePortion *FindPrevPortion( const SwLinePortion *pRoot );
SwLinePortion *FindLastPortion();
- /// the parameter is actually SwTwips apparently?
- virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const;
- virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const;
+ virtual TextFrameIndex GetModelPositionForViewPoint(SwTwips nOfst) const;
+ virtual SwPositiveSize GetTextSize( const SwTextSizeInfo &rInfo ) const;
void CalcTextSize( const SwTextSizeInfo &rInfo );
// Output
@@ -152,45 +158,63 @@ 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;
// For SwFieldPortion, SwSoftHyphPortion
- virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const;
+ virtual SwTwips 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;
bool GetJoinBorderWithPrev() const { return m_bJoinBorderWithPrev; }
bool GetJoinBorderWithNext() const { return m_bJoinBorderWithNext; }
+ bool IsFieldmarkText() const {return m_bIsFieldmarkText;}
void SetJoinBorderWithPrev( const bool bJoinPrev ) { m_bJoinBorderWithPrev = bJoinPrev; }
void SetJoinBorderWithNext( const bool bJoinNext ) { m_bJoinBorderWithNext = bJoinNext; }
+ void SetFieldmarkText(bool bSet) { m_bIsFieldmarkText = bSet; }
+
+ 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)
{
- *static_cast<SwPosSize*>(this) = rPortion;
+ *static_cast<SwPositiveSize*>(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;
+ m_nExtraShrunkWidth = rPortion.m_nExtraShrunkWidth;
+ m_nExtraSpaceSize = rPortion.m_nExtraSpaceSize;
+ m_nLayoutContext = rPortion.m_nLayoutContext;
return *this;
}
inline SwLinePortion::SwLinePortion(const SwLinePortion &rPortion) :
- SwPosSize( rPortion ),
+ SwPositiveSize( 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_bIsFieldmarkText( rPortion.m_bIsFieldmarkText ),
+ m_nExtraBlankWidth(rPortion.m_nExtraBlankWidth),
+ m_nExtraShrunkWidth(rPortion.m_nExtraShrunkWidth),
+ m_nExtraSpaceSize(rPortion.m_nExtraSpaceSize),
+ m_nLayoutContext(rPortion.m_nLayoutContext)
{
}
diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx
index c51fb973ad29..d4495587d5f1 100644
--- a/sw/source/core/text/pormulti.cxx
+++ b/sw/source/core/text/pormulti.cxx
@@ -31,6 +31,10 @@
#include <charfmt.hxx>
#include <layfrm.hxx>
#include <SwPortionHandler.hxx>
+#include <EnhancedPDFExportHelper.hxx>
+#include <com/sun/star/i18n/BreakType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <breakit.hxx>
#include "pormulti.hxx"
#include "inftxt.hxx"
#include "itrpaint.hxx"
@@ -119,7 +123,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 +138,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,8 +222,9 @@ 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
{
+ nSpaceAdd = nSpaceAdd > LONG_MAX/2 ? LONG_MAX/2 - nSpaceAdd : nSpaceAdd;
return HasTabulator() ? 0 : sal_Int32(GetSpaceCnt(rInf)) * nSpaceAdd / SPACING_PRECISION_FACTOR;
}
@@ -351,12 +381,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 +414,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() );
- SwPosSize aSize = rInf.GetTextSize( aStr );
+ aTmpFnt.SetActual( m_pBracket->nPreScript );
+ SwFontSave aSave( rInf, &aTmpFnt );
+ SwPositiveSize 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,9 +447,9 @@ 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() );
- SwPosSize aSize = rInf.GetTextSize( aStr );
+ aTmpFnt.SetActual( m_pBracket->nPostScript );
+ SwFontSave aSave( rInf, &aTmpFnt );
+ SwPositiveSize aSize = rInf.GetTextSize( aStr );
const sal_uInt16 nTmpAsc = rInf.GetAscent();
if( nTmpAsc > m_pBracket->nAscent )
{
@@ -479,8 +509,9 @@ 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
{
+ nSpaceAdd = nSpaceAdd > LONG_MAX/2 ? LONG_MAX/2 - nSpaceAdd : nSpaceAdd;
return HasTabulator() ? 0 : sal_Int32(GetSpaceCnt()) * nSpaceAdd / SPACING_PRECISION_FACTOR;
}
@@ -592,7 +623,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 +685,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 +714,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 +877,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 +900,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 +937,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 +969,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 +1037,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 +1099,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 +1165,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 +1201,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 +1228,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 +1269,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 +1328,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 +1369,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 +1422,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 +1585,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 +1596,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 +1667,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,13 +1720,15 @@ 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() );
}
- else
- GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) );
+ else if (!rMulti.IsBidi())
+ {
+ GetInfo().Y(nOfst + AdjustBaseLine(*pLay, pPor));
+ }
bool bSeeked = true;
GetInfo().SetLen( pPor->GetLen() );
@@ -1740,7 +1783,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 +1850,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 +1879,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 +1894,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 );
}
@@ -1857,11 +1923,63 @@ static bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpField
return bRet;
}
+// Determines if any part of the bidi portion fits on the current line
+namespace
+{
+enum class BidiTruncationType
+{
+ None,
+ Truncate,
+ Underflow
+};
+
+BidiTruncationType lcl_BidiPortionNeedsTruncation(const SwMultiPortion& rMulti,
+ const SwTextFormatInfo& rExternalInf,
+ const SwTextFormatInfo& rLocalInf,
+ TextFrameIndex const nStartIdx)
+{
+ if (!rLocalInf.IsUnderflow())
+ {
+ // Some amount of text fits in the bidi portion without triggering underflow,
+ // so the portion should not be truncated.
+ return BidiTruncationType::None;
+ }
+
+ auto nCurrLen = rMulti.GetLen();
+
+ css::i18n::LineBreakHyphenationOptions aHyphOptions;
+ css::i18n::LineBreakUserOptions aUserOptions;
+ css::lang::Locale aLocale;
+ auto aResult = g_pBreakIt->GetBreakIter()->getLineBreak(
+ rExternalInf.GetText(), sal_Int32(nStartIdx + nCurrLen), aLocale,
+ sal_Int32(rExternalInf.GetLineStart()), aHyphOptions, aUserOptions);
+
+ if (aResult.breakIndex < sal_Int32(nStartIdx))
+ {
+ // The bidi portion doesn't fit on the line, and the first break opportunity
+ // is before the bidi portion. Underflow to the preceding text.
+ return BidiTruncationType::Underflow;
+ }
+
+ if (aResult.breakIndex > sal_Int32(nStartIdx)
+ && aResult.breakIndex <= sal_Int32(nStartIdx + nCurrLen))
+ {
+ // The bidi portion fits on this line, but ended with underflow.
+ return BidiTruncationType::None;
+ }
+
+ // The bidi portion doesn't fit on the line, but a break position exists between the bidi
+ // portion and the preceding text. Truncating is sufficient.
+ return BidiTruncationType::Truncate;
+}
+}
+
// If a multi portion completely has to go to the
// next line, this function is called to truncate
// the rest of the remaining multi portion
-static void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTextFormatInfo& rInf,
- TextFrameIndex const nStartIdx)
+static void lcl_TruncateMultiPortion(SwMultiPortion& rMulti, SwTextFormatInfo& rInf,
+ TextFrameIndex const nStartIdx,
+ BidiTruncationType nBidiTruncType = BidiTruncationType::None)
{
rMulti.GetRoot().Truncate();
rMulti.GetRoot().SetLen(TextFrameIndex(0));
@@ -1876,6 +1994,18 @@ static void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTextFormatInfo&
rMulti.Width( 0 );
rMulti.SetLen(TextFrameIndex(0));
rInf.SetIdx( nStartIdx );
+
+ if (rMulti.IsBidi())
+ {
+ // The truncated portion is a bidi portion. Bidi portions contain ordinary text, and may
+ // potentially underflow in the case that none of the text fits on the current line.
+ if (nBidiTruncType == BidiTruncationType::Underflow)
+ {
+ // The start of the bidi portion is not a valid break. Instead, a break should be
+ // inserted into a previous text portion on this line.
+ rInf.SetUnderflow(&rMulti);
+ }
+ }
}
// Manages the formatting of a SwMultiPortion. External, for the calling
@@ -1898,16 +2028,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() );
@@ -1952,7 +2083,9 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
( rInf.GetTextFrame()->IsVertical() ?
pUpperFrame->getFramePrintArea().Width() :
pUpperFrame->getFramePrintArea().Height() ) :
- USHRT_MAX;
+ std::numeric_limits<SwTwips>::max();
+ if (nMaxWidth < 0)
+ nMaxWidth = 0;
}
else
nTmpX = rInf.X();
@@ -1995,7 +2128,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())) );
@@ -2010,7 +2143,7 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
bool bRet = false;
SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
- const bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType();
+ const bool bHasGrid = pGrid && SwTextGrid::LinesAndChars == pGrid->GetGridType();
bool bRubyTop = false;
@@ -2024,12 +2157,21 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
bRet = false;
FormatReset( aInf );
aInf.X( nTmpX );
- aInf.Width( sal_uInt16(nActWidth) );
- aInf.RealWidth( sal_uInt16(nActWidth) );
+ aInf.Width(nActWidth);
+ aInf.RealWidth(nActWidth);
aInf.SetFirstMulti( bFirstMulti );
aInf.SetNumDone( rInf.IsNumDone() );
aInf.SetFootnoteDone( rInf.IsFootnoteDone() );
+ // tdf#157829: Bidi portions contain text; word wrapping should underflow.
+ // By default, the SwTextFormatInfo constructor assumes the current index is the start of
+ // a new line. As a result, Writer cut breaks MultiPortions as if they were wider than the
+ // entire document. This is incorrect behavior for bidi portions.
+ if (rMulti.IsBidi())
+ {
+ aInf.SetLineStart(rInf.GetLineStart());
+ }
+
// if there's a bookmark at the start of the MultiPortion, it will be
// painted with the rotation etc. of the MultiPortion; move it *inside*
// so it gets positioned correctly; currently there's no other portion
@@ -2283,14 +2425,17 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
else
{
// we try to keep our ruby portion together
- lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
+ 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() )
{
// we try to keep our rotated portion together
- lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
+ lcl_TruncateMultiPortion(rMulti, rInf, nStartIdx);
pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(),
rMulti.GetDirection() );
}
@@ -2298,8 +2443,11 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
// a new SwBidiPortion, this would cause a memory leak
else if( rMulti.IsBidi() && ! m_pMulti )
{
- if ( ! rMulti.GetLen() )
- lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
+ auto nTruncType = lcl_BidiPortionNeedsTruncation(rMulti, rInf, aInf, nStartIdx);
+ if (nTruncType != BidiTruncationType::None)
+ {
+ lcl_TruncateMultiPortion(rMulti, rInf, nStartIdx, nTruncType);
+ }
// If there is a HolePortion at the end of the bidi portion,
// it has to be moved behind the bidi portion. Otherwise
@@ -2354,10 +2502,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 +2606,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 +2619,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 +2663,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 +2697,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..3cf5449d2814 100644
--- a/sw/source/core/text/porref.cxx
+++ b/sw/source/core/text/porref.cxx
@@ -40,12 +40,12 @@ SwIsoRefPortion::SwIsoRefPortion() : m_nViewWidth(0)
SetWhichPor( PortionType::IsoRef );
}
-sal_uInt16 SwIsoRefPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+SwTwips 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/porref.hxx b/sw/source/core/text/porref.hxx
index d12803080345..953d7462b3d6 100644
--- a/sw/source/core/text/porref.hxx
+++ b/sw/source/core/text/porref.hxx
@@ -29,14 +29,14 @@ public:
class SwIsoRefPortion : public SwRefPortion
{
- sal_uInt16 m_nViewWidth;
+ SwTwips m_nViewWidth;
public:
SwIsoRefPortion();
virtual bool Format(SwTextFormatInfo& rInf) override;
virtual void Paint(const SwTextPaintInfo& rInf) const override;
virtual SwLinePortion* Compress() override;
- virtual sal_uInt16 GetViewWidth(const SwTextSizeInfo& rInf) const override;
+ virtual SwTwips GetViewWidth(const SwTextSizeInfo& rInf) const override;
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion(SwPortionHandler& rPH) const override;
diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx
index 6f578e0744cd..53fc1e3eff71 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()).
@@ -82,7 +102,7 @@ void SwTmpEndPortion::Paint( const SwTextPaintInfo &rInf ) const
}
- aFont.SetColor( NON_PRINTING_CHARACTER_COLOR );
+ aFont.SetColor( SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor() );
aFont.SetStrikeout( STRIKEOUT_NONE );
aFont.SetUnderline( LINESTYLE_NONE );
const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
@@ -93,21 +113,27 @@ 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
+TextFrameIndex SwBreakPortion::GetModelPositionForViewPoint(const SwTwips) const
{
return TextFrameIndex(0);
}
-sal_uInt16 SwBreakPortion::GetViewWidth( const SwTextSizeInfo & ) const
-{ return 0; }
+SwTwips SwBreakPortion::GetViewWidth(const SwTextSizeInfo&) const { return 0; }
SwLinePortion *SwBreakPortion::Compress()
{ return (GetNextPortion() && GetNextPortion()->InTextGrp() ? nullptr : this); }
@@ -117,37 +143,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 +203,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 +237,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 )
@@ -213,7 +298,7 @@ void SwKernPortion::Paint( const SwTextPaintInfo &rInf ) const
rInf.CalcRect( *this, &aClipRect );
SwSaveClip aClip( const_cast<OutputDevice*>(rInf.GetOut()) );
aClip.ChgClip( aClipRect );
- rInf.DrawText(" ", *this, TextFrameIndex(0), TextFrameIndex(2), true );
+ rInf.DrawText(u" "_ustr, *this, TextFrameIndex(0), TextFrameIndex(2), true );
}
}
@@ -243,7 +328,7 @@ SwArrowPortion::SwArrowPortion( const SwLinePortion &rPortion ) :
SwArrowPortion::SwArrowPortion( const SwTextPaintInfo &rInf )
: m_bLeft( false )
{
- Height( static_cast<sal_uInt16>(rInf.GetTextFrame()->getFramePrintArea().Height()) );
+ Height(rInf.GetTextFrame()->getFramePrintArea().Height());
m_aPos.setX( rInf.GetTextFrame()->getFrameArea().Left() +
rInf.GetTextFrame()->getFramePrintArea().Right() );
m_aPos.setY( rInf.GetTextFrame()->getFrameArea().Top() +
@@ -335,10 +420,22 @@ bool SwTextFrame::FormatEmpty()
{
OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::FormatEmpty with swapped frame" );
- bool bCollapse = EmptyHeight( ) == 1 && IsCollapse( );
+ SwTwips nHeight = EmptyHeight();
+ bool bCollapse = nHeight == 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,29 +449,40 @@ bool SwTextFrame::FormatEmpty()
const SvxLineSpacingItem &rSpacing = aSet.GetLineSpacing();
if( !bCollapse && ( SvxLineSpaceRule::Min == rSpacing.GetLineSpaceRule() ||
SvxLineSpaceRule::Fix == rSpacing.GetLineSpaceRule() ||
- aSet.GetLRSpace().IsAutoFirst() ) )
+ (rSpacing.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop
+ && rSpacing.GetPropLineSpace() < 100) ||
+ 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();
- if (dynamic_cast<const sw::mark::IBookmark*>(pMark) != nullptr)
+ if (!pIndex->GetOwner() || pIndex->GetOwner()->GetOwnerType() != SwContentIndexOwnerType::Mark)
+ continue;
+ auto const pMark = static_cast<sw::mark::MarkBase const*>(pIndex->GetOwner());
+ if (dynamic_cast<const sw::mark::Bookmark*>(pMark) != nullptr)
{ // need bookmark portions!
return false;
}
}
- SwTwips nHeight = EmptyHeight();
-
if (aSet.GetParaGrid().GetValue() &&
IsInDocBody() )
{
@@ -384,7 +492,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 +582,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 +620,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,15 +654,14 @@ void SwHiddenTextPortion::Paint( const SwTextPaintInfo & rInf) const
{
#ifdef DBG_UTIL
OutputDevice* pOut = const_cast<OutputDevice*>(rInf.GetOut());
- Color aCol( SwViewOption::GetFieldShadingsColor() );
- Color aOldColor( pOut->GetFillColor() );
- pOut->SetFillColor( aCol );
+ pOut->Push(vcl::PushFlags::FILLCOLOR);
+ pOut->SetFillColor( rInf.GetOpt().GetFieldShadingsColor() );
Point aPos( rInf.GetPos() );
aPos.AdjustY( -150 );
aPos.AdjustX( -25 );
SwRect aRect( aPos, Size( 100, 200 ) );
pOut->DrawRect( aRect.SVRect() );
- pOut->SetFillColor( aOldColor );
+ pOut->Pop();
#else
(void)rInf;
#endif
@@ -549,10 +675,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().IsViewMetaChars())
{
return false;
}
@@ -571,6 +697,7 @@ bool SwControlCharPortion::DoPaint(SwTextPaintInfo const&,
}
rTmpFont.SetEscapement( CHAR_ZWSP == mcChar ? DFLT_ESC_AUTO_SUB : -25 );
+ rTmpFont.SetColor( SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor() );
const sal_uInt16 nProp = 40;
rTmpFont.SetProportion( nProp ); // a smaller font
@@ -580,6 +707,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;
@@ -590,14 +718,15 @@ bool SwBookmarkPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo,
// init font: we want OpenSymbol to ensure it doesn't look too crazy;
// thin and a bit higher than the surrounding text
auto const nOrigAscent(rFont.GetAscent(rTextPaintInfo.GetVsh(), *rTextPaintInfo.GetOut()));
- rFont.SetName("OpenSymbol", rFont.GetActual());
+ rFont.SetName(u"OpenSymbol"_ustr, rFont.GetActual());
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 +797,221 @@ 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;
+
+ auto nHeight = rInf.GetTextSize( aOutString ).Height();
+
+ 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(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(mnHalfCharWidth * -(2 * m_nEnd - 1 + m_nPoint) );
+
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
+
+ SwTwips nTypePos = 0; // shift to the position of the next rdf:type label
+ sal_Int32 nDirection = -1; // start with the closing brackets
+ bool bStart = true;
+ 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 ? '[' : ']');
+
+ if (nDirection == -1 && std::get<0>(it) != SwScriptInfo::MarkKind::End)
+ {
+ nDirection = 1;
+ nTypePos = mnHalfCharWidth * 2; // start label after the opening bracket
+ }
+
+ // 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 );
+
+ // show rdf:type labels, left-aligned top position after the opening brackets
+ // right-aligned bottom position before the closing brackets
+ // if there are multiple opening or closing brackets, collect
+ // their length in nTypePos to show non-overlapping labels
+ OUString sType = std::get<3>(it);
+ if ( !sType.isEmpty() )
+ {
+ Size aTmpSz = aTmpFont.GetSize( SwFontScript::Latin );
+ auto origSize = aTmpSz;
+
+ // calculate label size
+ aTmpSz.setHeight( std::min( tools::Long(60), 100 * aTmpSz.Height() / 250 ) );
+ aTmpSz.setWidth( std::min( tools::Long(60), 100 * aTmpSz.Width() / 250 ) );
+
+ // vertical rdf:type label position for the opening and closing brackets
+ sal_Int32 fPos = std::get<0>(it) == SwScriptInfo::MarkKind::Start
+ ? -0.65 * nHeight
+ : aTmpSz.Height();
+
+ if ( aTmpSz.Width() || aTmpSz.Height() )
+ {
+ aTmpFont.SetSize( aTmpSz, SwFontScript::Latin );
+ auto aTextSize = rInf.GetTextSize(sType);
+
+ aNewPos.AdjustY(fPos);
+ if ( nDirection == -1 )
+ {
+ if (bStart)
+ {
+ nTypePos += aTextSize.Width();
+ bStart = false;
+ }
+ else
+ nTypePos += aTextSize.Width() +
+ rInf.GetTextSize( " " ).Width() + 2 * mnHalfCharWidth;
+ }
+ aNewPos.AdjustX( nDirection * nTypePos );
+
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
+
+ SwRect aRect( rInf.GetPos(), Size(aTextSize.Width(), -aTextSize.Height() * 0.8) );
+ rInf.DrawRect( aRect, true ); // white background
+ rInf.DrawText( sType, *this ); // label
+
+ // restore original position
+ aNewPos.AdjustX( -nDirection * nTypePos );
+ if ( nDirection == 1 )
+ nTypePos += aTextSize.Width() +
+ rInf.GetTextSize( " " ).Width() - mnHalfCharWidth * 2;
+
+ aNewPos.AdjustY(-fPos);
+ }
+ // restore original text size
+ aTmpSz.setHeight(origSize.Height());
+ aTmpSz.setWidth(origSize.Width());
+ aTmpFont.SetSize( origSize, SwFontScript::Latin );
+ }
+
+ // 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).toString() + " " + 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).toString() + " " + 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();
@@ -678,7 +1022,7 @@ bool SwControlCharPortion::Format( SwTextFormatInfo &rInf )
return false;
}
-sal_uInt16 SwControlCharPortion::GetViewWidth( const SwTextSizeInfo& rInf ) const
+SwTwips SwControlCharPortion::GetViewWidth(const SwTextSizeInfo& rInf) const
{
if( !mnViewWidth )
mnViewWidth = rInf.GetTextSize(OUString(' ')).Width();
diff --git a/sw/source/core/text/porrst.hxx b/sw/source/core/text/porrst.hxx
index c92c50db3faa..15cf813d4d8c 100644
--- a/sw/source/core/text/porrst.hxx
+++ b/sw/source/core/text/porrst.hxx
@@ -24,21 +24,19 @@
#include <txttypes.hxx>
#include <txtfrm.hxx>
#include <svx/ctredlin.hxx>
+#include <scriptinfo.hxx>
+#include <names.hxx>
#include "porlin.hxx"
#include "portxt.hxx"
#include "possiz.hxx"
-class SwPortionHandler;
class SwTextPaintInfo;
-class SwTextSizeInfo;
class SwFont;
#define LINE_BREAK_WIDTH 150
#define SPECIAL_FONT_HEIGHT 200
-class SwTextFormatInfo;
-
class SwTmpEndPortion : public SwLinePortion
{
const FontLineStyle m_eUnderline;
@@ -53,24 +51,37 @@ 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;
virtual bool Format( SwTextFormatInfo &rInf ) override;
- virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
- virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual SwTwips GetViewWidth(const SwTextSizeInfo& rInf) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(SwTwips nOfst) const override;
// 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 +108,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;
@@ -116,16 +128,16 @@ public:
// The SwHangingPortion is the corresponding textportion to do that.
class SwHangingPortion : public SwTextPortion
{
- sal_uInt16 m_nInnerWidth;
+ SwTwips m_nInnerWidth;
public:
- explicit SwHangingPortion( SwPosSize aSize ) : m_nInnerWidth( aSize.Width() )
+ explicit SwHangingPortion( SwPositiveSize aSize ) : m_nInnerWidth( aSize.Width() )
{
SetWhichPor( PortionType::Hanging );
SetLen(TextFrameIndex(1));
Height( aSize.Height() );
}
- sal_uInt16 GetInnerWidth() const { return m_nInnerWidth; }
+ SwTwips GetInnerWidth() const { return m_nInnerWidth; }
};
// Used to hide text
@@ -145,9 +157,9 @@ 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
+ mutable SwTwips mnViewWidth; // used to cache a calculated value
protected:
+ mutable SwTwips mnHalfCharWidth; // used to cache a calculated value
sal_Unicode mcChar;
public:
@@ -162,24 +174,46 @@ public:
OUString & rOutString, SwFont & rTmpFont, int & rDeltaY) const;
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
- virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo& rInf ) const override;
+ virtual SwTwips GetViewWidth(const SwTextSizeInfo& rInf) const override;
};
/// for showing bookmark starts and ends; note that in contrast to
/// 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, SwMarkName, 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, SwMarkName, 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/portab.hxx b/sw/source/core/text/portab.hxx
index 9ffe7be50651..a1552afeffc2 100644
--- a/sw/source/core/text/portab.hxx
+++ b/sw/source/core/text/portab.hxx
@@ -22,20 +22,20 @@
class SwTabPortion : public SwFixPortion
{
- const sal_uInt16 m_nTabPos;
+ const SwTwips m_nTabPos;
const sal_Unicode m_cFill;
const bool m_bAutoTabStop;
// Format() branches either into PreFormat() or PostFormat()
- bool PreFormat( SwTextFormatInfo &rInf );
+ bool PreFormat(SwTextFormatInfo &rInf, SwTabPortion const*);
public:
- SwTabPortion( const sal_uInt16 nTabPos, const sal_Unicode cFill, const bool bAutoTab = true );
+ SwTabPortion(const SwTwips nTabPos, const sal_Unicode cFill, const bool bAutoTab = true);
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
virtual void FormatEOL( SwTextFormatInfo &rInf ) override;
bool PostFormat( SwTextFormatInfo &rInf );
bool IsFilled() const { return 0 != m_cFill; }
- sal_uInt16 GetTabPos() const { return m_nTabPos; }
+ SwTwips GetTabPos() const { return m_nTabPos; }
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
@@ -44,7 +44,7 @@ public:
class SwTabLeftPortion : public SwTabPortion
{
public:
- SwTabLeftPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cFillChar, bool bAutoTab )
+ SwTabLeftPortion(const SwTwips nTabPosVal, const sal_Unicode cFillChar, bool bAutoTab)
: SwTabPortion( nTabPosVal, cFillChar, bAutoTab )
{ SetWhichPor( PortionType::TabLeft ); }
};
@@ -52,7 +52,7 @@ public:
class SwTabRightPortion : public SwTabPortion
{
public:
- SwTabRightPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cFillChar )
+ SwTabRightPortion(const SwTwips nTabPosVal, const sal_Unicode cFillChar)
: SwTabPortion( nTabPosVal, cFillChar )
{ SetWhichPor( PortionType::TabRight ); }
};
@@ -60,7 +60,7 @@ public:
class SwTabCenterPortion : public SwTabPortion
{
public:
- SwTabCenterPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cFillChar )
+ SwTabCenterPortion(const SwTwips nTabPosVal, const sal_Unicode cFillChar)
: SwTabPortion( nTabPosVal, cFillChar )
{ SetWhichPor( PortionType::TabCenter ); }
};
@@ -74,23 +74,23 @@ class SwTabDecimalPortion : public SwTabPortion
* following the tab stop up to the decimal position. This value is
* evaluated during pLastTab->FormatEOL. FME 2006-01-06 #127428#.
*/
- sal_uInt16 mnWidthOfPortionsUpTpDecimalPosition;
+ SwTwips mnWidthOfPortionsUpTpDecimalPosition;
public:
- SwTabDecimalPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cTab,
+ SwTabDecimalPortion(const SwTwips nTabPosVal, const sal_Unicode cTab,
const sal_Unicode cFillChar )
: SwTabPortion( nTabPosVal, cFillChar ),
mcTab(cTab),
- mnWidthOfPortionsUpTpDecimalPosition( USHRT_MAX )
+ mnWidthOfPortionsUpTpDecimalPosition( std::numeric_limits<SwTwips>::max() )
{ SetWhichPor( PortionType::TabDecimal ); }
sal_Unicode GetTabDecimal() const { return mcTab; }
- void SetWidthOfPortionsUpToDecimalPosition( sal_uInt16 nNew )
+ void SetWidthOfPortionsUpToDecimalPosition(SwTwips nNew)
{
mnWidthOfPortionsUpTpDecimalPosition = nNew;
}
- sal_uInt16 GetWidthOfPortionsUpToDecimalPosition() const
+ SwTwips GetWidthOfPortionsUpToDecimalPosition() const
{
return mnWidthOfPortionsUpTpDecimalPosition;
}
@@ -99,7 +99,7 @@ public:
class SwAutoTabDecimalPortion : public SwTabDecimalPortion
{
public:
- SwAutoTabDecimalPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cTab,
+ SwAutoTabDecimalPortion(const SwTwips nTabPosVal, const sal_Unicode cTab,
const sal_Unicode cFillChar )
: SwTabDecimalPortion( nTabPosVal, cTab, cFillChar )
{
diff --git a/sw/source/core/text/portox.cxx b/sw/source/core/text/portox.cxx
index 982ec4f5fb25..f953316c1375 100644
--- a/sw/source/core/text/portox.cxx
+++ b/sw/source/core/text/portox.cxx
@@ -40,7 +40,7 @@ SwIsoToxPortion::SwIsoToxPortion() : m_nViewWidth(0)
SetWhichPor( PortionType::IsoTox );
}
-sal_uInt16 SwIsoToxPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+SwTwips SwIsoToxPortion::GetViewWidth(const SwTextSizeInfo& rInf) const
{
// Although we are const, nViewWidth should be calculated in the last
// moment possible
@@ -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/portox.hxx b/sw/source/core/text/portox.hxx
index 07d7c44fb24e..541963570823 100644
--- a/sw/source/core/text/portox.hxx
+++ b/sw/source/core/text/portox.hxx
@@ -30,14 +30,14 @@ public:
class SwIsoToxPortion : public SwToxPortion
{
- sal_uInt16 m_nViewWidth;
+ SwTwips m_nViewWidth;
public:
SwIsoToxPortion();
virtual bool Format(SwTextFormatInfo& rInf) override;
virtual void Paint(const SwTextPaintInfo& rInf) const override;
virtual SwLinePortion* Compress() override;
- virtual sal_uInt16 GetViewWidth(const SwTextSizeInfo& rInf) const override;
+ virtual SwTwips GetViewWidth(const SwTextSizeInfo& rInf) const override;
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion(SwPortionHandler& rPH) const override;
diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx
index ed9d3c517bb5..40473f8458d3 100644
--- a/sw/source/core/text/portxt.cxx
+++ b/sw/source/core/text/portxt.cxx
@@ -36,6 +36,7 @@
#include <IMark.hxx>
#include <pam.hxx>
#include <doc.hxx>
+#include <o3tl/temporary.hxx>
#include <xmloff/odffields.hxx>
#include <viewopt.hxx>
@@ -43,6 +44,11 @@ using namespace ::sw::mark;
using namespace ::com::sun::star;
using namespace ::com::sun::star::i18n::ScriptType;
+static TextFrameIndex lcl_AddSpace_Latin(const SwTextSizeInfo& rInf, const OUString* pStr,
+ const SwLinePortion& rPor, TextFrameIndex nPos,
+ TextFrameIndex nEnd, const SwScriptInfo* pSI,
+ sal_uInt8 nScript);
+
// Returns for how many characters an extra space has to be added
// (for justified alignment).
static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf,
@@ -111,13 +117,12 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf,
// Kashida Justification: Insert Kashidas
if ( nEnd > nPos && pSI && COMPLEX == nScript )
{
- if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() )
+ if (pSI->ParagraphContainsKashidaScript()
+ && SwScriptInfo::IsKashidaScriptText(*pStr, 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)
- return TextFrameIndex(nKashRes);
+ // tdf#163105: For kashida justification, also expand whitespace.
+ return lcl_AddSpace_Latin(rInf, pStr, rPor, nPos, nEnd, pSI, nScript)
+ + TextFrameIndex{ pSI->CountKashidaPositions(nPos, nEnd) };
}
}
@@ -129,7 +134,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() ||
@@ -144,6 +149,16 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf,
}
}
+ return lcl_AddSpace_Latin(rInf, pStr, rPor, nPos, nEnd, pSI, nScript);
+}
+
+static TextFrameIndex lcl_AddSpace_Latin(const SwTextSizeInfo& rInf, const OUString* pStr,
+ const SwLinePortion& rPor, TextFrameIndex nPos,
+ TextFrameIndex nEnd, const SwScriptInfo* pSI,
+ sal_uInt8 nScript)
+{
+ TextFrameIndex nCnt(0);
+
// Here starts the good old "Look for blanks and add space to them" part.
// Note: We do not want to add space to an isolated latin blank in front
// of some complex characters in RTL environment
@@ -208,6 +223,36 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf,
return nCnt;
}
+static void GetLimitedStringPart(const SwTextFormatInfo& rInf, TextFrameIndex nIndex,
+ TextFrameIndex nLength, sal_uInt16 nComp, SwTwips nOriginalWidth,
+ SwTwips nMaxWidth, TextFrameIndex& rOutLength, SwTwips& rOutWidth)
+{
+ assert(nLength >= TextFrameIndex(0));
+ const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
+ rOutLength = nLength;
+ rOutWidth = nOriginalWidth;
+ if (nMaxWidth < 0)
+ nMaxWidth = 0;
+ while (rOutWidth > nMaxWidth)
+ {
+ TextFrameIndex nNewOnLineLengthGuess(rOutLength.get() * nMaxWidth / rOutWidth);
+ assert(nNewOnLineLengthGuess < rOutLength);
+ if (nNewOnLineLengthGuess < (rOutLength - TextFrameIndex(1)))
+ ++nNewOnLineLengthGuess; // to avoid too aggressive decrease
+ rOutLength = nNewOnLineLengthGuess;
+ rInf.GetTextSize(&rSI, nIndex, rOutLength, std::nullopt, nComp, rOutWidth,
+ o3tl::temporary(tools::Long()), o3tl::temporary(SwTwips()),
+ o3tl::temporary(SwTwips()), rInf.GetCachedVclData().get());
+ }
+}
+
+static bool IsMsWordUlTrailSpace(const SwTextFormatInfo& rInf)
+{
+ const auto& settings = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess();
+ return settings.get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS)
+ && settings.get(DocumentSettingId::MS_WORD_UL_TRAIL_SPACE);
+}
+
SwTextPortion * SwTextPortion::CopyLinePortion(const SwLinePortion &rPortion)
{
SwTextPortion *const pNew(new SwTextPortion);
@@ -221,7 +266,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 SwTwips nLineWidth = rInf.Width() - rInf.X();
TextFrameIndex nLen = rGuess.CutPos() - rInf.GetIdx();
if (nLen > TextFrameIndex(0))
{
@@ -235,7 +280,7 @@ void SwTextPortion::BreakCut( SwTextFormatInfo &rInf, const SwTextGuess &rGuess
// changing these values requires also changing them in
// guess.cxx
- sal_uInt16 nItalic = 0;
+ SwTwips nItalic = 0;
if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
{
nItalic = Height() / 12;
@@ -258,6 +303,8 @@ void SwTextPortion::BreakCut( SwTextFormatInfo &rInf, const SwTextGuess &rGuess
{
SetLen( TextFrameIndex(0) );
Width( 0 );
+ ExtraShrunkWidth( 0 );
+ ExtraSpaceSize( 0 );
}
}
@@ -266,6 +313,8 @@ void SwTextPortion::BreakUnderflow( SwTextFormatInfo &rInf )
Truncate();
Height( 0 );
Width( 0 );
+ ExtraShrunkWidth( 0 );
+ ExtraSpaceSize( 0 );
SetLen( TextFrameIndex(0) );
SetAscent( 0 );
rInf.SetUnderflow( this );
@@ -277,6 +326,15 @@ static bool lcl_HasContent( const SwFieldPortion& rField, SwTextFormatInfo const
return rField.GetExpText( rInf, aText ) && !aText.isEmpty();
}
+sal_uInt16 SwTextPortion::GetMaxComp(const SwTextFormatInfo& rInf) const
+{
+ const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
+ return (SwFontScript::CJK == rInf.GetFont()->GetActual()) && rSI.CountCompChg()
+ && !rInf.IsMulti() && !InFieldGrp() && !IsDropPortion()
+ ? 10000
+ : 0;
+}
+
bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
{
// 5744: If only the hyphen does not fit anymore, we still need to wrap
@@ -301,8 +359,179 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
return bFull;
}
- SwTextGuess aGuess;
- const bool bFull = !aGuess.Guess( *this, rInf, Height() );
+ ExtraShrunkWidth( 0 );
+ ExtraSpaceSize( 0 );
+ std::optional<SwTextGuess> pGuess(std::in_place);
+ bool bFull = !pGuess->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
+ SvxAdjustItem aAdjustItem = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
+ const SvxAdjust aAdjust = aAdjustItem.GetAdjust();
+ bool bFullJustified = bFull && aAdjust == SvxAdjust::Block &&
+ pGuess->BreakPos() != TextFrameIndex(COMPLETE_STRING);
+ bool bInteropSmartJustify = bFullJustified &&
+ rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::JUSTIFY_LINES_WITH_SHRINKING);
+ bool bNoWordSpacing = aAdjustItem.GetPropWordSpacing() == 100 &&
+ aAdjustItem.GetPropWordSpacingMinimum() == 100 &&
+ aAdjustItem.GetPropWordSpacingMaximum() == 100;
+ // support old ODT documents, where only JustifyLinesWithShrinking was set
+ bool bOldInterop = bInteropSmartJustify && bNoWordSpacing;
+ bool bWordSpacing = bFullJustified && (!bNoWordSpacing || bOldInterop);
+ bool bWordSpacingMaximum = bWordSpacing && !bOldInterop &&
+ aAdjustItem.GetPropWordSpacingMaximum() > aAdjustItem.GetPropWordSpacing();
+ bool bWordSpacingMinimum = bWordSpacing && ( bOldInterop ||
+ aAdjustItem.GetPropWordSpacingMinimum() < aAdjustItem.GetPropWordSpacing() );
+
+ if ( ( bInteropSmartJustify || bWordSpacing || bWordSpacingMaximum || bWordSpacingMinimum ) &&
+ // tdf#164499 no shrinking in tabulated line
+ ( !rInf.GetLast() || !rInf.GetLast()->InTabGrp() ) &&
+ // tdf#158436 avoid shrinking at underflow, e.g. no-break space after a
+ // very short word resulted endless loop
+ !rInf.IsUnderflow() )
+ {
+ sal_Int32 nSpacesInLine = rInf.GetLineSpaceCount( pGuess->BreakPos() );
+ sal_Int32 nSpacesInLineOrig = nSpacesInLine;
+ SwTextSizeInfo aOrigInf( rInf );
+
+ // 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
+ !pGuess->HyphWord().is() &&
+ // no hyphenation at soft hyphen
+ pGuess->BreakPos() < TextFrameIndex(rInf.GetText().getLength()) &&
+ rInf.GetText()[sal_Int32(pGuess->BreakPos())] != CHAR_SOFTHYPHEN )
+ {
+ ++nSpacesInLine;
+ }
+
+ // there are spaces in the line, so it's possible to shrink them
+ if ( nSpacesInLineOrig > 0 )
+ {
+ SwTwips nOldWidth = pGuess->BreakWidth();
+ bool bIsPortion = rInf.GetLineWidth() < rInf.GetBreakWidth();
+
+ // measure ten spaces for higher precision
+ static constexpr OUStringLiteral STR_BLANK = u" ";
+ sal_Int16 nSpaceWidth = rInf.GetTextSize(STR_BLANK).Width();
+ sal_Int32 nRealSpaces = rInf.GetLineSpaceCount( pGuess->BreakPos() );
+ float fSpaceNormal = (rInf.GetLineWidth() - (rInf.GetBreakWidth() - nRealSpaces * nSpaceWidth/10.0))/nRealSpaces;
+
+ float fExpansionOverMax = fSpaceNormal - nSpaceWidth/10.0 * aAdjustItem.GetPropWordSpacingMaximum()/100.0;
+ ExtraSpaceSize( rInf.GetBreakWidth() > rInf.GetLineWidth()/2 && fExpansionOverMax > 0 ? fExpansionOverMax : 0);
+
+ bool bOrigHyphenated = pGuess->HyphWord().is() &&
+ pGuess->BreakPos() > rInf.GetLineStart();
+ // calculate line breaking with desired word spacing, also
+ // if the desired word spacing is 100%, but there is a greater
+ // maximum word spacing, and the word is hyphenated at the desired
+ // word spacing: to skip hyphenation, if the maximum word spacing allows it
+ if ( bWordSpacing || ( bWordSpacingMaximum && bOrigHyphenated ) )
+ {
+ pGuess.emplace();
+ bFull = !pGuess->Guess( *this, rInf, Height(), nSpacesInLine, aAdjustItem.GetPropWordSpacing(), nSpaceWidth );
+ sal_Int32 nSpacesInLine2 = rInf.GetLineSpaceCount( pGuess->BreakPos() );
+
+ if ( rInf.GetBreakWidth() <= rInf.GetLineWidth() )
+ {
+ fSpaceNormal = (rInf.GetLineWidth() - (rInf.GetBreakWidth() - nSpacesInLine2 * nSpaceWidth/10.0))/nSpacesInLine2;
+ fExpansionOverMax = fSpaceNormal - nSpaceWidth/10.0 * aAdjustItem.GetPropWordSpacingMaximum()/100.0;
+ ExtraSpaceSize( rInf.GetBreakWidth() > rInf.GetLineWidth()/2 && fExpansionOverMax > 0 ? fExpansionOverMax : 0);
+ }
+ }
+
+ sal_Int32 nSpacesInLineShrink = 0;
+ // TODO if both maximum word spacing or minimum word spacing can disable hyphenation, prefer the last one
+ if ( bWordSpacingMinimum )
+ {
+ std::optional<SwTextGuess> pGuess2(std::in_place);
+ SwTwips nOldExtraSpace = rInf.GetExtraSpace();
+ // break the line after the hyphenated word, if it's possible
+ // (hyphenation is disabled in Guess(), when called with GetPropWordSpacingMinimum())
+ sal_uInt16 nMinimum = bOldInterop ? 75 : aAdjustItem.GetPropWordSpacingMinimum();
+ bool bFull2 = !pGuess2->Guess( *this, rInf, Height(), nSpacesInLine, nMinimum, nSpaceWidth );
+ nSpacesInLineShrink = rInf.GetLineSpaceCount( pGuess2->BreakPos() );
+ if ( pGuess2->BreakWidth() > nOldWidth )
+ {
+ // instead of the maximum shrinking, break after the word which was hyphenated before
+ sal_Int32 i = sal_Int32(pGuess->BreakPos());
+ sal_Int32 j = sal_Int32(pGuess2->BreakPos());
+ // skip terminal spaces
+ for (; i < j && rInf.GetText()[i] == CH_BLANK; ++i);
+ for (; j > i && rInf.GetText()[i] == CH_BLANK; --j);
+ sal_Int32 nOldBreakTrim = i;
+ sal_Int32 nOldBreak = j - i;
+ for (; i < j; ++i)
+ {
+ sal_Unicode cChar = rInf.GetText()[i];
+ // first space after the hyphenated word, and it's not the chosen one
+ if ( cChar == CH_BLANK )
+ {
+ // using a weighted word spacing, try to break the line after the hyphenated word
+ sal_Int32 nNewBreak = i - nOldBreakTrim;
+ SwTwips nWeightedSpacing = nMinimum * (1.0 * nNewBreak/nOldBreak) +
+ aAdjustItem.GetPropWordSpacing() * (1.0 * (nOldBreak - nNewBreak)/nOldBreak);
+ std::optional<SwTextGuess> pGuess3(std::in_place);
+ pGuess3->Guess( *this, rInf, Height(), nSpacesInLineShrink-1, nWeightedSpacing, nSpaceWidth );
+
+ sal_Int32 nSpacesInLineShrink2 = rInf.GetLineSpaceCount( pGuess3->BreakPos() );
+ if ( nSpacesInLineShrink2 == nSpacesInLineShrink )
+ {
+ nNewBreak = i - nOldBreakTrim - 1;
+ nWeightedSpacing = nMinimum * (1.0 * nNewBreak/nOldBreak) +
+ aAdjustItem.GetPropWordSpacing() * (1.0 * (nOldBreak - nNewBreak)/nOldBreak);
+ pGuess3->Guess( *this, rInf, Height(), nSpacesInLineShrink-1, nWeightedSpacing, nSpaceWidth );
+ }
+
+ if ( pGuess3->BreakWidth() > nOldWidth )
+ {
+ pGuess2.emplace();
+ pGuess2 = std::move(pGuess3);
+ }
+ break;
+ }
+ }
+
+ nSpacesInLineShrink = rInf.GetLineSpaceCount( pGuess2->BreakPos() );
+ if ( rInf.GetBreakWidth() > rInf.GetLineWidth() || bIsPortion )
+ {
+ float fExpansionWeight = static_cast<float>(1/1.7);
+ float fSpaceShrunk = nSpacesInLineShrink > 0
+ ? (rInf.GetLineWidth() - (rInf.GetBreakWidth() - nSpacesInLineShrink * nSpaceWidth/10.0))/nSpacesInLineShrink
+ : 1;
+ float z0 = (nSpaceWidth/10.0)/fSpaceShrunk;
+ float z1 = (nSpaceWidth/10.0+((fSpaceNormal-nSpaceWidth/10.0)*fExpansionWeight))/(nSpaceWidth/10.0);
+ // TODO shrink line portions only if needed
+ if ( z1 >= z0 || bIsPortion )
+ {
+ pGuess = std::move(pGuess2);
+ ExtraSpaceSize(0);
+ bFull = bFull2;
+ }
+ }
+ else if ( bOldInterop )
+ {
+ pGuess = std::move(pGuess2);
+ ExtraSpaceSize(0);
+ bFull = bFull2;
+ }
+ }
+ else
+ // minimum word spacing is not applicable
+ rInf.SetExtraSpace(nOldExtraSpace);
+ }
+
+ if ( pGuess->BreakWidth() != nOldWidth )
+ {
+ ExtraShrunkWidth( pGuess->BreakWidth() );
+ ExtraSpaceSize( 0 );
+ }
+ }
+ }
// these are the possible cases:
// A Portion fits to current line
@@ -320,7 +549,8 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
// case A: line not yet full
if ( !bFull )
{
- Width( aGuess.BreakWidth() );
+ Width( pGuess->BreakWidth() );
+ ExtraBlankWidth(pGuess->ExtraBlankWidth());
// Caution!
if( !InExpGrp() || InFieldGrp() )
SetLen( rInf.GetLen() );
@@ -336,22 +566,22 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
new SwKernPortion( *this, nKern );
}
// special case: hanging portion
- else if( bFull && aGuess.GetHangingPortion() )
+ else if( pGuess->GetHangingPortion() )
{
- Width( aGuess.BreakWidth() );
- SetLen( aGuess.BreakPos() - rInf.GetIdx() );
- aGuess.GetHangingPortion()->SetAscent( GetAscent() );
- Insert( aGuess.ReleaseHangingPortion() );
+ Width( pGuess->BreakWidth() );
+ SetLen( pGuess->BreakPos() - rInf.GetIdx() );
+ pGuess->GetHangingPortion()->SetAscent( GetAscent() );
+ Insert( pGuess->ReleaseHangingPortion() );
}
// breakPos >= index
- else if (aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != TextFrameIndex(COMPLETE_STRING))
+ else if (pGuess->BreakPos() >= rInf.GetIdx() && pGuess->BreakPos() != TextFrameIndex(COMPLETE_STRING))
{
// case B1
- if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart()
- && ( aGuess.BreakPos() > rInf.GetIdx() ||
+ if( pGuess->HyphWord().is() && pGuess->BreakPos() > rInf.GetLineStart()
+ && ( pGuess->BreakPos() > rInf.GetIdx() ||
( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) )
{
- CreateHyphen( rInf, aGuess );
+ CreateHyphen( rInf, *pGuess );
if ( rInf.GetFly() )
rInf.GetRoot()->SetMidHyph( true );
else
@@ -372,14 +602,14 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT) &&
rInf.GetLast()->InTabGrp() &&
rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() &&
- aGuess.BreakPos() == rInf.GetIdx() &&
+ pGuess->BreakPos() == rInf.GetIdx() &&
CH_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
CH_FULL_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
CH_SIX_PER_EM != rInf.GetChar( rInf.GetIdx() ) ) )
BreakUnderflow( rInf );
// case B2
else if( rInf.GetIdx() > rInf.GetLineStart() ||
- aGuess.BreakPos() > rInf.GetIdx() ||
+ pGuess->BreakPos() > rInf.GetIdx() ||
// this is weird: during formatting the follow of a field
// the values rInf.GetIdx and rInf.GetLineStart are replaced
// IsFakeLineStart indicates GetIdx > GetLineStart
@@ -393,34 +623,67 @@ 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( pGuess->BreakWidth() );
+
+ SetLen( pGuess->BreakPos() - rInf.GetIdx() );
- SetLen( aGuess.BreakPos() - rInf.GetIdx() );
+ // Clamp layout context to the end of the line
+ if(auto stClampedContext = GetLayoutContext(); stClampedContext.has_value()) {
+ stClampedContext->m_nEnd = pGuess->BreakPos().get();
+ SetLayoutContext(stClampedContext);
+ }
- OSL_ENSURE( aGuess.BreakStart() >= aGuess.FieldDiff(),
+ OSL_ENSURE( pGuess->BreakStart() >= pGuess->FieldDiff(),
"Trouble with expanded field portions during line break" );
- TextFrameIndex const nRealStart = aGuess.BreakStart() - aGuess.FieldDiff();
- if( aGuess.BreakPos() < nRealStart && !InExpGrp() )
+ TextFrameIndex const nRealStart = pGuess->BreakStart() - pGuess->FieldDiff();
+ if( pGuess->BreakPos() < nRealStart && !InExpGrp() )
{
- SwHolePortion *pNew = new SwHolePortion( *this );
- pNew->SetLen( nRealStart - aGuess.BreakPos() );
+ TextFrameIndex nTotalExtraLen(nRealStart - pGuess->BreakPos());
+ TextFrameIndex nExtraLen(nTotalExtraLen);
+ TextFrameIndex nExtraLenOutOfLine(0);
+ SwTwips nTotalExtraWidth(pGuess->ExtraBlankWidth());
+ SwTwips nExtraWidth(nTotalExtraWidth);
+ SwTwips nExtraWidthOutOfLine(0);
+ SwTwips nAvailableLineWidth(rInf.GetLineWidth() - Width());
+ const bool bMsWordUlTrailSpace(IsMsWordUlTrailSpace(rInf));
+ if (nExtraWidth > nAvailableLineWidth && bMsWordUlTrailSpace)
+ {
+ GetLimitedStringPart(rInf, pGuess->BreakPos(), nTotalExtraLen, GetMaxComp(rInf),
+ nTotalExtraWidth, nAvailableLineWidth, nExtraLen,
+ nExtraWidth);
+ nExtraLenOutOfLine = nTotalExtraLen - nExtraLen;
+ nExtraWidthOutOfLine = nTotalExtraWidth - nExtraWidth;
+ }
+
+ SwHolePortion* pNew = new SwHolePortion(*this, bMsWordUlTrailSpace);
+ pNew->SetLen(nExtraLen);
+ pNew->ExtraBlankWidth(nExtraWidth);
Insert( pNew );
+
+ if (nExtraWidthOutOfLine)
+ {
+ // Out-of-line hole portion - will not show underline
+ SwHolePortion* pNewOutOfLine = new SwHolePortion(*this, false);
+ pNewOutOfLine->SetLen(nExtraLenOutOfLine);
+ pNewOutOfLine->ExtraBlankWidth(nExtraWidthOutOfLine);
+ pNew->Insert(pNewOutOfLine);
+ }
+
+ // 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(pGuess->BreakStart()); !ch || ch == CH_BREAK)
+ bFull = false; // Keep following SwBreakPortion / para break in the same line
}
}
else // case C2, last exit
- BreakCut( rInf, aGuess );
+ BreakCut( rInf, *pGuess );
}
// breakPos < index or no breakpos at all
else
{
bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
- if (aGuess.BreakPos() != TextFrameIndex(COMPLETE_STRING) &&
- aGuess.BreakPos() != rInf.GetLineStart() &&
+ if (pGuess->BreakPos() != TextFrameIndex(COMPLETE_STRING) &&
+ pGuess->BreakPos() != rInf.GetLineStart() &&
( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() ||
rInf.IsFirstMulti() ) &&
( !rInf.GetLast()->IsBlankPortion() ||
@@ -430,7 +693,7 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
}
else
// case C2, last exit
- BreakCut( rInf, aGuess );
+ BreakCut(rInf, *pGuess);
}
return bFull;
@@ -439,10 +702,12 @@ bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
bool SwTextPortion::Format( SwTextFormatInfo &rInf )
{
// GetLineWidth() takes care of DocumentSettingId::TAB_OVER_MARGIN.
- if( rInf.GetLineWidth() < 0 || (!GetLen() && !InExpGrp()) )
+ if( rInf.GetLineWidth() + rInf.GetExtraSpace() < 0 || (!GetLen() && !InExpGrp()) )
{
Height( 0 );
Width( 0 );
+ ExtraShrunkWidth( 0 );
+ ExtraSpaceSize( 0 );
SetLen( TextFrameIndex(0) );
SetAscent( 0 );
SetNextPortion( nullptr ); // ????
@@ -483,7 +748,7 @@ void SwTextPortion::FormatEOL( SwTextFormatInfo &rInf )
// First set ourselves and the insert, because there could be
// a SwLineLayout
- sal_uInt16 nBlankSize;
+ SwTwips nBlankSize;
if( nHoleLen == GetLen() )
nBlankSize = Width();
else
@@ -491,23 +756,23 @@ 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 );
}
-TextFrameIndex SwTextPortion::GetModelPositionForViewPoint(const sal_uInt16 nOfst) const
+TextFrameIndex SwTextPortion::GetModelPositionForViewPoint(const SwTwips nOfst) const
{
OSL_ENSURE( false, "SwTextPortion::GetModelPositionForViewPoint: don't use this method!" );
return SwLinePortion::GetModelPositionForViewPoint( nOfst );
}
// The GetTextSize() assumes that the own length is correct
-SwPosSize SwTextPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+SwPositiveSize SwTextPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
{
- SwPosSize aSize = rInf.GetTextSize();
+ SwPositiveSize aSize = rInf.GetTextSize(GetLayoutContext());
if( !GetJoinBorderWithPrev() )
aSize.Width(aSize.Width() + rInf.GetFont()->GetLeftBorderSpace() );
if( !GetJoinBorderWithNext() )
@@ -543,6 +808,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 );
@@ -578,13 +845,16 @@ TextFrameIndex SwTextPortion::GetSpaceCnt(const SwTextSizeInfo &rInf,
if ( rInf.SnapToGrid() )
{
SwTextGridItem const*const pGrid(GetGridItem(rInf.GetTextFrame()->FindPageFrame()));
- if (pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars())
+ if (pGrid && SwTextGrid::LinesAndChars == pGrid->GetGridType() && pGrid->IsSnapToChars())
return TextFrameIndex(0);
}
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,20 +878,23 @@ 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);
if ( rInf.SnapToGrid() )
{
SwTextGridItem const*const pGrid(GetGridItem(rInf.GetTextFrame()->FindPageFrame()));
- if (pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars())
+ if (pGrid && SwTextGrid::LinesAndChars == pGrid->GetGridType() && pGrid->IsSnapToChars())
return 0;
}
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 +935,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 );
}
@@ -694,18 +967,18 @@ void SwTextInputFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
// highlight empty input field, elsewhere they are completely invisible for the user
SwRect aIntersect;
rInf.CalcRect(*this, &aIntersect);
- const sal_uInt16 aAreaWidth = rInf.GetTextSize(OUString(' ')).Width();
+ const SwTwips aAreaWidth = rInf.GetTextSize(OUString(' ')).Width();
aIntersect.Left(aIntersect.Left() - aAreaWidth/2);
aIntersect.Width(aAreaWidth);
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();
@@ -731,55 +1004,85 @@ bool SwTextInputFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &
return true;
}
-SwPosSize SwTextInputFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+SwPositiveSize SwTextInputFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
{
SwTextSlot aFormatText( &rInf, this, true, false );
if (rInf.GetLen() == TextFrameIndex(0))
{
- return SwPosSize( 0, 0 );
+ return SwPositiveSize( 0, 0 );
}
return rInf.GetTextSize();
}
-SwHolePortion::SwHolePortion( const SwTextPortion &rPor )
+SwHolePortion::SwHolePortion(const SwTextPortion& rPor, bool bShowUnderline)
: m_nBlankWidth( 0 )
+ , m_bShowUnderline(bShowUnderline)
{
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
+SwPositiveSize SwHolePortion::GetTextSize(const SwTextSizeInfo& rInf) const
+{
+ SwPositiveSize 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;
- if( pOrigFont->GetUnderline() != LINESTYLE_NONE
+ std::optional<SwFontSave> oFontSave;
+ if( (!m_bShowUnderline && pOrigFont->GetUnderline() != LINESTYLE_NONE)
|| pOrigFont->GetOverline() != LINESTYLE_NONE
|| pOrigFont->GetStrikeout() != STRIKEOUT_NONE )
{
pHoleFont.reset(new SwFont( *pOrigFont ));
- pHoleFont->SetUnderline( LINESTYLE_NONE );
+ if (!m_bShowUnderline)
+ 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(u" "_ustr, *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 +1096,21 @@ 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)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("show-underline"),
+ BAD_CAST(OString::boolean(m_bShowUnderline).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
void SwFieldMarkPortion::Paint( const SwTextPaintInfo & /*rInf*/) const
{
// These shouldn't be painted!
@@ -809,14 +1127,14 @@ void SwFieldFormCheckboxPortion::Paint( const SwTextPaintInfo& rInf ) const
{
SwPosition const aPosition(rInf.GetTextFrame()->MapViewToModelPos(rInf.GetIdx()));
- IFieldmark const*const pBM = rInf.GetTextFrame()->GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(aPosition);
+ Fieldmark const*const pBM = rInf.GetTextFrame()->GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(aPosition);
OSL_ENSURE(pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX,
"Where is my form field bookmark???");
if (pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
{
- const ICheckboxFieldmark* pCheckboxFm = dynamic_cast<ICheckboxFieldmark const*>(pBM);
+ const CheckboxFieldmark* pCheckboxFm = dynamic_cast<CheckboxFieldmark const*>(pBM);
bool bChecked = pCheckboxFm && pCheckboxFm->IsChecked();
rInf.DrawCheckBox(*this, bChecked);
}
@@ -825,7 +1143,7 @@ void SwFieldFormCheckboxPortion::Paint( const SwTextPaintInfo& rInf ) const
bool SwFieldFormCheckboxPortion::Format( SwTextFormatInfo & rInf )
{
SwPosition const aPosition(rInf.GetTextFrame()->MapViewToModelPos(rInf.GetIdx()));
- IFieldmark const*const pBM = rInf.GetTextFrame()->GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(aPosition);
+ Fieldmark const*const pBM = rInf.GetTextFrame()->GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(aPosition);
OSL_ENSURE(pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX, "Where is my form field bookmark???");
if (pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
{
diff --git a/sw/source/core/text/portxt.hxx b/sw/source/core/text/portxt.hxx
index ec44a2bafc76..132efe86dd20 100644
--- a/sw/source/core/text/portxt.hxx
+++ b/sw/source/core/text/portxt.hxx
@@ -35,10 +35,10 @@ public:
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
virtual void FormatEOL( SwTextFormatInfo &rInf ) override;
- virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
- virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(SwTwips nOfst) const override;
+ virtual SwPositiveSize 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;
@@ -47,6 +47,8 @@ public:
// Accessibility: pass information about this portion to the PortionHandler
virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ sal_uInt16 GetMaxComp(const SwTextFormatInfo &rInf) const;
};
class SwTextInputFieldPortion : public SwTextPortion
@@ -57,22 +59,28 @@ public:
virtual bool Format( SwTextFormatInfo &rInf ) override;
virtual void Paint( const SwTextPaintInfo &rInf ) const override;
virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
- virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual SwPositiveSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
};
class SwHolePortion : public SwLinePortion
{
- sal_uInt16 m_nBlankWidth;
+ SwTwips m_nBlankWidth;
+ bool m_bShowUnderline;
+
public:
- explicit SwHolePortion( const SwTextPortion &rPor );
- sal_uInt16 GetBlankWidth( ) const { return m_nBlankWidth; }
- void SetBlankWidth( const sal_uInt16 nNew ) { m_nBlankWidth = nNew; }
+ explicit SwHolePortion(const SwTextPortion& rPor, bool bShowUnderline = false);
+ SwTwips GetBlankWidth() const { return m_nBlankWidth; }
+ void SetBlankWidth(const SwTwips nNew) { m_nBlankWidth = nNew; }
virtual SwLinePortion *Compress() override;
virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual SwPositiveSize 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..858ef70da594 100644
--- a/sw/source/core/text/possiz.hxx
+++ b/sw/source/core/text/possiz.hxx
@@ -19,52 +19,48 @@
#pragma once
#include <tools/gen.hxx>
-#include <sal/types.h>
+#include <swtypes.hxx>
-// Compared to the SV sizes SwPosSize is always positive
-class SwPosSize
+// Compared to the SV sizes SwPositiveSize is always positive
+class SwPositiveSize
{
- 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 )
+ SwPositiveSize( 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()))
+ explicit SwPositiveSize( const Size &rSize )
+ : m_nWidth(SwTwips(rSize.Width()))
+ , m_nHeight(SwTwips(rSize.Height()))
{
}
-#if defined(__COVERITY__)
- virtual ~SwPosSize() COVERITY_NOEXCEPT_FALSE {}
-#else
- virtual ~SwPosSize() {}
-#endif
- SwPosSize(SwPosSize const &) = default;
- 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; }
+ virtual ~SwPositiveSize() {}
+ SwPositiveSize(SwPositiveSize const &) = default;
+ SwPositiveSize(SwPositiveSize &&) = default;
+ SwPositiveSize & operator =(SwPositiveSize const &) = default;
+ SwPositiveSize & operator =(SwPositiveSize &&) = default;
+ 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 )
+ SwPositiveSize& 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..87564e2a3fbb 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,13 @@
#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>
+#include <officecfg/Office/Writer.hxx>
using namespace ::com::sun::star;
@@ -59,30 +66,37 @@ 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::Fieldmark 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;
+ SwNode const* m_pCurrentRedlineNode;
public:
SwPosition const* GetStartPos() const { return m_pStartPos; }
SwPosition const* GetEndPos() const { return m_pEndPos; }
- HideIterator(SwTextNode & rTextNode,
- bool const isHideRedlines, sw::FieldmarkMode const eMode)
+ HideIterator(const SwTextNode & rTextNode,
+ 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)
+ , m_pCurrentRedlineNode(&rTextNode)
{
}
@@ -96,27 +110,34 @@ public:
assert(m_pEndPos);
if (m_isHideRedlines)
{
+ // GetRedlinePos() returns npos if there is no redline on the
+ // node but something else could have merged nodes so search again!
+ if (m_RedlineIndex == SwRedlineTable::npos
+ && &m_pEndPos->GetNode() != m_pCurrentRedlineNode)
+ {
+ m_RedlineIndex = m_rIDRA.GetRedlinePos(m_pEndPos->GetNode(), RedlineType::Any);
+ m_pCurrentRedlineNode = &m_pEndPos->GetNode();
+ }
// position on current or next redline
for (; m_RedlineIndex < m_rIDRA.GetRedlineTable().size(); ++m_RedlineIndex)
{
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 +155,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(
+ sw::mark::Fieldmark 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 +173,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 +204,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;
+ }
}
}
};
@@ -201,6 +275,47 @@ public:
namespace sw {
+void FindParaPropsNodeIgnoreHidden(sw::MergedPara & rMerged,
+ sw::ParagraphBreakMode const eMode, SwScriptInfo * pScriptInfo)
+{
+ if (eMode == sw::ParagraphBreakMode::Hidden)
+ {
+ ::std::optional<SwScriptInfo> oScriptInfo;
+ if (pScriptInfo == nullptr)
+ {
+ oScriptInfo.emplace();
+ pScriptInfo = &*oScriptInfo;
+ }
+ // always init: when called from SwTextFrame::SwClientNotify() it is stale!
+ pScriptInfo->InitScriptInfoHidden(*rMerged.pFirstNode, &rMerged);
+ TextFrameIndex nHiddenStart{COMPLETE_STRING};
+ TextFrameIndex nHiddenEnd{0};
+ pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex{0}, nHiddenStart, nHiddenEnd);
+ if (TextFrameIndex{0} == nHiddenStart)
+ {
+ if (nHiddenEnd == TextFrameIndex{rMerged.mergedText.getLength()})
+ {
+ rMerged.pParaPropsNode = const_cast<SwTextNode*>(rMerged.pLastNode);
+ }
+ else
+ { // this requires MapViewToModel to never return a position at
+ // the end of a node (when all its text is hidden)
+ rMerged.pParaPropsNode = sw::MapViewToModel(rMerged, nHiddenEnd).first;
+ }
+ return;
+ }
+ }
+ if (!rMerged.extents.empty())
+ { // para props from first node that isn't empty (OOo/LO compat)
+ rMerged.pParaPropsNode = rMerged.extents.begin()->pNode;
+ }
+ else
+ { // if every node is empty, the last one wins (Word compat)
+ // (OOo/LO historically used first one)
+ rMerged.pParaPropsNode = const_cast<SwTextNode*>(rMerged.pLastNode);
+ }
+}
+
std::unique_ptr<sw::MergedPara>
CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
FrameMode const eMode)
@@ -215,30 +330,31 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
std::vector<SwSectionNode *> sections;
std::vector<sw::Extent> extents;
OUStringBuffer mergedText;
- SwTextNode * pParaPropsNode(nullptr);
SwTextNode * pNode(&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 +384,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 +398,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 +424,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())
@@ -355,22 +471,23 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
if (extents.empty()) // there was no text anywhere
{
assert(mergedText.isEmpty());
- pParaPropsNode = pNode; // if every node is empty, the last one wins
}
else
{
assert(!mergedText.isEmpty());
- pParaPropsNode = extents.begin()->pNode; // para props from first node that isn't empty
}
-// pParaPropsNode = &rTextNode; // well, actually...
+ auto pRet{std::make_unique<sw::MergedPara>(rFrame, std::move(extents),
+ mergedText.makeStringAndClear(), &rTextNode, nodes.back())};
+ FindParaPropsNodeIgnoreHidden(*pRet, rFrame.getRootFrame()->GetParagraphBreakMode(), nullptr);
+ assert(pRet->pParaPropsNode);
// keep lists up to date with visible nodes
- if (pParaPropsNode->IsInList() && !pParaPropsNode->GetNum(rFrame.getRootFrame()))
+ if (pRet->pParaPropsNode->IsInList() && !pRet->pParaPropsNode->GetNum(rFrame.getRootFrame()))
{
- pParaPropsNode->AddToListRLHidden(); // try to add it...
+ pRet->pParaPropsNode->AddToListRLHidden(); // try to add it...
}
for (auto const pTextNode : nodes)
{
- if (pTextNode != pParaPropsNode)
+ if (pTextNode != pRet->pParaPropsNode)
{
pTextNode->RemoveFromListRLHidden();
}
@@ -384,12 +501,12 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
// for non-first nodes that are already merged with this frame,
// need to remove here too, otherwise footnotes can be removed only
// by lucky accident, e.g. TruncLines().
- auto itExtent(extents.begin());
+ auto itExtent(pRet->extents.begin());
for (auto const pTextNode : nodes)
{
sal_Int32 nLast(0);
std::vector<std::pair<sal_Int32, sal_Int32>> hidden;
- for ( ; itExtent != extents.end(); ++itExtent)
+ for ( ; itExtent != pRet->extents.end(); ++itExtent)
{
if (itExtent->pNode != pTextNode)
{
@@ -422,12 +539,9 @@ 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),
- mergedText.makeStringAndClear(), pParaPropsNode, &rTextNode,
- nodes.back()));
for (SwTextNode * pTmp : nodes)
{
pRet->listener.StartListening(pTmp);
@@ -441,7 +555,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 +625,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 +760,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;
@@ -667,14 +783,14 @@ SwRedlineItr::~SwRedlineItr() COVERITY_NOEXCEPT_FALSE
m_pExt.reset();
}
-// The return value of SwRedlineItr::Seek tells you if the current font
-// has been manipulated by leaving (-1) or accessing (+1) of a section
+/// The return value of SwRedlineItr::Seek tells if the current font
+/// has been manipulated by leaving (-1) or entering (+1) a range redline
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() )
- return 0; // Abbreviation: if we're within an ExtendTextInputs
+ return 0; // shortcut: if we're within an ExtendTextInputs
// there can't be other changes of attributes (not even by redlining)
if (m_eMode == Mode::Show)
{
@@ -683,37 +799,57 @@ short SwRedlineItr::Seek(SwFont& rFnt,
if (nNew >= m_nEnd)
{
--nRet;
- Clear_( &rFnt ); // We go behind the current section
- ++m_nAct; // and check the next one
+ Clear_( &rFnt ); // We go behind the current range
+// ++m_nAct; // don't increment, could be in next range too if overlap
}
else if (nNew < m_nStart)
{
--nRet;
- Clear_( &rFnt ); // We go in front of the current section
+ Clear_( &rFnt ); // We go before the current range
if (m_nAct > m_nFirst)
- m_nAct = m_nFirst; // the test has to run from the beginning
+ m_nAct = m_nFirst; // need to start over
else
return nRet + EnterExtend(rFnt, nNode, nNew); // There's none prior to us
}
else
- return nRet + EnterExtend(rFnt, nNode, nNew); // We stayed in the same section
+ return nRet + EnterExtend(rFnt, nNode, nNew); // We stayed in the same range
}
if (SwRedlineTable::npos == m_nAct || nOld > nNew)
m_nAct = m_nFirst;
m_nStart = COMPLETE_STRING;
m_nEnd = COMPLETE_STRING;
+ const SwRedlineTable& rTable = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ ::std::optional<decltype(m_nAct)> oFirstMatch;
- 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);
+ decltype(m_nStart) nStart;
+ decltype(m_nEnd) nEnd;
+ if (rTable[m_nAct]->CalcStartEnd(nNode, nStart, nEnd))
+ { // previous redline intersected nNode but this one precedes it
+ continue;
+ }
- if (nNew < m_nEnd)
+ // redline table is sorted, but here it's not the complete redlines
+ assert(m_nStart == COMPLETE_STRING || m_nStart <= nStart);
+ assert(m_nStart == COMPLETE_STRING || m_nStart <= nEnd);
+ if (oFirstMatch && nNew < nStart)
+ {
+ m_nEnd = std::min(m_nEnd, nStart);
+ break;
+ }
+ if (nNew < nEnd)
{
- if (nNew >= m_nStart) // only possible candidate
+ m_nStart = nStart;
+ m_nEnd = std::min(m_nEnd, nEnd);
+ if (nStart <= nNew) // there can be a format and another redline...
{
- m_bOn = true;
- const SwRangeRedline *pRed = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ];
+ if (!oFirstMatch)
+ {
+ oFirstMatch.emplace(m_nAct);
+ }
+ const SwRangeRedline *pRed = rTable[ m_nAct ];
if (m_pSet)
m_pSet->ClearItem();
@@ -721,7 +857,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 +865,24 @@ short SwRedlineItr::Seek(SwFont& rFnt,
FillHints( pRed->GetAuthor(), pRed->GetType() );
SfxWhichIter aIter( *m_pSet );
+
+ // moved text: dark green with double underline or strikethrough
+ bool bDisplayMovedTextInGreen = officecfg::Office::Writer::Comparison::DisplayMovedTextInGreen::get();
+ if ( bDisplayMovedTextInGreen && 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),
@@ -745,13 +893,19 @@ short SwRedlineItr::Seek(SwFont& rFnt,
}
nWhich = aIter.NextWhich();
}
-
- ++nRet;
}
- break;
+ else
+ {
+ break;
+ }
}
- m_nStart = COMPLETE_STRING;
- m_nEnd = COMPLETE_STRING;
+ }
+
+ if (oFirstMatch)
+ {
+ m_bOn = true;
+ m_nAct = *oFirstMatch; // rewind
+ ++nRet; // increment only once per m_nStart/m_nEnd range
}
}
else if (m_eMode == Mode::Hide)
@@ -773,9 +927,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;
@@ -792,14 +946,15 @@ void SwRedlineItr::FillHints( std::size_t nAuthor, RedlineType eType )
switch ( eType )
{
case RedlineType::Insert:
- SW_MOD()->GetInsertAuthorAttr(nAuthor, *m_pSet);
+ SwModule::get()->GetInsertAuthorAttr(nAuthor, *m_pSet);
break;
case RedlineType::Delete:
- SW_MOD()->GetDeletedAuthorAttr(nAuthor, *m_pSet);
+ SwModule::get()->GetDeletedAuthorAttr(nAuthor, *m_pSet);
break;
case RedlineType::Format:
case RedlineType::FmtColl:
- SW_MOD()->GetFormatAuthorAttr(nAuthor, *m_pSet);
+ case RedlineType::ParagraphFormat:
+ SwModule::get()->GetFormatAuthorAttr(nAuthor, *m_pSet);
break;
default:
break;
@@ -837,7 +992,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 +1094,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 +1133,8 @@ bool SwRedlineItr::CheckLine(
eRedlineEnd = pRedline->GetType();
bRedlineEnd = true;
isBreak = true;
+ if (pAuthorAtPos)
+ *pAuthorAtPos = pRedline->GetAuthor();
[[fallthrough]];
case SwComparePosition::OverlapBefore:
case SwComparePosition::CollideEnd:
@@ -987,7 +1144,7 @@ bool SwRedlineItr::CheckLine(
// start to collect text of invisible redlines for ChangesInMargin layout
if (rRedlineText.isEmpty() && !pRedline->IsVisible())
{
- rRedlineText = const_cast<SwRangeRedline*>(pRedline)->GetDescr(/*bSimplified=*/true);
+ rRedlineText = pRedline->GetDescr(/*bSimplified=*/true);
pPrevRedline = pRedline;
isExtendText = true;
}
@@ -996,7 +1153,7 @@ bool SwRedlineItr::CheckLine(
else if (pPrevRedline && !pRedline->IsVisible() &&
*pRedline->Start() == *pPrevRedline->Start() && *pRedline->End() == *pPrevRedline->End() )
{
- OUString sExtendText(const_cast<SwRangeRedline*>(pRedline)->GetDescr(/*bSimplified=*/true));
+ OUString sExtendText(pRedline->GetDescr(/*bSimplified=*/true));
if (!sExtendText.isEmpty())
{
if (rRedlineText.getLength() < 12)
@@ -1004,7 +1161,7 @@ bool SwRedlineItr::CheckLine(
// TODO: remove extra space from GetDescr(true),
// but show deletion of paragraph or line break
rRedlineText = rRedlineText +
- const_cast<SwRangeRedline*>(pRedline)->GetDescr(/*bSimplified=*/true).subView(1);
+ pRedline->GetDescr(/*bSimplified=*/true).subView(1);
}
else
rRedlineText = OUString::Concat(rRedlineText.subView(0, rRedlineText.getLength() - 3)) + "...";
@@ -1032,6 +1189,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 +1211,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 +1227,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 +1252,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..681785bbbb0a 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;
@@ -45,8 +46,7 @@ using namespace ::com::sun::star;
* The width and height of the drop caps portion are passed as arguments,
* the position is calculated from the values in rInf
*/
-static bool lcl_IsDropFlyInter( const SwTextFormatInfo &rInf,
- sal_uInt16 nWidth, sal_uInt16 nHeight )
+static bool lcl_IsDropFlyInter(const SwTextFormatInfo& rInf, SwTwips nWidth, sal_uInt16 nHeight)
{
const SwTextFly& rTextFly = rInf.GetTextFly();
if( rTextFly.IsOn() )
@@ -102,9 +102,9 @@ SwDropPortionPart::~SwDropPortionPart()
/// SwDropPortion CTor, DTor
SwDropPortion::SwDropPortion( const sal_uInt16 nLineCnt,
- const sal_uInt16 nDrpHeight,
- const sal_uInt16 nDrpDescent,
- const sal_uInt16 nDist )
+ const SwTwips nDrpHeight,
+ const SwTwips nDrpDescent,
+ const SwTwips nDist )
: m_nLines( nLineCnt ),
m_nDropHeight(nDrpHeight),
m_nDropDescent(nDrpDescent),
@@ -303,8 +303,8 @@ void SwDropPortion::PaintText( const SwTextPaintInfo &rInf ) const
const SwDropPortionPart* pCurrPart = GetPart();
const TextFrameIndex nOldLen = GetLen();
- const sal_uInt16 nOldWidth = Width();
- const sal_uInt16 nOldAscent = GetAscent();
+ const SwTwips nOldWidth = Width();
+ const SwTwips nOldAscent = GetAscent();
const SwTwips nBasePosY = rInf.Y();
const_cast<SwTextPaintInfo&>(rInf).Y( nBasePosY + m_nY );
@@ -324,7 +324,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 );
@@ -352,9 +352,9 @@ void SwDropPortion::PaintDrop( const SwTextPaintInfo &rInf ) const
return;
// set the lying values
- const sal_uInt16 nOldHeight = Height();
- const sal_uInt16 nOldWidth = Width();
- const sal_uInt16 nOldAscent = GetAscent();
+ const SwTwips nOldHeight = Height();
+ const SwTwips nOldWidth = Width();
+ const SwTwips nOldAscent = GetAscent();
const SwTwips nOldPosY = rInf.Y();
const SwTwips nOldPosX = rInf.X();
const SwParaPortion *pPara = rInf.GetParaPortion();
@@ -397,7 +397,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
@@ -432,9 +432,9 @@ bool SwDropPortion::FormatText( SwTextFormatInfo &rInf )
return true;
}
-SwPosSize SwDropPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+SwPositiveSize SwDropPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
{
- sal_uInt16 nMyX = 0;
+ SwTwips nMyX = 0;
TextFrameIndex nIdx(0);
const SwDropPortionPart* pCurrPart = GetPart();
@@ -461,7 +461,7 @@ SwPosSize SwDropPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
// robust
SwFontSave aFontSave( rInf, pCurrPart ? &pCurrPart->GetFont() : nullptr );
- SwPosSize aPosSize( SwTextPortion::GetTextSize( rInf ) );
+ SwPositiveSize aPosSize( SwTextPortion::GetTextSize( rInf ) );
aPosSize.Width( aPosSize.Width() + nMyX );
const_cast<SwTextSizeInfo&>(rInf).SetIdx( nOldIdx );
@@ -475,7 +475,7 @@ SwPosSize SwDropPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
return aPosSize;
}
-TextFrameIndex SwDropPortion::GetModelPositionForViewPoint(const sal_uInt16) const
+TextFrameIndex SwDropPortion::GetModelPositionForViewPoint(const SwTwips) const
{
return TextFrameIndex(0);
}
@@ -483,9 +483,9 @@ TextFrameIndex SwDropPortion::GetModelPositionForViewPoint(const sal_uInt16) con
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 nDropHght = 0;
+ SwTwips nAscent = 0;
+ SwTwips nHeight = 0;
sal_uInt16 nDropLns = 0;
const bool bRegisterOld = IsRegisterOn();
m_bRegisterOn = false;
@@ -545,8 +545,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 +678,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() );
@@ -698,9 +698,9 @@ class SwDropCapCache
{
const void* m_aFontCacheId[ DROP_CACHE_SIZE ] = {};
OUString m_aText[ DROP_CACHE_SIZE ];
- sal_uInt16 m_aFactor[ DROP_CACHE_SIZE ];
- sal_uInt16 m_aWishedHeight[ DROP_CACHE_SIZE ] = {};
- short m_aDescent[ DROP_CACHE_SIZE ];
+ tools::Long m_aFactor[ DROP_CACHE_SIZE ];
+ SwTwips m_aWishedHeight[DROP_CACHE_SIZE] = {};
+ SwTwips m_aDescent[DROP_CACHE_SIZE];
sal_uInt16 m_nIndex = 0;
public:
SwDropCapCache() = default;
@@ -751,7 +751,7 @@ void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf
m_nIndex %= DROP_CACHE_SIZE;
nTmpIdx = m_nIndex;
- tools::Long nWishedHeight = pDrop->GetDropHeight();
+ SwTwips nWishedHeight = pDrop->GetDropHeight();
tools::Long nAscent = 0;
// find out biggest font size for initial scaling factor
@@ -773,15 +773,15 @@ void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf
// save keys for cache
m_aFontCacheId[ nTmpIdx ] = nFntCacheId;
m_aText[ nTmpIdx ] = aStr;
- m_aWishedHeight[ nTmpIdx ] = sal_uInt16(nWishedHeight);
+ m_aWishedHeight[ nTmpIdx ] = nWishedHeight;
// save initial scaling factor
- m_aFactor[ nTmpIdx ] = static_cast<sal_uInt16>(nFactor);
+ m_aFactor[ nTmpIdx ] = nFactor;
}
bool bGrow = (pDrop->GetLen() != TextFrameIndex(0));
// for growing control
- tools::Long nMax = USHRT_MAX;
+ tools::Long nMax = std::numeric_limits<tools::Long>::max();
tools::Long nMin = 0;
#if OSL_DEBUG_LEVEL > 1
tools::Long nGrow = 0;
@@ -793,10 +793,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 +860,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 +890,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 +938,7 @@ void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf
else
{
if ( bUseCache )
- m_aFactor[ nTmpIdx ] = static_cast<sal_uInt16>(nFactor);
+ m_aFactor[ nTmpIdx ] = nFactor;
nMin = nFactor;
}
@@ -982,7 +995,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 = rInf.X();
SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
aLayoutModeModifier.SetAuto();
@@ -995,7 +1008,7 @@ bool SwDropPortion::Format( SwTextFormatInfo &rInf )
// adjust font sizes to fit into the rectangle
pDropCapCache->CalcFontSize( this, rInf );
- const tools::Long nOldX = rInf.X();
+ const SwTwips nOldX = rInf.X();
{
SwDropSave aSave( rInf );
SwDropPortionPart* pCurrPart = m_pPart.get();
@@ -1020,7 +1033,7 @@ bool SwDropPortion::Format( SwTextFormatInfo &rInf )
Width();
// set values
- pCurrPart->SetWidth( static_cast<sal_uInt16>(nTmpWidth) );
+ pCurrPart->SetWidth(nTmpWidth);
// Move
rInf.SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
@@ -1029,7 +1042,7 @@ bool SwDropPortion::Format( SwTextFormatInfo &rInf )
}
SetJoinBorderWithNext(false);
SetJoinBorderWithPrev(false);
- Width( static_cast<sal_uInt16>(rInf.X() - nOldX) );
+ Width(rInf.X() - nOldX);
}
// reset my length
@@ -1065,10 +1078,10 @@ bool SwDropPortion::Format( SwTextFormatInfo &rInf )
m_nDistance = 0;
else
{
- const sal_uInt16 nWant = Width() + GetDistance();
- const sal_uInt16 nRest = static_cast<sal_uInt16>(rInf.Width() - rInf.X());
+ const SwTwips nWant = Width() + GetDistance();
+ const SwTwips nRest = rInf.Width() - rInf.X();
if( ( nWant > nRest ) ||
- lcl_IsDropFlyInter( rInf, Width() + GetDistance(), m_nDropHeight ) )
+ lcl_IsDropFlyInter( rInf, nWant, m_nDropHeight ) )
m_nDistance = 0;
Width( Width() + m_nDistance );
diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx
index e150f7f489f1..13100a23ed77 100644
--- a/sw/source/core/text/txtfld.cxx
+++ b/sw/source/core/text/txtfld.cxx
@@ -55,244 +55,153 @@
#include <svl/grabbagitem.hxx>
#include <svl/itemiter.hxx>
#include <svl/whiter.hxx>
+#include <editeng/cmapitem.hxx>
#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 );
+ SwDocStatField* pDocStatField = static_cast<SwDocStatField*>(pField);
+ sal_uInt16 nVirtPageCount = 0;
+ if (pDocStatField->GetSubType() == SwDocStatSubType::PageRange)
+ nVirtPageCount = m_pFrame->GetVirtPageCount();
+ pDocStatField->ChangeExpansion(m_pFrame, nVirtPageCount);
}
- 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();
+ auto pPageNumberField = static_cast<SwPageNumberField*>(pField);
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 == pPageNumberField->GetFormat())
+ nNumFormat
+ = m_pFrame->FindPageFrame()->GetPageDesc()->GetNumType().GetNumberingType();
+ pPageNumberField->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 )
+ if (!bName)
{
- SwDBField* pDBField = static_cast<SwDBField*>(pField);
- pDBField->ChgBodyTextFlag( ::lcl_IsInBody( pFrame ) );
- }
- {
- 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() )
+ if (!bName && pSh && !pSh->Imp()->IsUpdateExpFields())
{
- static_cast<SwRefPageGetField*>(pField)->ChangeExpansion(*pFrame,
- static_txtattr_cast<SwTextField const*>(pHint));
- }
- {
- 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();
+ {
+ auto pJumpEditField = static_cast<SwJumpEditField*>(pField);
+ 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(
+ &pJumpEditField->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), pJumpEditField->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() == ReferencesSubtype::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 +213,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 +315,12 @@ SwLinePortion *SwTextFormatter::NewExtraPortion( SwTextFormatInfo &rInf )
}
if( !pRet )
{
- pRet = new SwFieldPortion( "" );
+ auto pFieldPortion = new SwFieldPortion( u""_ustr );
+ if (pHint->Which() == RES_TXTATR_CONTENTCONTROL)
+ {
+ pFieldPortion->SetContentControl(true);
+ }
+ pRet = pFieldPortion;
rInf.SetLen(TextFrameIndex(1));
}
return pRet;
@@ -423,39 +338,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;
@@ -466,18 +351,20 @@ static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextForm
{
// Insert attributes of referenced char format into current set
const SwFormatCharFormat& rCharFormat = pCleanedSet->Get(RES_TXTATR_CHARFMT);
- const SwAttrSet& rStyleAttrs = static_cast<const SwCharFormat *>(rCharFormat.GetRegisteredIn())->GetAttrSet();
+ const SwAttrSet& rStyleAttrs = rCharFormat.GetCharFormat()->GetAttrSet();
SfxWhichIter aIter(rStyleAttrs);
sal_uInt16 nWhich = aIter.FirstWhich();
while (nWhich)
{
+ const SfxPoolItem* pItem = nullptr;
if (!SwTextNode::IsIgnoredCharFormatForNumbering(nWhich, /*bIsCharStyle=*/true)
&& !pCleanedSet->HasItem(nWhich)
- && !(pFormat && pFormat->HasItem(nWhich)) )
+ && !(pFormat && pFormat->HasItem(nWhich))
+ && rStyleAttrs.GetItemState(nWhich, true, &pItem) > SfxItemState::DEFAULT)
{
// Copy from parent sets only allowed items which will not overwrite
// values explicitly defined in current set (pCleanedSet) or in pFormat
- if (const SfxPoolItem* pItem = rStyleAttrs.GetItem(nWhich, true))
+ if (pItem)
pCleanedSet->Put(*pItem);
}
nWhich = aIter.NextWhich();
@@ -503,8 +390,8 @@ static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextForm
if (pCleanedSet->HasItem(RES_CHRATR_GRABBAG))
{
SfxGrabBagItem aGrabBag = pCleanedSet->Get(RES_CHRATR_GRABBAG, /*bSrchInParent=*/false);
- std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
- auto aIterator = rMap.find("CharShadingMarker");
+ const std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
+ auto aIterator = rMap.find(u"CharShadingMarker"_ustr);
if (aIterator != rMap.end())
aIterator->second >>= bShadingWasImported;
}
@@ -517,6 +404,13 @@ static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextForm
pCleanedSet->ClearItem(pItem->Which());
}
}
+ else if (pItem->Which() == RES_CHRATR_CASEMAP)
+ {
+ SvxCaseMap eCaseMap = static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap();
+ // MS only knows about "all caps" and "small caps". Small caps is not set on numbering
+ if (eCaseMap == SvxCaseMap::SmallCaps)
+ pCleanedSet->ClearItem(pItem->Which());
+ }
pItem = aIter.NextItem();
};
@@ -530,63 +424,85 @@ 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
+ bool bDisplayMovedTextInGreen = officecfg::Office::Writer::Comparison::DisplayMovedTextInGreen::get();
+ if ( bDisplayMovedTextInGreen && 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);
+ SwModule::get()->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() );
+ SwModule::get()->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 +573,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,17 +631,45 @@ 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 aTextNow( 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();
+ // previous (outdated) text
+ OUString aOriginalText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame(), SwListRedlineType::ORIGTEXT) );
+
+ if ( !aTextNow.isEmpty() || !aOriginalText.isEmpty() )
+ {
+
+ bool bDisplayChangedParagraphNumbering = officecfg::Office::Writer::Comparison::DisplayChangedParagraphNumbering::get();
+ if (bDisplayChangedParagraphNumbering && aTextNow != aOriginalText && !aOriginalText.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.".
+ aTextNow = aTextNow + "[" + aOriginalText + "]"
+ + pTextNd->GetLabelFollowedBy().replaceAll("\t", " ");
+ }
+ else if (!aTextNow.isEmpty())
+ aTextNow += pTextNd->GetLabelFollowedBy();
+ }
}
+ else if (pTextNd->getIDocumentSettingAccess()->get(DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY)
+ || !aTextNow.isEmpty())
+ aTextNow += pTextNd->GetLabelFollowedBy();
// Not just an optimization ...
// A number portion without text will be assigned a width of 0.
// The succeeding text portion will flow into the BreakCut in the BreakLine,
// although we have rInf.GetLast()->GetFlyPortion()!
- if( !aText.isEmpty() )
+ if( !aTextNow.isEmpty() )
{
// Build a new numbering font basing on the current paragraph font:
@@ -748,12 +692,13 @@ 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(SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor());
// we do not allow a vertical font
pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() );
- pRet = new SwNumberPortion( aText, std::move(pNumFnt),
+ pRet = new SwNumberPortion( aTextNow, std::move(pNumFnt),
bLeft, bCenter, nMinDist,
bLabelAlignmentPosAndSpaceModeActive );
}
diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx
index a5fb1f6b6731..9c47224cdfc5 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)
@@ -175,13 +178,14 @@ SwRect SwContourCache::CalcBoundRect( const SwAnchoredObject* pAnchoredObj,
}
}
+ const SwFlyFrame* pFlyFrame = pAnchoredObj->DynCastFlyFrame();
+ const SwFrame* pLower = pFlyFrame
+ ? static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower() : nullptr;
if( bHandleContour &&
- ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr ||
- ( static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower() &&
- static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower()->IsNoTextFrame() ) ) )
+ ( !pFlyFrame || ( pLower && pLower->IsNoTextFrame() ) ) )
{
aRet = pAnchoredObj->GetObjRectWithSpaces();
- if( aRet.IsOver( rLine ) )
+ if( aRet.Overlaps( rLine ) )
{
if( !pContourCache )
pContourCache = new SwContourCache;
@@ -216,7 +220,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,29 +228,28 @@ 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()),
- pFormat->GetSurround().IsOutside(), false, pFrame->IsVertical() )
- };
+ CacheItem item{ pObj, // due to #37347 the Object must be entered only after GetContour()
+ std::make_unique<TextRanger>(
+ aPolyPolygon, pPolyPolygon ? &*pPolyPolygon : nullptr, 20,
+ o3tl::narrowing<sal_uInt16>(rLRSpace.ResolveLeft({})),
+ o3tl::narrowing<sal_uInt16>(rLRSpace.ResolveRight({})),
+ pFormat->GetSurround().IsOutside(), false, pFrame->IsVertical()) };
mvItems.insert(mvItems.begin(), std::move(item));
mvItems[0].mxTextRanger->SetUpper( rULSpace.GetUpper() );
mvItems[0].mxTextRanger->SetLower( rULSpace.GetLower() );
@@ -371,7 +374,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 +415,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 +438,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 +494,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 +502,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 +528,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 +579,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,26 +591,27 @@ 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
// and <SwAlignRect(..)> fly frame area
// #i47804# - consider transparent graphics
// and OLE objects.
+ const SwFrame* pLower = pFly->Lower();
bool bClipFlyArea =
( ( css::text::WrapTextMode_THROUGH == rSur.GetSurround() )
// #i68520#
? (pAnchoredObjTmp->GetDrawObj()->GetLayer() != nHellId)
: !rSur.IsContour() ) &&
!pFly->IsBackgroundTransparent() &&
- ( !pFly->Lower() ||
- !pFly->Lower()->IsNoTextFrame() ||
- !static_cast<const SwNoTextFrame*>(pFly->Lower())->IsTransparent() );
+ ( !pLower ||
+ !pLower->IsNoTextFrame() ||
+ !static_cast<const SwNoTextFrame*>(pLower)->IsTransparent() );
if ( bClipFlyArea )
{
// #i68520#
@@ -648,8 +657,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 +666,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 +679,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 +712,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 +763,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 +771,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 +897,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 +929,7 @@ SwAnchoredObjList* SwTextFly::InitAnchoredObjList()
!pAnchoredObj->ConsiderForTextWrap() ||
( mbIgnoreObjsInHeaderFooter && !bFooterHeader &&
pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() ) ||
- ( bAllowCompatWrap && !pAnchoredObj->GetFrameFormat().GetFollowTextFlow().GetValue() )
+ ( bAllowCompatWrap && !pAnchoredObj->GetFrameFormat()->GetFollowTextFlow().GetValue() )
)
{
continue;
@@ -928,13 +967,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 +990,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 +1008,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 +1029,71 @@ 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;
+ }
+
+ const SwFormatSurround& rSurround = pAnchoredObj->GetFrameFormat()->GetSurround();
+ if (rSurround.GetValue() == text::WrapTextMode_THROUGH)
+ {
+ // Wrap through has no influence on clearing breaks.
+ 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 +1118,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 +1132,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 +1169,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 +1197,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 +1224,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 +1277,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 +1315,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 +1343,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 +1417,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 acd1b1b4f5be..bea6d81e3b44 100644
--- a/sw/source/core/text/txtfrm.cxx
+++ b/sw/source/core/text/txtfrm.cxx
@@ -17,14 +17,17 @@
* 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>
#include <editeng/lspcitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/brushitem.hxx>
+#include <editeng/charhiddenitem.hxx>
#include <editeng/pgrditem.hxx>
-#include <unotools/configmgr.hxx>
+#include <comphelper/configuration.hxx>
#include <swmodule.hxx>
#include <SwSmartTagMgr.hxx>
#include <doc.hxx>
@@ -66,11 +69,15 @@
#include <fmtflcnt.hxx>
#include <fmtcntnt.hxx>
#include <numrule.hxx>
-#include <IGrammarContact.hxx>
+#include <GrammarContact.hxx>
#include <calbck.hxx>
#include <ftnidx.hxx>
#include <ftnfrm.hxx>
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <edtwin.hxx>
+#include <FrameControlsManager.hxx>
namespace sw {
@@ -281,15 +288,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
@@ -321,16 +328,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);
}
}
@@ -338,19 +345,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)))
@@ -379,8 +383,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);
@@ -398,10 +401,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;
@@ -709,31 +712,30 @@ 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 );
}
-SwDigitModeModifier::SwDigitModeModifier( const OutputDevice& rOutp, LanguageType eCurLang ) :
+SwDigitModeModifier::SwDigitModeModifier( const OutputDevice& rOutp, LanguageType eCurLang,
+ SvtCTLOptions::TextNumerals eCTLTextNumerals ) :
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();
-
- if ( SvtCTLOptions::NUMERALS_HINDI == nTextNumerals )
+ if ( SvtCTLOptions::NUMERALS_HINDI == eCTLTextNumerals )
eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
- else if ( SvtCTLOptions::NUMERALS_ARABIC == nTextNumerals )
+ else if ( SvtCTLOptions::NUMERALS_ARABIC == eCTLTextNumerals )
eLang = LANGUAGE_ENGLISH;
- else if ( SvtCTLOptions::NUMERALS_SYSTEM == nTextNumerals )
+ else if ( SvtCTLOptions::NUMERALS_SYSTEM == eCTLTextNumerals )
eLang = ::GetAppLanguage();
}
@@ -771,6 +773,8 @@ SwTextFrame::SwTextFrame(SwTextNode * const pNode, SwFrame* pSib,
, mnHeightOfLastLine( 0 )
, mnAdditionalFirstLineOffset( 0 )
, mnOffset( 0 )
+ , mnNoHyphOffset( COMPLETE_STRING )
+ , mnNoHyphEndZone( 0 )
, mnCacheIndex( USHRT_MAX )
, mbLocked( false )
, mbWidow( false )
@@ -791,6 +795,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,
@@ -809,13 +931,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 > 0 && rTextNode == (rFootnoteIdxs[ nPos ]->GetTextNode()))
--nPos;
- if (nPos || &rTextNode != &(rFootnoteIdxs[ nPos ]->GetTextNode()))
+ if (nPos || rTextNode != (rFootnoteIdxs[ nPos ]->GetTextNode()))
++nPos;
}
size_t iter(0);
@@ -878,6 +1000,12 @@ void SwTextFrame::DestroyImpl()
}
}
+ if (!GetDoc().IsInDtor())
+ {
+ if (SwView* pView = GetActiveView())
+ pView->GetEditWin().GetFrameControlsManager().RemoveControls(this);
+ }
+
SwContentFrame::DestroyImpl();
}
@@ -891,6 +1019,7 @@ namespace sw {
// 1. if real insert => correct nStart/nEnd for full nLen
// 2. if rl un-delete => do not correct nStart/nEnd but just include un-deleted
static TextFrameIndex UpdateMergedParaForInsert(MergedPara & rMerged,
+ sw::ParagraphBreakMode const eMode, SwScriptInfo *const pScriptInfo,
bool const isRealInsert,
SwTextNode const& rNode, sal_Int32 const nIndex, sal_Int32 const nLen)
{
@@ -1002,13 +1131,6 @@ static TextFrameIndex UpdateMergedParaForInsert(MergedPara & rMerged,
rMerged.extents.emplace(itInsert, const_cast<SwTextNode*>(&rNode), nIndex, nIndex + nLen);
text.insert(nTFIndex, rNode.GetText().subView(nIndex, nLen));
nInserted = nLen;
- if (rMerged.extents.size() == 1 // also if it was empty!
- || rMerged.pParaPropsNode->GetIndex() < rNode.GetIndex())
- { // text inserted after current para-props node
- rMerged.pParaPropsNode->RemoveFromListRLHidden();
- rMerged.pParaPropsNode = &const_cast<SwTextNode&>(rNode);
- rMerged.pParaPropsNode->AddToListRLHidden();
- }
// called from SwRangeRedline::InvalidateRange()
if (rNode.GetRedlineMergeFlag() == SwNode::Merge::Hidden)
{
@@ -1016,12 +1138,24 @@ static TextFrameIndex UpdateMergedParaForInsert(MergedPara & rMerged,
}
}
rMerged.mergedText = text.makeStringAndClear();
+ if ((!bInserted && rMerged.extents.size() == 1) // also if it was empty!
+ || rNode.GetIndex() <= rMerged.pParaPropsNode->GetIndex())
+ { // text inserted before current para-props node
+ SwTextNode *const pOldParaPropsNode{rMerged.pParaPropsNode};
+ FindParaPropsNodeIgnoreHidden(rMerged, eMode, pScriptInfo);
+ if (rMerged.pParaPropsNode != pOldParaPropsNode)
+ {
+ pOldParaPropsNode->RemoveFromListRLHidden();
+ rMerged.pParaPropsNode->AddToListRLHidden();
+ }
+ }
return TextFrameIndex(nInserted);
}
// 1. if real delete => correct nStart/nEnd for full nLen
// 2. if rl delete => do not correct nStart/nEnd but just exclude deleted
TextFrameIndex UpdateMergedParaForDelete(MergedPara & rMerged,
+ sw::ParagraphBreakMode const eMode, SwScriptInfo *const pScriptInfo,
bool const isRealDelete,
SwTextNode const& rNode, sal_Int32 nIndex, sal_Int32 const nLen)
{
@@ -1032,7 +1166,7 @@ TextFrameIndex UpdateMergedParaForDelete(MergedPara & rMerged,
sal_Int32 nToDelete(nLen);
sal_Int32 nDeleted(0);
size_t nFoundNode(0);
- size_t nErased(0);
+// size_t nErased(0);
auto it = rMerged.extents.begin();
for (; it != rMerged.extents.end(); )
{
@@ -1068,7 +1202,7 @@ TextFrameIndex UpdateMergedParaForDelete(MergedPara & rMerged,
bErase = nDeleteHere == it->nEnd - it->nStart;
if (bErase)
{
- ++nErased;
+// ++nErased;
assert(it->nStart == nIndex);
it = rMerged.extents.erase(it);
}
@@ -1134,21 +1268,23 @@ TextFrameIndex UpdateMergedParaForDelete(MergedPara & rMerged,
// can't do: might be last one in node was erased assert(nLen == 0 || rMerged.empty() || (it-1)->nEnd <= nIndex);
// note: if first node gets deleted then that must call DelFrames as
// pFirstNode is never updated
- if (nErased && nErased == nFoundNode)
+ rMerged.mergedText = text.makeStringAndClear();
+// could be all-hidden now so always check! if (nErased && nErased == nFoundNode)
{ // all visible text from node was erased
#if 1
if (rMerged.pParaPropsNode == &rNode)
{
- rMerged.pParaPropsNode->RemoveFromListRLHidden();
- rMerged.pParaPropsNode = rMerged.extents.empty()
- ? const_cast<SwTextNode*>(rMerged.pLastNode)
- : rMerged.extents.front().pNode;
- rMerged.pParaPropsNode->AddToListRLHidden();
+ SwTextNode *const pOldParaPropsNode{rMerged.pParaPropsNode};
+ FindParaPropsNodeIgnoreHidden(rMerged, eMode, pScriptInfo);
+ if (rMerged.pParaPropsNode != pOldParaPropsNode)
+ {
+ pOldParaPropsNode->RemoveFromListRLHidden();
+ rMerged.pParaPropsNode->AddToListRLHidden();
+ }
}
#endif
// NOPE must listen on all non-hidden nodes; particularly on pLastNode rMerged.listener.EndListening(&const_cast<SwTextNode&>(rNode));
}
- rMerged.mergedText = text.makeStringAndClear();
return TextFrameIndex(nDeleted);
}
@@ -1251,15 +1387,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);
}
@@ -1275,7 +1410,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
@@ -1333,11 +1468,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()
@@ -1349,6 +1484,19 @@ void SwTextFrame::ResetPreps()
}
}
+static auto FindCellFrame(SwFrame const* pLower) -> SwLayoutFrame const*
+{
+ while (pLower)
+ {
+ if (pLower->IsCellFrame())
+ {
+ return static_cast<SwLayoutFrame const*>(pLower);
+ }
+ pLower = pLower->GetUpper();
+ }
+ return nullptr;
+}
+
bool SwTextFrame::IsHiddenNow() const
{
SwFrameSwapper aSwapper( this, true );
@@ -1359,26 +1507,40 @@ bool SwTextFrame::IsHiddenNow() const
return true;
}
+ // TODO: what is the above check good for and can it be removed?
+ return IsHiddenNowImpl();
+}
+
+bool SwTextFrame::IsHiddenNowImpl() const
+{
+ if (SwContentFrame::IsHiddenNow())
+ return true;
+
bool bHiddenCharsHidePara(false);
bool bHiddenParaField(false);
if (m_pMergedPara)
{
TextFrameIndex nHiddenStart(COMPLETE_STRING);
TextFrameIndex nHiddenEnd(0);
+ bool hasHidden{false};
if (auto const pScriptInfo = GetScriptInfo())
{
- pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex(0),
+ hasHidden = pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex(0),
nHiddenStart, nHiddenEnd);
}
else // ParaPortion is created in Format, but this is called earlier
{
SwScriptInfo aInfo;
- aInfo.InitScriptInfo(*m_pMergedPara->pFirstNode, m_pMergedPara.get(), IsRightToLeft());
- aInfo.GetBoundsOfHiddenRange(TextFrameIndex(0),
+ aInfo.InitScriptInfoHidden(*m_pMergedPara->pFirstNode, m_pMergedPara.get());
+ hasHidden = aInfo.GetBoundsOfHiddenRange(TextFrameIndex(0),
nHiddenStart, nHiddenEnd);
}
- if (TextFrameIndex(0) == nHiddenStart &&
- TextFrameIndex(GetText().getLength()) <= nHiddenEnd)
+ if ((TextFrameIndex(0) == nHiddenStart
+ && TextFrameIndex(GetText().getLength()) <= nHiddenEnd)
+ // special case: GetBoundsOfHiddenRange doesn't assign!
+ // but it does return that there *is* something hidden, in case
+ // the frame is empty then the whole thing must be hidden
+ || (hasHidden && m_pMergedPara->mergedText.isEmpty()))
{
bHiddenCharsHidePara = true;
}
@@ -1412,6 +1574,46 @@ bool SwTextFrame::IsHiddenNow() const
bHiddenCharsHidePara = static_cast<SwTextNode const*>(SwFrame::GetDep())->HasHiddenCharAttribute( true );
bHiddenParaField = static_cast<SwTextNode const*>(SwFrame::GetDep())->IsHiddenByParaField();
}
+ if (bHiddenCharsHidePara && GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH))
+ {
+ // apparently in Word it's always the last para marker that determines hidden?
+ // even in case when they are merged by delete redline (it's obvious when they are merged by hidden-attribute
+ SwTextNode const*const pNode{ m_pMergedPara
+ ? m_pMergedPara->pLastNode
+ : static_cast<SwTextNode const*>(SwFrame::GetDep()) };
+ // Word ignores hidden formatting on the cell end marker
+ bool isLastInCell{false};
+ if (SwLayoutFrame const*const pCellFrame{FindCellFrame(this)})
+ {
+ SwContentFrame const* pNext{GetNextContentFrame()};
+ // skip frame in hidden section ("this" is *not* in hidden section!)
+ while (pNext && pNext->SwContentFrame::IsHiddenNow())
+ {
+ pNext = pNext->GetNextContentFrame();
+ }
+ isLastInCell = pNext == nullptr || !pCellFrame->IsAnLower(pNext);
+ }
+ if (!isLastInCell)
+ {
+ SwFormatAutoFormat const& rListAutoFormat{pNode->GetAttr(RES_PARATR_LIST_AUTOFMT)};
+ std::shared_ptr<SfxItemSet> const pSet{rListAutoFormat.GetStyleHandle()};
+ SvxCharHiddenItem const* pItem{pSet ? pSet->GetItemIfSet(RES_CHRATR_HIDDEN) : nullptr};
+ if (!pItem)
+ {
+ // don't use node's mpAttrSet, it doesn't apply to para marker
+ SwFormatColl const*const pStyle{pNode->GetFormatColl()};
+ if (pStyle)
+ {
+ pItem = &pStyle->GetFormatAttr(RES_CHRATR_HIDDEN);
+ }
+ }
+ if (!pItem || !pItem->GetValue())
+ {
+ bHiddenCharsHidePara = false;
+ }
+ }
+ }
const SwViewShell* pVsh = getRootFrame()->GetCurrShell();
if ( pVsh && ( bHiddenCharsHidePara || bHiddenParaField ) )
@@ -1424,6 +1626,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;
}
}
@@ -1477,7 +1692,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 );
@@ -1491,9 +1706,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;
@@ -1552,7 +1767,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 );
@@ -1586,13 +1801,18 @@ void SwTextFrame::HideAndShowObjects()
{
sal_Int32 nHiddenStart;
sal_Int32 nHiddenEnd;
- const SwPosition& rAnchor = pContact->GetContentAnchor();
+ const SwFormatAnchor& rAnchorFormat = pContact->GetAnchorFormat();
+ const SwNode* pNode = rAnchorFormat.GetAnchorNode();
+ // When the object was already removed from text, but the layout hasn't been
+ // updated yet, this can be nullptr:
+ if (!pNode)
+ continue;
SwScriptInfo::GetBoundsOfHiddenRange(
- *rAnchor.nNode.GetNode().GetTextNode(),
- rAnchor.nContent.GetIndex(), nHiddenStart, nHiddenEnd);
+ *pNode->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 );
}
@@ -1616,21 +1836,33 @@ void SwTextFrame::HideAndShowObjects()
}
}
+void SwLayoutFrame::HideAndShowObjects()
+{
+ for (SwFrame * pLower = Lower(); pLower; pLower = pLower->GetNext())
+ {
+ pLower->HideAndShowObjects();
+ }
+}
+
+void SwFrame::HideAndShowObjects()
+{
+}
+
/**
* Returns the first possible break point in the current line.
* This method is used in SwTextFrame::Format() to decide whether the previous
* 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++;
}
@@ -1639,7 +1871,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++;
}
@@ -1649,6 +1881,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
@@ -1699,7 +1933,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();
@@ -1725,7 +1959,7 @@ void SwTextFrame::CalcLineSpace()
return;
if( GetDrawObjs() ||
- GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace().IsAutoFirst())
+ GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent().IsAutoFirst())
{
Init();
return;
@@ -1792,48 +2026,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();
@@ -1920,13 +2154,14 @@ void UpdateMergedParaForMove(sw::MergedPara & rMerged,
for (auto const& it : deleted)
{
sal_Int32 const nStart(it.first - nSourceStart + nDestStart);
- TextFrameIndex const nDeleted = UpdateMergedParaForDelete(rMerged, false,
- rDestNode, nStart, it.second - it.first);
+ TextFrameIndex const nDeleted = UpdateMergedParaForDelete(rMerged,
+ rTextFrame.getRootFrame()->GetParagraphBreakMode(), rTextFrame.GetScriptInfo(),
+ false, rDestNode, nStart, it.second - it.first);
//FIXME asserts valid for join - but if called from split, the new node isn't there yet and it will be added later... assert(nDeleted);
// 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>);
@@ -1940,6 +2175,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;
@@ -1953,6 +2189,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
@@ -1965,27 +2202,81 @@ 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);
+ SwFormatChangeHint const * pFormatChangedHint(nullptr);
+ sw::AttrSetChangeHint const* pAttrSetChangeHint(nullptr);
+ sw::UpdateAttrHint const* pUpdateAttrHint(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 (auto const pHt = dynamic_cast<sw::MoveText const*>(&rHint))
+ else if (rHint.GetId() == SfxHintId::SwUpdateAttr)
{
- pMoveText = pHt;
+ pUpdateAttrHint = static_cast<const sw::UpdateAttrHint*>(&rHint);
}
- else if (auto const pHynt = dynamic_cast<sw::RedlineDelText const*>(&rHint))
+ else if (rHint.GetId() == SfxHintId::SwInsertText)
{
- pRedlineDelText = pHynt;
+ pInsertText = static_cast<const sw::InsertText*>(&rHint);
}
- else if (auto const pHnt = dynamic_cast<sw::RedlineUnDelText const*>(&rHint))
+ else if (rHint.GetId() == SfxHintId::SwDeleteText)
{
- pRedlineUnDelText = pHnt;
+ 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 (rHint.GetId() == SfxHintId::SwMoveText)
+ {
+ pMoveText = static_cast<sw::MoveText const*>(&rHint);
+ }
+ else if (rHint.GetId() == SfxHintId::SwRedlineDelText)
+ {
+ pRedlineDelText = static_cast<sw::RedlineDelText const*>(&rHint);
+ }
+ else if (rHint.GetId() == SfxHintId::SwRedlineUnDelText)
+ {
+ pRedlineUnDelText = static_cast<sw::RedlineUnDelText const*>(&rHint);
+ }
+ else if (rHint.GetId() == SfxHintId::SwFormatChange)
+ {
+ pFormatChangedHint = static_cast<const SwFormatChangeHint*>(&rHint);
+ }
+ else if (rHint.GetId() == SfxHintId::SwAttrSetChange)
+ {
+ pAttrSetChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
}
else
{
@@ -2000,7 +2291,7 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
SwTextNode const& rNode(static_cast<SwTextNode const&>(rModify));
// modifications concerning frame attributes are processed by the base class
- if( IsInRange( aFrameFormatSetRange, nWhich ) || RES_FMT_CHG == nWhich )
+ if( IsInRange( aFrameFormatSetRange, nWhich ) || pFormatChangedHint )
{
if (m_pMergedPara)
{ // ignore item set changes that don't apply
@@ -2013,8 +2304,8 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
return;
}
}
- SwContentFrame::SwClientNotify(rModify, sw::LegacyModifyHint(pOld, pNew));
- if( nWhich == RES_FMT_CHG && getRootFrame()->GetCurrShell() )
+ SwContentFrame::SwClientNotify(rModify, rHint);
+ if( pFormatChangedHint && getRootFrame()->GetCurrShell() )
{
// collection has changed
Prepare();
@@ -2058,7 +2349,9 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
sal_Int32 const nNLen = pRedlineDelText->nLen;
nPos = MapModelToView(&rNode, nNPos);
// update merged before doing anything else
- nLen = UpdateMergedParaForDelete(*m_pMergedPara, false, rNode, nNPos, nNLen);
+ nLen = UpdateMergedParaForDelete(*m_pMergedPara,
+ getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(),
+ false, rNode, nNPos, nNLen);
const sal_Int32 m = -nNLen;
if (nLen && IsIdxInside(nPos, nLen))
{
@@ -2080,7 +2373,9 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
sal_Int32 const nNPos = pRedlineUnDelText->nStart;
sal_Int32 const nNLen = pRedlineUnDelText->nLen;
nPos = MapModelToView(&rNode, nNPos);
- nLen = UpdateMergedParaForInsert(*m_pMergedPara, false, rNode, nNPos, nNLen);
+ nLen = UpdateMergedParaForInsert(*m_pMergedPara,
+ getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(),
+ false, rNode, nNPos, nNLen);
if (IsIdxInside(nPos, nLen))
{
if (!nLen)
@@ -2106,7 +2401,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,
@@ -2121,23 +2416,30 @@ 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,
+ getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(),
+ true, rNode, pInsertText->nPos, pInsertText->nLen);
}
if( IsIdxInside( nPos, nLen ) )
{
@@ -2150,107 +2452,351 @@ 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,
+ getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(),
+ 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);
- }
+ nLen = TextFrameIndex(pDeleteText->nLen);
+ }
+ const sal_Int32 m = -pDeleteText->nLen;
+ if ((!m_pMergedPara || nLen) && IsIdxInside(nPos, nLen))
+ {
+ if( !nLen )
+ InvalidateSize();
else
+ 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,
+ getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(),
+ 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 if (pAttrSetChangeHint)
+ {
+ InvalidateLineNum();
+
+ const SwAttrSet& rNewSet = *pAttrSetChangeHint->m_pNew->GetChgSet();
+ int nClear = 0;
+ sal_uInt16 nCount = rNewSet.Count();
+
+ if( const SwFormatFootnote* pItem = rNewSet.GetItemIfSet( RES_TXTATR_FTN, false ) )
+ {
+ nPos = MapModelToView(&rNode, pItem->GetTextFootnote()->GetStart());
+ if (IsIdxInside(nPos, TextFrameIndex(1)))
+ Prepare( PrepareHint::FootnoteInvalidation, pAttrSetChangeHint->m_pNew );
+ nClear = 0x01;
+ --nCount;
+ }
+
+ if( const SwFormatField* pItem = rNewSet.GetItemIfSet( RES_TXTATR_FIELD, false ) )
+ {
+ nPos = MapModelToView(&rNode, pItem->GetTextField()->GetStart());
+ if (IsIdxInside(nPos, TextFrameIndex(1)))
{
- 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>);
+ const SwFormatField* pOldItem = pAttrSetChangeHint->m_pOld ?
+ &(pAttrSetChangeHint->m_pOld->GetChgSet()->Get(RES_TXTATR_FIELD)) : nullptr;
+ if (SfxPoolItem::areSame( pItem, pOldItem ))
+ {
+ InvalidatePage();
+ SetCompletePaint();
+ }
+ else
+ InvalidateRange_(SwCharRange(nPos, TextFrameIndex(1)));
}
+ nClear |= 0x02;
+ --nCount;
}
- break;
- case RES_DEL_TXT:
+ bool bLineSpace = SfxItemState::SET == rNewSet.GetItemState(
+ RES_PARATR_LINESPACING, false ),
+ bRegister = SfxItemState::SET == rNewSet.GetItemState(
+ RES_PARATR_REGISTER, false );
+ if ( bLineSpace || bRegister )
{
- 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 (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
+ {
+ Prepare( bRegister ? PrepareHint::Register : PrepareHint::AdjustSizeWithoutFormatting );
+ CalcLineSpace();
+ InvalidateSize();
+ InvalidatePrt_();
+
+ // i#11859
+ // (1) Also invalidate next frame on next page/column.
+ // (2) Skip empty sections and hidden paragraphs
+ // Thus, use method <InvalidateNextPrtArea()>
+ InvalidateNextPrtArea();
+
+ SetCompletePaint();
}
- else
+ nClear |= 0x04;
+ if ( bLineSpace )
{
- nLen = TextFrameIndex(nNLen);
+ --nCount;
+ if ((!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
+ && IsInSct() && !GetPrev())
+ {
+ SwSectionFrame *pSect = FindSctFrame();
+ if( pSect->ContainsAny() == this )
+ pSect->InvalidatePrt();
+ }
}
- const sal_Int32 m = -nNLen;
- if ((!m_pMergedPara || nLen) && IsIdxInside(nPos, nLen))
+ if ( bRegister )
+ --nCount;
+ }
+ if ( SfxItemState::SET == rNewSet.GetItemState( RES_PARATR_SPLIT,
+ false ))
+ {
+ if (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
{
- if( !nLen )
- InvalidateSize();
- else
- InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), m );
+ if (GetPrev())
+ CheckKeep();
+ Prepare();
+ InvalidateSize();
}
- lcl_SetWrong( *this, rNode, nNPos, m, true );
- if (nLen)
+ nClear |= 0x08;
+ --nCount;
+ }
+
+ if( SfxItemState::SET == rNewSet.GetItemState( RES_BACKGROUND, false)
+ && (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
+ && !IsFollow() && GetDrawObjs() )
+ {
+ SwSortedObjs *pObjs = GetDrawObjs();
+ for ( size_t i = 0; GetDrawObjs() && i < pObjs->size(); ++i )
{
- lcl_SetScriptInval( *this, nPos );
- bSetFieldsDirty = bRecalcFootnoteFlag = true;
- lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if( !pFly->IsFlyInContentFrame() )
+ {
+ const SvxBrushItem &rBack =
+ pFly->GetAttrSet()->GetBackground();
+ // #GetTransChg#
+ // following condition determines, if the fly frame
+ // "inherites" the background color of text frame.
+ // This is the case, if fly frame background
+ // color is "no fill"/"auto fill" and if the fly frame
+ // has no background graphic.
+ // Thus, check complete fly frame background
+ // color and *not* only its transparency value
+ if ( (rBack.GetColor() == COL_TRANSPARENT) &&
+ rBack.GetGraphicPos() == GPOS_NONE )
+ {
+ pFly->SetCompletePaint();
+ pFly->InvalidatePage();
+ }
+ }
+ }
}
}
- break;
- case RES_UPDATE_ATTR:
+
+ if ( SfxItemState::SET ==
+ rNewSet.GetItemState( RES_TXTATR_CHARFMT, false ) )
+ {
+ lcl_SetWrong( *this, rNode, 0, COMPLETE_STRING, false );
+ lcl_SetScriptInval( *this, TextFrameIndex(0) );
+ }
+ else if ( SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_LANGUAGE, false ) ||
+ SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_CJK_LANGUAGE, false ) ||
+ SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_CTL_LANGUAGE, false ) )
+ lcl_SetWrong( *this, rNode, 0, COMPLETE_STRING, false );
+ else if ( SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_FONT, false ) ||
+ SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_CJK_FONT, false ) ||
+ SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_CTL_FONT, false ) )
+ lcl_SetScriptInval( *this, TextFrameIndex(0) );
+ else if ( SfxItemState::SET ==
+ rNewSet.GetItemState( RES_FRAMEDIR, false )
+ && (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify))
{
- const SwUpdateAttr* pNewUpdate = static_cast<const SwUpdateAttr*>(pNew);
+ SetDerivedR2L( false );
+ CheckDirChange();
+ // Force complete paint due to existing indents.
+ SetCompletePaint();
+ }
- sal_Int32 const nNPos = pNewUpdate->getStart();
- sal_Int32 const nNLen = pNewUpdate->getEnd() - nNPos;
- nPos = MapModelToView(&rNode, nNPos);
- nLen = MapModelToView(&rNode, nNPos + nNLen) - nPos;
- if( IsIdxInside( nPos, nLen ) )
+ if( nCount )
+ {
+ if( getRootFrame()->GetCurrShell() )
{
- // We need to reformat anyways, even if the invalidated
- // range is empty.
- // E.g.: empty line, set 14 pt!
+ Prepare();
+ InvalidatePrt_();
+ }
- // FootnoteNumbers need to be formatted
- if( !nLen )
- nLen = TextFrameIndex(1);
+ if (nClear || (m_pMergedPara &&
+ (m_pMergedPara->pParaPropsNode != &rModify ||
+ m_pMergedPara->pFirstNode != &rModify)))
+ {
+ assert(pAttrSetChangeHint->m_pOld);
+ SwAttrSetChg aOldSet( *pAttrSetChangeHint->m_pOld );
+ SwAttrSetChg aNewSet( *pAttrSetChangeHint->m_pNew );
- InvalidateRange_( SwCharRange( nPos, nLen) );
- const sal_uInt16 nTmp = pNewUpdate->getWhichAttr();
+ if (m_pMergedPara && m_pMergedPara->pParaPropsNode != &rModify)
+ {
+ for (sal_uInt16 i = RES_PARATR_BEGIN; i != RES_FRMATR_END; ++i)
+ {
+ if (i != RES_BREAK && i != RES_PAGEDESC)
+ {
+ aOldSet.ClearItem(i);
+ aNewSet.ClearItem(i);
+ }
+ }
+ for (sal_uInt16 i = XATTR_FILL_FIRST; i <= XATTR_FILL_LAST; ++i)
+ {
+ aOldSet.ClearItem(i);
+ aNewSet.ClearItem(i);
+ }
+ }
+ if (m_pMergedPara && m_pMergedPara->pFirstNode != &rModify)
+ {
+ aOldSet.ClearItem(RES_BREAK);
+ aNewSet.ClearItem(RES_BREAK);
+ aOldSet.ClearItem(RES_PAGEDESC);
+ aNewSet.ClearItem(RES_PAGEDESC);
+ }
- if( ! nTmp || RES_TXTATR_CHARFMT == nTmp || RES_TXTATR_INETFMT == nTmp || RES_TXTATR_AUTOFMT == nTmp ||
- RES_FMT_CHG == nTmp || RES_ATTRSET_CHG == nTmp )
+ if( 0x01 & nClear )
+ {
+ aOldSet.ClearItem( RES_TXTATR_FTN );
+ aNewSet.ClearItem( RES_TXTATR_FTN );
+ }
+ if( 0x02 & nClear )
+ {
+ aOldSet.ClearItem( RES_TXTATR_FIELD );
+ aNewSet.ClearItem( RES_TXTATR_FIELD );
+ }
+ if ( 0x04 & nClear )
+ {
+ if ( bLineSpace )
+ {
+ aOldSet.ClearItem( RES_PARATR_LINESPACING );
+ aNewSet.ClearItem( RES_PARATR_LINESPACING );
+ }
+ if ( bRegister )
+ {
+ aOldSet.ClearItem( RES_PARATR_REGISTER );
+ aNewSet.ClearItem( RES_PARATR_REGISTER );
+ }
+ }
+ if ( 0x08 & nClear )
+ {
+ aOldSet.ClearItem( RES_PARATR_SPLIT );
+ aNewSet.ClearItem( RES_PARATR_SPLIT );
+ }
+ if (aOldSet.Count() || aNewSet.Count())
{
- lcl_SetWrong( *this, rNode, nNPos, nNPos + nNLen, false );
- lcl_SetScriptInval( *this, nPos );
+ SwContentFrame::SwClientNotify(rModify, sw::AttrSetChangeHint(&aOldSet, &aNewSet));
}
}
+ else
+ SwContentFrame::SwClientNotify(rModify, rHint);
+ }
- if( isA11yRelevantAttribute( pNewUpdate->getWhichAttr() ) &&
- hasA11yRelevantAttribute( pNewUpdate->getFmtAttrs() ) )
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (isA11yRelevantAttribute(nWhich))
+ {
+ SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if ( pViewSh )
{
- SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
- if ( pViewSh )
- {
- pViewSh->InvalidateAccessibleParaAttrs( *this );
- }
+ pViewSh->InvalidateAccessibleParaAttrs( *this );
}
}
- break;
- case RES_OBJECTDYING:
+#endif
+ }
+ else if (rHint.GetId() == SfxHintId::SwObjectDying)
+ ; // do nothing
+ else if (pUpdateAttrHint)
+ {
+ const SwUpdateAttr* pNewUpdate = pUpdateAttrHint->m_pNew;
+
+ sal_Int32 const nNPos = pNewUpdate->getStart();
+ sal_Int32 const nNLen = pNewUpdate->getEnd() - nNPos;
+ nPos = MapModelToView(&rNode, nNPos);
+ nLen = MapModelToView(&rNode, nNPos + nNLen) - nPos;
+ if( IsIdxInside( nPos, nLen ) )
+ {
+ // We need to reformat anyways, even if the invalidated
+ // range is empty.
+ // E.g.: empty line, set 14 pt!
+
+ // FootnoteNumbers need to be formatted
+ if( !nLen )
+ nLen = TextFrameIndex(1);
+
+ InvalidateRange_( SwCharRange( nPos, nLen) );
+ const sal_uInt16 nTmp = pNewUpdate->getWhichAttr();
+
+ if( ! nTmp || RES_TXTATR_CHARFMT == nTmp || RES_TXTATR_INETFMT == nTmp || RES_TXTATR_AUTOFMT == nTmp ||
+ RES_UPDATEATTR_FMT_CHG == nTmp || RES_UPDATEATTR_ATTRSET_CHG == nTmp )
+ {
+ lcl_SetWrong( *this, rNode, nNPos, nNPos + nNLen, false );
+ lcl_SetScriptInval( *this, nPos );
+ }
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( isA11yRelevantAttribute( pNewUpdate->getWhichAttr() ) &&
+ hasA11yRelevantAttribute( pNewUpdate->getFmtAttrs() ) )
+ {
+ SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if ( pViewSh )
+ {
+ pViewSh->InvalidateAccessibleParaAttrs( *this );
+ }
+ }
+#endif
+ }
+ else switch (nWhich)
+ {
+ case RES_LINENUMBER:
+ {
+ assert(false); // should have been forwarded to SwContentFrame
+ InvalidateLineNum();
+ }
break;
case RES_PARATR_LINESPACING:
@@ -2282,7 +2828,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?
@@ -2305,7 +2851,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());
}
@@ -2317,261 +2863,6 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
break;
}
- case RES_ATTRSET_CHG:
- {
- 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 ))
- {
- nPos = MapModelToView(&rNode,
- static_cast<const SwFormatFootnote*>(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 ))
- {
- nPos = MapModelToView(&rNode,
- static_cast<const SwFormatField*>(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 )
- {
- InvalidatePage();
- SetCompletePaint();
- }
- else
- InvalidateRange_(SwCharRange(nPos, TextFrameIndex(1)));
- }
- nClear |= 0x02;
- --nCount;
- }
- bool bLineSpace = SfxItemState::SET == rNewSet.GetItemState(
- RES_PARATR_LINESPACING, false ),
- bRegister = SfxItemState::SET == rNewSet.GetItemState(
- RES_PARATR_REGISTER, false );
- if ( bLineSpace || bRegister )
- {
- if (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
- {
- Prepare( bRegister ? PrepareHint::Register : PrepareHint::AdjustSizeWithoutFormatting );
- CalcLineSpace();
- InvalidateSize();
- InvalidatePrt_();
-
- // i#11859
- // (1) Also invalidate next frame on next page/column.
- // (2) Skip empty sections and hidden paragraphs
- // Thus, use method <InvalidateNextPrtArea()>
- InvalidateNextPrtArea();
-
- SetCompletePaint();
- }
- nClear |= 0x04;
- if ( bLineSpace )
- {
- --nCount;
- if ((!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
- && IsInSct() && !GetPrev())
- {
- SwSectionFrame *pSect = FindSctFrame();
- if( pSect->ContainsAny() == this )
- pSect->InvalidatePrt();
- }
- }
- if ( bRegister )
- --nCount;
- }
- if ( SfxItemState::SET == rNewSet.GetItemState( RES_PARATR_SPLIT,
- false ))
- {
- if (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
- {
- if (GetPrev())
- CheckKeep();
- Prepare();
- InvalidateSize();
- }
- nClear |= 0x08;
- --nCount;
- }
-
- if( SfxItemState::SET == rNewSet.GetItemState( RES_BACKGROUND, false)
- && (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
- && !IsFollow() && GetDrawObjs() )
- {
- SwSortedObjs *pObjs = GetDrawObjs();
- for ( size_t i = 0; GetDrawObjs() && i < pObjs->size(); ++i )
- {
- SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
- if ( auto pFly = dynamic_cast<SwFlyFrame *>( pAnchoredObj ) )
- {
- if( !pFly->IsFlyInContentFrame() )
- {
- const SvxBrushItem &rBack =
- pFly->GetAttrSet()->GetBackground();
- // #GetTransChg#
- // following condition determines, if the fly frame
- // "inherites" the background color of text frame.
- // This is the case, if fly frame background
- // color is "no fill"/"auto fill" and if the fly frame
- // has no background graphic.
- // Thus, check complete fly frame background
- // color and *not* only its transparency value
- if ( (rBack.GetColor() == COL_TRANSPARENT) &&
- rBack.GetGraphicPos() == GPOS_NONE )
- {
- pFly->SetCompletePaint();
- pFly->InvalidatePage();
- }
- }
- }
- }
- }
-
- if ( SfxItemState::SET ==
- rNewSet.GetItemState( RES_TXTATR_CHARFMT, false ) )
- {
- lcl_SetWrong( *this, rNode, 0, COMPLETE_STRING, false );
- lcl_SetScriptInval( *this, TextFrameIndex(0) );
- }
- else if ( SfxItemState::SET ==
- rNewSet.GetItemState( RES_CHRATR_LANGUAGE, false ) ||
- SfxItemState::SET ==
- rNewSet.GetItemState( RES_CHRATR_CJK_LANGUAGE, false ) ||
- SfxItemState::SET ==
- rNewSet.GetItemState( RES_CHRATR_CTL_LANGUAGE, false ) )
- lcl_SetWrong( *this, rNode, 0, COMPLETE_STRING, false );
- else if ( SfxItemState::SET ==
- rNewSet.GetItemState( RES_CHRATR_FONT, false ) ||
- SfxItemState::SET ==
- rNewSet.GetItemState( RES_CHRATR_CJK_FONT, false ) ||
- SfxItemState::SET ==
- rNewSet.GetItemState( RES_CHRATR_CTL_FONT, false ) )
- lcl_SetScriptInval( *this, TextFrameIndex(0) );
- else if ( SfxItemState::SET ==
- rNewSet.GetItemState( RES_FRAMEDIR, false )
- && (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify))
- {
- SetDerivedR2L( false );
- CheckDirChange();
- // Force complete paint due to existing indents.
- SetCompletePaint();
- }
-
- if( nCount )
- {
- if( getRootFrame()->GetCurrShell() )
- {
- Prepare();
- InvalidatePrt_();
- }
-
- if (nClear || (m_pMergedPara &&
- (m_pMergedPara->pParaPropsNode != &rModify ||
- m_pMergedPara->pFirstNode != &rModify)))
- {
- assert(pOld);
- SwAttrSetChg aOldSet( *static_cast<const SwAttrSetChg*>(pOld) );
- SwAttrSetChg aNewSet( *static_cast<const SwAttrSetChg*>(pNew) );
-
- if (m_pMergedPara && m_pMergedPara->pParaPropsNode != &rModify)
- {
- for (sal_uInt16 i = RES_PARATR_BEGIN; i != RES_FRMATR_END; ++i)
- {
- if (i != RES_BREAK && i != RES_PAGEDESC)
- {
- aOldSet.ClearItem(i);
- aNewSet.ClearItem(i);
- }
- }
- for (sal_uInt16 i = XATTR_FILL_FIRST; i <= XATTR_FILL_LAST; ++i)
- {
- aOldSet.ClearItem(i);
- aNewSet.ClearItem(i);
- }
- }
- if (m_pMergedPara && m_pMergedPara->pFirstNode != &rModify)
- {
- aOldSet.ClearItem(RES_BREAK);
- aNewSet.ClearItem(RES_BREAK);
- aOldSet.ClearItem(RES_PAGEDESC);
- aNewSet.ClearItem(RES_PAGEDESC);
- }
-
- if( 0x01 & nClear )
- {
- aOldSet.ClearItem( RES_TXTATR_FTN );
- aNewSet.ClearItem( RES_TXTATR_FTN );
- }
- if( 0x02 & nClear )
- {
- aOldSet.ClearItem( RES_TXTATR_FIELD );
- aNewSet.ClearItem( RES_TXTATR_FIELD );
- }
- if ( 0x04 & nClear )
- {
- if ( bLineSpace )
- {
- aOldSet.ClearItem( RES_PARATR_LINESPACING );
- aNewSet.ClearItem( RES_PARATR_LINESPACING );
- }
- if ( bRegister )
- {
- aOldSet.ClearItem( RES_PARATR_REGISTER );
- aNewSet.ClearItem( RES_PARATR_REGISTER );
- }
- }
- if ( 0x08 & nClear )
- {
- aOldSet.ClearItem( RES_PARATR_SPLIT );
- aNewSet.ClearItem( RES_PARATR_SPLIT );
- }
- if (aOldSet.Count() || aNewSet.Count())
- {
- SwContentFrame::SwClientNotify(rModify, sw::LegacyModifyHint(&aOldSet, &aNewSet));
- }
- }
- else
- SwContentFrame::SwClientNotify(rModify, sw::LegacyModifyHint(pOld, pNew));
- }
-
- if (isA11yRelevantAttribute(nWhich))
- {
- SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
- if ( pViewSh )
- {
- pViewSh->InvalidateAccessibleParaAttrs( *this );
- }
- }
- }
- 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();
@@ -2598,38 +2889,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");
@@ -2765,14 +3030,24 @@ 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" );
- if ( bNotify )
- InvalidateSize();
- else
- InvalidateSize_();
+ // check while ignoring frame width (testParagraphMarkInCell)
+ // because it's called from InvalidateAllContent()
+ if (!IsHiddenNowImpl())
+ {
+ SetInvalidVert( true ); // Test
+ if ( bNotify )
+ InvalidateSize();
+ else
+ InvalidateSize_();
+ }
return bParaPossiblyInvalid;
}
@@ -2943,7 +3218,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;
@@ -2958,7 +3233,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();
+ }
}
}
}
@@ -3200,7 +3481,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);
}
/**
@@ -3215,7 +3496,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" );
@@ -3298,7 +3579,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
@@ -3312,7 +3593,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" );
@@ -3320,11 +3601,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());
+ SwTwips nRet = getFramePrintArea().SSize().Height();
if( IsUndersized() )
{
if( IsEmpty() || GetText().isEmpty() )
- nRet = static_cast<sal_uInt16>(EmptyHeight());
+ nRet = EmptyHeight();
else
++nRet;
}
@@ -3333,7 +3614,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
@@ -3442,7 +3723,26 @@ SwTwips SwTextFrame::CalcFitToContent()
SetPara( pOldPara );
- return nMax;
+ // tdf#164932 handle numbering list offset
+ const SwTextNode* pTextNode( GetTextNodeForParaProps() );
+ SwTwips nNumOffset = 0;
+ if ( pTextNode->IsNumbered(getRootFrame()) &&
+ pTextNode->IsCountedInList() && pTextNode->GetNumRule() )
+ {
+ sal_uInt16 nListLevel = std::clamp(pTextNode->GetActualListLevel(), 0, MAXLEVEL - 1);
+ const SwNumFormat& rNumFormat = pTextNode->GetNumRule()->Get(nListLevel);
+ if ( rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ const SwAttrSet& rSet = pTextNode->GetSwAttrSet();
+ ::sw::ListLevelIndents const indents(pTextNode->AreListLevelIndentsApplicable());
+ SvxTextLeftMarginItem leftMargin(rSet.GetTextLeftMargin());
+ if (indents & ::sw::ListLevelIndents::LeftMargin)
+ leftMargin.SetTextLeft(SvxIndentValue::twips(rNumFormat.GetAbsLSpace()));
+ nNumOffset = leftMargin.ResolveTextLeft(/*metrics*/ {});
+ }
+ }
+
+ return nMax + nNumOffset;
}
/**
@@ -3466,16 +3766,8 @@ void SwTextFrame::CalcAdditionalFirstLineOffset()
pTextNode->IsCountedInList() && pTextNode->GetNumRule()))
return;
- int nListLevel = pTextNode->GetActualListLevel();
-
- if (nListLevel < 0)
- nListLevel = 0;
-
- if (nListLevel >= MAXLEVEL)
- nListLevel = MAXLEVEL - 1;
-
- const SwNumFormat& rNumFormat =
- pTextNode->GetNumRule()->Get( static_cast<sal_uInt16>(nListLevel) );
+ sal_uInt16 nListLevel = std::clamp(pTextNode->GetActualListLevel(), 0, MAXLEVEL - 1);
+ const SwNumFormat& rNumFormat = pTextNode->GetNumRule()->Get(nListLevel);
if ( rNumFormat.GetPositionAndSpaceMode() != SvxNumberFormat::LABEL_ALIGNMENT )
return;
@@ -3717,24 +4009,38 @@ tools::Long SwTextFrame::GetLineSpace( const bool _bNoPropLineSpace ) const
return nRet;
}
-sal_uInt16 SwTextFrame::FirstLineHeight() const
+SwTwips SwTextFrame::FirstLineHeight() const
{
if ( !HasPara() )
{
if( IsEmpty() && isFrameAreaDefinitionValid() )
- return IsVertical() ? static_cast<sal_uInt16>(getFramePrintArea().Width()) : static_cast<sal_uInt16>(getFramePrintArea().Height());
- return USHRT_MAX;
+ return IsVertical() ? getFramePrintArea().Width() : getFramePrintArea().Height();
+ return std::numeric_limits<SwTwips>::max();
}
const SwParaPortion *pPara = GetPara();
if ( !pPara )
- return USHRT_MAX;
-
- return pPara->Height();
+ return std::numeric_limits<SwTwips>::max();
+
+ // 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.
+ SwTwips 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
{
@@ -3756,7 +4062,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() )
{
@@ -3816,9 +4122,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() )
@@ -3882,7 +4188,7 @@ void SwTextFrame::VisitPortions( SwPortionHandler& rPH ) const
pPor = pPor->GetNextPortion();
}
- rPH.LineBreak(pLine->Width());
+ rPH.LineBreak();
pLine = pLine->GetNext();
}
}
@@ -3896,6 +4202,12 @@ const SwScriptInfo* SwTextFrame::GetScriptInfo() const
return pPara ? &pPara->GetScriptInfo() : nullptr;
}
+SwScriptInfo* SwTextFrame::GetScriptInfo()
+{
+ SwParaPortion* pPara = GetPara();
+ return pPara ? &pPara->GetScriptInfo() : nullptr;
+}
+
/**
* Helper function for SwTextFrame::CalcBasePosForFly()
*/
@@ -4024,4 +4336,15 @@ void SwTextFrame::repaintTextFrames( const SwTextNode& rNode )
}
}
+void SwTextFrame::UpdateOutlineContentVisibilityButton(SwWrtShell* pWrtSh) const
+{
+ if (pWrtSh && pWrtSh->GetViewOptions()->IsShowOutlineContentVisibilityButton() &&
+ GetTextNodeFirst()->IsOutline())
+ {
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+ rMngr.SetOutlineContentVisibilityButton(this);
+ }
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtftn.cxx b/sw/source/core/text/txtftn.cxx
index 5b4b9f7492e0..cea62efffb6a 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>
@@ -31,6 +32,7 @@
#include <txtftn.hxx>
#include <flyfrm.hxx>
#include <fmtftn.hxx>
+#include <fmtsrnd.hxx>
#include <ftninfo.hxx>
#include <charfmt.hxx>
#include <rowfrm.hxx>
@@ -53,6 +55,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>
@@ -232,15 +241,21 @@ static SwTwips lcl_GetFootnoteLower( const SwTextFrame* pFrame, SwTwips nLower )
SwTwips nFlyLower = aRectFnSet.IsVert() ? LONG_MAX : 0;
while ( pStartFrame != pFrame )
{
- OSL_ENSURE( pStartFrame, "Frame chain is broken" );
+ assert(pStartFrame && "Frame chain is broken");
if ( pStartFrame->GetDrawObjs() )
{
const SwSortedObjs &rObjs = *pStartFrame->GetDrawObjs();
for (SwAnchoredObject* pAnchoredObj : rObjs)
{
+ if (pAnchoredObj->GetFrameFormat()->GetSurround().GetSurround()
+ == text::WrapTextMode_THROUGH)
+ {
+ continue; // tdf#161718 no effect on text flow, skip
+ }
+
SwRect aRect( pAnchoredObj->GetObjRect() );
- auto pFlyFrame = dynamic_cast<SwFlyFrame*>( pAnchoredObj );
+ auto pFlyFrame = pAnchoredObj->DynCastFlyFrame();
if ( !pFlyFrame ||
pFlyFrame->isFrameAreaDefinitionValid() )
{
@@ -288,7 +303,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 );
}
@@ -336,8 +351,9 @@ SwTwips SwTextFrame::GetFootnoteFrameHeight_() const
const SwLayoutFrame* pTmp = GetUpper();
while( !bInvalidPos && pTmp )
{
+ const SwFrame* pLower = pTmp->Lower();
bInvalidPos = !pTmp->isFrameAreaPositionValid() ||
- !pTmp->Lower()->isFrameAreaPositionValid();
+ !pLower || !pLower->isFrameAreaPositionValid();
if( pTmp == pCont )
break;
pTmp = pTmp->GetUpper();
@@ -522,7 +538,7 @@ void SwTextFrame::RemoveFootnote(TextFrameIndex const nStart, TextFrameIndex con
else
{
if (!bEndDoc || ( bEndn && pEndBoss->IsInSct() &&
- !SwLayouter::Collecting( &GetDoc(),
+ !SwLayouter::Collecting( GetDoc(),
pEndBoss->FindSctFrame(), nullptr ) ))
{
if( bEndn )
@@ -590,8 +606,6 @@ void SwTextFrame::ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDea
mbFootnote = true;
mbInFootnoteConnect = true; // Just reset!
// See if pFootnote is an endnote on a separate endnote page.
- const IDocumentSettingAccess& rSettings = GetDoc().getIDocumentSettingAccess();
- const bool bContinuousEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
const bool bEnd = pFootnote->GetFootnote().IsEndNote();
// We want to store this value, because it is needed as a fallback
@@ -622,15 +636,11 @@ void SwTextFrame::ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDea
if( bDocEnd )
{
- if ((pSect || bContinuousEndnotes) && pSrcFrame)
+ if (pSect && pSrcFrame)
{
SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote );
- if (pFootnoteFrame && (pFootnoteFrame->IsInSct() || bContinuousEndnotes))
+ if (pFootnoteFrame && pFootnoteFrame->IsInSct())
{
- // We either have a foot/endnote that goes to the end of the section or are in Word
- // compatibility mode where endnotes go to the end of the document. Handle both
- // cases by removing the footnote here, then later appending them to the correct
- // last page of the document or section.
pBoss->RemoveFootnote( pSrcFrame, pFootnote );
pSrcFrame = nullptr;
}
@@ -641,22 +651,22 @@ void SwTextFrame::ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDea
SwFootnoteFrame *pFootnoteFrame = pSrcFrame ? SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote ) : nullptr;
if( pFootnoteFrame && !pFootnoteFrame->GetUpper() )
pFootnoteFrame = nullptr;
- SwDoc *const pDoc = &GetDoc();
- if( SwLayouter::Collecting( pDoc, pSect, pFootnoteFrame ) )
+ SwDoc& rDoc = GetDoc();
+ if( SwLayouter::Collecting( rDoc, pSect, pFootnoteFrame ) )
{
if( !pSrcFrame )
{
- SwFootnoteFrame *pNew = new SwFootnoteFrame(pDoc->GetDfltFrameFormat(),this,this,pFootnote);
+ SwFootnoteFrame *pNew = new SwFootnoteFrame(rDoc.GetDfltFrameFormat(),this,this,pFootnote);
SwNodeIndex aIdx( *pFootnote->GetStartNode(), 1 );
- ::InsertCnt_( pNew, pDoc, aIdx.GetIndex() );
- pDoc->getIDocumentLayoutAccess().GetLayouter()->CollectEndnote( pNew );
+ ::InsertCnt_( pNew, rDoc, aIdx.GetIndex() );
+ rDoc.getIDocumentLayoutAccess().GetLayouter()->CollectEndnote( pNew );
}
else if( pSrcFrame != this )
SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
mbInFootnoteConnect = false;
return;
}
- else if( pSrcFrame )
+ else if (pSrcFrame && pFootnoteFrame)
{
SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
if( !pFootnoteBoss->IsInSct() ||
@@ -788,10 +798,10 @@ SwFootnotePortion *SwTextFormatter::NewFootnotePortion( SwTextFormatInfo &rInf,
OSL_ENSURE( ! m_pFrame->IsVertical() || m_pFrame->IsSwapped(),
"NewFootnotePortion with unswapped frame" );
- SwTextFootnote *pFootnote = static_cast<SwTextFootnote*>(pHint);
+ if (!m_pFrame->IsFootnoteAllowed())
+ return new SwFootnotePortion(u""_ustr, nullptr);
- if( !m_pFrame->IsFootnoteAllowed() )
- return new SwFootnotePortion("", pFootnote);
+ SwTextFootnote *pFootnote = static_cast<SwTextFootnote*>(pHint);
const SwFormatFootnote& rFootnote = pFootnote->GetFootnote();
SwDoc *const pDoc = &m_pFrame->GetDoc();
@@ -801,11 +811,11 @@ SwFootnotePortion *SwTextFormatter::NewFootnotePortion( SwTextFormatInfo &rInf,
SwSwapIfSwapped swap(m_pFrame);
- sal_uInt16 nReal;
+ SwTwips nReal;
{
- sal_uInt16 nOldReal = m_pCurr->GetRealHeight();
- sal_uInt16 nOldAscent = m_pCurr->GetAscent();
- sal_uInt16 nOldHeight = m_pCurr->Height();
+ SwTwips nOldReal = m_pCurr->GetRealHeight();
+ SwTwips nOldAscent = m_pCurr->GetAscent();
+ SwTwips nOldHeight = m_pCurr->Height();
CalcRealHeight();
nReal = m_pCurr->GetRealHeight();
if( nReal < nOldReal )
@@ -964,16 +974,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(u"CharFontCharSet"_ustr);
sal_Int16 eCharSet;
if ((aAny >>= eCharSet) && eCharSet == awt::CharSet::SYMBOL)
{
OUString aFontName;
- aAny = xAnchorProps->getPropertyValue("CharFontName");
+ aAny = xAnchor->getPropertyValue(u"CharFontName"_ustr);
if (aAny >>= aFontName)
{
pNumFnt->SetName(aFontName, SwFontScript::Latin);
@@ -990,6 +999,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() )
+ SwModule::get()->GetDeletedAuthorAttr(aAuthor, aSet);
+ else
+ SwModule::get()->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;
@@ -997,7 +1036,7 @@ SwNumberPortion *SwTextFormatter::NewFootnoteNumPortion( SwTextFormatInfo const
static OUString lcl_GetPageNumber( const SwPageFrame* pPage )
{
- OSL_ENSURE( pPage, "GetPageNumber: Homeless TextFrame" );
+ assert(pPage && "GetPageNumber: Homeless TextFrame");
const sal_uInt16 nVirtNum = pPage->GetVirtPageNum();
const SvxNumberType& rNum = pPage->GetPageDesc()->GetNumType();
return rNum.GetNumStr( nVirtNum );
@@ -1083,7 +1122,7 @@ TextFrameIndex SwTextFormatter::FormatQuoVadis(TextFrameIndex const nOffset)
// position we want to insert the Quovadis text
// Let's see if it is that bad indeed:
SwLinePortion *pPor = m_pCurr->GetFirstPortion();
- sal_uInt16 nLastLeft = 0;
+ SwTwips nLastLeft = 0;
while( pPor )
{
if ( pPor->IsFlyPortion() )
@@ -1095,7 +1134,7 @@ TextFrameIndex SwTextFormatter::FormatQuoVadis(TextFrameIndex const nOffset)
// The old game all over again: we want the Line to wrap around
// at a certain point, so we adjust the width.
// nLastLeft is now basically the right margin
- const sal_uInt16 nOldRealWidth = rInf.RealWidth();
+ const SwTwips nOldRealWidth = rInf.RealWidth();
rInf.RealWidth( nOldRealWidth - nLastLeft );
OUString aErgo = lcl_GetPageNumber( pErgoFrame->FindPageFrame() );
@@ -1103,7 +1142,7 @@ TextFrameIndex SwTextFormatter::FormatQuoVadis(TextFrameIndex const nOffset)
pQuo->SetAscent( rInf.GetAscent() );
pQuo->Height( rInf.GetTextHeight() );
pQuo->Format( rInf );
- sal_uInt16 nQuoWidth = pQuo->Width();
+ SwTwips nQuoWidth = pQuo->Width();
SwLinePortion* pCurrPor = pQuo;
while ( rInf.GetRest() )
@@ -1181,12 +1220,12 @@ TextFrameIndex SwTextFormatter::FormatQuoVadis(TextFrameIndex const nOffset)
if( nDiff < 0 )
{
nLastLeft = pQuo->GetAscent();
- nQuoWidth = static_cast<sal_uInt16>(-nDiff + nLastLeft);
+ nQuoWidth = -nDiff + nLastLeft;
}
else
{
nQuoWidth = 0;
- nLastLeft = sal_uInt16(( pQuo->GetAscent() + nDiff ) / 2);
+ nLastLeft = (pQuo->GetAscent() + nDiff) / 2;
}
break;
}
@@ -1237,7 +1276,7 @@ TextFrameIndex SwTextFormatter::FormatQuoVadis(TextFrameIndex const nOffset)
*/
void SwTextFormatter::MakeDummyLine()
{
- sal_uInt16 nRstHeight = GetFrameRstHeight();
+ SwTwips nRstHeight = GetFrameRstHeight();
if( m_pCurr && nRstHeight > m_pCurr->Height() )
{
SwLineLayout *pLay = new SwLineLayout;
@@ -1313,17 +1352,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;
@@ -1341,8 +1377,7 @@ SwFootnoteSave::~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE
}
}
-SwFootnotePortion::SwFootnotePortion( const OUString &rExpand,
- SwTextFootnote *pFootn, sal_uInt16 nReal )
+SwFootnotePortion::SwFootnotePortion(const OUString& rExpand, SwTextFootnote* pFootn, SwTwips nReal)
: SwFieldPortion( rExpand, nullptr )
, m_pFootnote(pFootn)
, m_nOrigHeight( nReal )
@@ -1374,7 +1409,7 @@ bool SwFootnotePortion::Format( SwTextFormatInfo &rInf )
SetAscent( rInf.GetAscent() );
Height( rInf.GetTextHeight() );
rInf.SetFootnoteDone( !bFull );
- if( !bFull )
+ if (!bFull && m_pFootnote)
rInf.SetParaFootnote();
return bFull;
}
@@ -1388,7 +1423,7 @@ void SwFootnotePortion::Paint( const SwTextPaintInfo &rInf ) const
SwExpandPortion::Paint( rInf );
}
-SwPosSize SwFootnotePortion::GetTextSize( const SwTextSizeInfo &rInfo ) const
+SwPositiveSize SwFootnotePortion::GetTextSize( const SwTextSizeInfo &rInfo ) const
{
// #i98418#
// SwFootnoteSave aFootnoteSave( rInfo, pFootnote );
@@ -1408,8 +1443,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 );
@@ -1430,7 +1465,7 @@ bool SwQuoVadisPortion::Format( SwTextFormatInfo &rInf )
SetLen(TextFrameIndex(0));
if( bFull )
// Third try; we're done: we crush
- Width( sal_uInt16(rInf.Width() - rInf.X()) );
+ Width(rInf.Width() - rInf.X());
// No multiline Fields for QuoVadis and ErgoSum
if( rInf.GetRest() )
@@ -1486,7 +1521,7 @@ SwErgoSumPortion::SwErgoSumPortion(const OUString &rExp, std::u16string_view rSt
SetWhichPor( PortionType::ErgoSum );
}
-TextFrameIndex SwErgoSumPortion::GetModelPositionForViewPoint(const sal_uInt16) const
+TextFrameIndex SwErgoSumPortion::GetModelPositionForViewPoint(const SwTwips) const
{
return TextFrameIndex(0);
}
diff --git a/sw/source/core/text/txthyph.cxx b/sw/source/core/text/txthyph.cxx
index 7f9f6f6cd611..510654a74f82 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;
@@ -289,7 +288,7 @@ bool SwTextPortion::CreateHyphen( SwTextFormatInfo &rInf, SwTextGuess const &rGu
// length of pHyphPor is adjusted
pHyphPor->SetLen( TextFrameIndex(aAltText.getLength() + 1) );
- static_cast<SwPosSize&>(*pHyphPor) = pHyphPor->GetTextSize( rInf );
+ static_cast<SwPositiveSize&>(*pHyphPor) = pHyphPor->GetTextSize( rInf );
pHyphPor->SetLen( TextFrameIndex(aAltSpell.nChangedLength + nTmpLen) );
}
else
@@ -299,13 +298,13 @@ bool SwTextPortion::CreateHyphen( SwTextFormatInfo &rInf, SwTextGuess const &rGu
pHyphPor->SetLen(TextFrameIndex(1));
static const void* nLastFontCacheId = nullptr;
- static sal_uInt16 aMiniCacheH = 0, aMiniCacheW = 0;
+ static SwTwips aMiniCacheH = 0, aMiniCacheW = 0;
const void* nTmpFontCacheId;
sal_uInt16 nFntIdx;
rInf.GetFont()->GetFontCacheId( nTmpFontCacheId, nFntIdx, rInf.GetFont()->GetActual() );
if( !nLastFontCacheId || nLastFontCacheId != nTmpFontCacheId ) {
nLastFontCacheId = nTmpFontCacheId;
- static_cast<SwPosSize&>(*pHyphPor) = pHyphPor->GetTextSize( rInf );
+ static_cast<SwPositiveSize&>(*pHyphPor) = pHyphPor->GetTextSize( rInf );
aMiniCacheH = pHyphPor->Height();
aMiniCacheW = pHyphPor->Width();
} else {
@@ -325,8 +324,15 @@ bool SwTextPortion::CreateHyphen( SwTextFormatInfo &rInf, SwTextGuess const &rGu
( nPorEnd == rInf.GetIdx() && rInf.GetLineStart() != rInf.GetIdx() ) )
{
aInf.SetLen( nPorEnd - rInf.GetIdx() );
+ if (auto stClampedContext = aInf.GetLayoutContext(); stClampedContext.has_value())
+ {
+ stClampedContext->m_nEnd = nPorEnd.get();
+ aInf.SetLayoutContext(stClampedContext);
+ }
+
pHyphPor->SetAscent( GetAscent() );
SetLen( aInf.GetLen() );
+ SetLayoutContext(aInf.GetLayoutContext());
CalcTextSize( aInf );
Insert( pHyphPor.release() );
@@ -355,6 +361,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();
@@ -395,7 +411,7 @@ SwSoftHyphPortion::SwSoftHyphPortion() :
SetWhichPor( PortionType::SoftHyphen );
}
-sal_uInt16 SwSoftHyphPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+SwTwips SwSoftHyphPortion::GetViewWidth(const SwTextSizeInfo& rInf) const
{
// Although we're in the const, nViewWidth should be calculated at
// the last possible moment
@@ -422,10 +438,27 @@ sal_uInt16 SwSoftHyphPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
*/
void SwSoftHyphPortion::Paint( const SwTextPaintInfo &rInf ) const
{
- if( Width() )
+ if ( Width() )
{
rInf.DrawViewOpt( *this, PortionType::SoftHyphen );
SwExpandPortion::Paint( rInf );
+
+ if (rInf.GetOpt().IsViewMetaChars() && !rInf.GetOpt().IsPrinting()
+ && !rInf.GetOpt().IsPDFExport())
+ {
+ OUString aMarker = u"-"_ustr;
+ SwTextPaintInfo aInf(rInf, &aMarker);
+ SwTextPortion aMarkerPor;
+ SwPositiveSize aMarkerSize(rInf.GetTextSize(aMarker));
+ aMarkerPor.Width(aMarkerSize.Width());
+ aMarkerPor.Height(aMarkerSize.Height());
+ aMarkerPor.SetAscent(GetAscent());
+
+ Color colorBackup = aInf.GetFont()->GetColor();
+ aInf.GetFont()->SetColor(SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor());
+ aInf.DrawText(aMarkerPor, TextFrameIndex(aMarker.getLength()), true);
+ aInf.GetFont()->SetColor(colorBackup);
+ }
}
}
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/txtpaint.cxx b/sw/source/core/text/txtpaint.cxx
index ccd9647bd99c..5b05b413fab9 100644
--- a/sw/source/core/text/txtpaint.cxx
+++ b/sw/source/core/text/txtpaint.cxx
@@ -41,7 +41,6 @@ SwSaveClip::~SwSaveClip()
}
void SwSaveClip::ChgClip_( const SwRect &rRect, const SwTextFrame* pFrame,
- bool bEnlargeRect,
sal_Int32 nEnlargeTop,
sal_Int32 nEnlargeBottom )
{
@@ -74,12 +73,6 @@ void SwSaveClip::ChgClip_( const SwRect &rRect, const SwTextFrame* pFrame,
{
tools::Rectangle aRect( rRect.SVRect() );
- // Having underscores in our line, we enlarged the repaint area
- // (see frmform.cxx) because for some fonts it could be too small.
- // Consequently, we have to enlarge the clipping rectangle as well.
- if ( bEnlargeRect && ! bVertical )
- aRect.AdjustBottom(40 );
-
// enlarge clip for paragraph margins at small fixed line height
if ( nEnlargeTop > 0 )
aRect.AdjustTop( -nEnlargeTop );
diff --git a/sw/source/core/text/txtpaint.hxx b/sw/source/core/text/txtpaint.hxx
index 1f9700af4c96..3554a2421415 100644
--- a/sw/source/core/text/txtpaint.hxx
+++ b/sw/source/core/text/txtpaint.hxx
@@ -32,7 +32,6 @@ class SwSaveClip final
VclPtr<OutputDevice> m_pOut;
void ChgClip_( const SwRect &rRect, const SwTextFrame* pFrame,
- bool bEnlargeRect,
sal_Int32 nEnlargeTop,
sal_Int32 nEnlargeBottom );
public:
@@ -44,12 +43,14 @@ public:
}
~SwSaveClip();
- void ChgClip( const SwRect &rRect, const SwTextFrame* pFrame = nullptr,
- bool bEnlargeRect = false,
- sal_Int32 nEnlargeTop = 0,
- sal_Int32 nEnlargeBottom = 0)
- { if( m_pOut ) ChgClip_( rRect, pFrame,
- bEnlargeRect, nEnlargeTop, nEnlargeBottom ); }
+ void ChgClip(const SwRect& rRect, const SwTextFrame* pFrame = nullptr,
+ sal_Int32 nEnlargeTop = 0, sal_Int32 nEnlargeBottom = 0)
+ {
+ if (m_pOut)
+ {
+ ChgClip_(rRect, pFrame, nEnlargeTop, nEnlargeBottom);
+ }
+ }
bool IsOn() const { return m_bOn; }
bool IsChg() const { return m_bChg; }
};
diff --git a/sw/source/core/text/txttab.cxx b/sw/source/core/text/txttab.cxx
index 8e8203c72270..14d8b9072693 100644
--- a/sw/source/core/text/txttab.cxx
+++ b/sw/source/core/text/txttab.cxx
@@ -40,27 +40,40 @@
* 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 bTabOverMargin = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
+ 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()))
@@ -70,7 +83,7 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
sal_Unicode cDec = 0;
SvxTabAdjust eAdj;
- sal_uInt16 nNewTabPos;
+ SwTwips nNewTabPos;
bool bAutoTabStop = true;
{
const bool bRTL = m_pFrame->IsRightToLeft();
@@ -78,8 +91,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 +132,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 +144,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 || bTabOverMargin)
+ 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,17 +162,37 @@ 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;
+ // TODO: unclear if old Word has an upper limit for center/right
+ // tabs, UI allows setting 55.87cm max which is still one line
+ if (!bTabOverMargin || o3tl::toTwips(558, o3tl::Length::mm) < nNextPos)
+ {
+ nNextPos = rInf.Width();
+ }
+ }
bAutoTabStop = false;
}
else
{
- sal_uInt16 nDefTabDist = m_aLineInf.GetDefTabStop();
- if( USHRT_MAX == nDefTabDist )
+ SwTwips nDefTabDist = m_aLineInf.GetDefTabStop();
+ if (std::numeric_limits<SwTwips>::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 = rTab[0].GetTabPos();
else
nDefTabDist = SVX_TAB_DEFDIST;
m_aLineInf.SetDefTabStop( nDefTabDist );
@@ -247,9 +285,10 @@ 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);
+ nNewTabPos = nNextPos;
}
SwTabPortion *pTabPor = nullptr;
@@ -287,6 +326,8 @@ SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto
}
}
}
+ if (pTabPor)
+ rInf.UpdateTabSeen(pTabPor->GetWhichPor());
return pTabPor;
}
@@ -294,12 +335,12 @@ 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 )
+SwTabPortion::SwTabPortion( const SwTwips nTabPosition, const sal_Unicode cFillChar, const bool 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 )
@@ -309,7 +350,7 @@ bool SwTabPortion::Format( SwTextFormatInfo &rInf )
return PostFormat( rInf );
if( pLastTab )
pLastTab->PostFormat( rInf );
- return PreFormat( rInf );
+ return PreFormat(rInf, pLastTab);
}
void SwTabPortion::FormatEOL( SwTextFormatInfo &rInf )
@@ -318,35 +359,37 @@ void SwTabPortion::FormatEOL( SwTextFormatInfo &rInf )
PostFormat( rInf );
}
-bool SwTabPortion::PreFormat( SwTextFormatInfo &rInf )
+bool SwTabPortion::PreFormat(SwTextFormatInfo &rInf, SwTabPortion const*const pLastTab)
{
OSL_ENSURE( rInf.X() <= GetTabPos(), "SwTabPortion::PreFormat: rush hour" );
// Here we settle down ...
- SetFix( static_cast<sal_uInt16>(rInf.X()) );
+ SetFix(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 tools::Long nTextFrameWidth = rInf.GetTextFrame()->getFrameArea().Width();
// The minimal width of a tab is one blank at least.
// #i37686# In compatibility mode, the minimum width
// should be 1, even for non-left tab stops.
- sal_uInt16 nMinimumTabWidth = 1;
+ SwTwips nMinimumTabWidth = 1;
if ( !bTabCompat )
{
// #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 );
@@ -358,7 +401,8 @@ bool SwTabPortion::PreFormat( SwTextFormatInfo &rInf )
// 1. Minimal width does not fit to line anymore.
// 2. An underflow event was called for the tab portion.
bool bFull = ( bTabCompat && rInf.IsUnderflow() ) ||
- ( rInf.Width() <= rInf.X() + PrtWidth() && rInf.X() <= rInf.Width() ) ;
+ (rInf.Width() <= rInf.X() + PrtWidth() && rInf.X() <= rInf.Width()
+ && (!bTabOverMargin || !pLastTab));
// #95477# Rotated tab stops get the width of one blank
const Degree10 nDir = rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() );
@@ -381,13 +425,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 || rInf.Width() <= rInf.X()))
{
- 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(GetTabPos() - rInf.X());
bFull = rInf.Width() <= rInf.X() + PrtWidth();
// In tabulator compatibility mode, we reset the bFull flag
@@ -396,7 +450,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 +471,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(rInf.Width() - rInf.X());
SetFixWidth( PrtWidth() );
}
else
@@ -443,16 +497,22 @@ 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 SwTwips nRight
+ = bTabOverMargin
+ ? GetTabPos()
+ : bTabOverSpacing
+ ? std::min(GetTabPos(), rInf.GetTextFrame()->getFrameArea().Right())
+ : std::min(GetTabPos(), rInf.Width());
const SwLinePortion *pPor = GetNextPortion();
- sal_uInt16 nPorWidth = 0;
+ SwTwips nPorWidth = 0;
while( pPor )
{
nPorWidth = nPorWidth + pPor->Width();
@@ -462,7 +522,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;
}
@@ -470,10 +530,10 @@ bool SwTabPortion::PostFormat( SwTextFormatInfo &rInf )
// #127428# Abandon dec. tab position if line is full
if ( bTabCompat && PortionType::TabDecimal == nWhich )
{
- sal_uInt16 nPrePorWidth = static_cast<const SwTabDecimalPortion*>(this)->GetWidthOfPortionsUpToDecimalPosition();
+ SwTwips nPrePorWidth = static_cast<const SwTabDecimalPortion*>(this)->GetWidthOfPortionsUpToDecimalPosition();
// no value was set => no decimal character was found
- if ( USHRT_MAX != nPrePorWidth )
+ if (std::numeric_limits<SwTwips>::max() != nPrePorWidth)
{
if ( !bTabOverMargin && nPrePorWidth && nPorWidth - nPrePorWidth > rInf.Width() - nRight )
{
@@ -488,18 +548,18 @@ 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 )
+ SwTwips nNewWidth = nPorWidth / 2;
+ if (!bTabOverMargin && !bTabOverSpacing && nNewWidth > rInf.Width() - nRight)
nNewWidth = nPorWidth - (rInf.Width() - nRight);
nPorWidth = nNewWidth;
}
- const sal_uInt16 nDiffWidth = nRight - GetFix();
+ const SwTwips nDiffWidth = nRight - GetFix();
if( nDiffWidth > nPorWidth )
{
- const sal_uInt16 nOldWidth = GetFixWidth();
- const sal_uInt16 nAdjDiff = nDiffWidth - nPorWidth;
+ const SwTwips nOldWidth = GetFixWidth();
+ const SwTwips nAdjDiff = nDiffWidth - nPorWidth;
if( nAdjDiff > GetFixWidth() )
PrtWidth( nAdjDiff );
// Don't be afraid: we have to move rInf further.
@@ -524,7 +584,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 +596,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 +613,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 );
}
@@ -562,14 +622,14 @@ void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
if( rInf.GetFont()->IsPaintBlank() )
{
// Tabs with filling/filled tabs
- const sal_uInt16 nCharWidth = rInf.GetTextSize(OUString(' ')).Width();
+ const SwTwips nCharWidth = rInf.GetTextSize(OUString(' ')).Width();
// Robust:
if( nCharWidth )
{
// Always with kerning, also on printer!
- sal_uInt16 nChar = Width() / nCharWidth;
- OUStringBuffer aBuf;
+ sal_Int32 nChar = Width() / nCharWidth;
+ OUStringBuffer aBuf(nChar);
comphelper::string::padToLength(aBuf, nChar, ' ');
rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0),
TextFrameIndex(nChar), true);
@@ -581,17 +641,17 @@ void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
return;
// Tabs with filling/filled tabs
- const sal_uInt16 nCharWidth = rInf.GetTextSize(OUString(m_cFill)).Width();
+ const SwTwips nCharWidth = rInf.GetTextSize(OUString(m_cFill)).Width();
OSL_ENSURE( nCharWidth, "!SwTabPortion::Paint: sophisticated tabchar" );
// Robust:
if( nCharWidth )
{
// Always with kerning, also on printer!
- sal_uInt16 nChar = Width() / nCharWidth;
+ sal_Int32 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 +664,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..5379954d2a7f 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
@@ -63,7 +69,8 @@ SwTextFrameBreak::SwTextFrameBreak( SwTextFrame *pNewFrame, const SwTwips nRst )
if( !m_bKeep && m_pFrame->IsInSct() )
{
const SwSectionFrame* const pSct = m_pFrame->FindSctFrame();
- m_bKeep = pSct->Lower()->IsColumnFrame() && !pSct->MoveAllowed( m_pFrame );
+ if (const SwFrame* pLower = pSct->Lower())
+ m_bKeep = pLower->IsColumnFrame() && !pSct->MoveAllowed( m_pFrame );
}
m_bKeep = m_bKeep || !m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetSplit().GetValue() ||
m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep().GetValue();
@@ -99,7 +106,7 @@ SwTextFrameBreak::SwTextFrameBreak( SwTextFrame *pNewFrame, const SwTwips nRst )
* be done until the Follow is formatted. Unfortunately this is crucial
* to decide if the whole paragraph goes to the next page or not.
*/
-bool SwTextFrameBreak::IsInside( SwTextMargin const &rLine ) const
+bool SwTextFrameBreak::IsInside(SwTextMargin const& rLine, SwResizeLimitReason& reason) const
{
bool bFit = false;
@@ -127,15 +134,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())
{
@@ -161,7 +207,7 @@ bool SwTextFrameBreak::IsInside( SwTextMargin const &rLine ) const
// The LineHeight exceeds the current Frame height.
// Call a test Grow to detect if the Frame could
// grow the requested area.
- nHeight += m_pFrame->GrowTst( LONG_MAX );
+ nHeight += m_pFrame->GrowTst(LONG_MAX, reason);
// The Grow() returns the height by which the Upper of the TextFrame
// would let the TextFrame grow.
@@ -176,10 +222,11 @@ bool SwTextFrameBreak::IsInside( SwTextMargin const &rLine ) const
bool SwTextFrameBreak::IsBreakNow( SwTextMargin &rLine )
{
SwSwapIfSwapped swap(m_pFrame);
+ SwResizeLimitReason reason = SwResizeLimitReason::Unspecified;
// bKeep is stronger than IsBreakNow()
// Is there enough space ?
- if( m_bKeep || IsInside( rLine ) )
+ if (m_bKeep || IsInside(rLine, reason))
m_bBreak = false;
else
{
@@ -194,6 +241,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() ) )
{
@@ -206,6 +261,11 @@ bool SwTextFrameBreak::IsBreakNow( SwTextMargin &rLine )
if( !pTmp || !pTmp->Lower() )
m_bBreak = false;
}
+ else if (reason == SwResizeLimitReason::FixedSizeFrame)
+ {
+ // The content is in a clipping frame - no need to break at all
+ m_bBreak = false;
+ }
}
return m_bBreak;
@@ -267,7 +327,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:
@@ -364,7 +434,7 @@ bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
OSL_ENSURE( ! pFrame->IsVertical() || ! pFrame->IsSwapped(),
"WidowsAndOrphans::FindWidows with swapped frame" );
- if( !m_nWidLines || !pFrame->IsFollow() )
+ if( !pFrame->IsFollow() )
return false;
rLine.Bottom();
@@ -397,15 +467,156 @@ 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)
+ // hyphenation-keep-line: disable hyphenation in the last line instead of truncating it
+ // hyphenation-zone-always/page/column/spread: modify hyphenation in the last line (end zone)
+ int nExtraWidLines = 0;
+ if( rLine.GetLineNr() >= m_nWidLines && pMaster->HasPara() )
+ {
+ SwParaPortion *pMasterPara = pMaster->GetPara();
+ const SwAttrSet& rSet = pFrame->GetTextNodeForParaProps()->GetSwAttrSet();
+ const SvxHyphenZoneItem &rAttr = rSet.GetHyphenZone();
+
+ bool bKeep = rAttr.IsHyphen() && rAttr.IsKeep() && rAttr.GetKeepType();
+ bool bKeepLine = bKeep && rAttr.IsKeepLine();
+
+ // last line of a column inside a page
+ auto pMasterPage = pMaster->FindPageFrame();
+ auto pPage = pFrame->FindPageFrame();
+ // across column, but not page or spread, when both parts are there on the same page
+ bool bAcrossColumnNotPage = pMasterPage == pPage;
+ // across page, but not spread, when the parts are there not on the same page
+ bool bAcrossPageNotSpread = !bAcrossColumnNotPage &&
+ !pMasterPage->OnRightPage() && pPage->OnRightPage() &&
+ // linked text frames can be on a different spread, so check
+ // that the master is there on the previous page
+ pMasterPage == pPage->GetPrev();
+
+ // calculate end zones, based on their inheritance (0 means inheritance)
+ sal_Int16 nEndZoneParagraph = rAttr.GetTextHyphenZoneAlways() > 0
+ ? rAttr.GetTextHyphenZoneAlways()
+ : rAttr.GetTextHyphenZone();
+ sal_Int16 nEndZoneColumn = rAttr.GetTextHyphenZoneColumn() > 0
+ ? rAttr.GetTextHyphenZoneColumn()
+ : nEndZoneParagraph;
+ sal_Int16 nEndZonePage = rAttr.GetTextHyphenZonePage() > 0
+ ? rAttr.GetTextHyphenZonePage()
+ : nEndZoneColumn;
+ sal_Int16 nEndZoneSpread = rAttr.GetTextHyphenZoneSpread() > 0
+ ? rAttr.GetTextHyphenZoneSpread()
+ : nEndZonePage;
+
+ // set end zone
+ sal_Int16 nNoHyphEndZone = bAcrossColumnNotPage
+ ? nEndZoneColumn
+ : bAcrossPageNotSpread
+ ? nEndZonePage
+ : nEndZoneSpread;
+
+ // if PAGE or SPREAD, allow hyphenation in the not last column or in the
+ // not last linked frame on the same page
+ if( bKeep && bAcrossColumnNotPage && (
+ rAttr.GetKeepType() == css::text::ParagraphHyphenationKeepType::SPREAD ||
+ rAttr.GetKeepType() == css::text::ParagraphHyphenationKeepType::PAGE ) )
+ {
+ bKeep = false;
+ }
+
+ // if SPREAD, allow hyphenation at bottom of left page on the same spread
+ if ( bKeep && rAttr.GetKeepType() == css::text::ParagraphHyphenationKeepType::SPREAD &&
+ bAcrossPageNotSpread )
+ {
+ bKeep = false;
+ }
+
+ // remove remaining NoHyphOffset after enabling Hyphenate Across Spread (!bKeep) or
+ // enabling Move Line (!bKeepLine), or setting End Zone to no-limit,
+ // and invalidate the master to update the last line
+ if ( (!bKeep || !bKeepLine) &&
+ pMaster->GetNoHyphOffset() != TextFrameIndex(COMPLETE_STRING) )
+ {
+ pMaster->SetNoHyphOffset(TextFrameIndex(COMPLETE_STRING));
+ pMaster->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pMaster->InvalidateSize_();
+ }
+
+ // applied end zone, i.e. hyphenation is not disabled completely,
+ // end zone is not zero and different from the hyphenation zone
+ bool bApplyEndZone = !bKeep && nNoHyphEndZone > 0 &&
+ nNoHyphEndZone != rAttr.GetTextHyphenZone();
+ if ( ( bKeep || bApplyEndZone ) && pMasterPara && pMasterPara->GetNext() )
+ {
+ // calculate the beginning of last hyphenated line
+ TextFrameIndex nIdx(pMasterPara->GetLen());
+ SwLineLayout * pNext = pMasterPara->GetNext();
+ nIdx += pNext->GetLen();
+ SwLineLayout * pCurr = pNext;
+ SwLineLayout * pPrev = pNext;
+ while ( pNext->GetNext() )
+ {
+ pPrev = pCurr;
+ pCurr = pNext;
+ pNext = pNext->GetNext();
+ nIdx += pNext->GetLen();
+ }
+ nIdx -= pNext->GetLen();
+ // hyphenated line, but not the last remaining one
+ // in the case of shifting full line (bKeepLine = false)
+ if ( pNext->IsEndHyph() && ( bKeepLine || !pNext->IsLastHyph() || bApplyEndZone ) )
+ {
+ nExtraWidLines = rLine.GetLineNr() - m_nWidLines + 1;
+ // shift only a word: disable hyphenation in the line, if needed
+ if ( ( bKeepLine || bApplyEndZone ) && nExtraWidLines )
+ {
+ pMaster->SetNoHyphOffset(nIdx);
+ pMaster->SetNoHyphEndZone(bApplyEndZone ? nNoHyphEndZone : -1);
+ // update also columns and frames
+ pMaster->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pMaster->InvalidateSize_();
+ nExtraWidLines = 0; // no need to shift the full line
+ }
+ // shift full line:
+ // set remaining line to "last remaining hyphenated line"
+ // to avoid truncating multiple hyphenated lines instead
+ // of a single one
+ else if ( bKeep && !bKeepLine && 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 );
+ }
+
+ // update the old line with disabled hyphenation, i.e. when there is a line
+ // with disabled hyphenation, but it is not the last line any more
+ TextFrameIndex nNoHyphIdx = pMaster->GetNoHyphOffset();
+ if ( nNoHyphIdx != TextFrameIndex(COMPLETE_STRING) && nNoHyphIdx != nIdx )
+ {
+ // enable hyphenation for all the lines in the TextFrame again
+ pMaster->SetNoHyphOffset(TextFrameIndex(COMPLETE_STRING));
+ // update all the previous lines before the previous offset, e.g.
+ // when deleting all the lines before the last line with disabled hyphenation
+ // results a starting line with disabled hyphenation -> repaint it with enabled
+ // hyphenation again
+ pMaster->InvalidateRange_(SwCharRange(TextFrameIndex(0), nNoHyphIdx));
+ }
+ }
+ }
+
+ // no widow (e.g. in tables) and no hyphenation-keep
+ if( !m_nWidLines && !nExtraWidLines )
+ return 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 +626,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 +648,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 +672,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 +688,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 +759,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..bf3051df2298 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:
@@ -40,6 +40,7 @@ public:
void SetKeep( const bool bNew ) { m_bKeep = bNew; }
bool IsInside( SwTextMargin const &rLine ) const;
+ bool IsInside(SwTextMargin const& rLine, SwResizeLimitReason&) const;
// In order to be able to handle special cases with Footnote.
// SetRstHeight sets the rest height for SwTextFrameBreak. This is needed
@@ -63,7 +64,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 +80,15 @@ public:
}
};
+inline bool SwTextFrameBreak::IsInside(SwTextMargin const& rLine) const
+{
+ return IsInside(rLine, o3tl::temporary(SwResizeLimitReason()));
+}
+
+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..5a0ac3da9d93 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 );
}
}
@@ -848,6 +849,7 @@ sal_uInt16 WrongListIteratorCounter::GetElementCount()
InCurrentNode = 0;
pNode = rExtent.pNode;
}
+ assert(rExtent.pNode);
SwWrongList const*const pWrongList((rExtent.pNode->*m_pGetWrongList)());
for (; pWrongList && InCurrentNode < pWrongList->Count(); ++InCurrentNode)
{
@@ -892,6 +894,7 @@ WrongListIteratorCounter::GetElementAt(sal_uInt16 nIndex)
InCurrentNode = 0;
pNode = rExtent.pNode;
}
+ assert(rExtent.pNode);
SwWrongList const*const pWrongList((rExtent.pNode->*m_pGetWrongList)());
for (; pWrongList && InCurrentNode < pWrongList->Count(); ++InCurrentNode)
{
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: */