[Libreoffice-commits] core.git: desktop/qa desktop/source include/tools include/vcl sc/inc sc/qa sc/source sd/source sw/inc sw/qa sw/source tools/qa tools/source

Noel Grandin (via logerrit) logerrit at kemper.freedesktop.org
Sat Jun 20 19:25:44 UTC 2020


 desktop/qa/desktop_lib/test_desktop_lib.cxx    |    4 
 desktop/source/lib/init.cxx                    |   32 ++--
 include/tools/json_writer.hxx                  |   65 +++++++-
 include/vcl/ITiledRenderable.hxx               |   18 --
 sc/inc/docuno.hxx                              |    8 -
 sc/qa/unit/tiledrendering/tiledrendering.cxx   |   29 +++
 sc/source/ui/inc/tabview.hxx                   |    3 
 sc/source/ui/unoobj/docuno.cxx                 |   70 +++------
 sc/source/ui/view/tabview.cxx                  |  183 ++++++++++++-------------
 sd/source/ui/inc/unomodel.hxx                  |    2 
 sd/source/ui/unoidl/unomodel.cxx               |   26 +--
 sw/inc/swmodule.hxx                            |    3 
 sw/inc/unotxdoc.hxx                            |    6 
 sw/qa/extras/tiledrendering/tiledrendering.cxx |    6 
 sw/qa/extras/uiwriter/uiwriter2.cxx            |    8 -
 sw/qa/extras/uiwriter/uiwriter3.cxx            |    8 -
 sw/source/uibase/app/swmodul1.cxx              |   26 ---
 sw/source/uibase/inc/swruler.hxx               |    3 
 sw/source/uibase/misc/swruler.cxx              |   47 ++----
 sw/source/uibase/uno/unotxdoc.cxx              |   39 ++---
 tools/qa/cppunit/test_json_writer.cxx          |   38 ++++-
 tools/source/misc/json_writer.cxx              |  117 ++++++++++++++-
 22 files changed, 444 insertions(+), 297 deletions(-)

New commits:
commit cb95276e6e6bf12a1c06d5c252551e55c788fcb2
Author:     Noel Grandin <noelgrandin at gmail.com>
AuthorDate: Thu Jun 18 21:39:30 2020 +0200
Commit:     Noel Grandin <noel.grandin at collabora.co.uk>
CommitDate: Sat Jun 20 21:25:11 2020 +0200

    use JsonWriter for the rest of ITiledRenderable
    
    and fix bug in buffer reallacotion where mPos pointing
    at the beginning of the new buffer instead of at the
    correct index inside it.
    
    Change-Id: Ie1ffaa176f6165e2cec85c93adc945312eff38e4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96650
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.grandin at collabora.co.uk>

diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 8a651ab764da..8e02686f7f57 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -818,11 +818,13 @@ void DesktopLOKTest::testHiddenRowHeaders()
 
     boost::property_tree::ptree aTree;
     char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, aPayload.str().c_str());
+    SAL_WARN("desktop", "xxxxxxx " << pJSON);
     std::stringstream aStream(pJSON);
-    free(pJSON);
+    SAL_WARN("desktop", aStream.str());
     CPPUNIT_ASSERT(!aStream.str().empty());
 
     boost::property_tree::read_json(aStream, aTree);
+    free(pJSON);
     sal_Int32 nPrevious = 0;
     sal_Int32 nIndex = 0;
     for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("rows"))
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 357111d220af..a9202a0c90b5 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -3303,8 +3303,9 @@ static char* getPostIts(LibreOfficeKitDocument* pThis)
         SetLastExceptionMsg("Document doesn't support tiled rendering");
         return nullptr;
     }
-    OUString aComments = pDoc->getPostIts();
-    return strdup(aComments.toUtf8().getStr());
+    tools::JsonWriter aJsonWriter;
+    pDoc->getPostIts(aJsonWriter);
+    return aJsonWriter.extractData();
 }
 
 /// Returns the JSON representation of the positions of all the comments in the document
@@ -3317,8 +3318,9 @@ static char* getPostItsPos(LibreOfficeKitDocument* pThis)
         SetLastExceptionMsg("Document doesn't support tiled rendering");
         return nullptr;
     }
-    OUString aComments = pDoc->getPostItsPos();
-    return strdup(aComments.toUtf8().getStr());
+    tools::JsonWriter aJsonWriter;
+    pDoc->getPostItsPos(aJsonWriter);
+    return aJsonWriter.extractData();
 }
 
 static char* getRulerState(LibreOfficeKitDocument* pThis)
@@ -3330,8 +3332,9 @@ static char* getRulerState(LibreOfficeKitDocument* pThis)
         SetLastExceptionMsg("Document doesn't support tiled rendering");
         return nullptr;
     }
-    OUString state = pDoc->getRulerState();
-    return strdup(state.toUtf8().getStr());
+    tools::JsonWriter aJsonWriter;
+    pDoc->getRulerState(aJsonWriter);
+    return aJsonWriter.extractData();
 }
 
 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis, int nType, int nCharCode, int nKeyCode)
@@ -4781,8 +4784,9 @@ static char* getTrackedChangeAuthors(LibreOfficeKitDocument* pThis)
         SetLastExceptionMsg("Document doesn't support tiled rendering");
         return nullptr;
     }
-    OUString aAuthors = pDoc->getTrackedChangeAuthors();
-    return strdup(aAuthors.toUtf8().getStr());
+    tools::JsonWriter aJsonWriter;
+    pDoc->getTrackedChangeAuthors(aJsonWriter);
+    return aJsonWriter.extractData();
 }
 
 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand)
@@ -4885,11 +4889,9 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
             aRectangle = tools::Rectangle(nX, nY, nX + nWidth, nY + nHeight);
         }
 
-        OUString aHeaders = pDoc->getRowColumnHeaders(aRectangle);
-        if (aHeaders.isEmpty())
-            return nullptr;
-        else
-            return convertOUString(aHeaders);
+        tools::JsonWriter aJsonWriter;
+        pDoc->getRowColumnHeaders(aRectangle, aJsonWriter);
+        return aJsonWriter.extractData();
     }
     else if (aCommand.startsWith(aCellCursor))
     {
@@ -4900,7 +4902,9 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
             return nullptr;
         }
         // Ignore command's deprecated parameters.
-        return convertOString(pDoc->getCellCursor());
+        tools::JsonWriter aJsonWriter;
+        pDoc->getCellCursor(aJsonWriter);
+        return aJsonWriter.extractData();
     }
     else if (aCommand.startsWith(aFontSubset))
     {
diff --git a/include/tools/json_writer.hxx b/include/tools/json_writer.hxx
index c0312f6c581d..c15c4b7401da 100644
--- a/include/tools/json_writer.hxx
+++ b/include/tools/json_writer.hxx
@@ -12,6 +12,11 @@
 #include <rtl/ustring.hxx>
 #include <algorithm>
 
+namespace rtl
+{
+class OStringBuffer;
+}
+
 /** Simple JSON encoder designed specifically for LibreOfficeKit purposes.
  *
  * (1) Minimal allocations/re-allocations/copying
@@ -21,10 +26,14 @@
 namespace tools
 {
 class ScopedJsonWriterNode;
+class ScopedJsonWriterArray;
+class ScopedJsonWriterStruct;
 
 class TOOLS_DLLPUBLIC JsonWriter
 {
     friend class ScopedJsonWriterNode;
+    friend class ScopedJsonWriterArray;
+    friend class ScopedJsonWriterStruct;
 
     int mSpaceAllocated;
     char* mpBuffer;
@@ -37,32 +46,36 @@ public:
     ~JsonWriter();
 
     [[nodiscard]] ScopedJsonWriterNode startNode(const char*);
+    [[nodiscard]] ScopedJsonWriterArray startArray(const char*);
+    [[nodiscard]] ScopedJsonWriterStruct startStruct();
 
     void put(const char* pPropName, const OUString& rPropValue);
     void put(const char* pPropName, const OString& rPropValue);
     void put(const char* pPropName, const char* pPropVal);
     void put(const char*, int);
 
+    /// This assumes that this data belongs at this point in the stream, and is valid, and properly encoded
+    void putRaw(const rtl::OStringBuffer&);
+
     /** Hands ownership of the underlying storage buffer to the caller,
      * after this no more document modifications may be written. */
     char* extractData();
+    OString extractAsOString();
 
 private:
     void endNode();
+    void endArray();
+    void endStruct();
     void addCommaBeforeField();
+    void reallocBuffer(int noMoreBytesRequired);
 
+    // this part inline to speed up the fast path
     inline void ensureSpace(int noMoreBytesRequired)
     {
+        assert(mpBuffer && "already extracted data");
         int currentUsed = mPos - mpBuffer;
         if (currentUsed + noMoreBytesRequired >= mSpaceAllocated)
-        {
-            auto newSize = std::max(mSpaceAllocated * 2, (currentUsed + noMoreBytesRequired) * 2);
-            char* pNew = static_cast<char*>(malloc(newSize));
-            memcpy(pNew, mpBuffer, currentUsed);
-            free(mpBuffer);
-            mpBuffer = pNew;
-            mPos = mpBuffer;
-        }
+            reallocBuffer(noMoreBytesRequired);
     }
 };
 
@@ -83,5 +96,41 @@ class ScopedJsonWriterNode
 public:
     ~ScopedJsonWriterNode() { mrWriter.endNode(); }
 };
+
+/**
+ * Auto-closes the node.
+ */
+class ScopedJsonWriterArray
+{
+    friend class JsonWriter;
+
+    JsonWriter& mrWriter;
+
+    ScopedJsonWriterArray(JsonWriter& rWriter)
+        : mrWriter(rWriter)
+    {
+    }
+
+public:
+    ~ScopedJsonWriterArray() { mrWriter.endArray(); }
+};
+
+/**
+ * Auto-closes the node.
+ */
+class ScopedJsonWriterStruct
+{
+    friend class JsonWriter;
+
+    JsonWriter& mrWriter;
+
+    ScopedJsonWriterStruct(JsonWriter& rWriter)
+        : mrWriter(rWriter)
+    {
+    }
+
+public:
+    ~ScopedJsonWriterStruct() { mrWriter.endStruct(); }
+};
 };
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/vcl/ITiledRenderable.hxx b/include/vcl/ITiledRenderable.hxx
index e290c4960368..7b841844874d 100644
--- a/include/vcl/ITiledRenderable.hxx
+++ b/include/vcl/ITiledRenderable.hxx
@@ -173,9 +173,8 @@ public:
      * @param rRectangle - if not empty, then limit the output only to the area of this rectangle
      * @return a JSON describing position/content of rows/columns
      */
-    virtual OUString getRowColumnHeaders(const tools::Rectangle& /*rRectangle*/)
+    virtual void getRowColumnHeaders(const tools::Rectangle& /*rRectangle*/, tools::JsonWriter& /*rJsonWriter*/)
     {
-        return OUString();
     }
 
     /**
@@ -183,9 +182,8 @@ public:
      * current' views' co-ordinate system.
      * (This could maybe also be used for tables in Writer/Impress in future?)
      */
-    virtual OString getCellCursor()
+    virtual void getCellCursor(tools::JsonWriter& /*rJsonWriter*/)
     {
-        return OString();
     }
 
     virtual PointerStyle getPointer() = 0;
@@ -237,30 +235,26 @@ public:
 
     /// Implementation for
     /// lok::Document::getCommandValues(".uno:TrackedChangeAuthors").
-    virtual OUString getTrackedChangeAuthors()
+    virtual void getTrackedChangeAuthors(tools::JsonWriter& /*rJsonWriter*/)
     {
-        return OUString();
     }
 
     /// Implementation for
     /// lok::Document::getCommandValues(".uno:ViewAnnotations");
-    virtual OUString getPostIts()
+    virtual void getPostIts(tools::JsonWriter& /*rJsonWriter*/)
     {
-        return OUString();
     }
 
     /// Implementation for
     /// lok::Document::getCommandValues(".uno:ViewAnnotationsPosition");
-    virtual OUString getPostItsPos()
+    virtual void getPostItsPos(tools::JsonWriter& /*rJsonWriter*/)
     {
-        return OUString();
     }
 
     /// Implementation for
     /// lok::Document::getCommandValues(".uno:RulerState");
-    virtual OUString getRulerState()
+    virtual void getRulerState(tools::JsonWriter& /*rJsonWriter*/)
     {
-        return OUString();
     }
 
     /*
diff --git a/sc/inc/docuno.hxx b/sc/inc/docuno.hxx
index 02bd4c04981d..de902ebc20e9 100644
--- a/sc/inc/docuno.hxx
+++ b/sc/inc/docuno.hxx
@@ -366,10 +366,10 @@ public:
     virtual void setOutlineState(bool bColumn, int nLevel, int nIndex, bool bHidden) override;
 
     /// @see vcl::ITiledRenderable::getRowColumnHeaders().
-    virtual OUString getRowColumnHeaders(const tools::Rectangle& rRectangle) override;
+    virtual void getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter) override;
 
     /// @see vcl::ITiledRenderable::getCellCursor().
-    virtual OString getCellCursor() override;
+    virtual void getCellCursor(tools::JsonWriter& rJsonWriter) override;
 
     /// @see vcl::ITiledRenderable::getPointer().
     virtual PointerStyle getPointer() override;
@@ -381,10 +381,10 @@ public:
     virtual void setClientVisibleArea(const tools::Rectangle& rRectangle) override;
 
     /// @see vcl::ITiledRenderable::getPostIts().
-    OUString getPostIts() override;
+    void getPostIts(tools::JsonWriter& rJsonWriter) override;
 
     /// @see vcl::ITiledRenderable::getPostItsPos().
-    OUString getPostItsPos() override;
+    void getPostItsPos(tools::JsonWriter& rJsonWriter) override;
 
     /// @see vcl::ITiledRenderable::completeFunction().
     virtual void completeFunction(const OUString& rFunctionName) override;
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index 315910de60ac..2d4739a628c6 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -38,6 +38,7 @@
 #include <vcl/unohelp2.hxx>
 #include <sc.hrc>
 #include <comphelper/string.hxx>
+#include <tools/json_writer.hxx>
 
 #include <chrono>
 #include <cstddef>
@@ -1757,7 +1758,9 @@ void ScTiledRenderingTest::testGetRowColumnHeadersInvalidation()
     SfxLokHelper::setView(nView1);
     aView1.m_bInvalidateTiles = false;
     aView1.m_aInvalidations.clear();
-    pModelObj->getRowColumnHeaders(tools::Rectangle(0, 15, 19650, 5400));
+    tools::JsonWriter aJsonWriter1;
+    pModelObj->getRowColumnHeaders(tools::Rectangle(0, 15, 19650, 5400), aJsonWriter1);
+    free(aJsonWriter1.extractData());
     Scheduler::ProcessEventsToIdle();
     CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
     CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
@@ -1766,7 +1769,9 @@ void ScTiledRenderingTest::testGetRowColumnHeadersInvalidation()
     // Extend area top-to-bottom
     aView1.m_bInvalidateTiles = false;
     aView1.m_aInvalidations.clear();
-    pModelObj->getRowColumnHeaders(tools::Rectangle(0, 5400, 19650, 9800));
+    tools::JsonWriter aJsonWriter2;
+    pModelObj->getRowColumnHeaders(tools::Rectangle(0, 5400, 19650, 9800), aJsonWriter2);
+    free(aJsonWriter2.extractData());
     Scheduler::ProcessEventsToIdle();
     CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
     CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
@@ -1775,7 +1780,9 @@ void ScTiledRenderingTest::testGetRowColumnHeadersInvalidation()
     // Extend area left-to-right
     aView1.m_bInvalidateTiles = false;
     aView1.m_aInvalidations.clear();
-    pModelObj->getRowColumnHeaders(tools::Rectangle(5400, 5400, 25050, 9800));
+    tools::JsonWriter aJsonWriter3;
+    pModelObj->getRowColumnHeaders(tools::Rectangle(5400, 5400, 25050, 9800), aJsonWriter3);
+    free(aJsonWriter3.extractData());
     Scheduler::ProcessEventsToIdle();
     CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
     CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
@@ -1855,22 +1862,30 @@ void ScTiledRenderingTest::testRowColumnHeaders()
 
     // ViewRowColumnHeaders test
     SfxLokHelper::setView(nView1);
-    OUString aHeaders1 = pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695));
+    tools::JsonWriter aJsonWriter1;
+    pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter1);
+    OString aHeaders1 = aJsonWriter1.extractAsOString();
 
     SfxLokHelper::setView(nView2);
     // 50% zoom
     pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 22474, 47333));
     pModelObj->setClientZoom(256, 256, 6636, 6636);
-    OUString aHeaders2 = pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695));
+    tools::JsonWriter aJsonWriter2;
+    pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter2);
+    OString aHeaders2 = aJsonWriter2.extractAsOString();
 
     // Check vs. view #1
     SfxLokHelper::setView(nView1);
-    OUString aHeaders1_2 = pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695));
+    tools::JsonWriter aJsonWriter3;
+    pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter3);
+    OString aHeaders1_2 = aJsonWriter3.extractAsOString();
     CPPUNIT_ASSERT_EQUAL(aHeaders1, aHeaders1_2);
 
     // Check vs. view #2
     SfxLokHelper::setView(nView2);
-    OUString aHeaders2_2 = pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695));
+    tools::JsonWriter aJsonWriter4;
+    pModelObj->getRowColumnHeaders(tools::Rectangle(65,723,10410,4695), aJsonWriter4);
+    OString aHeaders2_2 = aJsonWriter4.extractAsOString();
     CPPUNIT_ASSERT_EQUAL(aHeaders2, aHeaders2_2);
 
     SfxLokHelper::setView(nView1);
diff --git a/sc/source/ui/inc/tabview.hxx b/sc/source/ui/inc/tabview.hxx
index a46c76666145..a9cf83dfe8c2 100644
--- a/sc/source/ui/inc/tabview.hxx
+++ b/sc/source/ui/inc/tabview.hxx
@@ -53,6 +53,7 @@ class ScPageBreakData;
 class SdrHdlList;
 class TabBar;
 namespace com::sun::star::chart2::data { struct HighlightedRange; }
+namespace tools { class JsonWriter; }
 
 enum HeaderType
 {
@@ -591,7 +592,7 @@ public:
     void ResetAutoSpell();
     void SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges );
     /// @see ScModelObj::getRowColumnHeaders().
-    OUString getRowColumnHeaders(const tools::Rectangle& rRectangle);
+    void getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter);
     static void OnLOKNoteStateChanged(const ScPostIt* pNote);
 
     SCROW GetLOKStartHeaderRow() const { return mnLOKStartHeaderRow; }
diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx
index 7e5dd7fe33dd..96ce59f37c9b 100644
--- a/sc/source/ui/unoobj/docuno.cxx
+++ b/sc/source/ui/unoobj/docuno.cxx
@@ -47,6 +47,7 @@
 #include <vcl/pdfextoutdevdata.hxx>
 #include <vcl/print.hxx>
 #include <vcl/svapp.hxx>
+#include <tools/json_writer.hxx>
 #include <tools/multisel.hxx>
 #include <toolkit/awt/vclxdevice.hxx>
 #include <unotools/saveopt.hxx>
@@ -895,34 +896,35 @@ void ScModelObj::setClientZoom(int nTilePixelWidth_, int nTilePixelHeight_, int
     pViewData->GetActiveWin()->updateOtherKitSelections();
 }
 
-OUString ScModelObj::getRowColumnHeaders(const tools::Rectangle& rRectangle)
+void ScModelObj::getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter)
 {
     ScViewData* pViewData = ScDocShell::GetViewData();
 
     if (!pViewData)
-        return OUString();
+        return;
 
     ScTabView* pTabView = pViewData->GetView();
     if (!pTabView)
-        return OUString();
+        return;
 
-    return pTabView->getRowColumnHeaders(rRectangle);
+    pTabView->getRowColumnHeaders(rRectangle, rJsonWriter);
 }
 
-OString ScModelObj::getCellCursor()
+void ScModelObj::getCellCursor(tools::JsonWriter& rJsonWriter)
 {
     SolarMutexGuard aGuard;
 
     ScViewData* pViewData = ScDocShell::GetViewData();
 
     if (!pViewData)
-        return OString();
+        return;
 
     ScGridWindow* pGridWindow = pViewData->GetActiveWin();
     if (!pGridWindow)
-        return OString();
+        return;
 
-    return "{ \"commandName\": \".uno:CellCursor\", \"commandValues\": \"" + pGridWindow->getCellCursor() + "\" }";
+    rJsonWriter.put("commandName", ".uno:CellCursor");
+    rJsonWriter.put("commandValues", pGridWindow->getCellCursor());
 }
 
 PointerStyle ScModelObj::getPointer()
@@ -975,25 +977,25 @@ void ScModelObj::setOutlineState(bool bColumn, int nLevel, int nIndex, bool bHid
         pFunc->SetOutlineState(bColumn, nLevel, nIndex, bHidden);
 }
 
-OUString ScModelObj::getPostIts()
+void ScModelObj::getPostIts(tools::JsonWriter& rJsonWriter)
 {
     if (!pDocShell)
-        return OUString();
+        return;
 
     ScDocument& rDoc = pDocShell->GetDocument();
     std::vector<sc::NoteEntry> aNotes;
     rDoc.GetAllNoteEntries(aNotes);
 
-    boost::property_tree::ptree aAnnotations;
+    auto commentsNode = rJsonWriter.startNode("comments");
     for (const sc::NoteEntry& aNote : aNotes)
     {
-        boost::property_tree::ptree aAnnotation;
+        auto commentNode = rJsonWriter.startNode("");
 
-        aAnnotation.put("id", aNote.mpNote->GetId());
-        aAnnotation.put("tab", aNote.maPos.Tab());
-        aAnnotation.put("author", aNote.mpNote->GetAuthor());
-        aAnnotation.put("dateTime", aNote.mpNote->GetDate());
-        aAnnotation.put("text", aNote.mpNote->GetText());
+        rJsonWriter.put("id", aNote.mpNote->GetId());
+        rJsonWriter.put("tab", aNote.maPos.Tab());
+        rJsonWriter.put("author", aNote.mpNote->GetAuthor());
+        rJsonWriter.put("dateTime", aNote.mpNote->GetDate());
+        rJsonWriter.put("text", aNote.mpNote->GetText());
 
         // Calculating the cell cursor position
         ScViewData* pViewData = ScDocShell::GetViewData();
@@ -1012,36 +1014,27 @@ OUString ScModelObj::getPostIts()
             tools::Rectangle aRect(Point(aScrPos.getX() / fPPTX, aScrPos.getY() / fPPTY),
                             Size(nSizeXPix / fPPTX, nSizeYPix / fPPTY));
 
-            aAnnotation.put("cellPos", aRect.toString());
+            rJsonWriter.put("cellPos", aRect.toString());
         }
-
-        aAnnotations.push_back(std::make_pair("", aAnnotation));
     }
-
-    boost::property_tree::ptree aTree;
-    aTree.add_child("comments", aAnnotations);
-    std::stringstream aStream;
-    boost::property_tree::write_json(aStream, aTree);
-
-    return OUString::fromUtf8(aStream.str().c_str());
 }
 
-OUString ScModelObj::getPostItsPos()
+void ScModelObj::getPostItsPos(tools::JsonWriter& rJsonWriter)
 {
     if (!pDocShell)
-        return OUString();
+        return;
 
     ScDocument& rDoc = pDocShell->GetDocument();
     std::vector<sc::NoteEntry> aNotes;
     rDoc.GetAllNoteEntries(aNotes);
 
-    boost::property_tree::ptree aAnnotations;
+    auto commentsNode = rJsonWriter.startNode("commentsPos");
     for (const sc::NoteEntry& aNote : aNotes)
     {
-        boost::property_tree::ptree aAnnotation;
+        auto commentNode = rJsonWriter.startNode("");
 
-        aAnnotation.put("id", aNote.mpNote->GetId());
-        aAnnotation.put("tab", aNote.maPos.Tab());
+        rJsonWriter.put("id", aNote.mpNote->GetId());
+        rJsonWriter.put("tab", aNote.maPos.Tab());
 
         // Calculating the cell cursor position
         ScViewData* pViewData = ScDocShell::GetViewData();
@@ -1060,18 +1053,9 @@ OUString ScModelObj::getPostItsPos()
             tools::Rectangle aRect(Point(aScrPos.getX() / fPPTX, aScrPos.getY() / fPPTY),
                             Size(nSizeXPix / fPPTX, nSizeYPix / fPPTY));
 
-            aAnnotation.put("cellPos", aRect.toString());
+            rJsonWriter.put("cellPos", aRect.toString());
         }
-
-        aAnnotations.push_back(std::make_pair("", aAnnotation));
     }
-
-    boost::property_tree::ptree aTree;
-    aTree.add_child("commentsPos", aAnnotations);
-    std::stringstream aStream;
-    boost::property_tree::write_json(aStream, aTree);
-
-    return OUString::fromUtf8(aStream.str().c_str());
 }
 
 void ScModelObj::completeFunction(const OUString& rFunctionName)
diff --git a/sc/source/ui/view/tabview.cxx b/sc/source/ui/view/tabview.cxx
index d8bd7996f4ae..4cc287403df0 100644
--- a/sc/source/ui/view/tabview.cxx
+++ b/sc/source/ui/view/tabview.cxx
@@ -25,6 +25,7 @@
 #include <vcl/settings.hxx>
 #include <sal/log.hxx>
 #include <tools/svborder.hxx>
+#include <tools/json_writer.hxx>
 
 #include <pagedata.hxx>
 #include <tabview.hxx>
@@ -2358,7 +2359,7 @@ void lcl_getGroupIndexes(const ScOutlineArray& rArray, SCCOLROW nStart, SCCOLROW
 void lcl_createGroupsData(
         SCCOLROW nHeaderIndex, SCCOLROW nEnd, long nSizePx, long nTotalPx,
         const ScOutlineArray& rArray, std::vector<size_t>& rGroupIndexes,
-        std::vector<long>& rGroupStartPositions, OUString& rGroupsBuffer)
+        std::vector<long>& rGroupStartPositions, OStringBuffer& rGroupsBuffer)
 {
     const size_t nGroupDepth = rArray.GetDepth();
     // create string data for group controls
@@ -2390,20 +2391,20 @@ void lcl_createGroupsData(
                 // nHeaderIndex is the end col/row of a group or is the last col/row and a group started and not yet ended
 
                 // append a new group control data
-                if (rGroupsBuffer.endsWith("}"))
+                auto len = rGroupsBuffer.getLength();
+                if (len && rGroupsBuffer[len-1] == '}')
                 {
-                    rGroupsBuffer += ", ";
+                    rGroupsBuffer.append(", ");
                 }
 
                 bool bGroupHidden = pEntry->IsHidden();
 
-                OUString aGroupData = "{ \"level\": \"" + OUString::number(nLevel + 1) + "\", "
-                    "\"index\": \"" + OUString::number(nIndex) + "\", "
-                    "\"startPos\": \"" + OUString::number(rGroupStartPositions[nLevel]) + "\", "
-                    "\"endPos\": \"" + OUString::number(nTotalPx) + "\", "
-                    "\"hidden\": \"" + OUString::number(bGroupHidden ? 1 : 0) + "\" }";
-
-                rGroupsBuffer += aGroupData;
+                rGroupsBuffer
+                    .append("{ \"level\": ").append(sal_Int32(nLevel + 1)).append(", ")
+                    .append("\"index\": ").append(sal_Int32(nIndex)).append(", ")
+                    .append("\"startPos\": ").append(rGroupStartPositions[nLevel]).append(", ")
+                    .append("\"endPos\": ").append(nTotalPx).append(", ")
+                    .append("\"hidden\": ").append(sal_Int32(bGroupHidden ? 1 : 0)).append(" }");
 
                 // look for the next visible group control at level nLevel
                 bool bFound = false;
@@ -2431,19 +2432,18 @@ void lcl_createGroupsData(
 
 } // anonymous namespace
 
-OUString ScTabView::getRowColumnHeaders(const tools::Rectangle& rRectangle)
+void ScTabView::getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter)
 {
     ScDocument* pDoc = aViewData.GetDocument();
     if (!pDoc)
-        return OUString();
+        return;
 
     if (rRectangle.IsEmpty())
-        return OUString();
+        return;
 
     bool bRangeHeaderSupport = comphelper::LibreOfficeKit::isRangeHeaders();
 
-    OUStringBuffer aBuffer(256);
-    aBuffer.append("{ \"commandName\": \".uno:ViewRowColumnHeaders\",\n");
+    rJsonWriter.put("commandName", ".uno:ViewRowColumnHeaders");
 
     SCTAB nTab = aViewData.GetTabNo();
     SCROW nStartRow = -1;
@@ -2551,55 +2551,56 @@ OUString ScTabView::getRowColumnHeaders(const tools::Rectangle& rRectangle)
 
     /// 3) create string data for rows
 
-    aBuffer.append("\"rows\": [\n");
-
     long nTotalPixels = nStartHeightPx;
-    SAL_INFO("sc.lok.header", "Row Header: [create string data for rows]: start row: "
-            << nStartRow << " start height: " << nTotalPixels);
-
-    if (nStartRow != nEndRow)
-    {
-        OUString aText = OUString::number(nStartRow + 1);
-        aBuffer.append("{ \"text\": \"").append(aText).append("\", ");
-        aBuffer.append("\"size\": \"").append(OUString::number(nTotalPixels)).append("\", ");
-        aBuffer.append("\"groupLevels\": \"").append(OUString::number(nRowGroupDepth)).append("\" }");
-    }
-
-    OUString aRowGroupsBuffer = "\"rowGroups\": [\n";
-    std::vector<long> aRowGroupStartPositions(nRowGroupDepth, -nTotalPixels);
     long nPrevSizePx = -1;
-    for (SCROW nRow = nStartRow + 1; nRow <= nEndRow; ++nRow)
+    OStringBuffer aRowGroupsBuffer = "\"rowGroups\": [\n";
     {
-        // nSize will be 0 for hidden rows.
-        const long nSizePx = lcl_GetRowHeightPx(aViewData, nRow, nTab);
-        nTotalPixels += nSizePx;
+        auto rowsNode = rJsonWriter.startArray("rows");
 
-        if (bRangeHeaderSupport && nRowGroupDepth > 0)
+        SAL_INFO("sc.lok.header", "Row Header: [create string data for rows]: start row: "
+                << nStartRow << " start height: " << nTotalPixels);
+
+        if (nStartRow != nEndRow)
         {
-            lcl_createGroupsData(nRow, nEndRow, nSizePx, nTotalPixels,
-                                 *pRowArray, aRowGroupIndexes, aRowGroupStartPositions,
-                                 aRowGroupsBuffer);
+            auto node = rJsonWriter.startStruct();
+            rJsonWriter.put("text", nStartRow + 1);
+            rJsonWriter.put("size",nTotalPixels);
+            rJsonWriter.put("groupLevels", nRowGroupDepth);
         }
 
-        if (bRangeHeaderSupport && nRow < nEndRow && nSizePx == nPrevSizePx)
-            continue;
-        nPrevSizePx = nSizePx;
+        std::vector<long> aRowGroupStartPositions(nRowGroupDepth, -nTotalPixels);
+        for (SCROW nRow = nStartRow + 1; nRow <= nEndRow; ++nRow)
+        {
+            // nSize will be 0 for hidden rows.
+            const long nSizePx = lcl_GetRowHeightPx(aViewData, nRow, nTab);
+            nTotalPixels += nSizePx;
 
-        OUString aText = pRowBar[SC_SPLIT_BOTTOM]->GetEntryText(nRow);
-        aBuffer.append(", ");
-        aBuffer.append("{ \"text\": \"").append(aText).append("\", ");
-        aBuffer.append("\"size\": \"").append(OUString::number(nTotalPixels)).append("\" }");
-    }
+            if (bRangeHeaderSupport && nRowGroupDepth > 0)
+            {
+                lcl_createGroupsData(nRow, nEndRow, nSizePx, nTotalPixels,
+                                     *pRowArray, aRowGroupIndexes, aRowGroupStartPositions,
+                                     aRowGroupsBuffer);
+            }
 
-    aRowGroupsBuffer += "]";
-    aBuffer.append("]");
+            if (bRangeHeaderSupport && nRow < nEndRow && nSizePx == nPrevSizePx)
+                continue;
+            nPrevSizePx = nSizePx;
+
+            auto node = rJsonWriter.startStruct();
+            rJsonWriter.put("text", pRowBar[SC_SPLIT_BOTTOM]->GetEntryText(nRow));
+            rJsonWriter.put("size", nTotalPixels);
+        }
+
+        aRowGroupsBuffer.append("]");
+    }
     if (nRowGroupDepth > 0)
-        aBuffer.append(",\n").append(aRowGroupsBuffer);
+    {
+        aRowGroupsBuffer.append(",\n");
+        rJsonWriter.putRaw(aRowGroupsBuffer.getStr());
+    }
     ///  end collecting ROWS
 
 
-    aBuffer.append(",\n");
-
     /// *** start collecting COLS ***
 
     /// 1) compute start and end columns
@@ -2629,8 +2630,6 @@ OUString ScTabView::getRowColumnHeaders(const tools::Rectangle& rRectangle)
         mnLOKEndHeaderCol = nEndCol;
     }
 
-    aBuffer.ensureCapacity( aBuffer.getCapacity() + (50 * (nEndCol - nStartCol + 1)) );
-
     long nVisibleCols = nEndCol - nStartCol;
     if (nVisibleCols < 10)
         nVisibleCols = 10;
@@ -2693,56 +2692,56 @@ OUString ScTabView::getRowColumnHeaders(const tools::Rectangle& rRectangle)
     }
 
     /// 3) create string data for columns
+    OStringBuffer aColGroupsBuffer = "\"columnGroups\": [\n";
+    {
+        auto columnsNode = rJsonWriter.startArray("columns");
 
-    aBuffer.append("\"columns\": [\n");
+        nTotalPixels = nStartWidthPx;
+        SAL_INFO("sc.lok.header", "Col Header: [create string data for cols]: start col: "
+                << nStartRow << " start width: " << nTotalPixels);
 
-    nTotalPixels = nStartWidthPx;
-    SAL_INFO("sc.lok.header", "Col Header: [create string data for cols]: start col: "
-            << nStartRow << " start width: " << nTotalPixels);
+        if (nStartCol != nEndCol)
+        {
+            auto node = rJsonWriter.startStruct();
+            rJsonWriter.put("text", nStartCol + 1);
+            rJsonWriter.put("size", nTotalPixels);
+            rJsonWriter.put("groupLevels", nColGroupDepth);
+        }
 
-    if (nStartCol != nEndCol)
-    {
-        OUString aText = OUString::number(nStartCol + 1);
-        aBuffer.append("{ \"text\": \"").append(aText).append("\", ");
-        aBuffer.append("\"size\": \"").append(OUString::number(nTotalPixels)).append("\", ");
-        aBuffer.append("\"groupLevels\": \"").append(OUString::number(nColGroupDepth)).append("\" }");
-    }
+        std::vector<long> aColGroupStartPositions(nColGroupDepth, -nTotalPixels);
+        nPrevSizePx = -1;
+        for (SCCOL nCol = nStartCol + 1; nCol <= nEndCol; ++nCol)
+        {
+            // nSize will be 0 for hidden columns.
+            const long nSizePx = lcl_GetColWidthPx(aViewData, nCol, nTab);
+            nTotalPixels += nSizePx;
 
-    OUString aColGroupsBuffer = "\"columnGroups\": [\n";
-    std::vector<long> aColGroupStartPositions(nColGroupDepth, -nTotalPixels);
-    nPrevSizePx = -1;
-    for (SCCOL nCol = nStartCol + 1; nCol <= nEndCol; ++nCol)
-    {
-        // nSize will be 0 for hidden columns.
-        const long nSizePx = lcl_GetColWidthPx(aViewData, nCol, nTab);
-        nTotalPixels += nSizePx;
+            if (bRangeHeaderSupport && nColGroupDepth > 0)
+                lcl_createGroupsData(nCol, nEndCol, nSizePx, nTotalPixels,
+                                     *pColArray, aColGroupIndexes,
+                                     aColGroupStartPositions, aColGroupsBuffer);
 
-        if (bRangeHeaderSupport && nColGroupDepth > 0)
-            lcl_createGroupsData(nCol, nEndCol, nSizePx, nTotalPixels,
-                                 *pColArray, aColGroupIndexes,
-                                 aColGroupStartPositions, aColGroupsBuffer);
+            if (bRangeHeaderSupport && nCol < nEndCol && nSizePx == nPrevSizePx)
+                continue;
+            nPrevSizePx = nSizePx;
 
-        if (bRangeHeaderSupport && nCol < nEndCol && nSizePx == nPrevSizePx)
-            continue;
-        nPrevSizePx = nSizePx;
+            OUString aText = bRangeHeaderSupport ?
+                    OUString::number(nCol + 1) : pColBar[SC_SPLIT_LEFT]->GetEntryText(nCol);
 
-        OUString aText = bRangeHeaderSupport ?
-                OUString::number(nCol + 1) : pColBar[SC_SPLIT_LEFT]->GetEntryText(nCol);
+            auto node = rJsonWriter.startStruct();
+            rJsonWriter.put("text", aText);
+            rJsonWriter.put("size", nTotalPixels);
+        }
 
-        aBuffer.append(", ");
-        aBuffer.append("{ \"text\": \"").append(aText).append("\", ");
-        aBuffer.append("\"size\": \"").append(OUString::number(nTotalPixels)).append("\" }");
+        aColGroupsBuffer.append("]");
     }
-
-    aColGroupsBuffer += "]";
-    aBuffer.append("]");
     if (nColGroupDepth > 0)
-        aBuffer.append(",\n").append(aColGroupsBuffer);
+    {
+        aColGroupsBuffer.append(",\n");
+        rJsonWriter.putRaw(aColGroupsBuffer.getStr());
+    }
     ///  end collecting COLs
 
-    aBuffer.append("\n}");
-    OUString sRet = aBuffer.makeStringAndClear();
-
     vcl::Region aNewVisArea(
             tools::Rectangle(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
                     mnLOKEndHeaderCol, mnLOKEndHeaderRow));
@@ -2753,8 +2752,6 @@ OUString ScTabView::getRowColumnHeaders(const tools::Rectangle& rRectangle)
         UpdateVisibleRange();
         UpdateFormulas(aChangedArea.Left(), aChangedArea.Top(), aChangedArea.Right(), aChangedArea.Bottom());
     }
-
-    return sRet;
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/inc/unomodel.hxx b/sd/source/ui/inc/unomodel.hxx
index f27ed3c21c7d..acd3d8ed02aa 100644
--- a/sd/source/ui/inc/unomodel.hxx
+++ b/sd/source/ui/inc/unomodel.hxx
@@ -262,7 +262,7 @@ public:
     /// @see vcl::ITiledRenderable::getPointer().
     virtual PointerStyle getPointer() override;
     /// @see vcl::ITiledRenderable::getPostIts().
-    virtual OUString getPostIts() override;
+    virtual void getPostIts(tools::JsonWriter& /*rJsonWriter*/) override;
     /// @see vcl::ITiledRenderable::selectPart().
     virtual void selectPart(int nPart, int nSelect) override;
     /// @see vcl::ITiledRenderable::moveSelectedParts().
diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx
index 54c3658183fa..fba4d8f68e6f 100644
--- a/sd/source/ui/unoidl/unomodel.cxx
+++ b/sd/source/ui/unoidl/unomodel.cxx
@@ -120,6 +120,7 @@
 #include <sfx2/lokcharthelper.hxx>
 #include <tools/debug.hxx>
 #include <tools/diagnose_ex.h>
+#include <tools/json_writer.hxx>
 
 #define TWIPS_PER_PIXEL 15
 
@@ -2399,9 +2400,9 @@ Size SdXImpressDocument::getDocumentSize()
     return Size(convertMm100ToTwip(aSize.getWidth()), convertMm100ToTwip(aSize.getHeight()));
 }
 
-OUString SdXImpressDocument::getPostIts()
+void SdXImpressDocument::getPostIts(::tools::JsonWriter& rJsonWriter)
 {
-    boost::property_tree::ptree aAnnotations;
+    auto commentsNode = rJsonWriter.startNode("comments");
     // Return annotations on master pages too ?
     const sal_uInt16 nMaxPages = mpDoc->GetPageCount();
     SdPage* pPage;
@@ -2412,24 +2413,15 @@ OUString SdXImpressDocument::getPostIts()
 
         for (const uno::Reference<office::XAnnotation>& xAnnotation : aPageAnnotations)
         {
-            boost::property_tree::ptree aAnnotation;
-            aAnnotation.put("id", sd::getAnnotationId(xAnnotation));
-            aAnnotation.put("author", xAnnotation->getAuthor());
-            aAnnotation.put("dateTime", utl::toISO8601(xAnnotation->getDateTime()));
+            auto commentNode = rJsonWriter.startNode("");
+            rJsonWriter.put("id", sd::getAnnotationId(xAnnotation));
+            rJsonWriter.put("author", xAnnotation->getAuthor());
+            rJsonWriter.put("dateTime", utl::toISO8601(xAnnotation->getDateTime()));
             uno::Reference<text::XText> xText(xAnnotation->getTextRange());
-            aAnnotation.put("text", xText->getString());
-            aAnnotation.put("parthash", OUString(OUString::number(pPage->GetHashCode())));
-
-            aAnnotations.push_back(std::make_pair("", aAnnotation));
+            rJsonWriter.put("text", xText->getString());
+            rJsonWriter.put("parthash", pPage->GetHashCode());
         }
     }
-
-    boost::property_tree::ptree aTree;
-    aTree.add_child("comments", aAnnotations);
-    std::stringstream aStream;
-    boost::property_tree::write_json(aStream, aTree);
-
-    return OUString::fromUtf8(aStream.str().c_str());
 }
 
 void SdXImpressDocument::initializeForTiledRendering(const css::uno::Sequence<css::beans::PropertyValue>& rArguments)
diff --git a/sw/inc/swmodule.hxx b/sw/inc/swmodule.hxx
index a8c07038b6b7..c4a53dcc3771 100644
--- a/sw/inc/swmodule.hxx
+++ b/sw/inc/swmodule.hxx
@@ -57,6 +57,7 @@ class SvtCTLOptions;
 class SvtUserOptions;
 enum class SwCompareMode;
 struct SwDBData;
+namespace tools { class JsonWriter; }
 
 enum class SvViewOpt {
     DestView,
@@ -198,7 +199,7 @@ public:
     std::size_t         GetRedlineAuthor();
     OUString const &    GetRedlineAuthor(std::size_t nPos);
     /// See SwXTextDocument::getTrackedChangeAuthors().
-    OUString GetRedlineAuthorInfo();
+    void                GetRedlineAuthorInfo(tools::JsonWriter& rJsonWriter);
     std::size_t         InsertRedlineAuthor(const OUString& rAuthor);
     void                SetRedlineAuthor(const OUString& rAuthor); // for unit tests
 
diff --git a/sw/inc/unotxdoc.hxx b/sw/inc/unotxdoc.hxx
index 16780ce2edeb..cf950c259c22 100644
--- a/sw/inc/unotxdoc.hxx
+++ b/sw/inc/unotxdoc.hxx
@@ -438,11 +438,11 @@ public:
     /// @see vcl::ITiledRenderable::getTrackedChanges().
     void getTrackedChanges(tools::JsonWriter&) override;
     /// @see vcl::ITiledRenderable::getTrackedChangeAuthors().
-    OUString getTrackedChangeAuthors() override;
+    void getTrackedChangeAuthors(tools::JsonWriter& rJsonWriter) override;
 
-    OUString getRulerState() override;
+    void getRulerState(tools::JsonWriter& rJsonWriter) override;
     /// @see vcl::ITiledRenderable::getPostIts().
-    OUString getPostIts() override;
+    void getPostIts(tools::JsonWriter& rJsonWriter) override;
 
     /// @see vcl::ITiledRenderable::executeFromFieldEvent().
     virtual void executeFromFieldEvent(const StringMap& aArguments) override;
diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx
index 487def29974b..69560ae2bd15 100644
--- a/sw/qa/extras/tiledrendering/tiledrendering.cxx
+++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx
@@ -49,6 +49,7 @@
 #include <flddat.hxx>
 #include <basesh.hxx>
 #include <vcl/ITiledRenderable.hxx>
+#include <tools/json_writer.hxx>
 
 static char const DATA_DIRECTORY[] = "/sw/qa/extras/tiledrendering/data/";
 
@@ -1619,8 +1620,9 @@ void SwTiledRenderingTest::testRedlineColors()
     pWrtShell->Insert("zzz");
 
     // Assert that info about exactly one author is returned.
-    OUString aInfo = pXTextDocument->getTrackedChangeAuthors();
-    std::stringstream aStream(aInfo.toUtf8().getStr());
+    tools::JsonWriter aJsonWriter;
+    pXTextDocument->getTrackedChangeAuthors(aJsonWriter);
+    std::stringstream aStream(aJsonWriter.extractAsOString().getStr());
     boost::property_tree::ptree aTree;
     boost::property_tree::read_json(aStream, aTree);
     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree.get_child("authors").size());
diff --git a/sw/qa/extras/uiwriter/uiwriter2.cxx b/sw/qa/extras/uiwriter/uiwriter2.cxx
index 01d05d963b3c..64e82663bdaf 100644
--- a/sw/qa/extras/uiwriter/uiwriter2.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter2.cxx
@@ -49,6 +49,7 @@
 #include <frameformats.hxx>
 #include <shellio.hxx>
 #include <editeng/fontitem.hxx>
+#include <tools/json_writer.hxx>
 
 namespace
 {
@@ -2261,8 +2262,11 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testImageComment)
     // rendering and on the desktop.
     SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
     CPPUNIT_ASSERT(pTextDoc);
-    OUString aPostits = pTextDoc->getPostIts();
-    std::stringstream aStream(aPostits.toUtf8().getStr());
+    tools::JsonWriter aJsonWriter;
+    pTextDoc->getPostIts(aJsonWriter);
+    char* pChar = aJsonWriter.extractData();
+    std::stringstream aStream(pChar);
+    free(pChar);
     boost::property_tree::ptree aTree;
     boost::property_tree::read_json(aStream, aTree);
     for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("comments"))
diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx
index dda897ee2ee3..0f9f3255f990 100644
--- a/sw/qa/extras/uiwriter/uiwriter3.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter3.cxx
@@ -17,6 +17,7 @@
 #include <textboxhelper.hxx>
 #include <fmtanchr.hxx>
 #include <o3tl/safeint.hxx>
+#include <tools/json_writer.hxx>
 
 #include <wrtsh.hxx>
 
@@ -855,8 +856,11 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf132603)
     dispatchCommand(mxComponent, ".uno:Copy", {});
     Scheduler::ProcessEventsToIdle();
 
-    OUString aPostits = pTextDoc->getPostIts();
-    std::stringstream aStream(aPostits.toUtf8().getStr());
+    tools::JsonWriter aJsonWriter;
+    pTextDoc->getPostIts(aJsonWriter);
+    char* pChar = aJsonWriter.extractData();
+    std::stringstream aStream(pChar);
+    free(pChar);
     boost::property_tree::ptree aTree;
     boost::property_tree::read_json(aStream, aTree);
     for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("comments"))
diff --git a/sw/source/uibase/app/swmodul1.cxx b/sw/source/uibase/app/swmodul1.cxx
index b6cfe847a59b..cbaf3bdc2d90 100644
--- a/sw/source/uibase/app/swmodul1.cxx
+++ b/sw/source/uibase/app/swmodul1.cxx
@@ -56,6 +56,7 @@
 #include <IDocumentLayoutAccess.hxx>
 
 #include <tools/color.hxx>
+#include <tools/json_writer.hxx>
 #include <PostItMgr.hxx>
 
 using namespace ::svx;
@@ -433,29 +434,16 @@ static Color lcl_GetAuthorColor(std::size_t nPos)
 }
 
 /// Returns a JSON representation of a redline author.
-static boost::property_tree::ptree lcl_AuthorToJson(const OUString& rAuthor, std::size_t nIndex)
+void SwModule::GetRedlineAuthorInfo(tools::JsonWriter& rJsonWriter)
 {
-    boost::property_tree::ptree aRet;
-    aRet.put("index", nIndex);
-    aRet.put("name", rAuthor.toUtf8().getStr());
-    aRet.put("color", sal_uInt32(lcl_GetAuthorColor(nIndex)));
-    return aRet;
-}
-
-OUString SwModule::GetRedlineAuthorInfo()
-{
-    boost::property_tree::ptree aTable;
+    auto authorsNode = rJsonWriter.startNode("authors");
     for (std::size_t nAuthor = 0; nAuthor < m_pAuthorNames.size(); ++nAuthor)
     {
-        boost::property_tree::ptree aAuthor = lcl_AuthorToJson(m_pAuthorNames[nAuthor], nAuthor);
-        aTable.push_back(std::make_pair("", aAuthor));
+        auto authorNode = rJsonWriter.startNode("");
+        rJsonWriter.put("index", nAuthor);
+        rJsonWriter.put("name", m_pAuthorNames[nAuthor]);
+        rJsonWriter.put("color", sal_uInt32(lcl_GetAuthorColor(nAuthor)));
     }
-
-    boost::property_tree::ptree aTree;
-    aTree.add_child("authors", aTable);
-    std::stringstream aStream;
-    boost::property_tree::write_json(aStream, aTree);
-    return OUString::fromUtf8(aStream.str().c_str());
 }
 
 std::size_t SwModule::InsertRedlineAuthor(const OUString& rAuthor)
diff --git a/sw/source/uibase/inc/swruler.hxx b/sw/source/uibase/inc/swruler.hxx
index 2dac3e6c7926..6c903f9ca9a5 100644
--- a/sw/source/uibase/inc/swruler.hxx
+++ b/sw/source/uibase/inc/swruler.hxx
@@ -18,6 +18,7 @@ class SwViewShell;
 class View;
 namespace vcl { class Window; }
 class SwEditWin;
+namespace tools { class JsonWriter; }
 
 /**
  * An horizontal ruler with a control for comment panel visibility for Writer.
@@ -42,7 +43,7 @@ public:
      * \param rRect ignored
      */
     virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
-    std::string CreateJsonNotification();
+    void CreateJsonNotification(tools::JsonWriter& rJsonWriter);
 
 private:
     SwViewShell * mpViewShell;     //< Shell to check if there is any comments on doc and their visibility
diff --git a/sw/source/uibase/misc/swruler.cxx b/sw/source/uibase/misc/swruler.cxx
index 680031a264b8..effe189148dc 100644
--- a/sw/source/uibase/misc/swruler.cxx
+++ b/sw/source/uibase/misc/swruler.cxx
@@ -21,6 +21,7 @@
 #include <vcl/event.hxx>
 #include <vcl/window.hxx>
 #include <vcl/settings.hxx>
+#include <tools/json_writer.hxx>
 #include <strings.hrc>
 #include <comphelper/lok.hxx>
 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
@@ -232,10 +233,8 @@ void SwCommentRuler::MouseButtonDown(const MouseEvent& rMEvt)
     Invalidate();
 }
 
-std::string SwCommentRuler::CreateJsonNotification()
+void SwCommentRuler::CreateJsonNotification(tools::JsonWriter& rJsonWriter)
 {
-    boost::property_tree::ptree jsonNotif;
-
     // Note that GetMargin1(), GetMargin2(), GetNullOffset(), and GetPageOffset() return values in
     // pixels. Not twips. So "converting" the returned values with convertTwipToMm100() is quite
     // wrong. (Also, even if the return values actually were in twips, it is questionable why we
@@ -252,35 +251,30 @@ std::string SwCommentRuler::CreateJsonNotification()
     // without LibreOfficeKit::isActive().) But in both web-based Online and in the iOS app, the
     // zoom level from the point of view of this code here apparently does not change even if one
     // zooms from the Online code's point of view.
-    jsonNotif.put("margin1", convertTwipToMm100(GetMargin1()));
-    jsonNotif.put("margin2", convertTwipToMm100(GetMargin2()));
-    jsonNotif.put("leftOffset", convertTwipToMm100(GetNullOffset()));
-    jsonNotif.put("pageOffset", convertTwipToMm100(GetPageOffset()));
+    rJsonWriter.put("margin1", convertTwipToMm100(GetMargin1()));
+    rJsonWriter.put("margin2", convertTwipToMm100(GetMargin2()));
+    rJsonWriter.put("leftOffset", convertTwipToMm100(GetNullOffset()));
+    rJsonWriter.put("pageOffset", convertTwipToMm100(GetPageOffset()));
 
     // GetPageWidth() on the other hand does return a value in twips.
     // So here convertTwipToMm100() really does produce actual mm100. Fun.
-    jsonNotif.put("pageWidth", convertTwipToMm100(GetPageWidth()));
-
-    boost::property_tree::ptree tabs;
+    rJsonWriter.put("pageWidth", convertTwipToMm100(GetPageWidth()));
 
-    // The RulerTab array elements that GetTabs() returns have their nPos field in twips. So these
-    // too are actual mm100.
-    for (auto const& tab : GetTabs())
     {
-        boost::property_tree::ptree element;
-        element.put("position", convertTwipToMm100(tab.nPos));
-        element.put("style", tab.nStyle);
-        tabs.push_back(std::make_pair("", element));
-    }
+        auto tabsNode = rJsonWriter.startNode("tabs");
 
-    jsonNotif.add_child("tabs", tabs);
+        // The RulerTab array elements that GetTabs() returns have their nPos field in twips. So these
+        // too are actual mm100.
+        for (auto const& tab : GetTabs())
+        {
+            auto tabNode = rJsonWriter.startNode("");
+            rJsonWriter.put("position", convertTwipToMm100(tab.nPos));
+            rJsonWriter.put("style", tab.nStyle);
+        }
+    }
 
     RulerUnitData aUnitData = GetCurrentRulerUnit();
-    jsonNotif.put("unit", aUnitData.aUnitStr);
-
-    std::stringstream aStream;
-    boost::property_tree::write_json(aStream, jsonNotif);
-    return aStream.str();
+    rJsonWriter.put("unit", aUnitData.aUnitStr);
 }
 
 void SwCommentRuler::NotifyKit()
@@ -288,9 +282,10 @@ void SwCommentRuler::NotifyKit()
     if (!comphelper::LibreOfficeKit::isActive())
         return;
 
-    const std::string test = CreateJsonNotification();
+    tools::JsonWriter aJsonWriter;
+    CreateJsonNotification(aJsonWriter);
     mpViewShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_RULER_UPDATE,
-                                                               test.c_str());
+                                                               aJsonWriter.extractData());
 }
 
 void SwCommentRuler::Update()
diff --git a/sw/source/uibase/uno/unotxdoc.cxx b/sw/source/uibase/uno/unotxdoc.cxx
index 0d934c32f39c..08baf270baff 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -3304,21 +3304,21 @@ void SwXTextDocument::getTrackedChanges(tools::JsonWriter& rJson)
     }
 }
 
-OUString SwXTextDocument::getTrackedChangeAuthors()
+void SwXTextDocument::getTrackedChangeAuthors(tools::JsonWriter& rJsonWriter)
 {
-    return SW_MOD()->GetRedlineAuthorInfo();
+    SW_MOD()->GetRedlineAuthorInfo(rJsonWriter);
 }
 
-OUString SwXTextDocument::getRulerState()
+void SwXTextDocument::getRulerState(tools::JsonWriter& rJsonWriter)
 {
     SwView* pView = pDocShell->GetView();
-    return OUString::fromUtf8(dynamic_cast<SwCommentRuler&>(pView->GetHRuler()).CreateJsonNotification().c_str());
+    dynamic_cast<SwCommentRuler&>(pView->GetHRuler()).CreateJsonNotification(rJsonWriter);
 }
 
-OUString SwXTextDocument::getPostIts()
+void SwXTextDocument::getPostIts(tools::JsonWriter& rJsonWriter)
 {
     SolarMutexGuard aGuard;
-    boost::property_tree::ptree aAnnotations;
+    auto commentsNode = rJsonWriter.startNode("comments");
     for (auto const& sidebarItem : *pDocShell->GetView()->GetPostItMgr())
     {
         sw::annotation::SwAnnotationWin* pWin = sidebarItem->pPostIt.get();
@@ -3344,25 +3344,16 @@ OUString SwXTextDocument::getPostIts()
         }
         const OString sRects = comphelper::string::join("; ", aRects);
 
-        boost::property_tree::ptree aAnnotation;
-        aAnnotation.put("id", pField->GetPostItId());
-        aAnnotation.put("parent", pWin->CalcParent());
-        aAnnotation.put("author", pField->GetPar1().toUtf8().getStr());
-        aAnnotation.put("text", pField->GetPar2().toUtf8().getStr());
-        aAnnotation.put("resolved", pField->GetResolved() ? "true" : "false");
-        aAnnotation.put("dateTime", utl::toISO8601(pField->GetDateTime().GetUNODateTime()));
-        aAnnotation.put("anchorPos", aSVRect.toString());
-        aAnnotation.put("textRange", sRects.getStr());
-
-        aAnnotations.push_back(std::make_pair("", aAnnotation));
+        auto commentNode = rJsonWriter.startNode("");
+        rJsonWriter.put("id", pField->GetPostItId());
+        rJsonWriter.put("parent", pWin->CalcParent());
+        rJsonWriter.put("author", pField->GetPar1());
+        rJsonWriter.put("text", pField->GetPar2());
+        rJsonWriter.put("resolved", pField->GetResolved() ? "true" : "false");
+        rJsonWriter.put("dateTime", utl::toISO8601(pField->GetDateTime().GetUNODateTime()));
+        rJsonWriter.put("anchorPos", aSVRect.toString());
+        rJsonWriter.put("textRange", sRects);
     }
-
-    boost::property_tree::ptree aTree;
-    aTree.add_child("comments", aAnnotations);
-    std::stringstream aStream;
-    boost::property_tree::write_json(aStream, aTree);
-
-    return OUString::fromUtf8(aStream.str().c_str());
 }
 
 void SwXTextDocument::executeFromFieldEvent(const StringMap& aArguments)
diff --git a/tools/qa/cppunit/test_json_writer.cxx b/tools/qa/cppunit/test_json_writer.cxx
index e27afc95f712..6a2cc7813574 100644
--- a/tools/qa/cppunit/test_json_writer.cxx
+++ b/tools/qa/cppunit/test_json_writer.cxx
@@ -30,12 +30,19 @@ public:
     virtual void setUp() override {}
 
     void test1();
+    void test2();
 
     CPPUNIT_TEST_SUITE(JsonWriterTest);
     CPPUNIT_TEST(test1);
+    CPPUNIT_TEST(test2);
     CPPUNIT_TEST_SUITE_END();
 };
 
+struct Free
+{
+    void operator()(void* p) const { std::free(p); }
+};
+
 void JsonWriterTest::test1()
 {
     tools::JsonWriter aJson;
@@ -48,10 +55,6 @@ void JsonWriterTest::test1()
         aJson.put("int", 12);
     }
 
-    struct Free
-    {
-        void operator()(void* p) const { std::free(p); }
-    };
     std::unique_ptr<char, Free> result(aJson.extractData());
 
     CPPUNIT_ASSERT_EQUAL(std::string("{ \"node\": { \"oustring\": \"val1\", \"ostring\": \"val2\", "
@@ -59,6 +62,33 @@ void JsonWriterTest::test1()
                          std::string(result.get()));
 }
 
+void JsonWriterTest::test2()
+{
+    tools::JsonWriter aJson;
+
+    {
+        auto testNode = aJson.startNode("node");
+        aJson.put("field1", OUString("val1"));
+        aJson.put("field2", OUString("val2"));
+        {
+            auto testNode2 = aJson.startNode("node");
+            aJson.put("field3", OUString("val3"));
+            {
+                auto testNode3 = aJson.startNode("node");
+                aJson.put("field4", OUString("val4"));
+                aJson.put("field5", OUString("val5"));
+            }
+        }
+    }
+
+    std::unique_ptr<char, Free> result(aJson.extractData());
+
+    CPPUNIT_ASSERT_EQUAL(std::string("{ \"node\": { \"field1\": \"val1\", \"field2\": \"val2\", "
+                                     "\"node\": { \"field3\": \"val3\", \"node\": { \"field4\": "
+                                     "\"val4\", \"field5\": \"val5\"}}}}"),
+                         std::string(result.get()));
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(JsonWriterTest);
 }
 
diff --git a/tools/source/misc/json_writer.cxx b/tools/source/misc/json_writer.cxx
index 8a57e2ac337f..251c44c0246f 100644
--- a/tools/source/misc/json_writer.cxx
+++ b/tools/source/misc/json_writer.cxx
@@ -10,6 +10,7 @@
 #include <tools/json_writer.hxx>
 #include <stdio.h>
 #include <cstring>
+#include <rtl/strbuf.hxx>
 
 namespace tools
 {
@@ -22,6 +23,7 @@ JsonWriter::JsonWriter()
     , mpBuffer(static_cast<char*>(malloc(mSpaceAllocated)))
     , mStartNodeCount(0)
     , mPos(mpBuffer)
+    , mbFirstFieldInNode(true)
 {
     *mPos = '{';
     ++mPos;
@@ -38,7 +40,10 @@ JsonWriter::~JsonWriter()
 ScopedJsonWriterNode JsonWriter::startNode(const char* pNodeName)
 {
     auto len = strlen(pNodeName);
-    ensureSpace(len + 4);
+    ensureSpace(len + 6);
+
+    addCommaBeforeField();
+
     *mPos = '"';
     ++mPos;
     memcpy(mPos, pNodeName, len);
@@ -57,15 +62,70 @@ void JsonWriter::endNode()
     ensureSpace(1);
     *mPos = '}';
     ++mPos;
+    mbFirstFieldInNode = false;
 }
 
-void JsonWriter::put(const char* pPropName, const OUString& rPropVal)
+ScopedJsonWriterArray JsonWriter::startArray(const char* pNodeName)
+{
+    auto len = strlen(pNodeName);
+    ensureSpace(len + 6);
+
+    addCommaBeforeField();
+
+    *mPos = '"';
+    ++mPos;
+    memcpy(mPos, pNodeName, len);
+    mPos += len;
+    strncpy(mPos, "\": [ ", 5);
+    mPos += 5;
+    mStartNodeCount++;
+    mbFirstFieldInNode = true;
+    return ScopedJsonWriterArray(*this);
+}
+
+void JsonWriter::endArray()
+{
+    assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
+    --mStartNodeCount;
+    ensureSpace(1);
+    *mPos = ']';
+    ++mPos;
+    mbFirstFieldInNode = false;
+}
+
+ScopedJsonWriterStruct JsonWriter::startStruct()
 {
+    ensureSpace(6);
+
     addCommaBeforeField();
 
+    *mPos = '{';
+    ++mPos;
+    *mPos = ' ';
+    ++mPos;
+    mStartNodeCount++;
+    mbFirstFieldInNode = true;
+    return ScopedJsonWriterStruct(*this);
+}
+
+void JsonWriter::endStruct()
+{
+    assert(mStartNodeCount && "mismatched StartNode/EndNode somewhere");
+    --mStartNodeCount;
+    ensureSpace(1);
+    *mPos = '}';
+    ++mPos;
+    mbFirstFieldInNode = false;
+}
+
+void JsonWriter::put(const char* pPropName, const OUString& rPropVal)
+{
     auto nPropNameLength = strlen(pPropName);
     auto nWorstCasePropValLength = rPropVal.getLength() * 2;
-    ensureSpace(nPropNameLength + nWorstCasePropValLength + 6);
+    ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
+
+    addCommaBeforeField();
+
     *mPos = '"';
     ++mPos;
     memcpy(mPos, pPropName, nPropNameLength);
@@ -120,11 +180,12 @@ void JsonWriter::put(const char* pPropName, const OUString& rPropVal)
 
 void JsonWriter::put(const char* pPropName, const OString& rPropVal)
 {
-    addCommaBeforeField();
-
     auto nPropNameLength = strlen(pPropName);
     auto nWorstCasePropValLength = rPropVal.getLength();
-    ensureSpace(nPropNameLength + nWorstCasePropValLength + 6);
+    ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
+
+    addCommaBeforeField();
+
     *mPos = '"';
     ++mPos;
     memcpy(mPos, pPropName, nPropNameLength);
@@ -163,12 +224,13 @@ void JsonWriter::put(const char* pPropName, const OString& rPropVal)
 
 void JsonWriter::put(const char* pPropName, const char* pPropVal)
 {
-    addCommaBeforeField();
-
     auto nPropNameLength = strlen(pPropName);
     auto nPropValLength = strlen(pPropVal);
     auto nWorstCasePropValLength = nPropValLength * 2;
-    ensureSpace(nPropNameLength + nWorstCasePropValLength + 6);
+    ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
+
+    addCommaBeforeField();
+
     *mPos = '"';
     ++mPos;
     memcpy(mPos, pPropName, nPropNameLength);
@@ -210,11 +272,12 @@ void JsonWriter::put(const char* pPropName, const char* pPropVal)
 
 void JsonWriter::put(const char* pPropName, int nPropVal)
 {
-    addCommaBeforeField();
-
     auto nPropNameLength = strlen(pPropName);
     auto nWorstCasePropValLength = 32;
-    ensureSpace(nPropNameLength + nWorstCasePropValLength + 6);
+    ensureSpace(nPropNameLength + nWorstCasePropValLength + 8);
+
+    addCommaBeforeField();
+
     *mPos = '"';
     ++mPos;
     memcpy(mPos, pPropName, nPropNameLength);
@@ -225,6 +288,16 @@ void JsonWriter::put(const char* pPropName, int nPropVal)
     mPos += sprintf(mPos, "%d", nPropVal);
 }
 
+void JsonWriter::putRaw(const rtl::OStringBuffer& rRawBuf)
+{
+    ensureSpace(rRawBuf.getLength() + 2);
+
+    addCommaBeforeField();
+
+    memcpy(mPos, rRawBuf.getStr(), rRawBuf.getLength());
+    mPos += rRawBuf.getLength();
+}
+
 void JsonWriter::addCommaBeforeField()
 {
     if (mbFirstFieldInNode)
@@ -238,12 +311,24 @@ void JsonWriter::addCommaBeforeField()
     }
 }
 
+void JsonWriter::reallocBuffer(int noMoreBytesRequired)
+{
+    int currentUsed = mPos - mpBuffer;
+    auto newSize = std::max<int>(mSpaceAllocated * 2, (currentUsed + noMoreBytesRequired) * 2);
+    char* pNew = static_cast<char*>(malloc(newSize));
+    memcpy(pNew, mpBuffer, currentUsed);
+    free(mpBuffer);
+    mpBuffer = pNew;
+    mPos = mpBuffer + currentUsed;
+}
+
 /** Hands ownership of the underlying storage buffer to the caller,
   * after this no more document modifications may be written. */
 char* JsonWriter::extractData()
 {
     assert(mStartNodeCount == 0 && "did not close all nodes");
     assert(mpBuffer && "data already extracted");
+    ensureSpace(2);
     // add closing brace
     *mPos = '}';
     ++mPos;
@@ -255,5 +340,13 @@ char* JsonWriter::extractData()
     return pRet;
 }
 
+OString JsonWriter::extractAsOString()
+{
+    char* pChar = extractData();
+    OString ret(pChar);
+    free(pChar);
+    return ret;
+}
+
 } // namespace tools
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */


More information about the Libreoffice-commits mailing list