WMF/EMF tdf#59814 tdf#142567 Fix RestoreDC record

With previous implementation, the RestoreDC index argument was skipped,
and always the last entry was taken.

With this commit the support for reading SaveDC by specific index
was added. The SaveDC/RestoreDC index support was added for
both EMF and WMF, according to [MS-WMF] and [MS-EMF] documentation.

Change-Id: I9b8a1a41462ae01de25ac3c85e453bcd80e05537
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/117033
Tested-by: Jenkins
Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
diff --git a/emfio/inc/mtftools.hxx b/emfio/inc/mtftools.hxx
index cffe5e4..456f234 100644
--- a/emfio/inc/mtftools.hxx
+++ b/emfio/inc/mtftools.hxx
@@ -614,7 +614,7 @@ namespace emfio
        void                ModifyWorldTransform(const XForm& rXForm, sal_uInt32 nMode);

        void                Push();
        void                Pop();
        void                Pop( const sal_Int32 nSavedDC = -1 );

        WMFRasterOp         SetRasterOp(WMFRasterOp nRasterOp);
        void                StrokeAndFillPath(bool bStroke, bool bFill);
diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx
index 8c561902..1383b5f 100644
--- a/emfio/qa/cppunit/emf/EmfImportTest.cxx
+++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx
@@ -65,10 +65,13 @@ class Test : public test::BootstrapFixture, public XmlTestTools, public unotest:
    void TestBitBltStretchBltWMF();
    void TestExtTextOutOpaqueAndClipWMF();
    void TestPaletteWMF();
    void TestRestoreDCWMF();
    void TestRoundrectWMF();
    void TestStretchDIBWMF();
    void TestPolylinetoCloseStroke();
    void TestPolyLineWidth();

    void TestRestoreDC();
    void TestRoundRect();
    void TestCreatePen();
    void TestPdfInEmf();
@@ -101,10 +104,12 @@ public:
    CPPUNIT_TEST(TestBitBltStretchBltWMF);
    CPPUNIT_TEST(TestExtTextOutOpaqueAndClipWMF);
    CPPUNIT_TEST(TestPaletteWMF);
    CPPUNIT_TEST(TestRestoreDCWMF);
    CPPUNIT_TEST(TestRoundrectWMF);
    CPPUNIT_TEST(TestStretchDIBWMF);
    CPPUNIT_TEST(TestPolylinetoCloseStroke);
    CPPUNIT_TEST(TestPolyLineWidth);
    CPPUNIT_TEST(TestRestoreDC);
    CPPUNIT_TEST(TestRoundRect);
    CPPUNIT_TEST(TestCreatePen);
    CPPUNIT_TEST(TestPdfInEmf);
@@ -699,6 +704,7 @@ void Test::TestExtTextOutOpaqueAndClipWMF()
#endif
}


void Test::TestPaletteWMF()
{
    // WMF import with records: CREATEPALETTE, SELECTOBJECT, CREATEPENINDIRECT, CREATEBRUSHINDIRECT, ELLIPSE.
@@ -735,6 +741,40 @@ void Test::TestPaletteWMF()
                "width", "132");
}

void Test::TestRestoreDCWMF()
{
    // WMF records: RESTOREDC, SAVEDC, CREATEBRUSHINDIRECT, RECTANGLE.
    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/wmf/data/TestRestoreDC.wmf");
    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
    drawinglayer::Primitive2dXmlDump dumper;
    xmlDocUniquePtr pDocument = dumper.dumpAndParse(comphelper::sequenceToContainer<Primitive2DContainer>(aSequence));
    CPPUNIT_ASSERT (pDocument);

    assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor", 3);
    assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[1]",
                "color", "#0000ff");
    assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[1]/polypolygon",
                "path", "m238 2884h1640v1110h-1640z");
    assertXPath(pDocument, "/primitive2D/metafile/transform/polygonhairline[1]",
                "color", "#000000");
    assertXPathContent(pDocument, "/primitive2D/metafile/transform/polygonhairline[1]/polygon",
                       "238,2884 1878,2884 1878,3994 238,3994");

    assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[2]",
                "color", "#ff0000");
    assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[2]/polypolygon",
                "path", "m238 238h1640v1110h-1640z");
    assertXPath(pDocument, "/primitive2D/metafile/transform/polygonhairline[2]",
                "color", "#000000");

    assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[3]",
                "color", "#ff0000");
    assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[3]/polypolygon",
                "path", "m238 5530h1640v1110h-1640z");
    assertXPath(pDocument, "/primitive2D/metafile/transform/polygonhairline[3]",
                "color", "#000000");
}

void Test::TestRoundrectWMF()
{
    // WMF records: ROUNDRECT, SETBKCOLOR, CREATEBRUSHINDIRECT
@@ -815,6 +855,25 @@ void Test::TestPolyLineWidth()
                "width", "71");
}

void Test::TestRestoreDC()
{
    // EMF records: SAVEDC, RESTOREDC, POLYGON16, MODIFYWORLDTRANSFORM
    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/TestRestoreDC.emf");
    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
    drawinglayer::Primitive2dXmlDump dumper;
    xmlDocUniquePtr pDocument = dumper.dumpAndParse(comphelper::sequenceToContainer<Primitive2DContainer>(aSequence));
    CPPUNIT_ASSERT (pDocument);

    assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor",
                "color", "#ff0000");
    assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor/polypolygon",
                "path", "m1148 4354v1481h4943v-1481z");
    assertXPath(pDocument, "/primitive2D/metafile/transform/polygonhairline",
                "color", "#000000");
    assertXPathContent(pDocument, "/primitive2D/metafile/transform/polygonhairline/polygon",
                       "1148,4354 1148,5835 6091,5835 6091,4354");
}

void Test::TestRoundRect()
{
    // EMF import with records: CREATEPEN, ROUNDRECT.
diff --git a/emfio/qa/cppunit/emf/data/TestRestoreDC.emf b/emfio/qa/cppunit/emf/data/TestRestoreDC.emf
new file mode 100644
index 0000000..b65b48d
--- /dev/null
+++ b/emfio/qa/cppunit/emf/data/TestRestoreDC.emf
Binary files differ
diff --git a/emfio/qa/cppunit/wmf/data/TestRestoreDC.wmf b/emfio/qa/cppunit/wmf/data/TestRestoreDC.wmf
new file mode 100644
index 0000000..c81244f
--- /dev/null
+++ b/emfio/qa/cppunit/wmf/data/TestRestoreDC.wmf
Binary files differ
diff --git a/emfio/source/reader/emfreader.cxx b/emfio/source/reader/emfreader.cxx
index 064a8b0..ac56c64 100644
--- a/emfio/source/reader/emfreader.cxx
+++ b/emfio/source/reader/emfreader.cxx
@@ -1098,7 +1098,11 @@ namespace emfio

                    case EMR_RESTOREDC :
                    {
                        Pop();
                        sal_Int32 nSavedDC;
                        mpInputStream->ReadInt32( nSavedDC );
                        SAL_INFO( "emfio", "\t\t SavedDC Index: " << nSavedDC );
                        if ( nSavedDC < 0 ) // For EMF values above -1 is ignored
                            Pop( nSavedDC );
                    }
                    break;

diff --git a/emfio/source/reader/mtftools.cxx b/emfio/source/reader/mtftools.cxx
index 866f699..e4d2ed4 100644
--- a/emfio/source/reader/mtftools.cxx
+++ b/emfio/source/reader/mtftools.cxx
@@ -2452,15 +2452,29 @@ namespace emfio
        SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY);
        SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY);
        SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight);
        SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor );
        mvSaveStack.push_back( pSave );
    }

    void MtfTools::Pop()
    void MtfTools::Pop( const sal_Int32 nSavedDC )
    {
        // Get the latest data from the stack
        if( mvSaveStack.empty() )
        if ( nSavedDC == 0 )
            return;

        sal_Int32 aIndex;
        if ( nSavedDC < 0 ) // WMF/EMF, if negative, nSavedDC represents an instance relative to the current state.
            aIndex = static_cast< sal_Int32 >( mvSaveStack.size() ) + nSavedDC;
        else
            aIndex = nSavedDC; // WMF, if positive, nSavedDC represents a specific instance of the state to be restored.
        if( aIndex < 0 )
        {
            mvSaveStack.clear();
            return;
        }
        if( mvSaveStack.empty() || ( aIndex >= static_cast< sal_Int32 >( mvSaveStack.size() ) ) )
            return;

        mvSaveStack.resize( aIndex + 1 );
        // Backup the current data on the stack
        std::shared_ptr<SaveStruct>& pSave( mvSaveStack.back() );

@@ -2508,6 +2522,7 @@ namespace emfio
        SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY);
        SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY);
        SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight);
        SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor );
        mvSaveStack.pop_back();
    }

diff --git a/emfio/source/reader/wmfreader.cxx b/emfio/source/reader/wmfreader.cxx
index 64dbf66..38f6252 100644
--- a/emfio/source/reader/wmfreader.cxx
+++ b/emfio/source/reader/wmfreader.cxx
@@ -600,7 +600,10 @@ namespace emfio

            case W_META_RESTOREDC:
            {
                Pop();
                sal_Int16 nSavedDC;
                mpInputStream->ReadInt16( nSavedDC );
                SAL_INFO( "emfio", "\t\t SavedDC: " << nSavedDC );
                Pop( nSavedDC );
            }
            break;