From 145656fdffd701c24b727141ea5d8203f014beb2 Mon Sep 17 00:00:00 2001 From: Mike Kaganski Date: Tue, 15 Apr 2025 13:55:29 +0500 Subject: [PATCH] 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 Reviewed-by: Miklos Vajna (cherry picked from commit 8e83a343a49b2053bc08326a109956f224a47ae1) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185283 Tested-by: Jenkins Reviewed-by: Mike Kaganski --- filter/Configuration_filter.mk | 2 + .../config/fragments/filters/Markdown.xcu | 21 +++ .../fragments/types/generic_Markdown.xcu | 18 +++ include/sal/log-areas.dox | 1 + .../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(-) create mode 100644 filter/source/config/fragments/filters/Markdown.xcu create mode 100644 filter/source/config/fragments/types/generic_Markdown.xcu create mode 100644 sw/source/filter/md/wrtmd.cxx create mode 100644 sw/source/filter/md/wrtmd.hxx diff --git a/filter/Configuration_filter.mk b/filter/Configuration_filter.mk index 42ebab575cd3..dbe04df3f7ba 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 @@ + + + EXPORT ALIEN + + + Markdown + 0 + generic_Markdown + + com.sun.star.text.TextDocument + + Markdown Document + + 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 @@ + + + com.sun.star.comp.filters.PlainTextFilterDetect + + md + text/markdown + false + + Markdown Document + + diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox index 7b4fc359997a..cfa4a2e9ee45 100644 --- a/include/sal/log-areas.dox +++ b/include/sal/log-areas.dox @@ -557,6 +557,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 1acc49a8c739..8fe6aa00847b 100644 --- a/officecfg/registry/schema/org/openoffice/Office/Writer.xcs +++ b/officecfg/registry/schema/org/openoffice/Office/Writer.xcs @@ -6238,6 +6238,17 @@ true + + + Contains settings for the Markdown filter. + + + + Whether hidden text (sections, etc) is included in the output. + + false + + diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk index b007b90de126..6aecbaf797d9 100644 --- a/sw/Library_sw.mk +++ b/sw/Library_sw.mk @@ -567,6 +567,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 260a88134d24..80189e8e707f 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 + +#include +#include +#include + +#include +#include +#include +#include +#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 + +#include + +#include +#include + +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: */