diff options
-rw-r--r-- | sw/qa/extras/ooxmlimport/ooxmlimport.cxx | 10 | ||||
-rw-r--r-- | sw/source/core/access/accportions.cxx | 3 | ||||
-rw-r--r-- | sw/source/core/inc/scriptinfo.hxx | 11 | ||||
-rw-r--r-- | sw/source/core/inc/txttypes.hxx | 1 | ||||
-rw-r--r-- | sw/source/core/text/inftxt.cxx | 23 | ||||
-rw-r--r-- | sw/source/core/text/inftxt.hxx | 3 | ||||
-rw-r--r-- | sw/source/core/text/itrform2.cxx | 51 | ||||
-rw-r--r-- | sw/source/core/text/porlay.cxx | 17 | ||||
-rw-r--r-- | sw/source/core/text/porlin.cxx | 4 | ||||
-rw-r--r-- | sw/source/core/text/porlin.hxx | 2 | ||||
-rw-r--r-- | sw/source/core/text/pormulti.cxx | 14 | ||||
-rw-r--r-- | sw/source/core/text/porrst.cxx | 122 | ||||
-rw-r--r-- | sw/source/core/text/porrst.hxx | 26 | ||||
-rw-r--r-- | sw/source/core/text/xmldump.cxx | 1 |
14 files changed, 262 insertions, 26 deletions
diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx index a9edd2b66eb1..fb6fbde35536 100644 --- a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx +++ b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx @@ -318,8 +318,14 @@ DECLARE_OOXMLIMPORT_TEST(testN758883, "n758883.docx") * The problem was that direct formatting of the paragraph was not applied * to the numbering. This is easier to test using a layout dump. */ - OUString aHeight = parseDump("/root/page/body/txt/Special", "nHeight"); - CPPUNIT_ASSERT_EQUAL(sal_Int32(220), aHeight.toInt32()); // It was 280 + xmlDocPtr pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "/root/page/body/txt/Special[1]", "nHeight", "220"); + + // check the bookmark portions are of the expected height + assertXPath(pXmlDoc, "/root/page/body/txt/Special[2]", "nType", "PortionType::Bookmark"); + assertXPath(pXmlDoc, "/root/page/body/txt/Special[2]", "nHeight", "253"); + assertXPath(pXmlDoc, "/root/page/body/txt/Special[3]", "nType", "PortionType::Bookmark"); + assertXPath(pXmlDoc, "/root/page/body/txt/Special[3]", "nHeight", "253"); /* * Next problem was that the page margin contained the width of the page border as well. diff --git a/sw/source/core/access/accportions.cxx b/sw/source/core/access/accportions.cxx index 00cabbe14eba..b929bc54b5ac 100644 --- a/sw/source/core/access/accportions.cxx +++ b/sw/source/core/access/accportions.cxx @@ -170,6 +170,9 @@ void SwAccessiblePortionData::Special( case PortionType::ControlChar: sDisplay = rText + OUStringChar(m_pTextFrame->GetText()[sal_Int32(m_nViewPosition)]); break; + case PortionType::Bookmark: + // TODO + break; default: sDisplay = rText; break; diff --git a/sw/source/core/inc/scriptinfo.hxx b/sw/source/core/inc/scriptinfo.hxx index 6d7ad0f4dfff..bb91dedea1a7 100644 --- a/sw/source/core/inc/scriptinfo.hxx +++ b/sw/source/core/inc/scriptinfo.hxx @@ -24,6 +24,7 @@ #include <deque> #include <unordered_set> #include <rtl/ustrbuf.hxx> +#include <o3tl/typed_flags_set.hxx> #include <i18nlangtag/lang.h> #include "TextFrameIndex.hxx" @@ -42,7 +43,7 @@ class SwScriptInfo { public: enum CompType { KANA, SPECIAL_LEFT, SPECIAL_RIGHT, NONE, SPECIAL_MIDDLE}; - enum class MarkKind { Start, End, Point }; + enum class MarkKind { Start = (1<<0), End = (1<<1), Point = (1<<2) }; private: //! Records a single change in script type. @@ -182,6 +183,7 @@ public: } TextFrameIndex NextHiddenChg(TextFrameIndex nPos) const; TextFrameIndex NextBookmark(TextFrameIndex nPos) const; + MarkKind GetBookmark(TextFrameIndex nPos) const; static void CalcHiddenRanges(const SwTextNode& rNode, MultiSelection& rHiddenMulti, std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> * pBookmarks); @@ -385,6 +387,13 @@ public: static SwFontScript WhichFont(sal_Int32 nIdx, OUString const & rText); }; +namespace o3tl +{ + +template<> struct typed_flags<SwScriptInfo::MarkKind> : is_typed_flags<SwScriptInfo::MarkKind, 0x07> {}; + +} + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/inc/txttypes.hxx b/sw/source/core/inc/txttypes.hxx index 2030fff25cc5..16f433e3b0b2 100644 --- a/sw/source/core/inc/txttypes.hxx +++ b/sw/source/core/inc/txttypes.hxx @@ -33,6 +33,7 @@ enum class PortionType Multi = 0x0085, HiddenText = 0x0086, ControlChar = 0x0087, + Bookmark = 0x0088, Text = 0x8000, Lay = 0x8001, diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx index 83e8bde03ad3..8538d6ebabd4 100644 --- a/sw/source/core/text/inftxt.cxx +++ b/sw/source/core/text/inftxt.cxx @@ -1375,6 +1375,14 @@ void SwTextPaintInfo::DrawViewOpt( const SwLinePortion &rPor, bDraw = true; } break; + case PortionType::Bookmark: + if (!GetOpt().IsPagePreview() + && !GetOpt().IsReadonly() + && GetOpt().IsViewMetaChars()) + { + bDraw = true; + } + break; case PortionType::InputField: // input field shading also in read-only mode if ( !GetOpt().IsPagePreview() @@ -1582,6 +1590,7 @@ void SwTextFormatInfo::Init() m_nForcedLeftMargin = 0; m_nSoftHyphPos = TextFrameIndex(0); m_nUnderScorePos = TextFrameIndex(COMPLETE_STRING); + m_nLastBookmarkPos = TextFrameIndex(-1); m_cHookChar = 0; SetIdx(TextFrameIndex(0)); SetLen(TextFrameIndex(GetText().getLength())); @@ -2007,4 +2016,18 @@ bool SwTextFormatInfo::ChgHyph( const bool bNew ) return bOld; } + +bool SwTextFormatInfo::CheckCurrentPosBookmark() +{ + if (m_nLastBookmarkPos != GetIdx()) + { + m_nLastBookmarkPos = GetIdx(); + return true; + } + else + { + return false; + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx index 30888843626d..63d030e896e9 100644 --- a/sw/source/core/text/inftxt.hxx +++ b/sw/source/core/text/inftxt.hxx @@ -477,6 +477,7 @@ 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 SwTwips m_nRight; // Right margin @@ -636,6 +637,8 @@ public: bool IsArrowDone() const { return m_bArrowDone; } void SetArrowDone( const bool bNew ) { m_bArrowDone = bNew; } + bool CheckCurrentPosBookmark(); + // For SwTextPortion::Hyphenate bool ChgHyph( const bool bNew ); diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index 3285f7f5ede9..6edf7e055184 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -691,6 +691,10 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf ) CalcAscent( rInf, pPor ); InsertPortion( rInf, pPor ); + if (pPor->IsMultiPortion() && (!m_pMulti || m_pMulti->IsBidi())) + { + (void) rInf.CheckCurrentPosBookmark(); // bookmark was already created inside MultiPortion! + } pPor = NewPortion( rInf ); } @@ -753,6 +757,19 @@ void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor ) pPor->Height( pLast->Height() ); pPor->SetAscent( pLast->GetAscent() ); } + else if (pPor->GetWhichPor() == PortionType::Bookmark + && rInf.GetIdx() == TextFrameIndex(rInf.GetText().getLength())) + { + // bookmark at end of paragraph: *don't* advance iterator, use the + // current font instead; it's possible that there's a font size on the + // paragraph and it's overridden on the last line of the paragraph and + // we don't want to apply it via SwBookmarkPortion and grow the line + // height (example: n758883.docx) + SwLinePortion const*const pLast = rInf.GetLast(); + assert(pLast); + pPor->Height( pLast->Height() ); + pPor->SetAscent( pLast->GetAscent() ); + } else { const SwLinePortion *pLast = rInf.GetLast(); @@ -1012,6 +1029,7 @@ SwTextPortion *SwTextFormatter::NewTextPortion( SwTextFormatInfo &rInf ) return pPor; } +// first portions have no length SwLinePortion *SwTextFormatter::WhichFirstPortion(SwTextFormatInfo &rInf) { SwLinePortion *pPor = nullptr; @@ -1144,6 +1162,39 @@ SwLinePortion *SwTextFormatter::WhichFirstPortion(SwTextFormatInfo &rInf) pPor = TryNewNoLengthPortion(rInf); } + // 12. bookmarks + // 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)) + { + 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(rInf.GetLast(), mark); + } + } + return pPor; } diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx index aa0bbf6860c3..9fa74a0d4833 100644 --- a/sw/source/core/text/porlay.cxx +++ b/sw/source/core/text/porlay.cxx @@ -1686,6 +1686,23 @@ TextFrameIndex SwScriptInfo::NextBookmark(TextFrameIndex const nPos) const return TextFrameIndex(COMPLETE_STRING); } +auto SwScriptInfo::GetBookmark(TextFrameIndex const nPos) const -> MarkKind +{ + MarkKind ret{0}; + for (auto const& it : m_Bookmarks) + { + if (nPos == it.first) + { + ret |= it.second; + } + else if (nPos < it.first) + { + break; + } + } + return ret; +} + // Takes a string and replaced the hidden ranges with cChar. sal_Int32 SwScriptInfo::MaskHiddenRanges( const SwTextNode& rNode, OUStringBuffer & rText, const sal_Int32 nStt, const sal_Int32 nEnd, diff --git a/sw/source/core/text/porlin.cxx b/sw/source/core/text/porlin.cxx index b5adccb07fba..3d5cb26aa4be 100644 --- a/sw/source/core/text/porlin.cxx +++ b/sw/source/core/text/porlin.cxx @@ -103,7 +103,9 @@ void SwLinePortion::PrePaint( const SwTextPaintInfo& rInf, 1800 : rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() ); - if (nLastWidth > nHalfView) + // pLast == this *only* for the 1st portion in the line so nLastWidth is 0; + // allow this too, will paint outside the frame but might look better... + if (nLastWidth > nHalfView || pLast == this) { switch (nDir) { diff --git a/sw/source/core/text/porlin.hxx b/sw/source/core/text/porlin.hxx index 3fe08cbacba4..ca7bc1a34fe1 100644 --- a/sw/source/core/text/porlin.hxx +++ b/sw/source/core/text/porlin.hxx @@ -133,7 +133,7 @@ public: bool IsArrowPortion() const { return nWhichPor == PortionType::Arrow; } bool IsMultiPortion() const { return nWhichPor == PortionType::Multi; } bool IsNumberPortion() const { return nWhichPor == PortionType::Number; } // #i23726# - bool IsControlCharPortion() const { return nWhichPor == PortionType::ControlChar; } + bool IsControlCharPortion() const { return nWhichPor == PortionType::ControlChar || nWhichPor == PortionType::Bookmark; } // Positioning SwLinePortion *FindPrevPortion( const SwLinePortion *pRoot ); diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx index 1ef0c04efd86..5e991cc4bed3 100644 --- a/sw/source/core/text/pormulti.cxx +++ b/sw/source/core/text/pormulti.cxx @@ -38,6 +38,7 @@ #include "itrform2.hxx" #include "porfld.hxx" #include "porglue.hxx" +#include "porrst.hxx" #include <pagefrm.hxx> #include <rowfrm.hxx> #include <tgrditem.hxx> @@ -2029,6 +2030,19 @@ bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf, aInf.SetNumDone( rInf.IsNumDone() ); aInf.SetFootnoteDone( rInf.IsFootnoteDone() ); + // 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 + // inserted between the end of WhichFirstPortion() and + // BuildMultiPortion() + if (rInf.GetLast()->GetWhichPor() == PortionType::Bookmark) + { + auto const pBookmark(static_cast<SwBookmarkPortion*>(rInf.GetLast())); + rInf.SetLast(pBookmark->Unchain()); + assert(m_pCurr->GetNextPortion() == nullptr); + m_pCurr->SetNextPortion(pBookmark); + } + if( pFirstRest ) { OSL_ENSURE( pFirstRest->InFieldGrp(), "BuildMulti: Fieldrest expected"); diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx index 686959609cd8..44d2288eaf9c 100644 --- a/sw/source/core/text/porrst.cxx +++ b/sw/source/core/text/porrst.cxx @@ -306,6 +306,17 @@ bool SwTextFrame::FormatEmpty() aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) ) return false; + // only need to check one node because of early return on GetMerged() + for (SwIndex 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) + { // need bookmark portions! + return false; + } + } + SwTwips nHeight = EmptyHeight(); if (aSet.GetParaGrid().GetValue() && @@ -488,35 +499,92 @@ bool SwHiddenTextPortion::Format( SwTextFormatInfo &rInf ) return false; }; +bool SwControlCharPortion::DoPaint(SwTextPaintInfo const&, + OUString & rOutString, SwFont & rTmpFont, int &) const +{ + if (mcChar == CHAR_ZWNBSP || !SwViewOption::IsFieldShadings()) + { + return false; + } + + switch (mcChar) + { + case CHAR_ZWSP: + rOutString = "/"; break; +// case CHAR_LRM : +// rText = sal_Unicode(0x2514); break; +// case CHAR_RLM : +// rText = sal_Unicode(0x2518); break; + default: + assert(false); + break; + } + + rTmpFont.SetEscapement( CHAR_ZWSP == mcChar ? DFLT_ESC_AUTO_SUB : -25 ); + const sal_uInt16 nProp = 40; + rTmpFont.SetProportion( nProp ); // a smaller font + + return true; +} + +bool SwBookmarkPortion::DoPaint(SwTextPaintInfo const& rInf, + OUString & rOutString, SwFont & rTmpFont, int & rDeltaY) const +{ + if (!rInf.GetOpt().IsViewMetaChars()) + { + return false; + } + + rOutString = OUStringChar(mcChar); + + // 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(rTmpFont.GetAscent(rInf.GetVsh(), *rInf.GetOut())); + rTmpFont.SetName("OpenSymbol", rTmpFont.GetActual()); + Size size(rTmpFont.GetSize(rTmpFont.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) / size.Height(); + rTmpFont.SetProportion(nFactor); + rTmpFont.SetWeight(WEIGHT_THIN, rTmpFont.GetActual()); + rTmpFont.SetColor(NON_PRINTING_CHARACTER_COLOR); + // reset these to default... + rTmpFont.SetAlign(ALIGN_BASELINE); + rTmpFont.SetUnderline(LINESTYLE_NONE); + rTmpFont.SetOverline(LINESTYLE_NONE); + rTmpFont.SetStrikeout(STRIKEOUT_NONE); + rTmpFont.SetOutline(false); + rTmpFont.SetShadow(false); + rTmpFont.SetTransparent(false); + rTmpFont.SetEmphasisMark(FontEmphasisMark::NONE); + rTmpFont.SetEscapement(0); + rTmpFont.SetPitch(PITCH_DONTKNOW, rTmpFont.GetActual()); + rTmpFont.SetRelief(FontRelief::NONE); + + // adjust Y position to account for different baselines of the fonts + auto const nOSAscent(rTmpFont.GetAscent(rInf.GetVsh(), *rInf.GetOut())); + rDeltaY = nOSAscent - nOrigAscent; + + return true; +} + void SwControlCharPortion::Paint( const SwTextPaintInfo &rInf ) const { if ( Width() ) // is only set during prepaint mode { - rInf.DrawViewOpt( *this, PortionType::ControlChar ); + rInf.DrawViewOpt(*this, GetWhichPor()); - if ( !rInf.GetOpt().IsPagePreview() && - !rInf.GetOpt().IsReadonly() && - SwViewOption::IsFieldShadings() && - CHAR_ZWNBSP != mcChar ) + int deltaY(0); + SwFont aTmpFont( *rInf.GetFont() ); + OUString aOutString; + + if (rInf.OnWin() + && !rInf.GetOpt().IsPagePreview() + && !rInf.GetOpt().IsReadonly() + && DoPaint(rInf, aOutString, aTmpFont, deltaY)) { - SwFont aTmpFont( *rInf.GetFont() ); - aTmpFont.SetEscapement( CHAR_ZWSP == mcChar ? DFLT_ESC_AUTO_SUB : -25 ); - const sal_uInt16 nProp = 40; - aTmpFont.SetProportion( nProp ); // a smaller font SwFontSave aFontSave( rInf, &aTmpFont ); - OUString aOutString; - - switch ( mcChar ) - { - case CHAR_ZWSP : - aOutString = "/"; break; -// case CHAR_LRM : -// rText = sal_Unicode(0x2514); break; -// case CHAR_RLM : -// rText = sal_Unicode(0x2518); break; - } - if ( !mnHalfCharWidth ) mnHalfCharWidth = rInf.GetTextSize( aOutString ).Width() / 2; @@ -527,12 +595,15 @@ void SwControlCharPortion::Paint( const SwTextPaintInfo &rInf ) const { 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); @@ -565,4 +636,13 @@ sal_uInt16 SwControlCharPortion::GetViewWidth( const SwTextSizeInfo& rInf ) cons return mnViewWidth; } +SwLinePortion * SwBookmarkPortion::Unchain() +{ + assert(!m_pPrevious || m_pPrevious->GetNextPortion() == this); + m_pPrevious->SetNextPortion(nullptr); + auto const pTmp(m_pPrevious); + m_pPrevious = nullptr; + return pTmp; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/text/porrst.hxx b/sw/source/core/text/porrst.hxx index 85e85d1570dd..dcb2cfcae7e9 100644 --- a/sw/source/core/text/porrst.hxx +++ b/sw/source/core/text/porrst.hxx @@ -31,6 +31,7 @@ class SwPortionHandler; class SwTextPaintInfo; class SwTextSizeInfo; +class SwFont; #define LINE_BREAK_WIDTH 150 #define SPECIAL_FONT_HEIGHT 200 @@ -133,6 +134,7 @@ class SwControlCharPortion : public SwLinePortion private: mutable sal_uInt16 mnViewWidth; // used to cache a calculated value mutable sal_uInt16 mnHalfCharWidth; // used to cache a calculated value +protected: sal_Unicode const mcChar; public: @@ -143,11 +145,35 @@ public: SetWhichPor( PortionType::ControlChar ); SetLen( TextFrameIndex(1) ); } + virtual bool DoPaint(SwTextPaintInfo const& rInf, + 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; }; +/// 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 +{ +private: + SwLinePortion * m_pPrevious; + +public: + explicit SwBookmarkPortion(SwLinePortion *const pPrevious, sal_Unicode const cChar) + : SwControlCharPortion(cChar) + , m_pPrevious(pPrevious) + { + SetWhichPor(PortionType::Bookmark); + SetLen(TextFrameIndex(0)); + } + + virtual bool DoPaint(SwTextPaintInfo const& rInf, + OUString & rOutString, SwFont & rTmpFont, int & rDeltaY) const override; + virtual SwLinePortion * Compress() override { return this; } + SwLinePortion * Unchain(); +}; + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/text/xmldump.cxx b/sw/source/core/text/xmldump.cxx index 510615b08a1f..3db640cb4774 100644 --- a/sw/source/core/text/xmldump.cxx +++ b/sw/source/core/text/xmldump.cxx @@ -52,6 +52,7 @@ class XmlPortionDumper:public SwPortionHandler 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"; |