filter/Configuration_filter.mk                             |    2 
 filter/source/config/fragments/filters/Markdown.xcu        |   21 +
 filter/source/config/fragments/types/generic_Markdown.xcu  |   18 +
 include/sal/log-areas.dox                                  |    1 
 officecfg/registry/schema/org/openoffice/Office/Writer.xcs |   11 
 sw/Library_sw.mk                                           |    1 
 sw/inc/iodetect.hxx                                        |    2 
 sw/inc/shellio.hxx                                         |    1 
 sw/source/filter/basflt/fltini.cxx                         |    3 
 sw/source/filter/basflt/iodetect.cxx                       |    3 
 sw/source/filter/md/wrtmd.cxx                              |  149 +++++++++++++
 sw/source/filter/md/wrtmd.hxx                              |   47 ++++
 12 files changed, 257 insertions(+), 2 deletions(-)

New commits:
commit 8e83a343a49b2053bc08326a109956f224a47ae1
Author:     Mike Kaganski <[email protected]>
AuthorDate: Tue Apr 15 13:55:29 2025 +0500
Commit:     Miklos Vajna <[email protected]>
CommitDate: Tue May 13 11:08:05 2025 +0200

    Initial Markdown export filter stub
    
    Based on the Writer's HTML export code. For now, it doesn't export
    anything yet, just creates an empty file.
    
    Change-Id: I0d484d2b5bc549f34e674c8f858acb352e275c83
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184201
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/filter/Configuration_filter.mk b/filter/Configuration_filter.mk
index 17e84036783a..117c281c6c8b 100644
--- a/filter/Configuration_filter.mk
+++ b/filter/Configuration_filter.mk
@@ -361,6 +361,7 @@ $(eval $(call 
filter_Configuration_add_types,fcfg_langpack,fcfg_writer_types.xcu
        StarOffice_Writer \
        writer_EPUB_Document \
        writer_PocketWord_File \
+       generic_Markdown \
 ))
 
 $(eval $(call 
filter_Configuration_add_filters,fcfg_langpack,fcfg_writer_filters.xcu,filter/source/config/fragments/filters,\
@@ -411,6 +412,7 @@ $(eval $(call 
filter_Configuration_add_filters,fcfg_langpack,fcfg_writer_filters
        StarOffice_Writer \
        EPUB \
        PocketWord_File \
+       Markdown \
 ))
 
 # fcfg_web
diff --git a/filter/source/config/fragments/filters/Markdown.xcu 
b/filter/source/config/fragments/filters/Markdown.xcu
new file mode 100644
index 000000000000..33ce9369d9ac
--- /dev/null
+++ b/filter/source/config/fragments/filters/Markdown.xcu
@@ -0,0 +1,21 @@
+<!--
+ * 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/.
+ *
+-->
+    <node oor:name="Markdown" oor:op="replace">
+        <prop oor:name="Flags"><value>EXPORT ALIEN</value></prop>
+        <prop oor:name="UIComponent"/>
+        <prop oor:name="FilterService"/>
+        <prop oor:name="UserData"><value>Markdown</value></prop>
+        <prop oor:name="FileFormatVersion"><value>0</value></prop>
+        <prop oor:name="Type"><value>generic_Markdown</value></prop>
+        <prop oor:name="TemplateName"/>
+        <prop 
oor:name="DocumentService"><value>com.sun.star.text.TextDocument</value></prop>
+        <prop oor:name="UIName">
+            <value xml:lang="en-US">Markdown Document</value>
+        </prop>
+    </node>
diff --git a/filter/source/config/fragments/types/generic_Markdown.xcu 
b/filter/source/config/fragments/types/generic_Markdown.xcu
new file mode 100644
index 000000000000..b6ad050f1066
--- /dev/null
+++ b/filter/source/config/fragments/types/generic_Markdown.xcu
@@ -0,0 +1,18 @@
+<!--
+ * 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/.
+ *
+-->
+    <node oor:name="generic_Markdown" oor:op="replace" >
+        <prop 
oor:name="DetectService"><value>com.sun.star.comp.filters.PlainTextFilterDetect</value></prop>
+        <prop oor:name="URLPattern"/>
+        <prop oor:name="Extensions"><value>md</value></prop>
+        <prop oor:name="MediaType"><value>text/markdown</value></prop>
+        <prop oor:name="Preferred"><value>false</value></prop>
+        <prop oor:name="PreferredFilter"/>
+        <prop oor:name="UIName"><value>Markdown Document</value></prop>
+        <prop oor:name="ClipboardFormat"/>
+    </node>
diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 2423996cac67..71e56879abb7 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -555,6 +555,7 @@ certain functionality.
 @li @c sw.layout - Writer core view: document layout
 @li @c sw.layout.debug - Writer layout dbg_lay output
 @li @c sw.mailmerge - Writer mail merge
+@li @c sw.md - Markdown export filter
 @li @c sw.pageframe - debug lifecycle of SwPageFrame
 @li @c sw.qa
 @li @c sw.rtf - .rtf export filter
diff --git a/officecfg/registry/schema/org/openoffice/Office/Writer.xcs 
b/officecfg/registry/schema/org/openoffice/Office/Writer.xcs
index af4fcfe28484..fa70c5444328 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Writer.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Writer.xcs
@@ -6228,6 +6228,17 @@
           <value>true</value>
         </prop>
       </group>
+      <group oor:name="Markdown">
+        <info>
+          <desc>Contains settings for the Markdown filter.</desc>
+        </info>
+        <prop oor:name="IncludeHiddenText" oor:type="xs:boolean" 
oor:nillable="false">
+          <info>
+            <desc>Whether hidden text (sections, etc) is included in the 
output.</desc>
+          </info>
+          <value>false</value>
+        </prop>
+      </group>
     </group>
     <group oor:name="Wizards">
       <info>
diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk
index fe08797c4830..1e3d14c1b8dc 100644
--- a/sw/Library_sw.mk
+++ b/sw/Library_sw.mk
@@ -566,6 +566,7 @@ $(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/wrtmd \
     sw/source/filter/writer/writer \
     sw/source/filter/writer/wrt_fn \
     sw/source/filter/writer/wrtswtbl \
diff --git a/sw/inc/iodetect.hxx b/sw/inc/iodetect.hxx
index cb47a7b7f3b2..1ab043a6cffc 100644
--- a/sw/inc/iodetect.hxx
+++ b/sw/inc/iodetect.hxx
@@ -39,6 +39,7 @@ inline constexpr OUString FILTER_XML = u"CXML"_ustr;      
///< XML filter
 #define FILTER_XMLV     "CXMLV"     ///< XML filter
 #define FILTER_XMLVW    "CXMLVWEB"  ///< XML filter
 inline constexpr OUString FILTER_DOCX = u"OXML"_ustr;
+inline constexpr OUString FILTER_MD = u"Markdown"_ustr; // markdown filter
 inline constexpr OUString sHTML = u"HTML"_ustr;
 inline constexpr OUString sWW5 = u"WW6"_ustr;
 inline constexpr OUString sWW6 = u"CWW6"_ustr;
@@ -80,6 +81,7 @@ enum ReaderWriterEnum {
     READER_WRITER_TEXT_DLG,
     READER_WRITER_TEXT,
     READER_WRITER_DOCX,
+    READER_WRITER_MD,
     MAXFILTER
 };
 
diff --git a/sw/inc/shellio.hxx b/sw/inc/shellio.hxx
index a0c3b2eb32ea..f82ac9301da5 100644
--- a/sw/inc/shellio.hxx
+++ b/sw/inc/shellio.hxx
@@ -575,6 +575,7 @@ void GetRTFWriter( std::u16string_view, const OUString&, 
WriterRef& );
 void GetASCWriter( std::u16string_view, const OUString&, WriterRef&);
 void GetHTMLWriter( std::u16string_view, const OUString&, WriterRef& );
 void GetXMLWriter( std::u16string_view, const OUString&, WriterRef& );
+void GetMDWriter(std::u16string_view, const OUString&, WriterRef&);
 
 #endif
 
diff --git a/sw/source/filter/basflt/fltini.cxx 
b/sw/source/filter/basflt/fltini.cxx
index 744ec4a1379d..b52401a5b650 100644
--- a/sw/source/filter/basflt/fltini.cxx
+++ b/sw/source/filter/basflt/fltini.cxx
@@ -63,7 +63,8 @@ static SwReaderWriterEntry aReaderWriter[] =
     SwReaderWriterEntry( nullptr,               &::GetXMLWriter,  true  ),
     SwReaderWriterEntry( nullptr,               &::GetASCWriter,  false ),
     SwReaderWriterEntry( nullptr,               &::GetASCWriter,  true  ),
-    SwReaderWriterEntry( &::GetDOCXReader,      nullptr,          true  )
+    SwReaderWriterEntry( &::GetDOCXReader,      nullptr,          true  ),
+    SwReaderWriterEntry(nullptr, &GetMDWriter, false),
 };
 
 Reader* SwReaderWriterEntry::GetReader()
diff --git a/sw/source/filter/basflt/iodetect.cxx 
b/sw/source/filter/basflt/iodetect.cxx
index ac857a806b55..144bc1c86bb0 100644
--- a/sw/source/filter/basflt/iodetect.cxx
+++ b/sw/source/filter/basflt/iodetect.cxx
@@ -49,7 +49,8 @@ SwIoDetect aFilterDetect[] =
     SwIoDetect( FILTER_XML ),
     SwIoDetect( FILTER_TEXT_DLG ),
     SwIoDetect( FILTER_TEXT ),
-    SwIoDetect( FILTER_DOCX )
+    SwIoDetect( FILTER_DOCX ),
+    SwIoDetect( FILTER_MD ),
 };
 
 OUString SwIoSystem::GetSubStorageName( const SfxFilter& rFltr )
diff --git a/sw/source/filter/md/wrtmd.cxx b/sw/source/filter/md/wrtmd.cxx
new file mode 100644
index 000000000000..8124a1100fac
--- /dev/null
+++ b/sw/source/filter/md/wrtmd.cxx
@@ -0,0 +1,149 @@
+/* -*- 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 <sal/config.h>
+
+#include <editeng/formatbreakitem.hxx>
+#include <sal/log.hxx>
+#include <officecfg/Office/Writer.hxx>
+
+#include <fmtpdsc.hxx>
+#include <mdiexp.hxx>
+#include <ndtxt.hxx>
+#include <strings.hrc>
+#include "wrtmd.hxx"
+
+namespace
+{
+/* Output of the nodes*/
+void OutMarkdown_SwTextNode(SwMDWriter& /*rWrt*/, const SwTextNode& /*rNode*/) 
{}
+}
+
+SwMDWriter::SwMDWriter(const OUString& rBaseURL) { SetBaseURL(rBaseURL); }
+
+ErrCode SwMDWriter::WriteStream()
+{
+    if (m_bShowProgress)
+        ::StartProgress(STR_STATSTR_W4WWRITE, 0, 
sal_Int32(m_pDoc->GetNodes().Count()),
+                        m_pDoc->GetDocShell());
+
+    // respect table and section at document beginning
+    {
+        if (m_bWriteAll)
+        {
+            while (const SwStartNode* pTNd = 
m_pCurrentPam->GetPointNode().FindTableBoxStartNode())
+            {
+                // start with table node !!
+                m_pCurrentPam->GetPoint()->Assign(*pTNd->FindTableNode());
+
+                if (m_bWriteOnlyFirstTable)
+                    m_pCurrentPam->GetMark()->Assign(
+                        *m_pCurrentPam->GetPointNode().EndOfSectionNode());
+            }
+        }
+
+        // first node (which can contain a page break)
+        m_nStartNodeIndex = m_pCurrentPam->GetPoint()->GetNode().GetIndex();
+
+        for (SwSectionNode* pSNd = 
m_pCurrentPam->GetPointNode().FindSectionNode(); pSNd;
+             pSNd = pSNd->StartOfSectionNode()->FindSectionNode())
+        {
+            if (m_bWriteAll)
+            {
+                // start with section node !!
+                m_pCurrentPam->GetPoint()->Assign(*pSNd);
+            }
+        }
+    }
+
+    Out_SwDoc(m_pOrigPam);
+
+    if (m_bShowProgress)
+        ::EndProgress(m_pDoc->GetDocShell());
+    return ERRCODE_NONE;
+}
+
+void SwMDWriter::Out_SwDoc(SwPaM* pPam)
+{
+    bool bSaveWriteAll = m_bWriteAll;
+    bool bIncludeHidden
+        = 
officecfg::Office::Writer::FilterFlags::Markdown::IncludeHiddenText::get();
+    bool bFirstLine = true;
+
+    do
+    {
+        m_bWriteAll = bSaveWriteAll;
+
+        while (*m_pCurrentPam->GetPoint() <= *m_pCurrentPam->GetMark())
+        {
+            SwNode& rNd = m_pCurrentPam->GetPointNode();
+
+            SAL_WARN_IF(rNd.IsGrfNode() || rNd.IsOLENode(), "sw.md",
+                        "Unexpected Grf- or OLE-Node here");
+
+            if (SwTextNode* pTextNd = rNd.GetTextNode())
+            {
+                if (bIncludeHidden || !pTextNd->IsHidden())
+                {
+                    if (!bFirstLine)
+                        m_pCurrentPam->GetPoint()->SetContent(0);
+
+                    OutMarkdown_SwTextNode(*this, *pTextNd);
+                }
+            }
+            else if (rNd.IsTableNode())
+            {
+                // TODO
+            }
+            else if (rNd.IsSectionNode())
+            {
+                SwSectionNode* pSectionNode = rNd.GetSectionNode();
+                if (!pSectionNode->GetSection().IsHiddenFlag() || 
bIncludeHidden)
+                {
+                    // TODO
+                }
+            }
+            else if (&rNd == &m_pDoc->GetNodes().GetEndOfContent())
+                break;
+
+            m_pCurrentPam->GetPoint()->Adjust(SwNodeOffset(+1)); // move
+            SwNodeOffset nPos = m_pCurrentPam->GetPoint()->GetNodeIndex();
+
+            if (m_bShowProgress)
+                ::SetProgressState(sal_Int32(nPos), m_pDoc->GetDocShell()); // 
How far ?
+
+            /* If only the selected area should be saved, so only the complete
+             * nodes should be saved, this means the first and n-th node
+             * partly, the 2nd till n-1 node complete. (complete means with
+             * all formats!)
+             */
+            m_bWriteAll = bSaveWriteAll || nPos != 
m_pCurrentPam->GetMark()->GetNodeIndex();
+            bFirstLine = false;
+        }
+    } while (CopyNextPam(&pPam)); // until all PaM's processed
+
+    m_bWriteAll = bSaveWriteAll; // reset to old values
+}
+
+void GetMDWriter(std::u16string_view /*rFilterOptions*/, const OUString& 
rBaseURL, WriterRef& xRet)
+{
+    xRet = new SwMDWriter(rBaseURL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sw/source/filter/md/wrtmd.hxx b/sw/source/filter/md/wrtmd.hxx
new file mode 100644
index 000000000000..6b26135ea5a5
--- /dev/null
+++ b/sw/source/filter/md/wrtmd.hxx
@@ -0,0 +1,47 @@
+/* -*- 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 <sal/config.h>
+
+#include <rtl/ustring.hxx>
+
+#include <shellio.hxx>
+#include <swdllapi.h>
+
+class SwMDWriter : public Writer
+{
+public:
+    SW_DLLPUBLIC explicit SwMDWriter(const OUString& rBaseURL);
+
+    bool isInTable() const { return m_bOutTable; }
+    SwNodeOffset StartNodeIndex() const { return m_nStartNodeIndex; }
+
+protected:
+    ErrCode WriteStream() override;
+
+private:
+    void Out_SwDoc(SwPaM* pPam);
+
+    bool m_bOutTable = false;
+    SwNodeOffset m_nStartNodeIndex{ 0 };
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */

Reply via email to