sw/Library_sw.mk                   |    3 
 sw/source/filter/md/mdcallbcks.cxx |  301 +++++++++++++++
 sw/source/filter/md/mdnum.cxx      |   52 ++
 sw/source/filter/md/mdnum.hxx      |  123 ++++++
 sw/source/filter/md/mdtab.cxx      |  116 ++++++
 sw/source/filter/md/swmd.cxx       |  711 ++++++++++++++++++++++++++++++++++++-
 sw/source/filter/md/swmd.hxx       |  105 +++++
 7 files changed, 1405 insertions(+), 6 deletions(-)

New commits:
commit a58059d76e4398f33f038b78a2838fb3a36641d5
Author:     Ujjawal Kumar <randomfores...@gmail.com>
AuthorDate: Fri Jul 4 22:51:31 2025 +0530
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Aug 26 09:27:38 2025 +0200

    Initial implementation of Markdown elements such as paras, lists, and 
headings
    
    Change-Id: I059090be2157141a8cdab44b28054460f648babb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190199
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk
index b6dad35ce8e4..1feefd848b65 100644
--- a/sw/Library_sw.mk
+++ b/sw/Library_sw.mk
@@ -567,6 +567,9 @@ $(eval $(call gb_Library_add_exception_objects,sw,\
     sw/source/filter/html/svxcss1 \
     sw/source/filter/html/swhtml \
     sw/source/filter/html/wrthtml \
+    sw/source/filter/md/mdcallbcks \
+    sw/source/filter/md/mdnum \
+    sw/source/filter/md/mdtab \
     sw/source/filter/md/swmd \
     sw/source/filter/md/wrtmd \
     sw/source/filter/writer/writer \
diff --git a/sw/source/filter/md/mdcallbcks.cxx 
b/sw/source/filter/md/mdcallbcks.cxx
new file mode 100644
index 000000000000..bff393e802ea
--- /dev/null
+++ b/sw/source/filter/md/mdcallbcks.cxx
@@ -0,0 +1,301 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/postitem.hxx>
+#include <IDocumentContentOperations.hxx>
+
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <fmtinfmt.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <fmtanchr.hxx>
+#include <tools/urlobj.hxx>
+
+#include "swmd.hxx"
+
+int SwMarkdownParser::enter_block_callback(MD_BLOCKTYPE type, void* detail, 
void* userdata)
+{
+    SwMarkdownParser* parser = static_cast<SwMarkdownParser*>(userdata);
+    switch (type)
+    {
+        case MD_BLOCK_DOC:
+            break;
+        case MD_BLOCK_QUOTE:
+        {
+            parser->AddBlockQuote();
+            break;
+        }
+        case MD_BLOCK_UL:
+        {
+            parser->StartNumberedBulletList(type);
+            break;
+        }
+        case MD_BLOCK_OL:
+        {
+            parser->StartNumberedBulletList(type);
+            break;
+        }
+        case MD_BLOCK_LI:
+        {
+            parser->StartNumberedBulletListItem();
+            break;
+        }
+        case MD_BLOCK_HR:
+        {
+            parser->AddHR();
+            break;
+        }
+        case MD_BLOCK_H:
+        {
+            const MD_BLOCK_H_DETAIL* pDetail = static_cast<const 
MD_BLOCK_H_DETAIL*>(detail);
+            parser->StartHeading(pDetail->level);
+            break;
+        }
+        case MD_BLOCK_CODE:
+            parser->BeginCodeBlock();
+            break;
+        case MD_BLOCK_HTML:
+            parser->BeginHtmlBlock();
+            break;
+        case MD_BLOCK_P:
+        {
+            parser->StartPara();
+            break;
+        }
+        case MD_BLOCK_TABLE:
+        {
+            const MD_BLOCK_TABLE_DETAIL* pDetail
+                = static_cast<const MD_BLOCK_TABLE_DETAIL*>(detail);
+            const sal_Int32 nRow = pDetail->body_row_count + 
pDetail->head_row_count;
+            const sal_Int32 nCol = pDetail->col_count;
+            parser->StartTable(nRow, nCol);
+            break;
+        }
+        case MD_BLOCK_THEAD:
+        case MD_BLOCK_TBODY:
+            break;
+        case MD_BLOCK_TR:
+            parser->StartRow();
+            break;
+        case MD_BLOCK_TH:
+        case MD_BLOCK_TD:
+            const MD_BLOCK_TD_DETAIL* pDetail = static_cast<const 
MD_BLOCK_TD_DETAIL*>(detail);
+            parser->StartCell(pDetail->align);
+            break;
+    }
+
+    return 0;
+}
+
+int SwMarkdownParser::leave_block_callback(MD_BLOCKTYPE type, void* 
/*detail*/, void* userdata)
+{
+    SwMarkdownParser* parser = static_cast<SwMarkdownParser*>(userdata);
+
+    switch (type)
+    {
+        case MD_BLOCK_DOC:
+            break;
+        case MD_BLOCK_QUOTE:
+            parser->EndBlockQuote();
+            break;
+        case MD_BLOCK_UL:
+            parser->EndNumberedBulletList();
+            break;
+        case MD_BLOCK_OL:
+            parser->EndNumberedBulletList();
+            break;
+        case MD_BLOCK_LI:
+            parser->EndNumberedBulletListItem();
+            break;
+        case MD_BLOCK_HR:
+        {
+            parser->EndHR();
+            break;
+        }
+        case MD_BLOCK_H:
+        {
+            parser->EndHeading();
+            break;
+        }
+        case MD_BLOCK_CODE:
+            parser->EndCodeBlock();
+            break;
+        case MD_BLOCK_HTML:
+        {
+            parser->EndHtmlBlock();
+            break;
+        }
+        case MD_BLOCK_P:
+        {
+            parser->EndPara();
+            break;
+        }
+        case MD_BLOCK_TABLE:
+            parser->EndTable();
+            break;
+        case MD_BLOCK_THEAD:
+        case MD_BLOCK_TBODY:
+            break;
+        case MD_BLOCK_TR:
+        case MD_BLOCK_TH:
+            break;
+        case MD_BLOCK_TD:
+            break;
+    }
+    return 0;
+}
+
+int SwMarkdownParser::enter_span_callback(MD_SPANTYPE type, void* detail, 
void* userdata)
+{
+    SwMarkdownParser* parser = static_cast<SwMarkdownParser*>(userdata);
+    std::unique_ptr<SfxPoolItem> pItem;
+    switch (type)
+    {
+        case MD_SPAN_EM:
+            pItem.reset(new SvxPostureItem(ITALIC_NORMAL, RES_CHRATR_POSTURE));
+            break;
+        case MD_SPAN_STRONG:
+            pItem.reset(new SvxWeightItem(WEIGHT_BOLD, RES_CHRATR_WEIGHT));
+            break;
+        case MD_SPAN_A:
+        {
+            const MD_SPAN_A_DETAIL* pHyperlinkDetail = static_cast<const 
MD_SPAN_A_DETAIL*>(detail);
+            const MD_ATTRIBUTE& rHref = pHyperlinkDetail->href;
+            OUString aURL = 
rtl::OStringToOUString(std::string_view(rHref.text, rHref.size),
+                                                   RTL_TEXTENCODING_UTF8);
+            aURL = INetURLObject::GetAbsURL(parser->m_sBaseURL, aURL);
+            pItem.reset(new SwFormatINetFormat(aURL, OUString()));
+            break;
+        }
+        case MD_SPAN_IMG:
+        {
+            const MD_SPAN_IMG_DETAIL* pImgDetail = static_cast<const 
MD_SPAN_IMG_DETAIL*>(detail);
+            const MD_ATTRIBUTE& rSrc = pImgDetail->src;
+            const MD_ATTRIBUTE& rTitle = pImgDetail->title;
+            OUString aURL = rtl::OStringToOUString(std::string_view(rSrc.text, 
rSrc.size),
+                                                   RTL_TEXTENCODING_UTF8);
+            OUString aTitle = 
rtl::OStringToOUString(std::string_view(rTitle.text, rTitle.size),
+                                                     RTL_TEXTENCODING_UTF8);
+            parser->InsertImage(aURL, aTitle);
+            parser->m_bInsideImage = true;
+            break;
+        }
+        case MD_SPAN_CODE:
+            break;
+        case MD_SPAN_DEL:
+            pItem.reset(new SvxCrossedOutItem(STRIKEOUT_SINGLE, 
RES_CHRATR_CROSSEDOUT));
+            break;
+        case MD_SPAN_LATEXMATH:
+            break;
+        case MD_SPAN_LATEXMATH_DISPLAY:
+            break;
+        case MD_SPAN_WIKILINK:
+        {
+            const MD_SPAN_WIKILINK_DETAIL* pWikilink
+                = static_cast<const MD_SPAN_WIKILINK_DETAIL*>(detail);
+            OUString target = rtl::OStringToOUString(
+                std::string_view(pWikilink->target.text, 
pWikilink->target.size),
+                RTL_TEXTENCODING_UTF8);
+            OUString aLang = GetAppLanguageTag().getLanguage();
+            OUString aURL = "https://"; + aLang + ".wikipedia.org/wiki/" + 
target;
+            pItem.reset(new SwFormatINetFormat(aURL, OUString()));
+            break;
+        }
+        case MD_SPAN_U:
+            pItem.reset(new SvxUnderlineItem(LINESTYLE_SINGLE, 
RES_CHRATR_CROSSEDOUT));
+            break;
+    }
+    parser->m_aAttrStack.push_back(std::move(pItem));
+    return 0;
+}
+
+int SwMarkdownParser::leave_span_callback(MD_SPANTYPE type, void* /*detail*/, 
void* userdata)
+{
+    SwMarkdownParser* parser = static_cast<SwMarkdownParser*>(userdata);
+    parser->m_aAttrStack.pop_back();
+    switch (type)
+    {
+        case MD_SPAN_EM:
+            break;
+        case MD_SPAN_STRONG:
+            break;
+        case MD_SPAN_A:
+        {
+            break;
+        }
+        case MD_SPAN_IMG:
+            parser->m_bInsideImage = false;
+            break;
+        case MD_SPAN_CODE:
+            break;
+        case MD_SPAN_DEL:
+            break;
+        case MD_SPAN_LATEXMATH:
+            break;
+        case MD_SPAN_LATEXMATH_DISPLAY:
+            break;
+        case MD_SPAN_WIKILINK:
+            break;
+        case MD_SPAN_U:
+            break;
+    }
+    return 0;
+}
+
+int SwMarkdownParser::text_callback(MD_TEXTTYPE type, const MD_CHAR* text, 
MD_SIZE size,
+                                    void* userdata)
+{
+    SwMarkdownParser* parser = static_cast<SwMarkdownParser*>(userdata);
+
+    OUString aText = rtl::OStringToOUString(std::string_view(text, size), 
RTL_TEXTENCODING_UTF8);
+
+    switch (type)
+    {
+        case MD_TEXT_CODE:
+            if (!parser->m_bInsideImage)
+                parser->InsertText(aText);
+            break;
+        case MD_TEXT_HTML:
+            parser->m_htmlData += aText;
+            break;
+        case MD_TEXT_NORMAL:
+            if (!parser->m_bInsideImage)
+                parser->InsertText(aText);
+            break;
+        case MD_TEXT_NULLCHAR:
+            break;
+        case MD_TEXT_BR:
+            
parser->m_xDoc->getIDocumentContentOperations().InsertString(*parser->m_pPam,
+                                                                         
OUString(u'
'));
+            break;
+        case MD_TEXT_SOFTBR:
+            
parser->m_xDoc->getIDocumentContentOperations().InsertString(*parser->m_pPam,
+                                                                         
OUString(CHAR_HARDBLANK));
+            break;
+        case MD_TEXT_ENTITY:
+            break;
+        case MD_TEXT_LATEXMATH:
+            break;
+    }
+
+    return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sw/source/filter/md/mdnum.cxx b/sw/source/filter/md/mdnum.cxx
new file mode 100644
index 000000000000..751ddbfccda8
--- /dev/null
+++ b/sw/source/filter/md/mdnum.cxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <doc.hxx>
+#include <ndtxt.hxx>
+
+#include "mdnum.hxx"
+
+void SwMdNumRuleInfo::Set(const SwTextNode& rTextNd)
+{
+    const SwNumRule* pTextNdNumRule(rTextNd.GetNumRule());
+    if (pTextNdNumRule && pTextNdNumRule != 
rTextNd.GetDoc().GetOutlineNumRule())
+    {
+        m_pNumRule = const_cast<SwNumRule*>(pTextNdNumRule);
+        m_nDeep = o3tl::narrowing<sal_uInt16>(m_pNumRule ? 
rTextNd.GetActualListLevel() + 1 : 0);
+        m_bNumbered = rTextNd.IsCountedInList();
+        m_bRestart = rTextNd.IsListRestart() && 
!rTextNd.HasAttrListRestartValue();
+    }
+    else
+    {
+        m_pNumRule = nullptr;
+        m_nDeep = 0;
+        m_bNumbered = m_bRestart = false;
+    }
+}
+
+bool SwMdNumRuleInfo::IsRestart(const SwMdNumRuleInfo& rPrev) const
+{
+    assert(rPrev.GetNumRule() == GetNumRule());
+
+    if (rPrev.GetDepth() < GetDepth())
+        return false;
+    return m_bRestart;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sw/source/filter/md/mdnum.hxx b/sw/source/filter/md/mdnum.hxx
new file mode 100644
index 000000000000..dda2b6c4b6bb
--- /dev/null
+++ b/sw/source/filter/md/mdnum.hxx
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <swtypes.hxx>
+#include <string.h>
+
+constexpr tools::Long MD_NUMBER_BULLET_MARGINLEFT = o3tl::toTwips(125, 
o3tl::Length::mm10);
+constexpr tools::Long MD_NUMBER_BULLET_INDENT = -o3tl::toTwips(5, 
o3tl::Length::mm);
+
+class SwTextNode;
+class SwNumRule;
+
+class SwMdNumRuleInfo
+{
+    sal_uInt16 m_aNumStarts[MAXLEVEL];
+    SwNumRule* m_pNumRule; // current numbering
+    sal_uInt16 m_nDeep; // current numbering depth (1, 2, 3, ...)
+    bool m_bRestart; // Export: restart numbering
+    bool m_bNumbered; // Export: paragraph is numbered
+
+public:
+    SwMdNumRuleInfo()
+        : m_pNumRule(nullptr)
+        , m_nDeep(0)
+        , m_bRestart(false)
+        , m_bNumbered(false)
+    {
+        memset(&m_aNumStarts, 0xff, sizeof(m_aNumStarts));
+    }
+
+    SwMdNumRuleInfo(const SwMdNumRuleInfo& rInf)
+        : m_pNumRule(rInf.m_pNumRule)
+        , m_nDeep(rInf.m_nDeep)
+        , m_bRestart(rInf.m_bRestart)
+        , m_bNumbered(rInf.m_bNumbered)
+    {
+        memcpy(&m_aNumStarts, &rInf.m_aNumStarts, sizeof(m_aNumStarts));
+    }
+
+    inline void Set(const SwMdNumRuleInfo& rInf);
+    void Set(const SwTextNode& rTextNd);
+
+    explicit SwMdNumRuleInfo(const SwTextNode& rTextNd) { Set(rTextNd); }
+    inline SwMdNumRuleInfo& operator=(const SwMdNumRuleInfo& rInf);
+
+    inline void Clear();
+
+    void SetNumRule(const SwNumRule* pRule) { m_pNumRule = 
const_cast<SwNumRule*>(pRule); }
+    SwNumRule* GetNumRule() { return m_pNumRule; }
+    const SwNumRule* GetNumRule() const { return m_pNumRule; }
+
+    void SetDepth(sal_uInt16 nDepth) { m_nDeep = nDepth; }
+    sal_uInt16 GetDepth() const { return m_nDeep; }
+    void IncDepth() { ++m_nDeep; }
+    void DecDepth()
+    {
+        if (m_nDeep != 0)
+            --m_nDeep;
+    }
+    inline sal_uInt8 GetLevel() const;
+
+    bool IsRestart(const SwMdNumRuleInfo& rPrev) const;
+
+    bool IsNumbered() const { return m_bNumbered; }
+
+    inline void SetNodeStartValue(sal_uInt8 nLvl, sal_uInt16 nVal = USHRT_MAX);
+    sal_uInt16 GetNodeStartValue(sal_uInt8 nLvl) const { return 
m_aNumStarts[nLvl]; }
+};
+
+inline SwMdNumRuleInfo& SwMdNumRuleInfo::operator=(const SwMdNumRuleInfo& rInf)
+{
+    Set(rInf);
+    return *this;
+}
+
+inline void SwMdNumRuleInfo::Set(const SwMdNumRuleInfo& rInf)
+{
+    m_pNumRule = rInf.m_pNumRule;
+    m_nDeep = rInf.m_nDeep;
+    m_bNumbered = rInf.m_bNumbered;
+    m_bRestart = rInf.m_bRestart;
+    memcpy(&m_aNumStarts, &rInf.m_aNumStarts, sizeof(m_aNumStarts));
+}
+
+inline void SwMdNumRuleInfo::Clear()
+{
+    m_pNumRule = nullptr;
+    m_nDeep = 0;
+    m_bRestart = m_bNumbered = false;
+    memset(&m_aNumStarts, 0xff, sizeof(m_aNumStarts));
+}
+
+inline sal_uInt8 SwMdNumRuleInfo::GetLevel() const
+{
+    return static_cast<sal_uInt8>(m_pNumRule != nullptr && m_nDeep != 0
+                                      ? (m_nDeep <= MAXLEVEL ? m_nDeep - 1 : 
MAXLEVEL - 1)
+                                      : 0);
+}
+
+inline void SwMdNumRuleInfo::SetNodeStartValue(sal_uInt8 nLvl, sal_uInt16 nVal)
+{
+    m_aNumStarts[nLvl] = nVal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sw/source/filter/md/mdtab.cxx b/sw/source/filter/md/mdtab.cxx
new file mode 100644
index 000000000000..7dd364348efb
--- /dev/null
+++ b/sw/source/filter/md/mdtab.cxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <swtable.hxx>
+#include <itabenum.hxx>
+#include <ndtxt.hxx>
+
+#include "swmd.hxx"
+
+class MDTable
+{
+    const SwTable* m_pTable;
+    SwMarkdownParser* m_pParser;
+    sal_Int32 m_nRow;
+    sal_Int32 m_nCol;
+    sal_Int32 m_nCurRow;
+    sal_Int32 m_nCurCol;
+
+public:
+    MDTable(SwMarkdownParser* pParser)
+        : m_pParser(pParser)
+        , m_nCurRow(-1)
+        , m_nCurCol(-1)
+    {
+        m_pParser->RegisterTable(this);
+    }
+
+    ~MDTable() { m_pParser->DeRegisterTable(this); }
+
+    const SwTable* GetTable() { return m_pTable; }
+    sal_Int32 GetCurRow() { return m_nCurRow; }
+    sal_Int32 GetCurCol() { return m_nCurCol; }
+
+    void SetTable(const SwTable* pTable, sal_Int32 nRow, sal_Int32 nCol);
+    inline void IncCurRow();
+    inline void IncCurCol();
+};
+
+void MDTable::SetTable(const SwTable* pTable, sal_Int32 nRow, sal_Int32 nCol)
+{
+    m_pTable = pTable;
+    m_nRow = nRow;
+    m_nCol = nCol;
+}
+
+void SwMarkdownParser::StartTable(sal_Int32 nRow, sal_Int32 nCol)
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+
+    std::shared_ptr<MDTable> xTable = std::make_shared<MDTable>(this);
+    m_xTable = xTable;
+
+    const SwTable* pTable
+        = m_xDoc->InsertTable(SwInsertTableOptions(SwInsertTableFlags::All, 
1), *m_pPam->GetPoint(),
+                              nRow, nCol, text::HoriOrientation::FULL);
+    m_xTable->SetTable(pTable, nRow, nCol);
+}
+
+void SwMarkdownParser::EndTable()
+{
+    m_pPam->Move(fnMoveForward);
+    AppendTextNode(AM_SPACE);
+    m_xTable.reset();
+}
+
+void SwMarkdownParser::StartRow() { m_xTable->IncCurRow(); }
+
+void SwMarkdownParser::StartCell(MD_ALIGN eAdjust)
+{
+    m_xTable->IncCurCol();
+    SwTableBox* pBox = m_xTable->GetTable()
+                           ->GetTabLines()[m_xTable->GetCurRow()]
+                           ->GetTabBoxes()[m_xTable->GetCurCol()];
+    const SwStartNode* pSttNd = pBox->GetSttNd();
+    SwTextNode* pTextNode
+        = pSttNd->GetNodes()[pSttNd->GetIndex() + 
1]->GetContentNode()->GetTextNode();
+
+    if (!pTextNode)
+    {
+        return;
+    }
+
+    SvxAdjustItem aAdjustItem(adjustMap.at(eAdjust), RES_PARATR_ADJUST);
+    pTextNode->SetAttr(aAdjustItem);
+
+    m_pPam->GetPoint()->Assign(*pTextNode, 0);
+}
+
+inline void MDTable::IncCurRow()
+{
+    m_nCurRow++;
+    m_nCurCol = -1;
+}
+
+inline void MDTable::IncCurCol() { m_nCurCol++; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sw/source/filter/md/swmd.cxx b/sw/source/filter/md/swmd.cxx
index 626e1e24b60f..63c61bf3c3ae 100644
--- a/sw/source/filter/md/swmd.cxx
+++ b/sw/source/filter/md/swmd.cxx
@@ -17,15 +17,665 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <osl/diagnose.h>
+#include <list.hxx>
+#include <numrule.hxx>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <fmthdft.hxx>
 #include <fltini.hxx>
+#include <itabenum.hxx>
+#include <fchrfmt.hxx>
+#include <swerror.h>
+#include <strings.hrc>
+#include <mdiexp.hxx>
+#include <poolfmt.hxx>
 #include <iodetect.hxx>
+#include <hintids.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/itemiter.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <fmtinfmt.hxx>
+#include <frmatr.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <unotools/securityoptions.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <ndgrf.hxx>
+#include <fmtcntnt.hxx>
+#include <swtypes.hxx>
 
 #include "swmd.hxx"
 
-SwMarkdownParser::SwMarkdownParser(SwDoc& rD, SwPaM& rCursor, SvStream& rIn, 
bool bReadNewDoc)
+namespace
+{
+bool allowAccessLink(const SwDoc& rDoc)
+{
+    OUString sReferer;
+    SfxObjectShell* sh = rDoc.GetPersist();
+    if (sh != nullptr && sh->HasName())
+    {
+        sReferer = sh->GetMedium()->GetName();
+    }
+    return !SvtSecurityOptions::isUntrustedReferer(sReferer);
+}
+}
+
+void SwMarkdownParser::SetNodeNum(sal_uInt8 nLevel)
+{
+    SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode();
+    if (!pTextNode)
+    {
+        SAL_WARN("sw.md", "No Text-Node at PaM-Position");
+        return;
+    }
+
+    OSL_ENSURE(GetNumInfo().GetNumRule(), "No numbering rule");
+    const OUString& rName = GetNumInfo().GetNumRule()->GetName();
+    static_cast<SwContentNode*>(pTextNode)->SetAttr(SwNumRuleItem(rName));
+
+    pTextNode->SetAttrListLevel(nLevel);
+    pTextNode->SetCountedInList(false);
+
+    // Invalidate NumRule, it may have been set valid because of an EndAction
+    GetNumInfo().GetNumRule()->Invalidate();
+}
+
+sal_Int32 SwMarkdownParser::StripTrailingLF()
+{
+    sal_Int32 nStripped = 0;
+
+    const sal_Int32 nLen = m_pPam->GetPoint()->GetContentIndex();
+    if (nLen)
+    {
+        SwTextNode* pTextNd = m_pPam->GetPoint()->GetNode().GetTextNode();
+
+        if (pTextNd)
+        {
+            sal_Int32 nPos = nLen;
+            sal_Int32 nLFCount = 0;
+            while (nPos && ('\x0a' == pTextNd->GetText()[--nPos]))
+                nLFCount++;
+
+            if (nLFCount)
+            {
+                if (nLFCount > 2)
+                {
+                    nLFCount = 2;
+                }
+
+                nPos = nLen - nLFCount;
+                SwContentIndex nIdx(pTextNd, nPos);
+                pTextNd->EraseText(nIdx, nLFCount);
+                nStripped = nLFCount;
+            }
+        }
+    }
+
+    return nStripped;
+}
+
+bool SwMarkdownParser::AppendTextNode(SwMdAppendMode eMode, bool bUpdateNum)
+{
+    sal_Int32 nLFStripped = StripTrailingLF();
+    if ((AM_NOSPACE == eMode || AM_SOFTNOSPACE == eMode) && nLFStripped > 1)
+        eMode = AM_SPACE;
+
+    SwTextNode* pTextNode = (AM_SPACE == eMode || AM_NOSPACE == eMode)
+                                ? m_pPam->GetPoint()->GetNode().GetTextNode()
+                                : nullptr;
+
+    if (pTextNode)
+    {
+        const SvxULSpaceItem& rULSpace = 
pTextNode->SwContentNode::GetAttr(RES_UL_SPACE);
+
+        bool bChange = AM_NOSPACE == eMode ? rULSpace.GetLower() > 0 : 
rULSpace.GetLower() == 0;
+
+        if (bChange)
+        {
+            const SvxULSpaceItem& rCollULSpace = 
pTextNode->GetAnyFormatColl().GetULSpace();
+
+            bool bMayReset
+                = AM_NOSPACE == eMode ? rCollULSpace.GetLower() == 0 : 
rCollULSpace.GetLower() > 0;
+
+            if (bMayReset && rCollULSpace.GetUpper() == rULSpace.GetUpper())
+            {
+                pTextNode->ResetAttr(RES_UL_SPACE);
+            }
+            else
+            {
+                pTextNode->SetAttr(SvxULSpaceItem(
+                    rULSpace.GetUpper(), AM_NOSPACE == eMode ? 0 : 
MD_PARSPACE, RES_UL_SPACE));
+            }
+        }
+    }
+    m_bNoParSpace = AM_NOSPACE == eMode || AM_SOFTNOSPACE == eMode;
+
+    bool bRet = 
m_xDoc->getIDocumentContentOperations().AppendTextNode(*m_pPam->GetPoint());
+
+    if (bUpdateNum)
+    {
+        if (GetNumInfo().GetDepth())
+        {
+            sal_uInt8 nLvl = GetNumInfo().GetLevel();
+            SetNodeNum(nLvl);
+        }
+        else
+            
m_pPam->GetPointNode().GetTextNode()->ResetAttr(RES_PARATR_NUMRULE);
+    }
+
+    return bRet;
+}
+
+void SwMarkdownParser::AddParSpace()
+{
+    //If it already has ParSpace, return
+    if (!m_bNoParSpace)
+        return;
+
+    m_bNoParSpace = false;
+
+    SwNodeOffset nNdIdx = m_pPam->GetPoint()->GetNodeIndex() - 1;
+
+    SwTextNode* pTextNode = m_xDoc->GetNodes()[nNdIdx]->GetTextNode();
+    if (!pTextNode)
+        return;
+
+    SvxULSpaceItem rULSpace = pTextNode->SwContentNode::GetAttr(RES_UL_SPACE);
+    if (rULSpace.GetLower())
+        return;
+
+    const SvxULSpaceItem& rCollULSpace = 
pTextNode->GetAnyFormatColl().GetULSpace();
+    if (rCollULSpace.GetLower() && rCollULSpace.GetUpper() == 
rULSpace.GetUpper())
+    {
+        pTextNode->ResetAttr(RES_UL_SPACE);
+    }
+
+    pTextNode->SetAttr(SvxULSpaceItem(rULSpace.GetUpper(), MD_PARSPACE, 
RES_UL_SPACE));
+}
+
+void SwMarkdownParser::AddBlockQuote()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+
+    SwTextFormatColl* pColl
+        = 
m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_HTML_BLOCKQUOTE);
+
+    m_nBlockQuoteDepth++;
+
+    sal_Int32 nBaseLeftIndent = pColl->GetTextLeftMargin().ResolveTextLeft({});
+    sal_Int32 nLeftIndent = nBaseLeftIndent + m_nBlockQuoteDepth * 
nBaseLeftIndent;
+    SvxTextLeftMarginItem aLeftMargin(pColl->GetTextLeftMargin());
+    aLeftMargin.SetTextLeft(SvxIndentValue::twips(nLeftIndent));
+    m_xDoc->getIDocumentContentOperations().InsertPoolItem(*m_pPam, 
aLeftMargin);
+}
+
+void SwMarkdownParser::EndBlockQuote()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+
+    if (m_nBlockQuoteDepth == 0)
+    {
+        SwTextFormatColl* pColl
+            = 
m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT);
+        m_xDoc->SetTextFormatColl(*m_pPam, pColl);
+    }
+
+    m_nBlockQuoteDepth--;
+}
+
+void SwMarkdownParser::AddHR()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+
+    SwTextFormatColl* pColl
+        = 
m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_HTML_HR);
+    m_xDoc->SetTextFormatColl(*m_pPam, pColl);
+}
+
+void SwMarkdownParser::EndHR()
+{
+    AppendTextNode(AM_SPACE);
+    SwTextFormatColl* pColl
+        = 
m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT);
+    m_xDoc->SetTextFormatColl(*m_pPam, pColl);
+}
+
+void SwMarkdownParser::StartPara()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+}
+
+void SwMarkdownParser::EndPara()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+}
+
+void SwMarkdownParser::StartHeading(sal_uInt8 nLvl)
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+
+    SwTextFormatColl* pColl = 
m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(
+        RES_POOLCOLL_HEADLINE_BASE + nLvl);
+    m_xDoc->SetTextFormatColl(*m_pPam, pColl);
+}
+
+void SwMarkdownParser::EndHeading()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+
+    SwTextFormatColl* pColl
+        = 
m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT);
+    m_xDoc->SetTextFormatColl(*m_pPam, pColl);
+}
+
+void SwMarkdownParser::StartNumberedBulletList(MD_BLOCKTYPE aListType)
+{
+    SwMdNumRuleInfo& rInfo = GetNumInfo();
+
+    bool bSpace = (rInfo.GetDepth()) == 0;
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(bSpace ? AM_SPACE : AM_NOSPACE, false);
+    else if (bSpace)
+        AddParSpace();
+
+    rInfo.IncDepth();
+    sal_uInt8 nLevel
+        = static_cast<sal_uInt8>((rInfo.GetDepth() <= MAXLEVEL ? 
rInfo.GetDepth() : MAXLEVEL) - 1);
+
+    if (!rInfo.GetNumRule())
+    {
+        sal_uInt16 nPos = m_xDoc->MakeNumRule(m_xDoc->GetUniqueNumRuleName());
+        rInfo.SetNumRule(m_xDoc->GetNumRuleTable()[nPos]);
+    }
+
+    bool bNewNumFormat = rInfo.GetNumRule()->GetNumFormat(nLevel) == nullptr;
+    bool bChangeNumFormat = false;
+
+    // Create the default numbering format
+    SwNumFormat aNumFormat(rInfo.GetNumRule()->Get(nLevel));
+    rInfo.SetNodeStartValue(nLevel);
+    if (bNewNumFormat)
+    {
+        sal_uInt16 nChrFormatPoolId = 0;
+        if (aListType == MD_BLOCK_OL)
+        {
+            aNumFormat.SetNumberingType(SVX_NUM_ARABIC);
+            nChrFormatPoolId = RES_POOLCHR_NUM_LEVEL;
+        }
+        else
+        {
+            if (numfunc::IsDefBulletFontUserDefined())
+            {
+                aNumFormat.SetBulletFont(&numfunc::GetDefBulletFont());
+            }
+            aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+            aNumFormat.SetBulletChar(cBulletChar);
+            nChrFormatPoolId = RES_POOLCHR_BULLET_LEVEL;
+        }
+
+        sal_Int32 nAbsLSpace = MD_NUMBER_BULLET_MARGINLEFT;
+
+        sal_Int32 nFirstLineIndent = MD_NUMBER_BULLET_INDENT;
+        if (nLevel > 0)
+        {
+            const SwNumFormat& rPrevNumFormat = rInfo.GetNumRule()->Get(nLevel 
- 1);
+            nAbsLSpace = nAbsLSpace + rPrevNumFormat.GetAbsLSpace();
+            nFirstLineIndent = rPrevNumFormat.GetFirstLineOffset();
+        }
+        aNumFormat.SetAbsLSpace(nAbsLSpace);
+        aNumFormat.SetFirstLineOffset(nFirstLineIndent);
+        aNumFormat.SetCharFormat(
+            
m_xDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(nChrFormatPoolId));
+
+        bChangeNumFormat = true;
+    }
+    else if (1 != aNumFormat.GetStart())
+    {
+        // If the layer has already been used, the start value may need to be 
set hard to the paragraph.
+        rInfo.SetNodeStartValue(nLevel, 1);
+    }
+
+    {
+        sal_uInt8 nLvl = nLevel;
+        SetNodeNum(nLvl);
+    }
+
+    if (bChangeNumFormat)
+    {
+        rInfo.GetNumRule()->Set(nLevel, aNumFormat);
+        m_xDoc->ChgNumRuleFormats(*rInfo.GetNumRule());
+    }
+}
+
+void SwMarkdownParser::EndNumberedBulletList()
+{
+    SwMdNumRuleInfo& rInfo = GetNumInfo();
+
+    // A new paragraph needs to be created, when
+    // - the current one isn't empty (it contains text or paragraph-bound 
objects)
+    // - the current one is numbered
+    bool bAppend = m_pPam->GetPoint()->GetContentIndex() > 0;
+    if (!bAppend)
+    {
+        SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode();
+
+        bAppend = (pTextNode && !pTextNode->IsOutline() && 
pTextNode->IsCountedInList());
+    }
+
+    bool bSpace = (rInfo.GetDepth()) == 1;
+    if (bAppend)
+        AppendTextNode(bSpace ? AM_SPACE : AM_NOSPACE, false);
+    else if (bSpace)
+        AddParSpace();
+
+    if (rInfo.GetDepth() > 0)
+    {
+        rInfo.DecDepth();
+        if (!rInfo.GetDepth())
+        {
+            // The formats not yet modified are now modified, to ease editing
+            const SwNumFormat* pRefNumFormat = nullptr;
+            bool bChanged = false;
+            for (sal_uInt16 i = 0; i < MAXLEVEL; i++)
+            {
+                const SwNumFormat* pNumFormat = 
rInfo.GetNumRule()->GetNumFormat(i);
+                if (pNumFormat)
+                {
+                    pRefNumFormat = pNumFormat;
+                }
+                else if (pRefNumFormat)
+                {
+                    SwNumFormat aNumFormat(rInfo.GetNumRule()->Get(i));
+                    
aNumFormat.SetNumberingType(pRefNumFormat->GetNumberingType() != SVX_NUM_BITMAP
+                                                    ? 
pRefNumFormat->GetNumberingType()
+                                                    : SVX_NUM_CHAR_SPECIAL);
+                    if (SVX_NUM_CHAR_SPECIAL == aNumFormat.GetNumberingType())
+                    {
+                        if (numfunc::IsDefBulletFontUserDefined())
+                        {
+                            
aNumFormat.SetBulletFont(&numfunc::GetDefBulletFont());
+                        }
+                        aNumFormat.SetBulletChar(cBulletChar);
+                    }
+                    aNumFormat.SetAbsLSpace((i + 1) * 
MD_NUMBER_BULLET_MARGINLEFT);
+                    aNumFormat.SetFirstLineOffset(MD_NUMBER_BULLET_INDENT);
+                    aNumFormat.SetCharFormat(pRefNumFormat->GetCharFormat());
+                    rInfo.GetNumRule()->Set(i, aNumFormat);
+                    bChanged = true;
+                }
+            }
+            if (bChanged)
+                m_xDoc->ChgNumRuleFormats(*rInfo.GetNumRule());
+
+            // On the last append, the NumRule item and NodeNum object were 
copied.
+            // Now we need to delete them. ResetAttr deletes the NodeNum 
object as well
+            if (SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode())
+                pTextNode->ResetAttr(RES_PARATR_NUMRULE);
+
+            rInfo.Clear();
+        }
+        else
+        {
+            // the next paragraph not numbered first
+            SetNodeNum(rInfo.GetLevel());
+        }
+    }
+}
+
+void SwMarkdownParser::StartNumberedBulletListItem()
+{
+    sal_uInt8 nLevel = GetNumInfo().GetLevel();
+    sal_uInt16 nStart = GetNumInfo().GetNodeStartValue(nLevel);
+
+    GetNumInfo().SetNodeStartValue(nLevel);
+
+    // create a new paragraph
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_NOSPACE, false);
+    m_bNoParSpace = false; // no space in <LI>!
+
+    SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode();
+    if (!pTextNode)
+    {
+        SAL_WARN("sw.md", "No Text-Node at PaM-Position");
+        return;
+    }
+
+    OUString aNumRuleName;
+    if (GetNumInfo().GetNumRule())
+    {
+        aNumRuleName = GetNumInfo().GetNumRule()->GetName();
+    }
+    else
+    {
+        aNumRuleName = m_xDoc->GetUniqueNumRuleName();
+        SwNumRule aNumRule(aNumRuleName, 
SvxNumberFormat::LABEL_WIDTH_AND_POSITION);
+        SwNumFormat aNumFormat(aNumRule.Get(0));
+        if (numfunc::IsDefBulletFontUserDefined())
+        {
+            aNumFormat.SetBulletFont(&numfunc::GetDefBulletFont());
+        }
+        aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+        aNumFormat.SetBulletChar(cBulletChar);
+        aNumFormat.SetCharFormat(
+            
m_xDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(RES_POOLCHR_BULLET_LEVEL));
+        aNumFormat.SetFirstLineOffset(MD_NUMBER_BULLET_INDENT);
+        aNumRule.Set(0, aNumFormat);
+
+        m_xDoc->MakeNumRule(aNumRuleName, &aNumRule);
+    }
+
+    
static_cast<SwContentNode*>(pTextNode)->SetAttr(SwNumRuleItem(aNumRuleName));
+    pTextNode->SetAttrListLevel(nLevel);
+
+    if (nLevel < MAXLEVEL)
+    {
+        pTextNode->SetCountedInList(true);
+    }
+
+    if (nStart != USHRT_MAX)
+    {
+        pTextNode->SetListRestart(true);
+        pTextNode->SetAttrListRestartValue(nStart);
+    }
+
+    if (GetNumInfo().GetNumRule())
+        GetNumInfo().GetNumRule()->Invalidate();
+}
+
+void SwMarkdownParser::EndNumberedBulletListItem()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+}
+
+void SwMarkdownParser::BeginHtmlBlock()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+}
+
+void SwMarkdownParser::InsertHtmlData()
+{
+    OString aData = rtl::OUStringToOString(m_htmlData, RTL_TEXTENCODING_UTF8);
+    SvMemoryStream aStream(const_cast<char*>(aData.getStr()), 
aData.getLength(), StreamMode::READ);
+    SwReader aReader(aStream, OUString(), OUString(), *m_pPam);
+    aReader.Read(*ReadHTML);
+}
+
+void SwMarkdownParser::EndHtmlBlock()
+{
+    InsertHtmlData();
+    m_htmlData.clear();
+}
+
+void SwMarkdownParser::BeginCodeBlock()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+
+    SwTextFormatColl* pColl
+        = 
m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_HTML_PRE);
+    m_xDoc->SetTextFormatColl(*m_pPam, pColl);
+
+    SvxBrushItem aBrushItem(COL_CODE_BLOCK, RES_BACKGROUND);
+    m_xDoc->getIDocumentContentOperations().InsertPoolItem(*m_pPam, 
aBrushItem);
+}
+
+void SwMarkdownParser::EndCodeBlock()
+{
+    if (m_pPam->GetPoint()->GetContentIndex())
+        AppendTextNode(AM_SPACE);
+    else
+        AddParSpace();
+
+    SwTextFormatColl* pColl
+        = 
m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT);
+    m_xDoc->SetTextFormatColl(*m_pPam, pColl);
+    ClearAttrs();
+}
+
+void SwMarkdownParser::InsertText(OUString& aStr)
+{
+    SwContentNode* pCnd = m_pPam->GetPointContentNode();
+    sal_Int32 nStartPos = m_pPam->GetPoint()->GetContentIndex();
+
+    m_xDoc->getIDocumentContentOperations().InsertString(*m_pPam, aStr);
+
+    SwPaM aAttrPam(*m_pPam->GetPoint());
+    aAttrPam.SetMark();
+    aAttrPam.GetMark()->Assign(*pCnd, nStartPos);
+
+    if (!m_aAttrStack.empty())
+    {
+        SetAttrs(aAttrPam);
+        ClearAttrs();
+    }
+}
+
+void SwMarkdownParser::SetAttrs(SwPaM& rRange)
+{
+    for (const auto& pItem : m_aAttrStack)
+    {
+        if (pItem)
+        {
+            if (rRange.HasMark() && (*rRange.GetMark() != *rRange.GetPoint()))
+            {
+                m_xDoc->getIDocumentContentOperations().InsertPoolItem(rRange, 
*pItem);
+            }
+        }
+    }
+}
+
+void SwMarkdownParser::ClearAttrs() { m_xDoc->ResetAttrs(*m_pPam, true); }
+
+void SwMarkdownParser::InsertImage(const OUString& aURL, const OUString& 
rTitle)
+{
+    OUString sGrfNm = INetURLObject::GetAbsURL(m_sBaseURL, aURL);
+
+    Graphic aGraphic;
+    INetURLObject aGraphicURL(sGrfNm);
+
+    if (!sGrfNm.isEmpty())
+    {
+        aGraphic.SetDefaultType();
+    }
+
+    Size aGrfSz(0, 0);
+    if (allowAccessLink(*m_xDoc) && !aGraphicURL.IsExoticProtocol())
+    {
+        GraphicDescriptor aDescriptor(aGraphicURL);
+        if (aDescriptor.Detect(true))
+            aGrfSz
+                = o3tl::convert(aDescriptor.GetSizePixel(), o3tl::Length::px, 
o3tl::Length::twip);
+    }
+
+    tools::Long nWidth = aGrfSz.getWidth();
+    tools::Long nHeight = aGrfSz.getHeight();
+
+    if (nWidth > 0 && nHeight > 0)
+    {
+        if (nWidth > MD_MAX_IMAGE_WIDTH_IN_TWIPS || nHeight > 
MD_MAX_IMAGE_HEIGH_IN_TWIPS)
+        {
+            double fScaleX = static_cast<double>(MD_MAX_IMAGE_WIDTH_IN_TWIPS) 
/ nWidth;
+            double fScaleY = static_cast<double>(MD_MAX_IMAGE_HEIGH_IN_TWIPS) 
/ nHeight;
+            double fScale = std::min(fScaleX, fScaleY);
+
+            nWidth = static_cast<tools::Long>(nWidth * fScale);
+            nHeight = static_cast<tools::Long>(nHeight * fScale);
+        }
+    }
+
+    if (nWidth < MINFLY || nHeight < MINFLY)
+    {
+        nWidth = MINFLY;
+        nHeight = MINFLY;
+    }
+
+    SfxItemSet aFlySet(
+        SfxItemSet::makeFixedSfxItemSet<RES_FRM_SIZE, RES_VERT_ORIENT, 
RES_HORI_ORIENT, RES_ANCHOR>(
+            m_xDoc->GetAttrPool()));
+
+    aFlySet.Put(SwFormatAnchor(RndStdIds::FLY_AS_CHAR));
+    aFlySet.Put(SwFormatFrameSize(SwFrameSize::Fixed, nWidth, nHeight));
+    aFlySet.Put(SwFormatHoriOrient(0, text::HoriOrientation::NONE, 
text::RelOrientation::CHAR));
+    aFlySet.Put(
+        SwFormatVertOrient(0, text::VertOrientation::CHAR_CENTER, 
text::RelOrientation::CHAR));
+
+    SanitizeAnchor(aFlySet);
+
+    SwFlyFrameFormat* pFlyFormat = 
m_xDoc->getIDocumentContentOperations().InsertGraphic(
+        *m_pPam, sGrfNm, OUString(), &aGraphic, &aFlySet, nullptr, nullptr);
+
+    SwGrfNode* pGrfNd = 
m_xDoc->GetNodes()[pFlyFormat->GetContent().GetContentIdx()->GetIndex() + 1]
+                            ->GetGrfNode();
+
+    if (pGrfNd && !rTitle.isEmpty())
+    {
+        pGrfNd->SetTitle(rTitle);
+    }
+
+    m_bNoParSpace = true;
+}
+
+void SwMarkdownParser::RegisterTable(MDTable* pTable) { 
m_aTables.push_back(pTable); }
+
+void SwMarkdownParser::DeRegisterTable(MDTable* pTable) { 
std::erase(m_aTables, pTable); }
+
+SwMarkdownParser::SwMarkdownParser(SwDoc& rD, SwPaM& rCursor, SvStream& rIn, 
OUString aBaseURL,
+                                   bool bReadNewDoc)
     : m_xDoc(&rD)
     , m_rInput(rIn)
     // , m_pMedium(&rMedium)
+    , m_pNumRuleInfo(new SwMdNumRuleInfo)
+    , m_sBaseURL(std::move(aBaseURL))
+    , m_nBlockQuoteDepth(-1)
     , m_bNewDoc(bReadNewDoc)
 {
     rCursor.DeleteMark();
@@ -34,18 +684,71 @@ SwMarkdownParser::SwMarkdownParser(SwDoc& rD, SwPaM& 
rCursor, SvStream& rIn, boo
     m_nFilesize = m_rInput.TellEnd();
     m_rInput.Seek(STREAM_SEEK_TO_BEGIN);
     m_rInput.ResetError();
-    m_pArr.reset(new char[m_nFilesize + 2]);
+    m_pArr.reset(new char[m_nFilesize + 1]);
 }
 
-ErrCodeMsg MarkdownReader::Read(SwDoc&, const OUString&, SwPaM&, const 
OUString&)
+ErrCodeMsg MarkdownReader::Read(SwDoc& rDoc, const OUString& rBaseURL, SwPaM& 
rPam, const OUString&)
 {
-    return ERRCODE_NONE;
+    ErrCode nRet;
+
+    SwMarkdownParser parser(rDoc, rPam, *m_pStream, rBaseURL, !m_bInsertMode);
+    nRet = parser.CallParser();
+
+    return nRet;
+}
+
+ErrCode SwMarkdownParser::CallParser()
+{
+    ::StartProgress(STR_STATSTR_W4WREAD, 0, m_nFilesize, 
m_xDoc->GetDocShell());
+
+    SwTextFormatColl* pColl
+        = 
m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT);
+    m_xDoc->SetTextFormatColl(*m_pPam, pColl);
+    m_rInput.ReadBytes(m_pArr.get(), m_nFilesize);
+    m_pArr[m_nFilesize] = '
+
+    ErrCode nRet;
+
+    MD_PARSER parser = { 0,
+                         MD_DIALECT_GITHUB | MD_FLAG_WIKILINKS,
+                         enter_block_callback,
+                         leave_block_callback,
+                         enter_span_callback,
+                         leave_span_callback,
+                         text_callback,
+                         nullptr,
+                         nullptr };
+
+    int result = md_parse(m_pArr.get(), m_nFilesize, &parser, 
static_cast<void*>(this));
+
+    if (result != 0)
+    {
+        nRet = ERRCODE_IO_GENERAL;
+    }
+
+    ::EndProgress(m_xDoc->GetDocShell());
+    return nRet;
 }
 
 SwMarkdownParser::~SwMarkdownParser()
 {
     m_pArr.reset();
+    m_pNumRuleInfo.reset();
     m_xDoc.clear();
 }
 
+//static
+void SwMarkdownParser::SanitizeAnchor(SfxItemSet& rFrameItemSet)
+{
+    const SwFormatAnchor& rAnch = rFrameItemSet.Get(RES_ANCHOR);
+    if (SwNode* pAnchorNode = rAnch.GetAnchorNode())
+    {
+        if (pAnchorNode->IsEndNode())
+        {
+            SAL_WARN("sw.md", "Invalid EndNode Anchor");
+            rFrameItemSet.ClearItem(RES_ANCHOR);
+        }
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sw/source/filter/md/swmd.hxx b/sw/source/filter/md/swmd.hxx
index dbdd2c6e3167..795b6d0d902d 100644
--- a/sw/source/filter/md/swmd.hxx
+++ b/sw/source/filter/md/swmd.hxx
@@ -21,11 +21,40 @@
 
 #include <memory>
 
+#include <tools/stream.hxx>
 #include <doc.hxx>
 #include <pam.hxx>
+
 #include <md4c.h>
 #include <o3tl/unit_conversion.hxx>
-#include <tools/stream.hxx>
+#include <frozen/unordered_map.h>
+
+#include "mdnum.hxx"
+
+class MDTable;
+
+constexpr tools::Long MD_PARSPACE = o3tl::toTwips(5, o3tl::Length::mm);
+constexpr tools::Long MD_MAX_IMAGE_WIDTH_IN_TWIPS = 2500;
+constexpr tools::Long MD_MAX_IMAGE_HEIGH_IN_TWIPS = 2500;
+
+constexpr frozen::unordered_map<MD_ALIGN, SvxAdjust, 4> adjustMap
+    = { { MD_ALIGN_DEFAULT, SvxAdjust::Left },
+        { MD_ALIGN_LEFT, SvxAdjust::Left },
+        { MD_ALIGN_CENTER, SvxAdjust::Center },
+        { MD_ALIGN_RIGHT, SvxAdjust::Right } };
+
+constexpr Color COL_CODE_BLOCK = { 225, 225, 225 };
+
+enum SwMdAppendMode
+{
+    AM_NORMAL, // no paragraph spacing handling
+    AM_NOSPACE, // set spacing hard to 0cm
+    AM_SPACE, // set spacing hard to 0.5cm
+    AM_SOFTNOSPACE, // don't set spacing, but save 0cm
+    AM_NONE // no append
+};
+
+typedef std::vector<std::unique_ptr<SfxPoolItem>> MDAttrStack;
 
 class SwMarkdownParser
 {
@@ -34,18 +63,90 @@ class SwMarkdownParser
     SvStream& m_rInput;
     // SfxMedium* m_pMedium;
     std::unique_ptr<char[]> m_pArr;
+    std::unique_ptr<SwMdNumRuleInfo> m_pNumRuleInfo;
     tools::Long m_nFilesize;
 
+    MDAttrStack m_aAttrStack;
+
+    OUString m_htmlData;
+    OUString m_sBaseURL;
+
+    int m_nBlockQuoteDepth;
+
     bool m_bNewDoc;
+    bool m_bNoParSpace : 1;
+    bool m_bInsideImage;
+
+    std::vector<MDTable*> m_aTables;
+    std::shared_ptr<MDTable> m_xTable;
 
     SwMarkdownParser(const SwMarkdownParser&) = delete;
     SwMarkdownParser& operator=(const SwMarkdownParser&) = delete;
 
+    void SetNodeNum(sal_uInt8 nLevel);
+
+    sal_Int32 StripTrailingLF();
+
+    bool AppendTextNode(SwMdAppendMode eMode, bool bUpdateNum = true);
+    void AddParSpace();
+
+    void AddBlockQuote();
+    void EndBlockQuote();
+
+    void AddHR();
+    void EndHR();
+
+    void StartPara();
+    void EndPara();
+    void StartHeading(sal_uInt8 nLvl);
+    void EndHeading();
+
+    void StartNumberedBulletList(MD_BLOCKTYPE aListType);
+    void EndNumberedBulletList();
+    void StartNumberedBulletListItem();
+    void EndNumberedBulletListItem();
+
+    void BeginHtmlBlock();
+    void InsertHtmlData();
+    void EndHtmlBlock();
+
+    void BeginCodeBlock();
+    void EndCodeBlock();
+
+    void InsertText(OUString& aStr);
+    void SetAttrs(SwPaM& rRange);
+    void ClearAttrs();
+
+    void InsertImage(const OUString& aURL, const OUString& rTitle);
+
+    void StartTable(sal_Int32 nRow, sal_Int32 nCol);
+    void EndTable();
+
+    void StartRow();
+
+    void StartCell(MD_ALIGN eAdjust);
+
+public:
+    void RegisterTable(MDTable* pTable);
+    void DeRegisterTable(MDTable* pTable);
+
 public:
-    SwMarkdownParser(SwDoc& rD, SwPaM& rCursor, SvStream& rIn, bool 
bReadNewDoc);
+    SwMarkdownParser(SwDoc& rD, SwPaM& rCursor, SvStream& rIn, OUString 
aBaseURL, bool bReadNewDoc);
+
+    SwMdNumRuleInfo& GetNumInfo() const { return *m_pNumRuleInfo; }
     bool IsNewDoc() const { return m_bNewDoc; }
+    ErrCode CallParser();
 
     ~SwMarkdownParser();
+
+private:
+    static int enter_block_callback(MD_BLOCKTYPE type, void* detail, void* 
userdata);
+    static int leave_block_callback(MD_BLOCKTYPE type, void* detail, void* 
userdata);
+    static int enter_span_callback(MD_SPANTYPE type, void* detail, void* 
userdata);
+    static int leave_span_callback(MD_SPANTYPE type, void* detail, void* 
userdata);
+    static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE 
size, void* userdata);
+
+    static void SanitizeAnchor(SfxItemSet& rFrameItemSet);
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */

Reply via email to