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 <vmik...@collabora.com> AuthorDate: Fri Jun 4 15:58:01 2021 +0200 Commit: Miklos Vajna <vmik...@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 <vmik...@collabora.com> AuthorDate: Fri Jun 4 14:02:52 2021 +0200 Commit: Miklos Vajna <vmik...@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 <vmik...@collabora.com> AuthorDate: Thu Jun 3 17:10:57 2021 +0200 Commit: Miklos Vajna <vmik...@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 <vmik...@collabora.com> AuthorDate: Thu Jun 3 10:03:19 2021 +0200 Commit: Miklos Vajna <vmik...@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 <vmik...@collabora.com> AuthorDate: Wed Jun 2 12:25:35 2021 +0200 Commit: Miklos Vajna <vmik...@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 <vmik...@collabora.com> AuthorDate: Wed Jun 2 08:48:56 2021 +0200 Commit: Miklos Vajna <vmik...@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 <vmik...@collabora.com> AuthorDate: Tue Jun 1 14:50:01 2021 +0200 Commit: Miklos Vajna <vmik...@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 <vmik...@collabora.com> AuthorDate: Mon May 31 17:37:02 2021 +0200 Commit: Miklos Vajna <vmik...@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 = "" ); _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits