[Libreoffice-commits] core.git: Branch 'libreoffice-7-0' - emfio/CppunitTest_emfio_emf_test.mk emfio/inc emfio/qa emfio/source include/vcl offapi/com svx/source vcl/source

Miklos Vajna (via logerrit) logerrit at kemper.freedesktop.org
Mon Sep 21 12:01:12 UTC 2020


 emfio/CppunitTest_emfio_emf_test.mk             |   15 ------
 emfio/inc/emfreader.hxx                         |    2 
 emfio/qa/cppunit/emf/EmfImportTest.cxx          |   57 +++++++++++++++++++++++-
 emfio/qa/cppunit/emf/data/pdf-in-emf.pptx       |binary
 emfio/source/emfuno/xemfparser.cxx              |   15 +++++-
 emfio/source/reader/emfreader.cxx               |    4 +
 include/vcl/pdfread.hxx                         |    3 -
 include/vcl/vectorgraphicdata.hxx               |   10 ++++
 offapi/com/sun/star/graphic/XEmfParser.idl      |    8 +++
 svx/source/sdr/properties/defaultproperties.cxx |   16 ++++++
 vcl/source/filter/ipdf/pdfread.cxx              |   21 +++++++-
 vcl/source/gdi/vectorgraphicdata.cxx            |   12 ++++-
 12 files changed, 141 insertions(+), 22 deletions(-)

New commits:
commit 69842afcab0c5043c3c554493dd92d58c2bb02af
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Fri Sep 18 17:52:41 2020 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Mon Sep 21 14:00:40 2020 +0200

    [API CHANGE] tdf#136836 emfio: set size hint on inner PDF if used as shape fill
    
    The bugdoc has a shape, its bitmap fill is an EMF, which is actually a
    PDF. The PDF is has a height of 5cm, but the shape has a height of 14
    cm.
    
    Inform vcl::RenderPDFBitmaps() about the size of the shape, so the
    result won't be blurry. This approach makes sure that we don't
    unconditionally render at higher resolution, i.e. the "load a PDF of 100
    pages into Online" use-case won't use more memory than before.
    
    API CHANGE, because the EMF reader is only available via UNO, though
    it's likely that no actual external code would ever invoke it directly.
    
    (cherry picked from commit 01024ee24c6e89044c68051f6fd5f1264905e90c)
    
    Conflicts:
            include/vcl/vectorgraphicdata.hxx
            vcl/source/gdi/vectorgraphicdata.cxx
    
    Change-Id: If1d8def0136d408a31a0cc54777a7f26430a0ff3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103087
    Tested-by: Jenkins
    Reviewed-by: Gülşah Köse <gulsah.kose at collabora.com>

diff --git a/emfio/CppunitTest_emfio_emf_test.mk b/emfio/CppunitTest_emfio_emf_test.mk
index 123e4b3549bd..5b32187b7ea8 100644
--- a/emfio/CppunitTest_emfio_emf_test.mk
+++ b/emfio/CppunitTest_emfio_emf_test.mk
@@ -38,20 +38,7 @@ $(eval $(call gb_CppunitTest_use_libraries,emfio_emf,\
 $(eval $(call gb_CppunitTest_use_ure,emfio_emf))
 $(eval $(call gb_CppunitTest_use_vcl,emfio_emf))
 
-$(eval $(call gb_CppunitTest_use_components,emfio_emf,\
-    configmgr/source/configmgr \
-    dtrans/util/mcnttype \
-    emfio/emfio \
-    framework/util/fwk \
-    i18npool/util/i18npool \
-    package/source/xstor/xstor \
-    package/util/package2 \
-    toolkit/util/tk \
-    sfx2/util/sfx \
-    ucb/source/core/ucb1 \
-    ucb/source/ucp/file/ucpfile1 \
-    unotools/util/utl \
-))
+$(eval $(call gb_CppunitTest_use_rdb,emfio_emf,services))
 
 $(eval $(call gb_CppunitTest_use_configuration,emfio_emf))
 
diff --git a/emfio/inc/emfreader.hxx b/emfio/inc/emfreader.hxx
index 75a77211ea61..c6caa0fb9f26 100644
--- a/emfio/inc/emfreader.hxx
+++ b/emfio/inc/emfreader.hxx
@@ -34,6 +34,7 @@ namespace emfio
         bool        mbEMFPlusDualMode : 1;
         /// An other format is read already, can ignore actual EMF data.
         bool mbReadOtherGraphicFormat = false;
+        basegfx::B2DTuple maSizeHint;
 
         bool        ReadHeader();
         // reads and converts the rectangle
@@ -47,6 +48,7 @@ namespace emfio
         void ReadGDIComment(sal_uInt32 nCommentId);
         /// Parses EMR_COMMENT_MULTIFORMATS.
         void ReadMultiformatsComment();
+        void SetSizeHint(const basegfx::B2DTuple& rSizeHint) { maSizeHint = rSizeHint; }
 
     private:
         template <class T> void ReadAndDrawPolyPolygon(sal_uInt32 nNextPos);
diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx
index ac435905d570..5ee4ad9e224a 100644
--- a/emfio/qa/cppunit/emf/EmfImportTest.cxx
+++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx
@@ -12,6 +12,12 @@
 
 #include <test/bootstrapfixture.hxx>
 #include <test/xmltesttools.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
 
 #include <comphelper/seqstream.hxx>
 #include <comphelper/sequence.hxx>
@@ -26,14 +32,17 @@
 namespace
 {
 
+using namespace css;
 using namespace css::uno;
 using namespace css::io;
 using namespace css::graphic;
 using drawinglayer::primitive2d::Primitive2DSequence;
 using drawinglayer::primitive2d::Primitive2DContainer;
 
-class Test : public test::BootstrapFixture, public XmlTestTools
+class Test : public test::BootstrapFixture, public XmlTestTools, public unotest::MacrosTest
 {
+    uno::Reference<lang::XComponent> mxComponent;
+
     void checkRectPrimitive(Primitive2DSequence const & rPrimitive);
 
     void testWorking();
@@ -41,19 +50,40 @@ class Test : public test::BootstrapFixture, public XmlTestTools
     void TestDrawStringTransparent();
     void TestDrawLine();
     void TestLinearGradient();
+    void TestPdfInEmf();
 
     Primitive2DSequence parseEmf(const OUString& aSource);
 
 public:
+    void setUp() override;
+    void tearDown() override;
+    uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+
     CPPUNIT_TEST_SUITE(Test);
     CPPUNIT_TEST(testWorking);
     CPPUNIT_TEST(TestDrawString);
     CPPUNIT_TEST(TestDrawStringTransparent);
     CPPUNIT_TEST(TestDrawLine);
     CPPUNIT_TEST(TestLinearGradient);
+    CPPUNIT_TEST(TestPdfInEmf);
     CPPUNIT_TEST_SUITE_END();
 };
 
+void Test::setUp()
+{
+    test::BootstrapFixture::setUp();
+
+    mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void Test::tearDown()
+{
+    if (mxComponent.is())
+        mxComponent->dispose();
+
+    test::BootstrapFixture::tearDown();
+}
+
 Primitive2DSequence Test::parseEmf(const OUString& aSource)
 {
     const Reference<XEmfParser> xEmfParser = EmfTools::create(m_xContext);
@@ -190,6 +220,31 @@ void Test::TestLinearGradient()
     assertXPath(pDocument, "/primitive2D/metafile/transform/mask/svglineargradient[2]/polypolygon", "path", "m7615.75822989746 0.216110019646294h7615.75822989746v7610.21611001965h-7615.75822989746z");
 }
 
+void Test::TestPdfInEmf()
+{
+    // Load a PPTX file, which has a shape, with a bitmap fill, which is an EMF, containing a PDF.
+    OUString aURL = m_directories.getURLFromSrc("emfio/qa/cppunit/emf/data/pdf-in-emf.pptx");
+    getComponent() = loadFromDesktop(aURL);
+
+    // Get the EMF.
+    uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY);
+    uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+    uno::Reference<graphic::XGraphic> xGraphic;
+    xShape->getPropertyValue("FillBitmap") >>= xGraphic;
+    Graphic aGraphic(xGraphic);
+
+    // Check the size hint of the EMF, which influences the bitmap generated from the PDF.
+    const std::shared_ptr<VectorGraphicData>& pVectorGraphicData = aGraphic.getVectorGraphicData();
+
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 14321
+    // - Actual  : 0
+    // i.e. there was no size hint, the shape with 14cm height had a bitmap-from-PDF fill, the PDF
+    // height was only 5cm, so it looked blurry.
+    CPPUNIT_ASSERT_EQUAL(14321.0, pVectorGraphicData->getSizeHint().getY());
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
 
 }
diff --git a/emfio/qa/cppunit/emf/data/pdf-in-emf.pptx b/emfio/qa/cppunit/emf/data/pdf-in-emf.pptx
new file mode 100644
index 000000000000..61b2af28c8b3
Binary files /dev/null and b/emfio/qa/cppunit/emf/data/pdf-in-emf.pptx differ
diff --git a/emfio/source/emfuno/xemfparser.cxx b/emfio/source/emfuno/xemfparser.cxx
index 3297dea8f30a..88a115f79c03 100644
--- a/emfio/source/emfuno/xemfparser.cxx
+++ b/emfio/source/emfuno/xemfparser.cxx
@@ -32,6 +32,7 @@
 #include <basegfx/matrix/b2dhommatrixtools.hxx>
 #include <unotools/ucbstreamhelper.hxx>
 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <sal/log.hxx>
 
 #include <wmfreader.hxx>
 #include <emfreader.hxx>
@@ -48,6 +49,7 @@ namespace emfio::emfreader
         {
         private:
             uno::Reference< uno::XComponentContext > context_;
+            basegfx::B2DTuple maSizeHint;
 
         protected:
         public:
@@ -61,6 +63,7 @@ namespace emfio::emfreader
                 const uno::Reference< ::io::XInputStream >& xEmfStream,
                 const OUString& aAbsolutePath,
                 const uno::Sequence< ::beans::PropertyValue >& rProperties) override;
+            void SAL_CALL setSizeHint(const geometry::RealPoint2D& rSize) override;
 
             // XServiceInfo
             virtual OUString SAL_CALL getImplementationName() override;
@@ -130,7 +133,9 @@ namespace emfio::emfreader
                     if (nMetaType == 0x464d4520)
                     {
                         // read and get possible failure/error, ReadEnhWMF returns success
-                        bReadError = !emfio::EmfReader(*pStream, aMtf).ReadEnhWMF();
+                        emfio::EmfReader aReader(*pStream, aMtf);
+                        aReader.SetSizeHint(maSizeHint);
+                        bReadError = !aReader.ReadEnhWMF();
                     }
                     else
                     {
@@ -200,12 +205,18 @@ namespace emfio::emfreader
             }
             else
             {
-                OSL_ENSURE(false, "Invalid stream (!)");
+                SAL_WARN("emfio", "Invalid stream (!)");
             }
 
             return comphelper::containerToSequence(aRetval);
         }
 
+        void XEmfParser::setSizeHint(const geometry::RealPoint2D& rSize)
+        {
+            maSizeHint.setX(rSize.X);
+            maSizeHint.setY(rSize.Y);
+        }
+
         OUString SAL_CALL XEmfParser::getImplementationName()
         {
             return XEmfParser_getImplementationName();
diff --git a/emfio/source/reader/emfreader.cxx b/emfio/source/reader/emfreader.cxx
index 7bb4d408203a..2c932a440eeb 100644
--- a/emfio/source/reader/emfreader.cxx
+++ b/emfio/source/reader/emfreader.cxx
@@ -503,6 +503,10 @@ namespace emfio
             return;
         }
 
+        // aGraphic will be the only output of the EMF parser, so its size hint can be the same as
+        // ours.
+        aGraphic.getVectorGraphicData()->setSizeHint(maSizeHint);
+
         maBmpSaveList.emplace_back(new BSaveStruct(aGraphic.GetBitmapEx(), aOutputRect, SRCCOPY));
         const std::shared_ptr<VectorGraphicData> pVectorGraphicData
             = aGraphic.getVectorGraphicData();
diff --git a/include/vcl/pdfread.hxx b/include/vcl/pdfread.hxx
index ba0eb1ca85be..6896d2e83573 100644
--- a/include/vcl/pdfread.hxx
+++ b/include/vcl/pdfread.hxx
@@ -13,6 +13,7 @@
 #include <vector>
 #include <tools/gen.hxx>
 #include <tools/stream.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
 
 namespace com::sun::star::uno
 {
@@ -26,7 +27,7 @@ namespace vcl
 /// Fills the rBitmaps vector with rendered pages.
 VCL_DLLPUBLIC size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<Bitmap>& rBitmaps,
                                       size_t nFirstPage = 0, int nPages = 1,
-                                      double fResolutionDPI = 96.);
+                                      const basegfx::B2DTuple* pSizeHint = nullptr);
 
 /// Imports a PDF stream into rGraphic as VectorGraphicData.
 VCL_DLLPUBLIC bool ImportPDF(SvStream& rStream, Graphic& rGraphic);
diff --git a/include/vcl/vectorgraphicdata.hxx b/include/vcl/vectorgraphicdata.hxx
index 8fce6666e6e8..041c59e832c7 100644
--- a/include/vcl/vectorgraphicdata.hxx
+++ b/include/vcl/vectorgraphicdata.hxx
@@ -74,6 +74,9 @@ private:
     // If the vector format has more pages this denotes which page to render
     sal_Int32 mnPageIndex;
 
+    /// Useful for PDF, which is vector-based, but still rendered to a bitmap.
+    basegfx::B2DTuple maSizeHint;
+
     // on demand creators
     void ensurePdfReplacement();
     void ensureReplacement();
@@ -113,6 +116,13 @@ public:
 
     sal_Int32 getPageIndex() const { return std::max(sal_Int32(0), mnPageIndex); }
 
+    void setSizeHint(const basegfx::B2DTuple& rSizeHint)
+    {
+        maSizeHint = rSizeHint;
+    }
+
+    const basegfx::B2DTuple& getSizeHint() const { return maSizeHint; }
+
     bool isPrimitiveSequenceCreated() const { return mbSequenceCreated; }
 };
 
diff --git a/offapi/com/sun/star/graphic/XEmfParser.idl b/offapi/com/sun/star/graphic/XEmfParser.idl
index 1c2fd10d7fff..234d70bc1937 100644
--- a/offapi/com/sun/star/graphic/XEmfParser.idl
+++ b/offapi/com/sun/star/graphic/XEmfParser.idl
@@ -20,6 +20,7 @@
 #ifndef __com_sun_star_graphic_XEmfParser_idl__
 #define __com_sun_star_graphic_XEmfParser_idl__
 
+#include <com/sun/star/geometry/RealPoint2D.idl>
 #include <com/sun/star/uno/XInterface.idl>
 #include <com/sun/star/io/XInputStream.idl>
 
@@ -51,6 +52,13 @@ interface XEmfParser : ::com::sun::star::uno::XInterface
         [in] io::XInputStream xEmfStream,
         [in] string aAbsolutePath,
         [in] ::com::sun::star::beans::PropertyValues Properties);
+
+    /** Sets a size hint on this object.
+
+        @param Size
+            the size in 100/th mm
+     */
+    void setSizeHint([in] com::sun::star::geometry::RealPoint2D Size);
 };
 
 }; }; }; };
diff --git a/svx/source/sdr/properties/defaultproperties.cxx b/svx/source/sdr/properties/defaultproperties.cxx
index 2110ece186b9..587e9a652454 100644
--- a/svx/source/sdr/properties/defaultproperties.cxx
+++ b/svx/source/sdr/properties/defaultproperties.cxx
@@ -30,6 +30,7 @@
 #include <libxml/xmlwriter.h>
 #include <svx/svdmodel.hxx>
 #include <svx/svdtrans.hxx>
+#include <svx/xbtmpit.hxx>
 
 namespace sdr::properties
 {
@@ -155,6 +156,21 @@ namespace sdr::properties
 
         void DefaultProperties::SetObjectItemSet(const SfxItemSet& rSet)
         {
+            if (rSet.HasItem(XATTR_FILLBITMAP))
+            {
+                const XFillBitmapItem* pItem = rSet.GetItem(XATTR_FILLBITMAP);
+                const std::shared_ptr<VectorGraphicData>& pVectorData
+                    = pItem->GetGraphicObject().GetGraphic().getVectorGraphicData();
+                if (pVectorData)
+                {
+                    // Shape is filled by a vector graphic: tell it our size as a hint.
+                    basegfx::B2DTuple aSizeHint;
+                    aSizeHint.setX(GetSdrObject().GetSnapRect().getWidth());
+                    aSizeHint.setY(GetSdrObject().GetSnapRect().getHeight());
+                    pVectorData->setSizeHint(aSizeHint);
+                }
+            }
+
             SfxWhichIter aWhichIter(rSet);
             sal_uInt16 nWhich(aWhichIter.FirstWhich());
             const SfxPoolItem *pPoolItem;
diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx
index 2a7e7ce04198..3066c4deba4a 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -22,6 +22,7 @@
 #include <unotools/ucbstreamhelper.hxx>
 
 #include <vcl/filter/PDFiumLibrary.hxx>
+#include <sal/log.hxx>
 
 using namespace com::sun::star;
 
@@ -145,9 +146,10 @@ VectorGraphicDataArray createVectorGraphicDataArray(SvStream& rStream)
 namespace vcl
 {
 size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<Bitmap>& rBitmaps,
-                        const size_t nFirstPage, int nPages, const double fResolutionDPI)
+                        const size_t nFirstPage, int nPages, const basegfx::B2DTuple* pSizeHint)
 {
 #if HAVE_FEATURE_PDFIUM
+    const double fResolutionDPI = 96;
     auto pPdfium = vcl::pdf::PDFiumLibrary::get();
 
     // Load the buffer using pdfium.
@@ -166,9 +168,19 @@ size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<Bitmap>& rBi
         if (!pPdfPage)
             break;
 
+        // Calculate the bitmap size in points.
+        size_t nPageWidthPoints = FPDF_GetPageWidth(pPdfPage);
+        size_t nPageHeightPoints = FPDF_GetPageHeight(pPdfPage);
+        if (pSizeHint && pSizeHint->getX() && pSizeHint->getY())
+        {
+            // Have a size hint, prefer that over the logic size from the PDF.
+            nPageWidthPoints = convertMm100ToTwip(pSizeHint->getX()) / 20;
+            nPageHeightPoints = convertMm100ToTwip(pSizeHint->getY()) / 20;
+        }
+
         // Returned unit is points, convert that to pixel.
-        const size_t nPageWidth = pointToPixel(FPDF_GetPageWidth(pPdfPage), fResolutionDPI);
-        const size_t nPageHeight = pointToPixel(FPDF_GetPageHeight(pPdfPage), fResolutionDPI);
+        const size_t nPageWidth = pointToPixel(nPageWidthPoints, fResolutionDPI);
+        const size_t nPageHeight = pointToPixel(nPageHeightPoints, fResolutionDPI);
         FPDF_BITMAP pPdfBitmap = FPDFBitmap_Create(nPageWidth, nPageHeight, /*alpha=*/1);
         if (!pPdfBitmap)
             break;
@@ -215,7 +227,10 @@ bool ImportPDF(SvStream& rStream, Graphic& rGraphic)
 {
     VectorGraphicDataArray aPdfDataArray = createVectorGraphicDataArray(rStream);
     if (!aPdfDataArray.hasElements())
+    {
+        SAL_WARN("vcl.filter", "ImportPDF: empty PDF data array");
         return false;
+    }
 
     auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aPdfDataArray, OUString(),
                                                                      VectorGraphicDataType::Pdf);
diff --git a/vcl/source/gdi/vectorgraphicdata.cxx b/vcl/source/gdi/vectorgraphicdata.cxx
index 7ef8c922f64d..80195dc26f84 100644
--- a/vcl/source/gdi/vectorgraphicdata.cxx
+++ b/vcl/source/gdi/vectorgraphicdata.cxx
@@ -150,7 +150,9 @@ void VectorGraphicData::ensurePdfReplacement()
     sal_Int32 nUsePageIndex = 0;
     if (mnPageIndex >= 0)
         nUsePageIndex = mnPageIndex;
-    vcl::RenderPDFBitmaps(maVectorGraphicDataArray.getConstArray(), maVectorGraphicDataArray.getLength(), aBitmaps, nUsePageIndex, 1/*, fResolutionDPI*/);
+    vcl::RenderPDFBitmaps(maVectorGraphicDataArray.getConstArray(),
+                          maVectorGraphicDataArray.getLength(), aBitmaps, nUsePageIndex, 1,
+                          &maSizeHint);
     if (!aBitmaps.empty())
         maReplacement = aBitmaps[0];
 }
@@ -211,7 +213,15 @@ void VectorGraphicData::ensureSequenceAndRange()
                 }
 
                 if (myInputStream.is())
+                {
+                    // Pass the size hint of the graphic to the EMF parser.
+                    geometry::RealPoint2D aSizeHint;
+                    aSizeHint.X = maSizeHint.getX();
+                    aSizeHint.Y = maSizeHint.getY();
+                    xEmfParser->setSizeHint(aSizeHint);
+
                     maSequence = comphelper::sequenceToContainer<std::deque<css::uno::Reference< css::graphic::XPrimitive2D >>>(xEmfParser->getDecomposition(myInputStream, maPath, aSequence));
+                }
 
                 break;
             }


More information about the Libreoffice-commits mailing list