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: */
