[Libreoffice-commits] core.git: filter/source sd/qa

Marco Cecchetti (via logerrit) logerrit at kemper.freedesktop.org
Tue Mar 9 14:36:33 UTC 2021


 filter/source/svg/svgexport.cxx |  184 +++++++++++++++++++++++++++++++++++++++-
 filter/source/svg/svgfilter.hxx |   13 ++
 filter/source/svg/svgwriter.cxx |   12 ++
 sd/qa/unit/SVGExportTests.cxx   |   64 ++++++++++---
 4 files changed, 255 insertions(+), 18 deletions(-)

New commits:
commit 66f88211d55c138da38c75a7b2c4d1695ce28f6a
Author:     Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Tue Feb 2 14:05:46 2021 +0100
Commit:     Marco Cecchetti <marco.cecchetti at collabora.com>
CommitDate: Tue Mar 9 15:35:44 2021 +0100

    filter: svg: export tiled background by exploiting svg:pattern element
    
    By exporting a tiled bitmap background by exploiting the <pattern>
    element we get performance improvement when the background is made of
    a big number of tiles.
    
    The unit test for the tiled background case has been updated.
    
    Change-Id: I80a4eebd081d2c59ec7d9906fc9c616692f7e0fa
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110319
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Ashod Nakashian <ash at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111844
    Tested-by: Jenkins
    Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>

diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index 4eb56a5a9ced..3e2da0cd0cb9 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -520,6 +520,32 @@ void MetaBitmapActionGetSize( const MetaAction* pAction, Size& rSz )
         OSL_FAIL( "MetaBitmapActionGetSize: passed MetaAction pointer is null." );
         return;
     }
+    const MetaActionType nType = pAction->GetType();
+    switch( nType )
+    {
+        case MetaActionType::BMPSCALE:
+        {
+            const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
+            rSz = pA->GetSize();
+        }
+        break;
+        case MetaActionType::BMPEXSCALE:
+        {
+            const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
+            rSz = pA->GetSize();
+        }
+        break;
+        default: break;
+    }
+}
+
+void MetaBitmapActionGetOrigSize( const MetaAction* pAction, Size& rSz )
+{
+    if( !pAction )
+    {
+        OSL_FAIL( "MetaBitmapActionGetOrigSize: passed MetaAction pointer is null." );
+        return;
+    }
 
     const MetaActionType nType = pAction->GetType();
     MapMode aSourceMode( MapUnit::MapPixel );
@@ -546,6 +572,16 @@ void MetaBitmapActionGetSize( const MetaAction* pAction, Size& rSz )
     rSz = OutputDevice::LogicToLogic( rSz, aSourceMode, aTargetMode );
 }
 
+OUString getPatternIdForTiledBackground( std::u16string_view sSlideId, BitmapChecksum nChecksum )
+{
+    return OUString::Concat("bg-pattern.") + sSlideId + "." + OUString::number( nChecksum );
+}
+
+OUString getIdForTiledBackground( std::u16string_view sSlideId, BitmapChecksum nChecksum )
+{
+    return OUString::Concat("bg-") + sSlideId + "." + OUString::number( nChecksum );
+}
+
 } // end anonymous namespace
 
 size_t HashBitmap::operator()( const ObjectRepresentation& rObjRep ) const
@@ -933,6 +969,7 @@ bool SVGFilter::implExportDocument()
                 implExportTextEmbeddedBitmaps();
                 implExportBackgroundBitmaps();
                 mpSVGWriter->SetEmbeddedBitmapRefs( &maBitmapActionMap );
+                implExportTiledBackground();
             }
 
             // #i124608# export a given object selection, so no MasterPage export at all
@@ -1513,6 +1550,77 @@ void SVGFilter::implExportBackgroundBitmaps()
     }
 }
 
+void SVGFilter::implExportTiledBackground()
+{
+    if( maPatterProps.empty() )
+        return;
+
+    mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "class", "BackgroundPatterns" );
+    SvXMLElementExport aDefsContainerElem( *mpSVGExport, XML_NAMESPACE_NONE, "defs", true, true );
+
+    for( const auto& [ rSlideId, rData ] : maPatterProps )
+    {
+        auto aBitmapActionIt = maBitmapActionMap.find( rData.aBitmapChecksum );
+        if( aBitmapActionIt != maBitmapActionMap.end() )
+        {
+            // pattern element attributes
+            const OUString sPatternId = getPatternIdForTiledBackground( rSlideId, rData.aBitmapChecksum );
+            // <pattern> <use>
+            {
+                // pattern element attributes
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "id", sPatternId );
+
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "x", OUString::number( rData.aPos.X() ) );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "y", OUString::number( rData.aPos.Y() ) );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "width", OUString::number( rData.aSize.Width() ) );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "height", OUString::number( rData.aSize.Height() ) );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "patternUnits", "userSpaceOnUse" );
+
+                SvXMLElementExport aPatternElem( *mpSVGExport, XML_NAMESPACE_NONE, "pattern", true, true );
+
+                // use element attributes
+                const Size& aOrigSize = aBitmapActionIt->second->GetPrefSize();
+                OUString sTransform;
+                Fraction aFractionX( rData.aSize.Width(), aOrigSize.Width() );
+                Fraction aFractionY( rData.aSize.Height(), aOrigSize.Height() );
+                double scaleX = rtl_math_round( double(aFractionX), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected );
+                double scaleY = rtl_math_round( double(aFractionY), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected );
+                if( !rtl_math_approxEqual( scaleX, 1.0 ) || !rtl_math_approxEqual( scaleY, 1.0 ) )
+                    sTransform += " scale(" + OUString::number( double(aFractionX) ) + ", " + OUString::number( double(aFractionY) ) + ")";
+
+                if( !sTransform.isEmpty() )
+                    mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "transform", sTransform );
+
+                // referenced bitmap
+                OUString sRefId = "#bitmap(" + OUString::number( rData.aBitmapChecksum ) + ")";
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "xlink:href", sRefId );
+
+                SvXMLElementExport aUseElem( *mpSVGExport, XML_NAMESPACE_NONE, "use", true, true );
+            } // </use> </pattern>
+
+            // <g> <rect>
+            {
+                // group
+                const OUString sBgId = getIdForTiledBackground( rSlideId, rData.aBitmapChecksum );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "id", sBgId );
+
+                SvXMLElementExport aGroupElem( *mpSVGExport, XML_NAMESPACE_NONE, "g", true, true );
+
+                // rectangle
+                const OUString sUrl = "url(#" + sPatternId + ")";
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "x", "0" );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "y", "0" );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "width", OUString::number( rData.aSlideSize.Width() ) );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "height", OUString::number( rData.aSlideSize.Height() ) );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "stroke", "none" );
+                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "fill", sUrl );
+
+                SvXMLElementExport aRectElem( *mpSVGExport, XML_NAMESPACE_NONE, "rect", true, true );
+            } // </g> </rect>
+        }
+    }
+}
+
 /** SVGFilter::implExportTextEmbeddedBitmaps
     We export bitmaps embedded into text shapes, such as those used by list
     items with image style, only once in a specific defs element.
@@ -2354,21 +2462,63 @@ void SVGFilter::implCreateObjectsFromBackground( const Reference< css::drawing::
     xExporter->filter( aDescriptor );
     aMtf.Read( *aFile.GetStream( StreamMode::READ ) );
 
-    MetaAction*   pAction;
+    bool bIsBitmap = false;
+    bool bIsTiled = false;
+
+    // look for background type
+    Reference< XPropertySet > xPropSet( rxDrawPage, UNO_QUERY );
+    if( xPropSet.is() )
+    {
+        Reference< XPropertySet > xBackground;
+        xPropSet->getPropertyValue( "Background" ) >>= xBackground;
+        if( xBackground.is() )
+        {
+            drawing::FillStyle aFillStyle;
+            if( xBackground->getPropertyValue( "FillStyle" ) >>= aFillStyle )
+            {
+                if( aFillStyle == drawing::FillStyle::FillStyle_BITMAP )
+                {
+                    bIsBitmap = true;
+                    xBackground->getPropertyValue( "FillBitmapTile" ) >>= bIsTiled;
+
+                    // we do not handle tiled background with a row or column offset
+                    sal_Int32 nFillBitmapOffsetX = 0, nFillBitmapOffsetY = 0;
+                    xBackground->getPropertyValue( "FillBitmapOffsetX" ) >>= nFillBitmapOffsetX;
+                    xBackground->getPropertyValue( "FillBitmapOffsetY" ) >>= nFillBitmapOffsetY;
+                    bIsTiled = bIsTiled && ( nFillBitmapOffsetX == 0 && nFillBitmapOffsetY == 0 );
+                }
+            }
+        }
+    }
+
+    if( !bIsBitmap )
+    {
+        (*mpObjects)[ rxDrawPage ] = ObjectRepresentation( rxDrawPage, aMtf );
+        return;
+    }
+
+    GDIMetaFile aTiledMtf;
+    bool bBitmapFound = false;
+    MetaAction* pAction;
     sal_uLong nCount = aMtf.GetActionSize();
     for( sal_uLong nCurAction = 0; nCurAction < nCount; ++nCurAction )
     {
         pAction = aMtf.GetAction( nCurAction );
         const MetaActionType nType = pAction->GetType();
 
+        // collect bitmap
         if( nType == MetaActionType::BMPSCALE  || nType == MetaActionType::BMPEXSCALE )
         {
+            if( bBitmapFound )
+                continue;
+            bBitmapFound = true; // the subsequent bitmaps are still the same just translated
+
             BitmapChecksum nChecksum = GetBitmapChecksum( pAction );
             if( maBitmapActionMap.find( nChecksum ) == maBitmapActionMap.end() )
             {
                 Point aPos; // (0, 0)
                 Size  aSize;
-                MetaBitmapActionGetSize( pAction, aSize );
+                MetaBitmapActionGetOrigSize( pAction, aSize );
                 MetaAction* pBitmapAction = CreateMetaBitmapAction( pAction, aPos, aSize );
                 if( pBitmapAction )
                 {
@@ -2380,10 +2530,38 @@ void SVGFilter::implCreateObjectsFromBackground( const Reference< css::drawing::
                     maBitmapActionMap[ nChecksum ].reset( pEmbeddedBitmapMtf );
                 }
             }
+
+            if( bIsTiled )
+            {
+                // collect data for <pattern> and <rect>
+                const OUString & sPageId = implGetValidIDFromInterface( rxDrawPage );
+                Point aPos;
+                MetaBitmapActionGetPoint( pAction,  aPos );
+                Size aSize;
+                MetaBitmapActionGetSize( pAction, aSize );
+
+                sal_Int32 nSlideWidth = 0, nSlideHeight = 0;
+                xPropSet->getPropertyValue( "Width" ) >>= nSlideWidth;
+                xPropSet->getPropertyValue( "Height" ) >>= nSlideHeight;
+
+                maPatterProps[ sPageId ] = { nChecksum, aPos, aSize, { nSlideWidth, nSlideHeight } };
+
+                // create meta comment action that is used to exporting
+                // a <use> element which points to the group element representing the background
+                const OUString sBgId = getIdForTiledBackground( sPageId, nChecksum );
+                OString sComment = sTiledBackgroundTag + " " + sBgId.toUtf8();
+                MetaCommentAction* pCommentAction = new MetaCommentAction( sComment );
+                if( pCommentAction )
+                    aTiledMtf.AddAction( pCommentAction );
+            }
+        }
+        else if( bIsTiled && nType != MetaActionType::CLIPREGION )
+        {
+            aTiledMtf.AddAction( pAction );
         }
     }
 
-    (*mpObjects)[ rxDrawPage ] = ObjectRepresentation( rxDrawPage, aMtf );
+    (*mpObjects)[ rxDrawPage ] = ObjectRepresentation( rxDrawPage, bIsTiled ? aTiledMtf : aMtf );
 }
 
 OUString SVGFilter::implGetClassFromShape( const Reference< css::drawing::XShape >& rxShape )
diff --git a/filter/source/svg/svgfilter.hxx b/filter/source/svg/svgfilter.hxx
index 9f612440ebdf..69a90d6e070d 100644
--- a/filter/source/svg/svgfilter.hxx
+++ b/filter/source/svg/svgfilter.hxx
@@ -56,6 +56,8 @@ using namespace ::com::sun::star::xml::sax;
 
 // Placeholder tag used into the ImplWriteActions method to filter text placeholder fields
 const OUString sPlaceholderTag( "<[:isPlaceholder:]>" );
+// This tag is used for exporting a slide background made of tiled bitmaps
+const OString sTiledBackgroundTag( "SLIDE_BACKGROUND" );
 
 class SVGExport : public SvXMLExport
 {
@@ -156,6 +158,15 @@ struct EqualityBitmap
 // This must match the same type definition in svgwriter.hxx
 typedef std::unordered_map< BitmapChecksum, std::unique_ptr< GDIMetaFile > > MetaBitmapActionMap;
 
+struct PatternData
+{
+    BitmapChecksum aBitmapChecksum;
+    Point aPos;
+    Size aSize;
+    Size aSlideSize;
+};
+typedef std::map<OUString, PatternData> PatternPropertySet;
+
 class SVGFontExport;
 class SVGActionWriter;
 class EditFieldInfo;
@@ -214,6 +225,7 @@ private:
     MetaBitmapActionSet                 mEmbeddedBitmapActionSet;
     ObjectMap                           mEmbeddedBitmapActionMap;
     MetaBitmapActionMap                 maBitmapActionMap;
+    PatternPropertySet                  maPatterProps;
     std::vector< Reference< css::drawing::XDrawPage > > mMasterPageTargets;
 
     Link<EditFieldInfo*,void>           maOldFieldHdl;
@@ -234,6 +246,7 @@ private:
     void                            implEmbedBulletGlyph( sal_Unicode cBullet, const OUString & sPathData );
     void                            implExportTextEmbeddedBitmaps();
     void                            implExportBackgroundBitmaps();
+    void                            implExportTiledBackground();
     void                            implGenerateScript();
 
     bool                            implExportDocument();
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 0b08de921549..b7eab491429d 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -3633,6 +3633,18 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
                         }
                     }
                 }
+                else if( pA->GetComment().startsWithIgnoreAsciiCase( sTiledBackgroundTag ) )
+                {
+                    // In the tile case the background is rendered through a rectangle
+                    // filled by exploiting an exported pattern element.
+                    // Both the pattern and the rectangle are embedded in a <defs> element.
+                    // The comment content has the following format: "SLIDE_BACKGROUND <background-id>"
+                    const OString& sComment = pA->GetComment();
+                    OUString sRefId = "#" + OUString::fromUtf8( sComment.getToken(1, ' ') );
+                    mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, sRefId );
+
+                    SvXMLElementExport aRefElem( mrExport, XML_NAMESPACE_NONE, "use", true, true );
+                }
             }
             break;
 
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index 107c698afb68..49bba87e90a0 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -30,6 +30,8 @@
 #define SVG_DEFS *[name()='defs']
 #define SVG_IMAGE *[name()='image']
 #define SVG_USE *[name()='use']
+#define SVG_PATTERN *[name()='pattern']
+#define SVG_RECT *[name()='rect']
 
 using namespace css;
 
@@ -50,6 +52,19 @@ BitmapChecksum getBitmapChecksumFromId(const OUString& sId)
     OUString sChecksum = sId.copy( nStart, nCount );
     return sChecksum.toUInt64();
 }
+
+bool isValidBackgroundPatternId(const OUString& sId)
+{
+    std::regex aRegEx( R"(bg\-pattern\.id\d+\.\d+)" );
+    return std::regex_match(sId.toUtf8().getStr(), aRegEx);
+}
+
+bool isValidTiledBackgroundId(const OUString& sId)
+{
+    std::regex aRegEx( R"(bg\-id\d+\.\d+)" );
+    return std::regex_match(sId.toUtf8().getStr(), aRegEx);
+}
+
 }
 
 class SdSVGFilterTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
@@ -222,31 +237,50 @@ public:
         xmlDocUniquePtr svgDoc = parseXml(maTempFile);
         CPPUNIT_ASSERT(svgDoc);
 
+        // check the bitmap
         assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9] ), "class", "BackgroundBitmaps");
         assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_IMAGE ), 1);
 
+        // check the pattern and background rectangle
+        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10] ), "class", "BackgroundPatterns");
+        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_PATTERN ), 1);
+        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_PATTERN/SVG_USE ), 1);
+        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_G/SVG_RECT ), 1);
+
+
+        // check that <pattern><use> is pointing to the correct <image>
         OUString sImageId = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_IMAGE ), "id");
         CPPUNIT_ASSERT_MESSAGE(OString("The exported bitmap has not a valid id: " + sImageId.toUtf8()).getStr(), isValidBitmapId(sImageId));
 
         BitmapChecksum nChecksum = getBitmapChecksumFromId(sImageId);
         CPPUNIT_ASSERT_MESSAGE(OString("The exported bitmap has not a valid checksum: " + sImageId.toUtf8()).getStr(), nChecksum != 0);
 
-        // tiles case
-        constexpr unsigned int nNumberOfTiles = 37;
+        OUString sRef = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_PATTERN/SVG_USE ), "href");
+        CPPUNIT_ASSERT_MESSAGE("The <pattern><use> element has not a valid href attribute: starting '#' not present.", sRef.startsWith("#"));
+        sRef = sRef.copy(1);
+        CPPUNIT_ASSERT_EQUAL_MESSAGE("The href attribute for <pattern><use> does not match the <image> id attribute: ", sImageId, sRef);
+
+        OUString sPatternId = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_PATTERN ), "id");
+        CPPUNIT_ASSERT_MESSAGE(OString("The exported pattern has not a valid id: " + sPatternId.toUtf8()).getStr(), isValidBackgroundPatternId(sPatternId));
+
+        OUString sFillUrl = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_G/SVG_RECT ), "fill");
+        bool bIsUrlFormat = sFillUrl.startsWith("url(#") && sFillUrl.endsWith(")");
+        CPPUNIT_ASSERT_MESSAGE("The fill attribute for the <rectangle> element has not a url format .", bIsUrlFormat);
+        // remove "url(#" and ")"
+        sFillUrl = sFillUrl.copy(5, sFillUrl.getLength() - 6);
+        CPPUNIT_ASSERT_EQUAL_MESSAGE("The fill url for <rectangle> does not match the <pattern> id attribute: ", sPatternId, sFillUrl);
+
+        OUString sBackgroundId = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_G ), "id");
+        CPPUNIT_ASSERT_MESSAGE(OString("The exported tiled background has not a valid id: " + sBackgroundId.toUtf8()).getStr(), isValidTiledBackgroundId(sBackgroundId));
+
+        // check <use> element that point to the tiled background
         assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS ), "class", "SlideBackground");
-        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_G/SVG_USE ), nNumberOfTiles);
-
-        for (unsigned int i = 1; i <= nNumberOfTiles; ++i)
-        {
-            OString sIndex = OStringLiteral("[") + OString::number(i) + OStringLiteral("]");
-            OUString sRef = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_G/SVG_USE ) + sIndex, "href");
-            CPPUNIT_ASSERT_MESSAGE("The <use> element has not a valid href attribute: starting '#' not present.", sRef.startsWith("#"));
-            sRef = sRef.copy(1);
-            CPPUNIT_ASSERT_MESSAGE(OString("The <use> element does not point to a valid bitmap id: " + sRef.toUtf8()).getStr(), isValidBitmapId(sRef));
-
-            BitmapChecksum nUseChecksum = getBitmapChecksumFromId(sRef);
-            CPPUNIT_ASSERT_EQUAL_MESSAGE("The bitmap checksum used in <use> does not match the expected one: ", nChecksum, nUseChecksum);
-        }
+        assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_USE ), 1);
+
+        sRef = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_USE ), "href");
+        CPPUNIT_ASSERT_MESSAGE("The <use> element has not a valid href attribute: starting '#' not present.", sRef.startsWith("#"));
+        sRef = sRef.copy(1);
+        CPPUNIT_ASSERT_EQUAL_MESSAGE("The href attribute for <use> does not match the tiled background id attribute: ", sBackgroundId, sRef);
     }
 
     CPPUNIT_TEST_SUITE(SdSVGFilterTest);


More information about the Libreoffice-commits mailing list