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 0e9b4f08a998269543b1826d6d006d315454173e
Author:     Miklos Vajna <[email protected]>
AuthorDate: Thu Oct 2 10:12:18 2025 +0200
Commit:     Caolán McNamara <[email protected]>
CommitDate: Thu Oct 2 13:27:47 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/+/191758
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Tested-by: Caolán McNamara <[email protected]>
    Reviewed-by: Caolán McNamara <[email protected]>

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 4b6883284216..32bcefc99c28 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>
@@ -830,6 +832,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 a475eb3597f7..81ee40177a4b 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>
 
@@ -216,12 +217,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
             {
@@ -232,7 +242,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