sw/qa/filter/md/data/test.png |binary
 sw/qa/filter/md/md.cxx        |   35 +++++++++++++++++++++++++++++++++++
 sw/source/filter/md/wrtmd.cxx |   21 ++++++++++++++++-----
 3 files changed, 51 insertions(+), 5 deletions(-)

New commits:
commit 8d60fcdac9bc79c7b8956ed3cd9e9f518b28c119
Author:     Miklos Vajna <[email protected]>
AuthorDate: Thu Oct 2 10:12:18 2025 +0200
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Oct 2 16:34:10 2025 +0200

    sw markdown filter: export non-linked inline images
    
    Open sw/qa/filter/md/data/embedded-image.md, save as markdown, the image
    is lost.
    
    What happens is that we have a check if the image is linked (which is
    the usual case if the document was imported from markdown), and only
    then export its URL.
    
    Fix the problem similar to what the HTML export does in
    OutHTML_ImageStart() for the rWrt.mbEmbedImages case: use
    XOutBitmap::GraphicToBase64() to construct a 'data:' URL and write the
    image embedded, since it doesn't really have a URL.
    
    Also don't try to convert this "URL" to a relative one, which makes
    no sense for the 'data:' protocol, but does produce a warning during
    export.
    
    Change-Id: I48ea76c83ac98869cb65830167733a6395e5f80d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191778
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins

diff --git a/sw/qa/filter/md/data/test.png b/sw/qa/filter/md/data/test.png
new file mode 100644
index 000000000000..5ae85192b3b6
Binary files /dev/null and b/sw/qa/filter/md/data/test.png differ
diff --git a/sw/qa/filter/md/md.cxx b/sw/qa/filter/md/md.cxx
index 847ad0bf5ad1..81461b7822cf 100644
--- a/sw/qa/filter/md/md.cxx
+++ b/sw/qa/filter/md/md.cxx
@@ -13,6 +13,8 @@
 
 #include <com/sun/star/style/ParagraphAdjust.hpp>
 
+#include <vcl/graphicfilter.hxx>
+
 #include <docsh.hxx>
 #include <wrtsh.hxx>
 #include <view.hxx>
@@ -840,6 +842,39 @@ CPPUNIT_TEST_FIXTURE(Test, testEmbeddedImageMdImport)
     CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(960), rSize.GetHeight());
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testEmbeddedImageMdExport)
+{
+    // Given a document with an embedded inline image:
+    createSwDoc();
+    SwDocShell* pDocShell = getSwDocShell();
+    SwDoc* pDoc = pDocShell->GetDoc();
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    pWrtShell->Insert(u"A "_ustr);
+    SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, 
RES_FRMATR_END - 1>);
+    SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
+    aFrameSet.Put(aAnchor);
+    OUString aImageURL = createFileURL(u"test.png");
+    SvFileStream aImageStream(aImageURL, StreamMode::READ);
+    GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+    Graphic aGraphic = rFilter.ImportUnloadedGraphic(aImageStream);
+    IDocumentContentOperations& rIDCO = pDoc->getIDocumentContentOperations();
+    SwCursor* pCursor = pWrtShell->GetCursor();
+    SwFlyFrameFormat* pFlyFormat
+        = rIDCO.InsertGraphic(*pCursor, /*rGrfName=*/OUString(), OUString(), 
&aGraphic, &aFrameSet,
+                              /*pGrfAttrSet=*/nullptr, 
/*SwFrameFormat=*/nullptr);
+    pFlyFormat->SetObjDescription(u"mydesc"_ustr);
+    pWrtShell->Insert(u" B"_ustr);
+
+    // When saving that to markdown:
+    save(mpFilter);
+
+    // Then make sure that the embedded image is exported:
+    std::string aActual = TempFileToString();
+    // Without the accompanying fix in place, this test would have failed, 
aActual was 'A  B
'.
+    CPPUNIT_ASSERT(aActual.starts_with("A ![mydesc](data:image/png;base64,"));
+    CPPUNIT_ASSERT(aActual.ends_with(") B" SAL_NEWLINE_STRING));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sw/source/filter/md/wrtmd.cxx b/sw/source/filter/md/wrtmd.cxx
index 6bc5dc8444c2..5ec4008939a2 100644
--- a/sw/source/filter/md/wrtmd.cxx
+++ b/sw/source/filter/md/wrtmd.cxx
@@ -31,6 +31,7 @@
 #include <editeng/fontitem.hxx>
 #include <comphelper/string.hxx>
 #include <svl/urihelper.hxx>
+#include <svx/xoutbmp.hxx>
 
 #include <officecfg/Office/Writer.hxx>
 
@@ -226,12 +227,21 @@ void ApplyItem(SwMDWriter& rWrt, FormattingStatus& 
rChange, const SfxPoolItem& r
             {
                 SwGrfNode* pGrfNode = 
rWrt.m_pDoc->GetNodes()[nStart]->GetGrfNode();
                 aGraphic = pGrfNode->GetGraphic();
-                if (!pGrfNode->IsLinkedFile())
+                if (pGrfNode->IsLinkedFile())
                 {
-                    // Not linked, ignore for now.
-                    break;
+                    pGrfNode->GetFileFilterNms(&aGraphicURL, 
/*pFilterNm=*/nullptr);
+                }
+                else
+                {
+                    // Not linked, image data goes into the "URL".
+                    OUString aGraphicInBase64;
+                    if (!XOutBitmap::GraphicToBase64(aGraphic, 
aGraphicInBase64))
+                    {
+                        break;
+                    }
+
+                    aGraphicURL = "data:" + aGraphicInBase64;
                 }
-                pGrfNode->GetFileFilterNms(&aGraphicURL, 
/*pFilterNm=*/nullptr);
             }
             else
             {
@@ -242,7 +252,8 @@ void ApplyItem(SwMDWriter& rWrt, FormattingStatus& rChange, 
const SfxPoolItem& r
             }
 
             const OUString& rBaseURL = rWrt.GetBaseURL();
-            if (!rBaseURL.isEmpty())
+            INetURLObject aGraphicURLObject(aGraphicURL);
+            if (!rBaseURL.isEmpty() && aGraphicURLObject.GetProtocol() != 
INetProtocol::Data)
             {
                 aGraphicURL = 
URIHelper::simpleNormalizedMakeRelative(rBaseURL, aGraphicURL);
             }

Reply via email to