[Libreoffice-commits] core.git: Branch 'distro/vector/vector-7.0' - 8 commits - drawinglayer/source include/svx include/vcl svx/source sw/inc sw/qa sw/source vcl/source

Miklos Vajna (via logerrit) logerrit at kemper.freedesktop.org
Mon Jun 7 10:43:48 UTC 2021


 drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx |   25 
 include/svx/svdxcgv.hxx                                      |    2 
 include/vcl/vectorgraphicdata.hxx                            |    5 
 svx/source/svdraw/svdxcgv.cxx                                |   14 
 sw/inc/frmfmt.hxx                                            |    6 
 sw/qa/extras/htmlexport/htmlexport.cxx                       |  363 ++++++++++-
 sw/source/core/layout/paintfrm.cxx                           |    8 
 sw/source/filter/html/README                                 |   42 +
 sw/source/filter/html/htmlflywriter.cxx                      |  237 +++++--
 sw/source/filter/html/htmlplug.cxx                           |    5 
 sw/source/filter/html/wrthtml.cxx                            |   18 
 sw/source/filter/html/wrthtml.hxx                            |    9 
 vcl/source/gdi/vectorgraphicdata.cxx                         |   14 
 13 files changed, 634 insertions(+), 114 deletions(-)

New commits:
commit 506054ed260e27717783b33bafd8289ba5e0d573
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Fri Jun 4 15:58:01 2021 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Mon Jun 7 10:50:22 2021 +0200

    sw HTML export: allow custom DPI for the bitmaps of shapes
    
    But leave the CSS pixel size of them unchanged in the HTML markup.
    
    Also add some documentation on the various options, so one doesn't have
    to dig them out from testcases.
    
    (cherry picked from commit 04716690f6c5193f15868bc71e7d17c53e085a54)
    
    Conflicts:
            include/vcl/vectorgraphicdata.hxx
            sw/qa/extras/htmlexport/htmlexport.cxx
            sw/source/core/layout/paintfrm.cxx
            vcl/source/gdi/vectorgraphicdata.cxx
    
    Change-Id: I6c6ee4e9c98d674f44e7c5835f2e6a6737e13f34

diff --git a/include/svx/svdxcgv.hxx b/include/svx/svdxcgv.hxx
index ea96eefe180d..0afaa7c6ad6f 100644
--- a/include/svx/svdxcgv.hxx
+++ b/include/svx/svdxcgv.hxx
@@ -58,7 +58,7 @@ public:
 
     // Draw all marked objects onto a bitmap, with the display's color depth
     // and resolution
-    BitmapEx GetMarkedObjBitmapEx(bool bNoVDevIfOneBmpMarked = false) const;
+    BitmapEx GetMarkedObjBitmapEx(bool bNoVDevIfOneBmpMarked = false, const std::optional<Size>& rTargetDPI = std::nullopt) const;
 
     // Copy all marked objects to a new model, consisting of exactly one page,
     // with the flag PageNotValid set. This means, that only the page's objects
diff --git a/include/vcl/vectorgraphicdata.hxx b/include/vcl/vectorgraphicdata.hxx
index 6eefa0e9804e..18c4b36615e7 100644
--- a/include/vcl/vectorgraphicdata.hxx
+++ b/include/vcl/vectorgraphicdata.hxx
@@ -27,6 +27,7 @@
 #include <deque>
 #include <memory>
 #include <algorithm>
+#include <optional>
 
 namespace com::sun::star::graphic { class XPrimitive2D; }
 struct WmfExternal;
@@ -41,7 +42,8 @@ BitmapEx VCL_DLLPUBLIC convertPrimitive2DSequenceToBitmapEx(
     const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > >& rSequence,
     const basegfx::B2DRange& rTargetRange,
     const sal_uInt32 nMaximumQuadraticPixels = 500000,
-    const MapUnit eTargetUnit = MapUnit::Map100thMM);
+    const MapUnit eTargetUnit = MapUnit::Map100thMM,
+    const std::optional<Size>& rTargetDPI = std::nullopt);
 
 
 enum class VectorGraphicDataType
diff --git a/svx/source/svdraw/svdxcgv.cxx b/svx/source/svdraw/svdxcgv.cxx
index f1852e712ec4..a576b8ee4ab0 100644
--- a/svx/source/svdraw/svdxcgv.cxx
+++ b/svx/source/svdraw/svdxcgv.cxx
@@ -425,7 +425,7 @@ void SdrExchangeView::ImpPasteObject(SdrObject* pObj, SdrObjList& rLst, const Po
     }
 }
 
-BitmapEx SdrExchangeView::GetMarkedObjBitmapEx(bool bNoVDevIfOneBmpMarked) const
+BitmapEx SdrExchangeView::GetMarkedObjBitmapEx(bool bNoVDevIfOneBmpMarked, const std::optional<Size>& rTargetDPI) const
 {
     BitmapEx aBmp;
 
@@ -503,7 +503,8 @@ BitmapEx SdrExchangeView::GetMarkedObjBitmapEx(bool bNoVDevIfOneBmpMarked) const
                         xPrimitives,
                         aRange,
                         /*nMaximumQuadraticPixels=*/ 500000,
-                        eRangeUnit);
+                        eRangeUnit,
+                        rTargetDPI);
                 }
             }
         }
diff --git a/sw/inc/frmfmt.hxx b/sw/inc/frmfmt.hxx
index d739925f81f4..e2cb600f2673 100644
--- a/sw/inc/frmfmt.hxx
+++ b/sw/inc/frmfmt.hxx
@@ -115,7 +115,7 @@ public:
     /// Creates the views.
     virtual void MakeFrames();
 
-    virtual Graphic MakeGraphic( ImageMap* pMap = nullptr );
+    virtual Graphic MakeGraphic( ImageMap* pMap = nullptr, const std::optional<Size>& rTargetDPI = std::nullopt );
 
     /**  @return the IMapObject defined at format (Fly)
         in the ImageMap at position Point.
@@ -210,7 +210,7 @@ public:
 
     SwAnchoredObject* GetAnchoredObj() const;
 
-    virtual Graphic MakeGraphic( ImageMap* pMap = nullptr ) override;
+    virtual Graphic MakeGraphic( ImageMap* pMap = nullptr, const std::optional<Size>& rTargetDPI = std::nullopt ) override;
 
     virtual bool GetInfo( SfxPoolItem& rInfo ) const override;
 
@@ -386,7 +386,7 @@ public:
      Reset delete marks. */
     virtual void MakeFrames() override;
 
-    virtual Graphic MakeGraphic( ImageMap* pMap = nullptr ) override;
+    virtual Graphic MakeGraphic( ImageMap* pMap = nullptr, const std::optional<Size>& rTargetDPI = std::nullopt ) override;
 
     virtual SwFrameFormat::tLayoutDir GetLayoutDir() const override;
     virtual void SetLayoutDir( const SwFrameFormat::tLayoutDir _eLayoutDir ) override;
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 68d816b7dacd..69ba0bd54c36 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -38,6 +38,7 @@
 #include <filter/msfilter/rtfutil.hxx>
 #include <sot/storage.hxx>
 #include <svl/eitem.hxx>
+#include <vcl/graphicfilter.hxx>
 
 namespace
 {
@@ -259,6 +260,7 @@ class SwHtmlDomExportTest : public SwModelTestBase, public HtmlTestTools
 public:
     /// Get the .ole path, assuming maTempFile is an XHTML export result.
     OUString GetOlePath();
+    OUString GetPngPath();
     /// Parse the ole1 data out of an RTF fragment URL.
     void ParseOle1FromRtfUrl(const OUString& rRtfUrl, SvMemoryStream& rOle1);
     /// Export using the C++ HTML export filter, with xhtmlns=reqif-xhtml.
@@ -281,6 +283,22 @@ OUString SwHtmlDomExportTest::GetOlePath()
     return aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE);
 }
 
+OUString SwHtmlDomExportTest::GetPngPath()
+{
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pDoc);
+    OUString aPngPath = getXPath(
+        pDoc, "/reqif-xhtml:html/reqif-xhtml:div/reqif-xhtml:p/reqif-xhtml:object", "data");
+    OUString aPngSuffix(".png");
+    CPPUNIT_ASSERT(aPngPath.endsWith(aPngSuffix));
+    INetURLObject aUrl(maTempFile.GetURL());
+    aUrl.setBase(aPngPath.copy(0, aPngPath.getLength() - aPngSuffix.getLength()));
+    aUrl.setExtension(u"png");
+    return aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+}
+
 void SwHtmlDomExportTest::ParseOle1FromRtfUrl(const OUString& rRtfUrl, SvMemoryStream& rOle1)
 {
     SvMemoryStream aRtf;
@@ -1736,6 +1754,55 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedShapeAsPNG)
                 OUString::number(aPixelSize.getWidth()));
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedShapeAsPNGCustomDPI)
+{
+    // Given a document with a shape:
+    loadURL("private:factory/swriter", nullptr);
+    uno::Reference<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+    uno::Reference<drawing::XShape> xShape(
+        xFactory->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
+    xShape->setSize(awt::Size(7145, 5240));
+    uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+    xDrawPageSupplier->getDrawPage()->add(xShape);
+    Size aSystemDPI(
+        Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+    sal_Int32 nDPI = aSystemDPI.getWidth() * 2;
+
+    // When exporting to XHTML:
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+        comphelper::makePropertyValue("ShapeDPI", nDPI),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+    // Then make sure the shape is embedded as a PNG:
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pXmlDoc);
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
+
+    // Then check the pixel size of the shape:
+    Size aPixelSize(Application::GetDefaultDevice()->LogicToPixel(Size(7145, 5240),
+                                                                  MapMode(MapUnit::Map100thMM)));
+    long nPNGWidth = aPixelSize.getWidth() * 2;
+    OUString aPngUrl = GetPngPath();
+    SvFileStream aFileStream(aPngUrl, StreamMode::READ);
+    GraphicDescriptor aDescriptor(aFileStream, nullptr);
+    aDescriptor.Detect(/*bExtendedInfo=*/true);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 540
+    // - Actual  : 270
+    // i.e. setting a double DPI didn't result in larger pixel width of the PNG.
+    CPPUNIT_ASSERT_EQUAL(nPNGWidth, aDescriptor.GetSizePixel().getWidth());
+
+    // Then make sure the shape's logic size (in CSS pixels) don't change:
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "width",
+                OUString::number(aPixelSize.getWidth()));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/paintfrm.cxx b/sw/source/core/layout/paintfrm.cxx
index 7e398600603d..4692affd6a8e 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -7364,12 +7364,12 @@ void SetOutDevAndWin( SwViewShell *pSh, OutputDevice *pO,
     pSh->mpOpt->SetZoom( nZoom );
 }
 
-Graphic SwFrameFormat::MakeGraphic( ImageMap* )
+Graphic SwFrameFormat::MakeGraphic( ImageMap*, const std::optional<Size>& /*rTargetDPI*/ )
 {
     return Graphic();
 }
 
-Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap )
+Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap, const std::optional<Size>& /*rTargetDPI*/ )
 {
     Graphic aRet;
     //search any Fly!
@@ -7476,7 +7476,7 @@ Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap )
     return aRet;
 }
 
-Graphic SwDrawFrameFormat::MakeGraphic( ImageMap* )
+Graphic SwDrawFrameFormat::MakeGraphic( ImageMap*, const std::optional<Size>& rTargetDPI )
 {
     Graphic aRet;
     SwDrawModel* pMod = getIDocumentDrawModelAccess().GetDrawModel();
@@ -7486,7 +7486,7 @@ Graphic SwDrawFrameFormat::MakeGraphic( ImageMap* )
         std::unique_ptr<SdrView> pView( new SdrView( *pMod ) );
         SdrPageView *pPgView = pView->ShowSdrPage(pView->GetModel()->GetPage(0));
         pView->MarkObj( pObj, pPgView );
-        aRet = pView->GetMarkedObjBitmapEx();
+        aRet = pView->GetMarkedObjBitmapEx(/*bNoVDevIfOneBmpMarked=*/false, rTargetDPI);
         pView->HideSdrPage();
     }
     return aRet;
diff --git a/sw/source/filter/html/README b/sw/source/filter/html/README
new file mode 100644
index 000000000000..7b16fb0838a3
--- /dev/null
+++ b/sw/source/filter/html/README
@@ -0,0 +1,42 @@
+This filter is used when the "HTML (StarWriter)" filter is invoked.
+
+Import options:
+
+- FilterOptions: string
+
+  - xhtmlns=reqif-xhtml: actives the ReqIF mode
+
+- AllowedRTFOLEMimeTypes: sequence<string>
+
+  In case an (UNO) client wants to limit the accepted set of MIME types for
+  OLE objects in ReqIF mode, that's possible via this parameter.
+
+  Any MIME type is accepted by default.
+
+Export options:
+
+- FilterOptions: string
+
+  - SkipImages: skips images
+
+  - SkipHeaderFooter: skips the header and footer
+
+  - EmbedImages: inlines images
+
+  - XHTML: activates the XHTML mode
+
+  - xhtmlns=reqif-xhtml: actives the ReqIF mode
+
+- RTFOLEMimeType: string
+
+  Defines what MIME type to use for OLE objects in ReqIF mode, defaults to text/rtf.
+
+- ExportImagesAsOLE: boolean
+
+  Defines if images should be exported as OLE objects or bitmaps, defaults to
+  false.
+
+- ShapeDPI: long (32bit signed int)
+
+  Defines a custom DPI when converting vector shapes to bitmaps, defaults to
+  the system DPI (96 when not on HiDPI).
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
index 182f7c7ead7e..c5a40b3cd67f 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -1807,7 +1807,12 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
         return rWrt;
 
     ImageMap aIMap;
-    Graphic aGraphic( const_cast<SwFrameFormat &>(rFrameFormat).MakeGraphic( &aIMap ) );
+    std::optional<Size> aDPI;
+    if (rHTMLWrt.m_nShapeDPI.has_value())
+    {
+        aDPI.emplace(*rHTMLWrt.m_nShapeDPI, *rHTMLWrt.m_nShapeDPI);
+    }
+    Graphic aGraphic( const_cast<SwFrameFormat &>(rFrameFormat).MakeGraphic( &aIMap, aDPI ) );
 
     if (rHTMLWrt.mbReqIF)
     {
diff --git a/sw/source/filter/html/wrthtml.cxx b/sw/source/filter/html/wrthtml.cxx
index c941d0b38597..080598036c9c 100644
--- a/sw/source/filter/html/wrthtml.cxx
+++ b/sw/source/filter/html/wrthtml.cxx
@@ -206,6 +206,14 @@ void SwHTMLWriter::SetupFilterOptions(SfxMedium& rMedium)
     {
         it->second >>= m_bExportImagesAsOLE;
     }
+
+    it = aStoreMap.find("ShapeDPI");
+    if (it != aStoreMap.end())
+    {
+        sal_Int32 nVal{};
+        it->second >>= nVal;
+        m_nShapeDPI.emplace(nVal);
+    }
 }
 
 void SwHTMLWriter::SetupFilterOptions(const OUString& rFilterOptions)
diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx
index 675c80772fd6..f53b7e659e9d 100644
--- a/sw/source/filter/html/wrthtml.hxx
+++ b/sw/source/filter/html/wrthtml.hxx
@@ -410,6 +410,9 @@ public:
     /// ReqIF mode: export images as OLE objects.
     bool m_bExportImagesAsOLE = false;
 
+    /// DPI used when exporting a vector shape as a bitmap.
+    std::optional<sal_Int32> m_nShapeDPI;
+
     /// Construct an instance of SwHTMLWriter and optionally give it
     /// the filter options directly, which can also be set via SetupFilterOptions().
     explicit SwHTMLWriter( const OUString& rBaseURL, const OUString& rFilterOptions = "" );
diff --git a/vcl/source/gdi/vectorgraphicdata.cxx b/vcl/source/gdi/vectorgraphicdata.cxx
index 855ffdb9083d..27a75b7bbb98 100644
--- a/vcl/source/gdi/vectorgraphicdata.cxx
+++ b/vcl/source/gdi/vectorgraphicdata.cxx
@@ -46,7 +46,8 @@ BitmapEx convertPrimitive2DSequenceToBitmapEx(
     const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > >& rSequence,
     const basegfx::B2DRange& rTargetRange,
     const sal_uInt32 nMaximumQuadraticPixels,
-    const MapUnit eTargetUnit)
+    const MapUnit eTargetUnit,
+    const std::optional<Size>& rTargetDPI)
 {
     BitmapEx aRetval;
 
@@ -70,7 +71,11 @@ BitmapEx convertPrimitive2DSequenceToBitmapEx(
             aRealRect.Y2 = rTargetRange.getMaxY();
 
             // get system DPI
-            const Size aDPI(Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+            Size aDPI(Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+            if (rTargetDPI.has_value())
+            {
+                aDPI = *rTargetDPI;
+            }
 
             const uno::Reference< rendering::XBitmap > xBitmap(
                 xPrimitive2DRenderer->rasterize(
commit 8bdd74766b1a03328cb1c8c5bb0bd61917d3abef
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Fri Jun 4 14:02:52 2021 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Jun 4 17:42:47 2021 +0200

    sw HTML export: fix pixel size of shapes
    
    - the twips logic size was set, but it was consumed as mm100 logic size,
      so the pixel size was about half of the correct one
    
    - the HTML export didn't write a logic size ("CSS pixels size") for
      shapes
    
    (cherry picked from commit 26d2b19d4e9388072b8dae574efdf00d7f7a0f2f)
    
    Conflicts:
            drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
    
    Change-Id: I37f6b4acde9d1298fae81f9975e9db95485631ee

diff --git a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
index e4708c4131b4..be63d455850b 100644
--- a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
+++ b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
@@ -36,6 +36,7 @@
 #include <converters.hxx>
 
 #include "xprimitive2drenderer.hxx"
+#include <comphelper/sequenceashashmap.hxx>
 
 using namespace ::com::sun::star;
 
@@ -108,6 +109,16 @@ namespace drawinglayer::unorenderer
             const css::geometry::RealRectangle2D& Range,
             ::sal_uInt32 MaximumQuadraticPixels)
         {
+            MapUnit eRangeUnit = MapUnit::Map100thMM;
+            comphelper::SequenceAsHashMap aViewInformationMap(aViewInformationSequence);
+            auto it = aViewInformationMap.find("RangeUnit");
+            if (it != aViewInformationMap.end())
+            {
+                sal_Int32 nVal{};
+                it->second >>= nVal;
+                eRangeUnit = static_cast<MapUnit>(nVal);
+            }
+
             uno::Reference< rendering::XBitmap > XBitmap;
 
             if(aPrimitive2DSequence.hasElements())
@@ -134,9 +145,17 @@ namespace drawinglayer::unorenderer
                     }
 
                     const geometry::ViewInformation2D aViewInformation2D(aViewInformationSequence);
-                    const double fFactor100th_mmToInch(1.0 / (2.54 * 1000.0));
-                    const sal_uInt32 nDiscreteWidth(basegfx::fround((fWidth * fFactor100th_mmToInch) * DPI_X));
-                    const sal_uInt32 nDiscreteHeight(basegfx::fround((fHeight * fFactor100th_mmToInch) * DPI_Y));
+                    double fFactor{};
+                    if (eRangeUnit == MapUnit::MapTwip)
+                    {
+                        fFactor = 1.0 / (1.44 * 1000.0);
+                    }
+                    else
+                    {
+                        fFactor = 1.0 / (2.54 * 1000.0);
+                    }
+                    const sal_uInt32 nDiscreteWidth(basegfx::fround((fWidth * fFactor) * DPI_X));
+                    const sal_uInt32 nDiscreteHeight(basegfx::fround((fHeight * fFactor) * DPI_Y));
 
                     basegfx::B2DHomMatrix aEmbedding(
                         basegfx::utils::createTranslateB2DHomMatrix(
diff --git a/include/vcl/vectorgraphicdata.hxx b/include/vcl/vectorgraphicdata.hxx
index 9151a3425197..6eefa0e9804e 100644
--- a/include/vcl/vectorgraphicdata.hxx
+++ b/include/vcl/vectorgraphicdata.hxx
@@ -40,7 +40,8 @@ typedef css::uno::Sequence<sal_Int8> VectorGraphicDataArray;
 BitmapEx VCL_DLLPUBLIC convertPrimitive2DSequenceToBitmapEx(
     const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > >& rSequence,
     const basegfx::B2DRange& rTargetRange,
-    const sal_uInt32 nMaximumQuadraticPixels = 500000);
+    const sal_uInt32 nMaximumQuadraticPixels = 500000,
+    const MapUnit eTargetUnit = MapUnit::Map100thMM);
 
 
 enum class VectorGraphicDataType
diff --git a/svx/source/svdraw/svdxcgv.cxx b/svx/source/svdraw/svdxcgv.cxx
index e8c8a3218a89..f1852e712ec4 100644
--- a/svx/source/svdraw/svdxcgv.cxx
+++ b/svx/source/svdraw/svdxcgv.cxx
@@ -490,11 +490,20 @@ BitmapEx SdrExchangeView::GetMarkedObjBitmapEx(bool bNoVDevIfOneBmpMarked) const
 
                 if(!aRange.isEmpty())
                 {
+                    MapUnit eRangeUnit = MapUnit::Map100thMM;
+
+                    if (GetModel()->IsWriter())
+                    {
+                        eRangeUnit = MapUnit::MapTwip;
+                    }
+
                     // if we have geometry and it has a range, convert to BitmapEx using
                     // common tooling
                     aBmp = convertPrimitive2DSequenceToBitmapEx(
                         xPrimitives,
-                        aRange);
+                        aRange,
+                        /*nMaximumQuadraticPixels=*/ 500000,
+                        eRangeUnit);
                 }
             }
         }
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 21367abfaa0f..68d816b7dacd 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -1725,6 +1725,15 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedShapeAsPNG)
     // - Actual  : image/x-vclgraphic
     // i.e. the result was invalid ReqIF.
     assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
+
+    // Then check the pixel size of the shape:
+    Size aPixelSize(Application::GetDefaultDevice()->LogicToPixel(Size(10000, 10000),
+                                                                  MapMode(MapUnit::Map100thMM)));
+    // Without the accompanying fix in place, this test would have failed with:
+    // - no attribute 'width' exist
+    // i.e. shapes had no width.
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "width",
+                OUString::number(aPixelSize.getWidth()));
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
index 4cc591c9d566..182f7c7ead7e 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -936,12 +936,28 @@ void SwHTMLWriter::writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameForma
 
     // "width" and/or "height"
     // Only output SwFrameSize::Variable/SwFrameSize::Minimum if ANYSIZE is set
+    const SwFormatFrameSize* pFSItem = nullptr;
+    std::optional<SwFormatFrameSize> aFrameSize;
+    if (SfxItemState::SET == rItemSet.GetItemState( RES_FRM_SIZE, true, &pItem ))
+    {
+        pFSItem = static_cast<const SwFormatFrameSize *>(pItem);
+    }
+    else if (const SdrObject* pObject = rFrameFormat.FindSdrObject())
+    {
+        // Write size for Draw shapes as well.
+        const tools::Rectangle& rSnapRect = pObject->GetSnapRect();
+        aFrameSize.emplace();
+        aFrameSize->SetWidthSizeType(SwFrameSize::Fixed);
+        aFrameSize->SetWidth(rSnapRect.getWidth());
+        aFrameSize->SetHeightSizeType(SwFrameSize::Fixed);
+        aFrameSize->SetHeight(rSnapRect.getHeight());
+        pFSItem = &*aFrameSize;
+    }
     if( (nFrameOptions & HtmlFrmOpts::Size) &&
-        SfxItemState::SET == rItemSet.GetItemState( RES_FRM_SIZE, true, &pItem ) &&
+        pFSItem &&
         ( (nFrameOptions & HtmlFrmOpts::AnySize) ||
-          SwFrameSize::Fixed == static_cast<const SwFormatFrameSize *>(pItem)->GetHeightSizeType()) )
+          SwFrameSize::Fixed == pFSItem->GetHeightSizeType()) )
     {
-        const SwFormatFrameSize *pFSItem = static_cast<const SwFormatFrameSize *>(pItem);
         sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent();
         sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent();
 
diff --git a/vcl/source/gdi/vectorgraphicdata.cxx b/vcl/source/gdi/vectorgraphicdata.cxx
index fe9ca5535e6e..855ffdb9083d 100644
--- a/vcl/source/gdi/vectorgraphicdata.cxx
+++ b/vcl/source/gdi/vectorgraphicdata.cxx
@@ -45,7 +45,8 @@ using namespace ::com::sun::star;
 BitmapEx convertPrimitive2DSequenceToBitmapEx(
     const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > >& rSequence,
     const basegfx::B2DRange& rTargetRange,
-    const sal_uInt32 nMaximumQuadraticPixels)
+    const sal_uInt32 nMaximumQuadraticPixels,
+    const MapUnit eTargetUnit)
 {
     BitmapEx aRetval;
 
@@ -58,7 +59,9 @@ BitmapEx convertPrimitive2DSequenceToBitmapEx(
             uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext());
             const uno::Reference< graphic::XPrimitive2DRenderer > xPrimitive2DRenderer = graphic::Primitive2DTools::create(xContext);
 
-            uno::Sequence< beans::PropertyValue > aViewParameters;
+            uno::Sequence< beans::PropertyValue > aViewParameters = {
+                comphelper::makePropertyValue("RangeUnit", static_cast<sal_Int32>(eTargetUnit)),
+            };
             geometry::RealRectangle2D aRealRect;
 
             aRealRect.X1 = rTargetRange.getMinX();
commit 72b12ac7afd40e5d51ac4edd1d57505994067cc9
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Thu Jun 3 17:10:57 2021 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Jun 4 16:46:43 2021 +0200

    sw XHTML / reqif export: fix PNG export of shapes
    
    image/x-vclgraphic is not something anybody else will understand.
    
    (cherry picked from commit c8a9396e5695675ffe92935a9ba40354fc76ed79)
    
    Change-Id: I5a5b37b7f769de351bd3dfc38ccd57381bc43319

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 4d0e66802d78..21367abfaa0f 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -1696,6 +1696,37 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGShapeAsOLE)
     assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "text/rtf");
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedShapeAsPNG)
+{
+    // Given a document with a shape:
+    loadURL("private:factory/swriter", nullptr);
+    uno::Reference<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+    uno::Reference<drawing::XShape> xShape(
+        xFactory->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
+    xShape->setSize(awt::Size(10000, 10000));
+    uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+    xDrawPageSupplier->getDrawPage()->add(xShape);
+
+    // When exporting to XHTML:
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+    // Then make sure the shape is embedded as a PNG:
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pXmlDoc);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: image/png
+    // - Actual  : image/x-vclgraphic
+    // i.e. the result was invalid ReqIF.
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
index 57a92eeed34f..4cc591c9d566 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -1800,6 +1800,11 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
         {
             aGraphic = pGrafObj->GetGraphic();
         }
+        else
+        {
+            // We only have a bitmap, write that as PNG without any fallback.
+            bWritePNGFallback = false;
+        }
     }
 
     Size aSz( 0, 0 );
commit 9beb47b7121fe992a4328719736e2e9f4e11aaf1
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Thu Jun 3 10:03:19 2021 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Jun 4 16:44:39 2021 +0200

    sw XHTML / reqif export: implement OLE wrapper support for shapes
    
    ExportImagesAsOLE=true was only handled for Writer images, not for Draw
    shapes, fix this.
    
    (cherry picked from commit 027c7b411389127d77a271e53922edb4d7095a2e)
    
    Conflicts:
            sw/source/filter/html/htmlflywriter.cxx
    
    Change-Id: If5018ff4b7c8b303d63a862f7428aa4e8b77d498

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 7824183fd466..4d0e66802d78 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -1661,6 +1661,41 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedJPGShapeDirectly)
                 "image/png");
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGShapeAsOLE)
+{
+    // Given a document with an image shape:
+    loadURL("private:factory/swriter", nullptr);
+    OUString aImageURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "ole2.png";
+    uno::Reference<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+    uno::Reference<drawing::XShape> xShape(
+        xFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY);
+    xShape->setSize(awt::Size(10000, 10000));
+    uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+    xShapeProps->setPropertyValue("GraphicURL", uno::makeAny(aImageURL));
+    uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+    xDrawPageSupplier->getDrawPage()->add(xShape);
+
+    // When exporting to XHTML:
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+        comphelper::makePropertyValue("ExportImagesAsOLE", true),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+    // Then make sure the PNG is embedded with an RTF wrapper:
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pXmlDoc);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: text/rtf
+    // - Actual  : image/png
+    // i.e. the OLE wrapper around the PNG was missing.
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "text/rtf");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
index 060107b67e85..57a92eeed34f 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -1740,9 +1740,52 @@ static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt,
     return rWrt;
 }
 
+/// Starts the OLE version of an image in the ReqIF + OLE case.
+static void OutHTML_ImageOLEStart(SwHTMLWriter& rHTMLWrt, const Graphic& rGraphic,
+                                  const SwFrameFormat& rFrameFormat)
+{
+    if (rHTMLWrt.mbReqIF && rHTMLWrt.m_bExportImagesAsOLE)
+    {
+        // Write the original image as an RTF fragment.
+        OUString aFileName;
+        if (rHTMLWrt.GetOrigFileName())
+            aFileName = *rHTMLWrt.GetOrigFileName();
+        INetURLObject aURL(aFileName);
+        OUString aName = aURL.getBase() + "_" + aURL.getExtension() + "_"
+                         + OUString::number(rGraphic.GetChecksum(), 16);
+        aURL.setBase(aName);
+        aURL.setExtension(u"ole");
+        aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+        SvFileStream aOutStream(aFileName, StreamMode::WRITE);
+        if (!SwReqIfReader::WrapGraphicInRtf(rGraphic, rFrameFormat, aOutStream))
+            SAL_WARN("sw.html", "SwReqIfReader::WrapGraphicInRtf() failed");
+
+        // Refer to this data.
+        aFileName = URIHelper::simpleNormalizedMakeRelative(rHTMLWrt.GetBaseURL(), aFileName);
+        rHTMLWrt.Strm().WriteOString(
+            OString("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object));
+        rHTMLWrt.Strm().WriteOString(OString(" data=\"" + aFileName.toUtf8() + "\""));
+        rHTMLWrt.Strm().WriteOString(" type=\"text/rtf\"");
+        rHTMLWrt.Strm().WriteOString(">");
+        rHTMLWrt.OutNewLine();
+    }
+}
+
+/// Ends the OLE version of an image in the ReqIF + OLE case.
+static void OutHTML_ImageOLEEnd(SwHTMLWriter& rHTMLWrt)
+{
+    if (rHTMLWrt.mbReqIF && rHTMLWrt.m_bExportImagesAsOLE)
+    {
+        rHTMLWrt.Strm().WriteOString(
+            OString("</" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">"));
+    }
+}
+
 static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFrameFormat, bool bPNGFallback)
 {
     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
+    bool bWritePNGFallback = !rHTMLWrt.m_bExportImagesAsOLE && bPNGFallback;
 
     if (rHTMLWrt.mbSkipImages)
         return rWrt;
@@ -1770,7 +1813,7 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
         OUString aFilterName("JPG");
         XOutFlags nFlags = XOutFlags::UseGifIfPossible | XOutFlags::UseNativeIfPossible;
 
-        if (rHTMLWrt.mbReqIF && !bPNGFallback)
+        if (rHTMLWrt.mbReqIF && !bWritePNGFallback)
         {
             // Writing image without fallback PNG in ReqIF mode: force PNG output.
             aFilterName = "PNG";
@@ -1803,19 +1846,23 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
     if (xGraphic.is() && aMimeType.isEmpty())
         xGraphic->getPropertyValue("MimeType") >>= aMimeType;
 
+    OutHTML_ImageOLEStart(rHTMLWrt, aGraphic, rFrameFormat);
+
     HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
     OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz,
                     HtmlFrmOpts::GenImgMask, "frame",
                     aIMap.GetIMapObjectCount() ? &aIMap : nullptr, aMimeType );
 
     GfxLink aLink = aGraphic.GetGfxLink();
-    if (bPNGFallback && aLink.GetType() != GfxLinkType::NativePng)
+    if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
     {
         OutHTML_FrameFormatAsImage( rWrt, rFrameFormat, /*bPNGFallback=*/false);
     }
 
     OutHTML_ImageEnd(aHtml, rWrt);
 
+    OutHTML_ImageOLEEnd(rHTMLWrt);
+
     return rWrt;
 }
 
@@ -1915,32 +1962,7 @@ static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rF
     if (xGraphic.is() && aMimeType.isEmpty())
         xGraphic->getPropertyValue("MimeType") >>= aMimeType;
 
-    if (rHTMLWrt.mbReqIF && rHTMLWrt.m_bExportImagesAsOLE)
-    {
-        // Write the original image as an RTF fragment.
-        OUString aFileName;
-        if (rHTMLWrt.GetOrigFileName())
-            aFileName = *rHTMLWrt.GetOrigFileName();
-        INetURLObject aURL(aFileName);
-        OUString aName = aURL.getBase() + "_" +
-            aURL.getExtension() + "_" +
-            OUString::number(aGraphic.GetChecksum(), 16);
-        aURL.setBase(aName);
-        aURL.setExtension("ole");
-        aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
-
-        SvFileStream aOutStream(aFileName, StreamMode::WRITE);
-        if (!SwReqIfReader::WrapGraphicInRtf(aGraphic, rFrameFormat, aOutStream))
-            SAL_WARN("sw.html", "SwReqIfReader::WrapGraphicInRtf() failed");
-
-        // Refer to this data.
-        aFileName = URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), aFileName);
-        rWrt.Strm().WriteOString("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object);
-        rWrt.Strm().WriteOString(" data=\"" + aFileName.toUtf8() + "\"");
-        rWrt.Strm().WriteOString(" type=\"text/rtf\"");
-        rWrt.Strm().WriteOString(">");
-        rHTMLWrt.OutNewLine();
-    }
+    OutHTML_ImageOLEStart(rHTMLWrt, aGraphic, rFrameFormat);
 
     HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
     OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, aGraphicURL, aGraphic, pGrfNd->GetTitle(),
@@ -1956,8 +1978,7 @@ static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rF
 
     OutHTML_ImageEnd(aHtml, rWrt);
 
-    if (rHTMLWrt.mbReqIF && rHTMLWrt.m_bExportImagesAsOLE)
-        rWrt.Strm().WriteOString("</" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">");
+    OutHTML_ImageOLEEnd(rHTMLWrt);
 
     return rWrt;
 }
commit 610c37f54f5ffa8bc007570fcdbf9800b09d44db
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Wed Jun 2 12:25:35 2021 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Jun 4 16:34:25 2021 +0200

    sw XHTML / reqif export: export non-PNG graphic shapes directly
    
    But non-PNG needs a PNG fallback, add logic similar to how we already
    handle this in the Writer image case.
    
    (cherry picked from commit e76471c5ce725dae9abb6f78b7674c6f77df34f4)
    
    Change-Id: Id853a7072878cada4a6e1e9367443f869028aa1b

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index a1739d9bbcf3..7824183fd466 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -1625,6 +1625,42 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGShapeDirectly)
     assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedJPGShapeDirectly)
+{
+    // Given a document with an image:
+    loadURL("private:factory/swriter", nullptr);
+    OUString aImageURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "reqif-ole-img.jpg";
+    uno::Reference<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+    uno::Reference<drawing::XShape> xShape(
+        xFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY);
+    xShape->setSize(awt::Size(10000, 10000));
+    uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+    xShapeProps->setPropertyValue("GraphicURL", uno::makeAny(aImageURL));
+    uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+    xDrawPageSupplier->getDrawPage()->add(xShape);
+
+    // When exporting to XHTML:
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+    // Then make sure the JPG is embedded directly, without an RTF wrapper:
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pXmlDoc);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: image/jpeg
+    // - Actual  : image/png
+    // i.e. first the original JPG data was lost, then the inner PNG fallback was missing.
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/jpeg");
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:object", "type",
+                "image/png");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
index f4fde08eeb32..060107b67e85 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -134,7 +134,7 @@ static Writer& OutHTML_FrameFormatAsMulticol( Writer& rWrt, const SwFrameFormat&
 static Writer& OutHTML_FrameFormatAsSpacer( Writer& rWrt, const SwFrameFormat& rFormat );
 static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt,
                                           const SwFrameFormat& rFrameFormat, bool bSpan );
-static Writer& OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFormat );
+static Writer& OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFormat, bool bPNGFallback );
 
 static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rFormat,
                                       bool bInCntnr, bool bPNGFallback );
@@ -501,7 +501,7 @@ void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFra
                     static_cast<const SwDrawFrameFormat &>(rFrameFormat), *pSdrObject );
         break;
     case HtmlOut::GraphicFrame:
-        OutHTML_FrameFormatAsImage( *this, rFrameFormat );
+        OutHTML_FrameFormatAsImage( *this, rFrameFormat, /*bPNGFallback=*/true );
         break;
     }
 
@@ -1740,7 +1740,7 @@ static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt,
     return rWrt;
 }
 
-static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFrameFormat )
+static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFrameFormat, bool bPNGFallback)
 {
     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
 
@@ -1749,6 +1749,16 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
 
     ImageMap aIMap;
     Graphic aGraphic( const_cast<SwFrameFormat &>(rFrameFormat).MakeGraphic( &aIMap ) );
+
+    if (rHTMLWrt.mbReqIF)
+    {
+        // ImageMap doesn't seem to be allowed in reqif.
+        if (auto pGrafObj = dynamic_cast<const SdrGrafObj*>(rFrameFormat.FindSdrObject()))
+        {
+            aGraphic = pGrafObj->GetGraphic();
+        }
+    }
+
     Size aSz( 0, 0 );
     OUString GraphicURL;
     OUString aMimeType("image/jpeg");
@@ -1760,13 +1770,19 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
         OUString aFilterName("JPG");
         XOutFlags nFlags = XOutFlags::UseGifIfPossible | XOutFlags::UseNativeIfPossible;
 
-        if (rHTMLWrt.mbReqIF)
+        if (rHTMLWrt.mbReqIF && !bPNGFallback)
         {
             // Writing image without fallback PNG in ReqIF mode: force PNG output.
             aFilterName = "PNG";
             nFlags = XOutFlags::NONE;
             aMimeType = "image/png";
         }
+        else if (rHTMLWrt.mbReqIF)
+        {
+            // Original format is wanted, don't force JPG.
+            aFilterName.clear();
+            aMimeType.clear();
+        }
 
         if( aGraphic.GetType() == GraphicType::NONE ||
             XOutBitmap::WriteGraphic( aGraphic, GraphicURL,
@@ -1783,11 +1799,21 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
             URIHelper::GetMaybeFileHdl() );
 
     }
+    uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY);
+    if (xGraphic.is() && aMimeType.isEmpty())
+        xGraphic->getPropertyValue("MimeType") >>= aMimeType;
 
     HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
     OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz,
                     HtmlFrmOpts::GenImgMask, "frame",
                     aIMap.GetIMapObjectCount() ? &aIMap : nullptr, aMimeType );
+
+    GfxLink aLink = aGraphic.GetGfxLink();
+    if (bPNGFallback && aLink.GetType() != GfxLinkType::NativePng)
+    {
+        OutHTML_FrameFormatAsImage( rWrt, rFrameFormat, /*bPNGFallback=*/false);
+    }
+
     OutHTML_ImageEnd(aHtml, rWrt);
 
     return rWrt;
commit 60d55e21597bd18314398437405bba1cbb016056
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Wed Jun 2 08:48:56 2021 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Jun 4 16:32:34 2021 +0200

    sw XHTML / reqif export: export PNG shapes directly
    
    The old markup was:
    
    <object data="draw-png_xhtml_89305eb5d44b6428.gif">
    
    The new markup is:
    
    <object data="draw-png_xhtml_89305eb5d44b6428.png" type="image/png">
    
    Which now passes the validator.
    
    (cherry picked from commit 4da0de52bbd9a2f53ce0e838f7b22c228e510f97)
    
    Conflicts:
            sw/source/filter/html/htmlflywriter.cxx
    
    Change-Id: Ibcdac7aeef0e39c926176bf8852cffe17a137fa2

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 31b42a5d293f..a1739d9bbcf3 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -1592,6 +1592,39 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedJPGDirectly)
                 "image/png");
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGShapeDirectly)
+{
+    // Given a document with an image shape:
+    loadURL("private:factory/swriter", nullptr);
+    OUString aImageURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "ole2.png";
+    uno::Reference<css::lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+    uno::Reference<drawing::XShape> xShape(
+        xFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY);
+    xShape->setSize(awt::Size(10000, 10000));
+    uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+    xShapeProps->setPropertyValue("GraphicURL", uno::makeAny(aImageURL));
+    uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
+    xDrawPageSupplier->getDrawPage()->add(xShape);
+
+    // When exporting to XHTML:
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+    // Then make sure the PNG is embedded directly, without an RTF wrapper:
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pXmlDoc);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - no attribute 'type' exist
+    // i.e. the PNG was exported as GIF, without an explicit type.
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
index e54502a770c1..f4fde08eeb32 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -40,6 +40,7 @@
 #include <editeng/ulspitem.hxx>
 #include <editeng/brushitem.hxx>
 #include <sal/log.hxx>
+#include <svx/svdograf.hxx>
 
 #include <fmtanchr.hxx>
 #include <fmtornt.hxx>
@@ -1750,15 +1751,27 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
     Graphic aGraphic( const_cast<SwFrameFormat &>(rFrameFormat).MakeGraphic( &aIMap ) );
     Size aSz( 0, 0 );
     OUString GraphicURL;
+    OUString aMimeType("image/jpeg");
     if(!rHTMLWrt.mbEmbedImages)
     {
         if( rHTMLWrt.GetOrigFileName() )
             GraphicURL = *rHTMLWrt.GetOrigFileName();
+
+        OUString aFilterName("JPG");
+        XOutFlags nFlags = XOutFlags::UseGifIfPossible | XOutFlags::UseNativeIfPossible;
+
+        if (rHTMLWrt.mbReqIF)
+        {
+            // Writing image without fallback PNG in ReqIF mode: force PNG output.
+            aFilterName = "PNG";
+            nFlags = XOutFlags::NONE;
+            aMimeType = "image/png";
+        }
+
         if( aGraphic.GetType() == GraphicType::NONE ||
             XOutBitmap::WriteGraphic( aGraphic, GraphicURL,
-                                      "JPG",
-                                      (XOutFlags::UseGifIfPossible|
-                                       XOutFlags::UseNativeIfPossible) ) != ERRCODE_NONE )
+                                      aFilterName,
+                                      nFlags ) != ERRCODE_NONE )
         {
             // empty or incorrect, because there is nothing to output
             rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD;
@@ -1770,10 +1783,11 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
             URIHelper::GetMaybeFileHdl() );
 
     }
+
     HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
     OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz,
                     HtmlFrmOpts::GenImgMask, "frame",
-                    aIMap.GetIMapObjectCount() ? &aIMap : nullptr );
+                    aIMap.GetIMapObjectCount() ? &aIMap : nullptr, aMimeType );
     OutHTML_ImageEnd(aHtml, rWrt);
 
     return rWrt;
commit 645b10a60b672f8162eb11faa8209f3a5aa4e36c
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Tue Jun 1 14:50:01 2021 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Jun 4 16:29:29 2021 +0200

    sw XHTML / reqif export: write PNG fallback for non-PNG images
    
    - split up OutHTML_Image, so it's possible to write
    
    <object type="image/jpeg">
        <object type="image/png"/>
    </object>
    
    - write PNG inside the original image
    
    - disable this when the original format is PNG already
    
    (cherry picked from commit 3fe661041aadbfd945a20afe2310a19f5e76976e)
    
    Conflicts:
            sw/source/filter/html/htmlflywriter.cxx
    
    Change-Id: I3ad40089ee2b02b8850823dd536c58ac59af37f2

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index d505d3ee521f..31b42a5d293f 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -1560,6 +1560,38 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGDirectly)
     assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedJPGDirectly)
+{
+    // Given a document with an image:
+    loadURL("private:factory/swriter", nullptr);
+    OUString aImageURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "reqif-ole-img.jpg";
+    uno::Sequence<beans::PropertyValue> aArgs = {
+        comphelper::makePropertyValue("FileName", aImageURL),
+    };
+    dispatchCommand(mxComponent, ".uno:InsertGraphic", aArgs);
+
+    // When exporting to XHTML:
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+    // Then make sure the JPG is embedded directly, without an RTF wrapper:
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pXmlDoc);
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/jpeg");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: image/jpeg
+    // - Actual  : image/png
+    // i.e. first the original JPG data was lost, then the inner PNG fallback was missing.
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object/reqif-xhtml:object", "type",
+                "image/png");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
index e76dd326d085..e54502a770c1 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -136,7 +136,7 @@ static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt,
 static Writer& OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFormat );
 
 static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rFormat,
-                                      bool bInCntnr );
+                                      bool bInCntnr, bool bPNGFallback );
 
 static Writer& OutHTML_FrameFormatAsMarquee( Writer& rWrt, const SwFrameFormat& rFrameFormat,
                                         const SdrObject& rSdrObj    );
@@ -466,7 +466,7 @@ void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFra
         OutHTML_FrameFormatTableNode( *this, rFrameFormat );
         break;
     case HtmlOut::GraphicNode:      // OK
-        OutHTML_FrameFormatGrfNode( *this, rFrameFormat, !aContainerStr.isEmpty() );
+        OutHTML_FrameFormatGrfNode( *this, rFrameFormat, !aContainerStr.isEmpty(), /*bPNGFallback=*/true );
         break;
     case HtmlOut::OleNode:      // OK
         OutHTML_FrameFormatOLENode( *this, rFrameFormat, !aContainerStr.isEmpty() );
@@ -1195,7 +1195,7 @@ OUString lclWriteOutImap(SwHTMLWriter& rHTMLWrt, const SfxItemSet& rItemSet, con
 
 }
 
-Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
+Writer& OutHTML_ImageStart( HtmlWriter& rHtml, Writer& rWrt, const SwFrameFormat &rFrameFormat,
                        const OUString& rGraphicURL,
                        Graphic const & rGraphic, const OUString& rAlternateText,
                        const Size &rRealSize, HtmlFrmOpts nFrameOpts,
@@ -1231,8 +1231,6 @@ Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
     if( rHTMLWrt.m_bLFPossible )
         rHTMLWrt.OutNewLine( true );
 
-    HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
-
     // <a name=...></a>...<img ...>
     if( pMarkType && !rFrameFormat.GetName().isEmpty() )
     {
@@ -1263,22 +1261,22 @@ Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
 
         if( !aMapURL.isEmpty() || !aName.isEmpty() || !aTarget.isEmpty() || bEvents )
         {
-            aHtml.start(OOO_STRING_SVTOOLS_HTML_anchor);
+            rHtml.start(OOO_STRING_SVTOOLS_HTML_anchor);
 
             // Output "href" element if a link or macro exists
             if( !aMapURL.isEmpty() || bEvents )
             {
-                aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_href, OUStringToOString(rHTMLWrt.convertHyperlinkHRefValue(aMapURL), RTL_TEXTENCODING_UTF8));
+                rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_href, OUStringToOString(rHTMLWrt.convertHyperlinkHRefValue(aMapURL), RTL_TEXTENCODING_UTF8));
             }
 
             if( !aName.isEmpty() )
             {
-                aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_name, OUStringToOString(aName, RTL_TEXTENCODING_UTF8));
+                rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_name, OUStringToOString(aName, RTL_TEXTENCODING_UTF8));
             }
 
             if( !aTarget.isEmpty() )
             {
-                aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_target, OUStringToOString(aTarget, RTL_TEXTENCODING_UTF8));
+                rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_target, OUStringToOString(aTarget, RTL_TEXTENCODING_UTF8));
             }
 
             if( pMacItem )
@@ -1286,7 +1284,7 @@ Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
                 const SvxMacroTableDtor& rMacTable = pMacItem->GetMacroTable();
                 if (!rMacTable.empty())
                 {
-                    HtmlWriterHelper::applyEvents(aHtml, rMacTable, aAnchorEventTable, rHTMLWrt.m_bCfgStarBasic);
+                    HtmlWriterHelper::applyEvents(rHtml, rMacTable, aAnchorEventTable, rHTMLWrt.m_bCfgStarBasic);
                 }
             }
         }
@@ -1352,8 +1350,8 @@ Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
 
         if( pColBorderLine )
         {
-            aHtml.start(OOO_STRING_SVTOOLS_HTML_font);
-            HtmlWriterHelper::applyColor(aHtml, OOO_STRING_SVTOOLS_HTML_O_color, pColBorderLine->GetColor());
+            rHtml.start(OOO_STRING_SVTOOLS_HTML_font);
+            HtmlWriterHelper::applyColor(rHtml, OOO_STRING_SVTOOLS_HTML_O_color, pColBorderLine->GetColor());
         }
     }
 
@@ -1361,7 +1359,7 @@ Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
     if (bReplacement)
         // Write replacement graphic of OLE object as <object>.
         aTag = OOO_STRING_SVTOOLS_HTML_object;
-    aHtml.start(aTag);
+    rHtml.start(aTag);
 
     OStringBuffer sBuffer;
     if(rHTMLWrt.mbEmbedImages)
@@ -1372,7 +1370,7 @@ Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
             sBuffer.append(OOO_STRING_SVTOOLS_HTML_O_data);
             sBuffer.append(":");
             sBuffer.append(OUStringToOString(aGraphicInBase64, RTL_TEXTENCODING_UTF8));
-            aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_src, sBuffer.makeStringAndClear().getStr());
+            rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_src, sBuffer.makeStringAndClear().getStr());
         }
         else
             rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD;
@@ -1383,14 +1381,14 @@ Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
         OString aAttribute(OOO_STRING_SVTOOLS_HTML_O_src);
         if (bReplacement)
             aAttribute = OOO_STRING_SVTOOLS_HTML_O_data;
-        aHtml.attribute(aAttribute, sBuffer.makeStringAndClear().getStr());
+        rHtml.attribute(aAttribute, sBuffer.makeStringAndClear().getStr());
     }
 
     if (bReplacement)
     {
         // Handle XHTML type attribute for OLE replacement images.
         if (!rMimeType.isEmpty())
-            aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_type, rMimeType.toUtf8());
+            rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_type, rMimeType.toUtf8());
     }
 
     // Events
@@ -1399,28 +1397,28 @@ Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
         const SvxMacroTableDtor& rMacTable = static_cast<const SvxMacroItem *>(pItem)->GetMacroTable();
         if (!rMacTable.empty())
         {
-            HtmlWriterHelper::applyEvents(aHtml, rMacTable, aImageEventTable, rHTMLWrt.m_bCfgStarBasic);
+            HtmlWriterHelper::applyEvents(rHtml, rMacTable, aImageEventTable, rHTMLWrt.m_bCfgStarBasic);
         }
     }
 
     // alt, align, width, height, hspace, vspace
-    rHTMLWrt.writeFrameFormatOptions(aHtml, rFrameFormat, rAlternateText, nFrameOpts);
+    rHTMLWrt.writeFrameFormatOptions(rHtml, rFrameFormat, rAlternateText, nFrameOpts);
     if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) )
         rHTMLWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameOpts );
 
     if ((nFrameOpts & HtmlFrmOpts::Border) && !bReplacement)
     {
-        aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_border, nBorderWidth);
+        rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_border, nBorderWidth);
     }
 
     if( pURLItem && pURLItem->IsServerMap() )
     {
-        aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_ismap);
+        rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_ismap);
     }
 
     if( !aIMapName.isEmpty() )
     {
-        aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_usemap, "#" + aIMapName);
+        rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_usemap, "#" + aIMapName);
     }
 
     if (bReplacement)
@@ -1429,12 +1427,18 @@ Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat,
         // "alt" attribute.
         if (rAlternateText.isEmpty())
             // Empty alternate text is not valid.
-            aHtml.characters(" ");
+            rHtml.characters(" ");
         else
-            aHtml.characters(rAlternateText.toUtf8());
+            rHtml.characters(rAlternateText.toUtf8());
     }
 
-    aHtml.flushStack();
+    return rHTMLWrt;
+}
+
+Writer& OutHTML_ImageEnd( HtmlWriter& rHtml, Writer& rWrt )
+{
+    SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
+    rHtml.flushStack();
 
     if( !rHTMLWrt.m_aINetFormats.empty() )
     {
@@ -1766,17 +1770,20 @@ static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& r
             URIHelper::GetMaybeFileHdl() );
 
     }
-    OutHTML_Image( rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz,
+    HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
+    OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz,
                     HtmlFrmOpts::GenImgMask, "frame",
                     aIMap.GetIMapObjectCount() ? &aIMap : nullptr );
+    OutHTML_ImageEnd(aHtml, rWrt);
 
     return rWrt;
 }
 
 static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rFrameFormat,
-                                      bool bInCntnr )
+                                      bool bInCntnr, bool bPNGFallback )
 {
     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
+    bool bWritePNGFallback = !rHTMLWrt.m_bExportImagesAsOLE && bPNGFallback;
 
     if (rHTMLWrt.mbSkipImages)
         return rWrt;
@@ -1827,10 +1834,12 @@ static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rF
 
             OUString aFilterName("");
 
-            if (rHTMLWrt.mbReqIF)
+            if (rHTMLWrt.mbReqIF && !bWritePNGFallback)
             {
                 // Writing image without fallback PNG in ReqIF mode: force PNG
                 // output.
+                // But don't force it when writing the original format and we'll write PNG inside
+                // that.
                 aFilterName = "PNG";
                 nFlags &= ~XOutFlags::UseNativeIfPossible;
                 nFlags &= ~XOutFlags::UseGifIfSensible;
@@ -1893,9 +1902,20 @@ static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rF
         rHTMLWrt.OutNewLine();
     }
 
-    OutHTML_Image( rWrt, rFrameFormat, aGraphicURL, aGraphic, pGrfNd->GetTitle(),
+    HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
+    OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, aGraphicURL, aGraphic, pGrfNd->GetTitle(),
                   pGrfNd->GetTwipSize(), nFrameFlags, "graphic", nullptr, aMimeType );
 
+    GfxLink aLink = aGraphic.GetGfxLink();
+    if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
+    {
+        // Not OLE mode, outer format is not PNG: write inner PNG.
+        OutHTML_FrameFormatGrfNode( rWrt, rFrameFormat,
+                                      bInCntnr, /*bPNGFallback=*/false );
+    }
+
+    OutHTML_ImageEnd(aHtml, rWrt);
+
     if (rHTMLWrt.mbReqIF && rHTMLWrt.m_bExportImagesAsOLE)
         rWrt.Strm().WriteOString("</" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">");
 
diff --git a/sw/source/filter/html/htmlplug.cxx b/sw/source/filter/html/htmlplug.cxx
index 5fd6b3e54d7b..8138e7d0bab5 100644
--- a/sw/source/filter/html/htmlplug.cxx
+++ b/sw/source/filter/html/htmlplug.cxx
@@ -79,6 +79,7 @@
 #include <o3tl/safeint.hxx>
 #include <osl/file.hxx>
 #include <comphelper/propertyvalue.hxx>
+#include <svtools/HtmlWriter.hxx>
 
 using namespace com::sun::star;
 
@@ -1661,9 +1662,11 @@ Writer& OutHTML_FrameFormatOLENodeGrf( Writer& rWrt, const SwFrameFormat& rFrame
         : HtmlFrmOpts::GenImgMask;
     if (bObjectOpened)
         nFlags |= HtmlFrmOpts::Replacement;
-    OutHTML_Image( rWrt, rFrameFormat, aGraphicURL, aGraphic,
+    HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
+    OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, aGraphicURL, aGraphic,
             pOLENd->GetTitle(), pOLENd->GetTwipSize(),
             nFlags, "ole", nullptr, aMimeType );
+    OutHTML_ImageEnd(aHtml, rWrt);
 
     if (bObjectOpened)
         // Close native data.
diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx
index e8bfbe25afe9..675c80772fd6 100644
--- a/sw/source/filter/html/wrthtml.hxx
+++ b/sw/source/filter/html/wrthtml.hxx
@@ -665,13 +665,14 @@ Writer& OutHTML_DrawFrameFormatAsMarquee( Writer& rWrt, const SwDrawFrameFormat&
 Writer& OutHTML_HeaderFooter( Writer& rWrt, const SwFrameFormat& rFrameFormat,
                               bool bHeader );
 
-Writer& OutHTML_Image( Writer&, const SwFrameFormat& rFormat,
+Writer& OutHTML_ImageStart( HtmlWriter& rHtml, Writer&, const SwFrameFormat& rFormat,
                        const OUString& rGraphicURL,
                        Graphic const & rGraphic, const OUString& rAlternateText,
                        const Size& rRealSize, HtmlFrmOpts nFrameOpts,
                        const char *pMarkType,
                        const ImageMap *pGenImgMap,
                        const OUString& rMimeType = OUString() );
+Writer& OutHTML_ImageEnd( HtmlWriter& rHtml, Writer& );
 
 Writer& OutHTML_BulletImage( Writer& rWrt, const char *pTag,
                              const SvxBrushItem* pBrush,
commit edccdebbde424695766f57c837d1da8de3f0f9d1
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Mon May 31 17:37:02 2021 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Jun 4 16:25:58 2021 +0200

    sw XHTML / reqif export: export PNG images directly, without an OLE wrapper
    
    Do this by default and add a ExportImagesAsOLE=true option for those who
    want the old behavior.
    
    Adapt existing tests to ask for the old behavior and add a new one that
    depends on the new default.
    
    (cherry picked from commit e1546b790763c1004dc2e2e3581c666466d7cf9c)
    
    Conflicts:
            sw/source/filter/html/htmlflywriter.cxx
    
    Change-Id: If863215ce267d6accc85b5c8f7bdbd3f2b1e9187

diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index 50e001c72890..d505d3ee521f 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -647,34 +647,55 @@ DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(testReqIfOleImg, "reqif-ole-img.xhtml")
     CPPUNIT_ASSERT(aStream.indexOf("type=\"image/png\"") != -1);
 }
 
-DECLARE_HTMLEXPORT_ROUNDTRIP_TEST(testReqIfPngImg, "reqif-png-img.xhtml")
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIfPngImg)
 {
-    uno::Reference<container::XNamed> xShape(getShape(1), uno::UNO_QUERY);
-    CPPUNIT_ASSERT(xShape.is());
+    auto verify = [this](bool bExported) {
+        uno::Reference<container::XNamed> xShape(getShape(1), uno::UNO_QUERY);
+        CPPUNIT_ASSERT(xShape.is());
 
-    if (!mbExported)
-    {
-        // Imported PNG image is not an object.
-        CPPUNIT_ASSERT_EQUAL(OUString("Image1"), xShape->getName());
-        return;
-    }
-
-    // All images are exported as objects in ReqIF mode.
-    CPPUNIT_ASSERT_EQUAL(OUString("Object1"), xShape->getName());
+        if (!bExported)
+        {
+            // Imported PNG image is not an object.
+            CPPUNIT_ASSERT_EQUAL(OUString("Image1"), xShape->getName());
+            return;
+        }
 
-    // This was <img>, not <object>, which is not valid in the reqif-xhtml
-    // subset.
-    SvStream* pStream = maTempFile.GetStream(StreamMode::READ);
-    CPPUNIT_ASSERT(pStream);
-    sal_uInt64 nLength = pStream->TellEnd();
-    OString aStream(read_uInt8s_ToOString(*pStream, nLength));
-    CPPUNIT_ASSERT(aStream.indexOf("<reqif-xhtml:object") != -1);
+        // All images are exported as objects in ReqIF mode.
+        CPPUNIT_ASSERT_EQUAL(OUString("Object1"), xShape->getName());
+
+        // This was <img>, not <object>, which is not valid in the reqif-xhtml
+        // subset.
+        SvStream* pStream = maTempFile.GetStream(StreamMode::READ);
+        CPPUNIT_ASSERT(pStream);
+        sal_uInt64 nLength = pStream->TellEnd();
+        OString aStream(read_uInt8s_ToOString(*pStream, nLength));
+        CPPUNIT_ASSERT(aStream.indexOf("<reqif-xhtml:object") != -1);
+
+        // Make sure that both RTF and PNG versions are written.
+        CPPUNIT_ASSERT(aStream.indexOf("text/rtf") != -1);
+        // This failed when images with a query in their file:// URL failed to
+        // import.
+        CPPUNIT_ASSERT(aStream.indexOf("image/png") != -1);
+    };
 
-    // Make sure that both RTF and PNG versions are written.
-    CPPUNIT_ASSERT(aStream.indexOf("text/rtf") != -1);
-    // This failed when images with a query in their file:// URL failed to
-    // import.
-    CPPUNIT_ASSERT(aStream.indexOf("image/png") != -1);
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "reqif-png-img.xhtml";
+    uno::Sequence<beans::PropertyValue> aLoadProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+    };
+    mxComponent = loadFromDesktop(aURL, "com.sun.star.text.TextDocument", aLoadProperties);
+    verify(/*bExported=*/false);
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+        comphelper::makePropertyValue("ExportImagesAsOLE", true),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+    mxComponent->dispose();
+    mxComponent
+        = loadFromDesktop(maTempFile.GetURL(), "com.sun.star.text.TextDocument", aLoadProperties);
+    verify(/*bExported=*/true);
 }
 
 DECLARE_HTMLEXPORT_TEST(testReqIfJpgImg, "reqif-jpg-img.xhtml")
@@ -858,10 +879,19 @@ DECLARE_HTMLEXPORT_TEST(testTransparentImage, "transparent-image.odt")
     CPPUNIT_ASSERT_MESSAGE(aMessage.toUtf8().getStr(), aSource.endsWith(".gif"));
 }
 
-DECLARE_HTMLEXPORT_TEST(testTransparentImageReqIf, "transparent-image.odt")
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testTransparentImageReqIf)
 {
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "transparent-image.odt";
+    mxComponent = loadFromDesktop(aURL, "com.sun.star.text.TextDocument", {});
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+        comphelper::makePropertyValue("ExportImagesAsOLE", true),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
     SvMemoryStream aStream;
-    wrapFragment(maTempFile, aStream);
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
     xmlDocUniquePtr pDoc = parseXmlStream(&aStream);
     CPPUNIT_ASSERT(pDoc);
 
@@ -1467,7 +1497,13 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifImageToOle)
     dispatchCommand(mxComponent, ".uno:InsertGraphic", aArgs);
 
     // When exporting to XHTML:
-    ExportToReqif();
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+        comphelper::makePropertyValue("ExportImagesAsOLE", true),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
 
     // Then make sure we export that PNG as WMF in ReqIF mode:
     OUString aRtfUrl = GetOlePath();
@@ -1494,6 +1530,36 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifImageToOle)
     CPPUNIT_ASSERT(aOle1Reader.m_nPresentationDataSize);
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqifEmbedPNGDirectly)
+{
+    // Given a document with an image:
+    loadURL("private:factory/swriter", nullptr);
+    OUString aImageURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "ole2.png";
+    uno::Sequence<beans::PropertyValue> aArgs = {
+        comphelper::makePropertyValue("FileName", aImageURL),
+    };
+    dispatchCommand(mxComponent, ".uno:InsertGraphic", aArgs);
+
+    // When exporting to XHTML:
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    uno::Sequence<beans::PropertyValue> aStoreProperties = {
+        comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")),
+        comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")),
+    };
+    xStorable->storeToURL(maTempFile.GetURL(), aStoreProperties);
+
+    // Then make sure the PNG is embedded directly, without an RTF wrapper:
+    SvMemoryStream aStream;
+    HtmlExportTest::wrapFragment(maTempFile, aStream);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    CPPUNIT_ASSERT(pXmlDoc);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: image/png
+    // - Actual  : text/rtf
+    // i.e. even PNG was wrapped in an RTF.
+    assertXPath(pXmlDoc, "//reqif-xhtml:p/reqif-xhtml:object", "type", "image/png");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
index e5a3b482bd61..e76dd326d085 100644
--- a/sw/source/filter/html/htmlflywriter.cxx
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -1866,7 +1866,7 @@ static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rF
     if (xGraphic.is() && aMimeType.isEmpty())
         xGraphic->getPropertyValue("MimeType") >>= aMimeType;
 
-    if (rHTMLWrt.mbReqIF)
+    if (rHTMLWrt.mbReqIF && rHTMLWrt.m_bExportImagesAsOLE)
     {
         // Write the original image as an RTF fragment.
         OUString aFileName;
@@ -1896,7 +1896,7 @@ static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rF
     OutHTML_Image( rWrt, rFrameFormat, aGraphicURL, aGraphic, pGrfNd->GetTitle(),
                   pGrfNd->GetTwipSize(), nFrameFlags, "graphic", nullptr, aMimeType );
 
-    if (rHTMLWrt.mbReqIF)
+    if (rHTMLWrt.mbReqIF && rHTMLWrt.m_bExportImagesAsOLE)
         rWrt.Strm().WriteOString("</" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">");
 
     return rWrt;
diff --git a/sw/source/filter/html/wrthtml.cxx b/sw/source/filter/html/wrthtml.cxx
index 2f83734aee37..c941d0b38597 100644
--- a/sw/source/filter/html/wrthtml.cxx
+++ b/sw/source/filter/html/wrthtml.cxx
@@ -196,12 +196,16 @@ void SwHTMLWriter::SetupFilterOptions(SfxMedium& rMedium)
 
     comphelper::SequenceAsHashMap aStoreMap(rMedium.GetArgs());
     auto it = aStoreMap.find("RTFOLEMimeType");
-    if (it == aStoreMap.end())
+    if (it != aStoreMap.end())
     {
-        return;
+        it->second >>= m_aRTFOLEMimeType;
     }
 
-    it->second >>= m_aRTFOLEMimeType;
+    it = aStoreMap.find("ExportImagesAsOLE");
+    if (it != aStoreMap.end())
+    {
+        it->second >>= m_bExportImagesAsOLE;
+    }
 }
 
 void SwHTMLWriter::SetupFilterOptions(const OUString& rFilterOptions)
diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx
index ffc416f22662..e8bfbe25afe9 100644
--- a/sw/source/filter/html/wrthtml.hxx
+++ b/sw/source/filter/html/wrthtml.hxx
@@ -407,6 +407,9 @@ public:
 
     OUString m_aRTFOLEMimeType;
 
+    /// ReqIF mode: export images as OLE objects.
+    bool m_bExportImagesAsOLE = false;
+
     /// Construct an instance of SwHTMLWriter and optionally give it
     /// the filter options directly, which can also be set via SetupFilterOptions().
     explicit SwHTMLWriter( const OUString& rBaseURL, const OUString& rFilterOptions = "" );


More information about the Libreoffice-commits mailing list