[Libreoffice-commits] core.git: sw/qa sw/source

Michael Stahl (via logerrit) logerrit at kemper.freedesktop.org
Thu Jan 30 13:37:12 UTC 2020


 sw/qa/extras/ooxmlimport/ooxmlimport.cxx |   10 ++
 sw/source/core/access/accportions.cxx    |    3 
 sw/source/core/inc/scriptinfo.hxx        |   11 ++
 sw/source/core/inc/txttypes.hxx          |    1 
 sw/source/core/text/inftxt.cxx           |   23 +++++
 sw/source/core/text/inftxt.hxx           |    3 
 sw/source/core/text/itrform2.cxx         |   51 ++++++++++++
 sw/source/core/text/porlay.cxx           |   17 ++++
 sw/source/core/text/porlin.cxx           |    4 -
 sw/source/core/text/porlin.hxx           |    2 
 sw/source/core/text/pormulti.cxx         |   14 +++
 sw/source/core/text/porrst.cxx           |  122 +++++++++++++++++++++++++------
 sw/source/core/text/porrst.hxx           |   26 ++++++
 sw/source/core/text/xmldump.cxx          |    1 
 14 files changed, 262 insertions(+), 26 deletions(-)

New commits:
commit 4ce8120f1e53f7b81e653b01d141643013bc69ab
Author:     Michael Stahl <Michael.Stahl at cib.de>
AuthorDate: Fri Jan 24 19:05:40 2020 +0100
Commit:     Michael Stahl <michael.stahl at cib.de>
CommitDate: Thu Jan 30 14:36:32 2020 +0100

    tdf#45589 sw: create and paint text portions for bookmarks
    
    Add a new SwBookmarkPortion, derived from SwControlCharPortion.
    
    There is no character for the bookmark in the text so the portion has
    length of 0, which makes things quite a bit more tricky.
    
    Formatting:
    
    * SwBookmarkPortion is created last in WhichFirstPortion().
    
    * In an empty paragraph the SwTextFrame::FormatEmpty() must be disabled.
    
    * If there's a bookmark at the end of a paragraph,
      SwTextFormatter::CalcAscent() must use the font of the previous
      character, not the paragraph font, because that could grow the line
      if it's higher.
    
    * The SwMultiPortion complicates matters, because it uses a nested
      SwTextFormatInfo and thus we need some extra steps to prevent
      duplicate SwBookmarkPortions; this is particluarly a problem for
      rotated text portions.
      - SwTextFormatter::BuildPortions() must advance the outer
        SwTextFormatInfo's bookmark position because BuildMultiPortion() has
        already created the SwBookmarkPortion.
      - If a SwBookmarkPortion is at the start of a SwMultiPortion, it will
        be created before the SwMultiPortion but must be painted inside the
        SwMultiPortion because its font is going to be initialised as inside
        the SwMultiPortion (e.g. it will be rotated) so its position must
        also be adapted to be inside, and only
        SwTextPainter::PaintMultiPortion() does the setup for that; add a
        hack to move it in SwTextFormatter::BuildMultiPortion().
    
    Painting:
    
    * Using the original font seems rather difficult, hard to predict what
      some character is going to look like, and how it scales if the size is
      increased; use OpenSymbol instead.  Unfortunately OpenSymbol doesn't
      have a good glyph that could be used for both the end of a bookmark
      and start of another bookmark at the same position.
    
    * SwLinePortion::PrePaint() wants to avoid moving the portion
      half-outside the frame but often it looks better that way (previously
      it was misaligned, now it's half-outside).
    
    * Not sure if it makes sense to draw a field shading in
      SwTextPaintInfo::DrawViewOpt() too; let's try it out, but dependent on
      the same IsViewMetaChars() setting as the bookmark character itself,
      not on the field shading setting.
    
    Change-Id: I1ab94afb417221e278dbb3afd6c978a05fc78497
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/87364
    Reviewed-by: Michael Stahl <michael.stahl at cib.de>
    Tested-by: Michael Stahl <michael.stahl at cib.de>

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";


More information about the Libreoffice-commits mailing list