diff options
-rw-r--r-- | emfio/inc/mtftools.hxx | 3 | ||||
-rw-r--r-- | emfio/qa/cppunit/emf/EmfImportTest.cxx | 113 | ||||
-rw-r--r-- | emfio/qa/cppunit/emf/data/TestExtTextOutOpaqueAndClipTransform.emf | bin | 0 -> 1232 bytes | |||
-rw-r--r-- | emfio/qa/cppunit/wmf/data/TestExtTextOutOpaqueAndClip.wmf | bin | 0 -> 328 bytes | |||
-rw-r--r-- | emfio/qa/cppunit/wmf/wmfimporttest.cxx | 18 | ||||
-rw-r--r-- | emfio/source/reader/emfreader.cxx | 37 | ||||
-rw-r--r-- | emfio/source/reader/mtftools.cxx | 35 | ||||
-rw-r--r-- | emfio/source/reader/wmfreader.cxx | 23 |
8 files changed, 190 insertions, 39 deletions
diff --git a/emfio/inc/mtftools.hxx b/emfio/inc/mtftools.hxx index 0fb99761d386..cffe5e4fa511 100644 --- a/emfio/inc/mtftools.hxx +++ b/emfio/inc/mtftools.hxx @@ -207,11 +207,13 @@ namespace emfio #define MAC_CHARSET 77 #define BALTIC_CHARSET 186 +#define ETO_OPAQUE 0x0002 #define ETO_CLIPPED 0x0004 /*WINVER >= 0x0400*/ #define ETO_GLYPH_INDEX 0x0010 #define ETO_RTLREADING 0x0080 /*_WIN32_WINNT >= 0x0500*/ +#define ETO_NO_RECT 0x0100 #define ETO_PDY 0x2000 #define DEFAULT_PITCH 0x00 @@ -642,6 +644,7 @@ namespace emfio void LineTo(const Point& rPoint, bool bRecordPath = false); void DrawPixel(const Point& rSource, const Color& rColor); void DrawRect(const tools::Rectangle& rRect, bool bEdge = true); + void DrawRectWithBGColor(const tools::Rectangle& rRect); void DrawRoundRect(const tools::Rectangle& rRect, const Size& rSize); void DrawEllipse(const tools::Rectangle& rRect); void DrawArc( diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx index 5de9faa36714..1b134551c7ff 100644 --- a/emfio/qa/cppunit/emf/EmfImportTest.cxx +++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx @@ -60,6 +60,9 @@ class Test : public test::BootstrapFixture, public XmlTestTools, public unotest: void TestEllipseXformIntersectClipRect(); void TestDrawPolyLine16WithClip(); void TestFillRegion(); + void TestExtTextOutOpaqueAndClipTransform(); + + void TestExtTextOutOpaqueAndClipWMF(); void TestPaletteWMF(); void TestRoundrectWMF(); void TestPolylinetoCloseStroke(); @@ -91,6 +94,8 @@ public: CPPUNIT_TEST(TestEllipseXformIntersectClipRect); CPPUNIT_TEST(TestDrawPolyLine16WithClip); CPPUNIT_TEST(TestFillRegion); + CPPUNIT_TEST(TestExtTextOutOpaqueAndClipTransform); + CPPUNIT_TEST(TestExtTextOutOpaqueAndClipWMF); CPPUNIT_TEST(TestPaletteWMF); CPPUNIT_TEST(TestRoundrectWMF); CPPUNIT_TEST(TestPolylinetoCloseStroke); @@ -504,6 +509,114 @@ void Test::TestPolylinetoCloseStroke() "color", "#000000"); } + +void Test::TestExtTextOutOpaqueAndClipTransform() +{ + // tdf#142495 EMF records: SETBKCOLOR, SELECTOBJECT, EXTTEXTOUTW, MODIFYWORLDTRANSFORM, CREATEFONTINDIRECT. + Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/TestExtTextOutOpaqueAndClipTransform.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/textsimpleportion", + "text", "No_rect- DLP-"); + assertXPath(pDocument, "/primitive2D/metafile/transform/textsimpleportion", + "fontcolor", "#000000"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor", 2); + assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[1]/polypolygon", + "path", "m966 490-477-275-84 147 476 275z"); + assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[1]", + "color", "#ff0000"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[2]/polypolygon", + "path", "m251 713 623 361-148 257-623-361z"); + assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[2]", + "color", "#0080ff"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/group", 3); + assertXPath(pDocument, "/primitive2D/metafile/transform/group[1]/polypolygoncolor", + "color", "#ff0000"); + assertXPath(pDocument, "/primitive2D/metafile/transform/group[1]/textsimpleportion", + "text", "Opaque - DLP-"); + assertXPath(pDocument, "/primitive2D/metafile/transform/group[1]/textsimpleportion", + "fontcolor", "#000000"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/group[2]/mask/group/polypolygoncolor", + "color", "#00ff00"); + assertXPath(pDocument, "/primitive2D/metafile/transform/group[2]/mask/polypolygon", + "path", "m320 508 586 340-169 293-586-339z"); + assertXPath(pDocument, "/primitive2D/metafile/transform/group[2]/mask/group/textsimpleportion", + "text", "Clip - DLP-"); + assertXPath(pDocument, "/primitive2D/metafile/transform/group[2]/mask/group/textsimpleportion", + "fontcolor", "#000000"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/group[3]/mask/group/polypolygoncolor", + "color", "#0080ff"); + assertXPath(pDocument, "/primitive2D/metafile/transform/group[3]/mask/polypolygon", + "path", "m251 713 623 361-148 257-623-361z"); + assertXPath(pDocument, "/primitive2D/metafile/transform/group[3]/mask/group/textsimpleportion", + "text", "Opaque ClipP-"); + assertXPath(pDocument, "/primitive2D/metafile/transform/group[3]/mask/group/textsimpleportion", + "fontcolor", "#000000"); +} + +void Test::TestExtTextOutOpaqueAndClipWMF() +{ + // tdf#53004 WMF records: SETBKCOLOR, SELECTOBJECT, EXTTEXTOUT, CREATEBRUSHINDIRECT. + Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/wmf/data/TestExtTextOutOpaqueAndClip.wmf"); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength())); + drawinglayer::Primitive2dXmlDump dumper; + xmlDocUniquePtr pDocument = dumper.dumpAndParse(comphelper::sequenceToContainer<Primitive2DContainer>(aSequence)); + CPPUNIT_ASSERT (pDocument); + +#ifdef MACOSX + // On some operating systems (Linux on LO Jenkins CI), the `/mask/` string is not added to XPath + // As a result tests are failing. On my Ubuntu 20.04 the `/mask/` string was added + // I would leave this test case for macOS to make sure there is no regression at least on one platform. + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor", 5); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor[1]/polypolygon", + "path", "m7219 1825h319v3608h-319z"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor[1]", + "color", "#ff0000"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor[2]/polypolygon", + "path", "m7219 5942h319v318h-319z"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor[2]", + "color", "#00ff00"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor[3]/polypolygon", + "path", "m10149 5942h319v318h-319z"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor[3]", + "color", "#8080ff"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group", 5); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[1]/polypolygoncolor", + "color", "#00ff00"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[1]/textsimpleportion", + "text", "ABCD"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[1]/textsimpleportion", + "fontcolor", "#000000"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[2]/polypolygoncolor", + "color", "#8080ff"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[2]/textsimpleportion", + "text", "MMMM"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[2]/textsimpleportion", + "fontcolor", "#000000"); + + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[3]/mask/group/polypolygoncolor", + "color", "#ff8000"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[3]/mask/group/polypolygoncolor/polypolygon", + "path", "m1062 1061h1270v473h-1270z"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[3]/mask/group/textsimpleportion", + "text", "OOOO"); + assertXPath(pDocument, "/primitive2D/metafile/transform/mask/group[3]/mask/group/textsimpleportion", + "fontcolor", "#000000"); +#endif +} + void Test::TestPaletteWMF() { // WMF import with records: CREATEPALETTE, SELECTOBJECT, CREATEPENINDIRECT, CREATEBRUSHINDIRECT, ELLIPSE. diff --git a/emfio/qa/cppunit/emf/data/TestExtTextOutOpaqueAndClipTransform.emf b/emfio/qa/cppunit/emf/data/TestExtTextOutOpaqueAndClipTransform.emf Binary files differnew file mode 100644 index 000000000000..7d59ac3e5bd2 --- /dev/null +++ b/emfio/qa/cppunit/emf/data/TestExtTextOutOpaqueAndClipTransform.emf diff --git a/emfio/qa/cppunit/wmf/data/TestExtTextOutOpaqueAndClip.wmf b/emfio/qa/cppunit/wmf/data/TestExtTextOutOpaqueAndClip.wmf Binary files differnew file mode 100644 index 000000000000..fb6fd20f0cb4 --- /dev/null +++ b/emfio/qa/cppunit/wmf/data/TestExtTextOutOpaqueAndClip.wmf diff --git a/emfio/qa/cppunit/wmf/wmfimporttest.cxx b/emfio/qa/cppunit/wmf/wmfimporttest.cxx index 41c15e3989f8..62a669fe3676 100644 --- a/emfio/qa/cppunit/wmf/wmfimporttest.cxx +++ b/emfio/qa/cppunit/wmf/wmfimporttest.cxx @@ -206,7 +206,7 @@ void WmfTest::testWorldTransformFontSize() CPPUNIT_ASSERT(pDoc); - assertXPath(pDoc, "/metafile/font", 8); + assertXPath(pDoc, "/metafile/font", 9); assertXPath(pDoc, "/metafile/font[1]", "color", "#595959"); assertXPath(pDoc, "/metafile/font[1]", "width", "0"); @@ -214,13 +214,19 @@ void WmfTest::testWorldTransformFontSize() assertXPath(pDoc, "/metafile/font[1]", "orientation", "0"); assertXPath(pDoc, "/metafile/font[1]", "weight", "bold"); - // World transform should not affect font size. Rotating text for 90 degrees - // should not exchange font width and height. assertXPath(pDoc, "/metafile/font[3]", "color", "#000000"); assertXPath(pDoc, "/metafile/font[3]", "width", "0"); - assertXPath(pDoc, "/metafile/font[3]", "height", "530"); - assertXPath(pDoc, "/metafile/font[3]", "orientation", "900"); - assertXPath(pDoc, "/metafile/font[3]", "weight", "normal"); + assertXPath(pDoc, "/metafile/font[3]", "height", "389"); + assertXPath(pDoc, "/metafile/font[3]", "orientation", "0"); + assertXPath(pDoc, "/metafile/font[3]", "weight", "bold"); + + // World transform should not affect font size. Rotating text for 90 degrees + // should not exchange font width and height. + assertXPath(pDoc, "/metafile/font[4]", "color", "#000000"); + assertXPath(pDoc, "/metafile/font[4]", "width", "0"); + assertXPath(pDoc, "/metafile/font[4]", "height", "530"); + assertXPath(pDoc, "/metafile/font[4]", "orientation", "900"); + assertXPath(pDoc, "/metafile/font[4]", "weight", "normal"); } void WmfTest::testTdf93750() diff --git a/emfio/source/reader/emfreader.cxx b/emfio/source/reader/emfreader.cxx index 9a8784c7e631..e6a758175fb5 100644 --- a/emfio/source/reader/emfreader.cxx +++ b/emfio/source/reader/emfreader.cxx @@ -1795,14 +1795,18 @@ namespace emfio [[fallthrough]]; case EMR_EXTTEXTOUTW : { - sal_Int32 nLeft, nTop, nRight, nBottom, ptlReferenceX, ptlReferenceY, nGfxMode, nXScale, nYScale; - sal_uInt32 nOffString, nOptions, offDx; - sal_Int32 nLen; + sal_Int32 nLeft, nTop, nRight, nBottom; + sal_uInt32 nGfxMode; + float nXScale, nYScale; + sal_Int32 ptlReferenceX, ptlReferenceY; + sal_uInt32 nLen, nOffString, nOptions, offDx; + sal_Int32 nLeftRect, nTopRect, nRightRect, nBottomRect; nCurPos = mpInputStream->Tell() - 8; - mpInputStream->ReadInt32( nLeft ).ReadInt32( nTop ).ReadInt32( nRight ).ReadInt32( nBottom ).ReadInt32( nGfxMode ).ReadInt32( nXScale ).ReadInt32( nYScale ) - .ReadInt32( ptlReferenceX ).ReadInt32( ptlReferenceY ).ReadInt32( nLen ).ReadUInt32( nOffString ).ReadUInt32( nOptions ); + mpInputStream->ReadInt32( nLeft ).ReadInt32( nTop ).ReadInt32( nRight ).ReadInt32( nBottom ) + .ReadUInt32( nGfxMode ).ReadFloat( nXScale ).ReadFloat( nYScale ) + .ReadInt32( ptlReferenceX ).ReadInt32( ptlReferenceY ).ReadUInt32( nLen ).ReadUInt32( nOffString ).ReadUInt32( nOptions ); SAL_INFO("emfio", "\t\tBounds: " << nLeft << ", " << nTop << ", " << nRight << ", " << nBottom); SAL_INFO("emfio", "\t\tiGraphicsMode: 0x" << std::hex << nGfxMode << std::dec); @@ -1810,7 +1814,13 @@ namespace emfio SAL_INFO("emfio", "\t\teyScale: " << nYScale); SAL_INFO("emfio", "\t\tReference: (" << ptlReferenceX << ", " << ptlReferenceY << ")"); - mpInputStream->SeekRel( 0x10 ); + mpInputStream->ReadInt32( nLeftRect ).ReadInt32( nTopRect ).ReadInt32( nRightRect ).ReadInt32( nBottomRect ); + const tools::Rectangle aRect( nLeftRect, nTopRect, nRightRect, nBottomRect ); + BkMode mnBkModeBackup = mnBkMode; + if ( nOptions & ETO_NO_RECT ) // Don't draw the background rectangle + mnBkMode = BkMode::Transparent; + if ( nOptions & ETO_OPAQUE ) + DrawRectWithBGColor( aRect ); mpInputStream->ReadUInt32( offDx ); ComplexTextLayoutFlags nTextLayoutMode = ComplexTextLayoutFlags::Default; @@ -1820,15 +1830,14 @@ namespace emfio SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "emfio", "SJ: ETO_PDY || ETO_GLYPH_INDEX in EMF" ); Point aPos( ptlReferenceX, ptlReferenceY ); - bool bLenSane = nLen > 0 && nLen < static_cast<sal_Int32>( SAL_MAX_UINT32 / sizeof(sal_Int32) ); bool bOffStringSane = nOffString <= mnEndPos - nCurPos; - if (bLenSane && bOffStringSane) + if ( bOffStringSane ) { mpInputStream->Seek( nCurPos + nOffString ); OUString aText; if ( bFlag ) { - if ( nLen <= static_cast<sal_Int32>( mnEndPos - mpInputStream->Tell() ) ) + if ( nLen <= ( mnEndPos - mpInputStream->Tell() ) ) { std::unique_ptr<char[]> pBuf(new char[ nLen ]); mpInputStream->ReadBytes(pBuf.get(), nLen); @@ -1862,7 +1871,7 @@ namespace emfio for (sal_Int32 i = 0; i < aText.getLength(); ++i) { sal_Int32 nDxCount = 1; - if (aText.getLength() != nLen) + if (aText.getLength() != static_cast<sal_Int32>( nLen ) ) { sal_Unicode cUniChar = aText[i]; OString aTmp(&cUniChar, 1, GetCharSet()); @@ -1894,7 +1903,15 @@ namespace emfio } } } + if ( nOptions & ETO_CLIPPED ) + { + Push(); // Save the current clip. It will be restored after text drawing + IntersectClipRect( aRect ); + } DrawText(aPos, aText, pDXAry.get(), pDYAry.get(), mbRecordPath, nGfxMode); + if ( nOptions & ETO_CLIPPED ) + Pop(); + mnBkMode = mnBkModeBackup; } } break; diff --git a/emfio/source/reader/mtftools.cxx b/emfio/source/reader/mtftools.cxx index d68e36e361ef..6bf0d98a3e76 100644 --- a/emfio/source/reader/mtftools.cxx +++ b/emfio/source/reader/mtftools.cxx @@ -1051,14 +1051,8 @@ namespace emfio { return; // empty rectangles cause trouble } - Point aPoints[] { Point(rRect.Left(), rRect.Top()), - Point(rRect.Right(), rRect.Top()), - Point(rRect.Right(), rRect.Bottom()), - Point(rRect.Left(), rRect.Bottom()) }; - tools::Polygon aPoly(4, aPoints); - aPoly = ImplMap( aPoly ); - aPoly.Optimize( PolyOptimizeFlags::CLOSE ); - tools::PolyPolygon aPolyPolyRect( aPoly ); + tools::Polygon aPoly( rRect ); + const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) ); maClipPath.intersectClip( aPolyPolyRect.getB2DPolyPolygon() ); } @@ -1067,15 +1061,8 @@ namespace emfio if (utl::ConfigManager::IsFuzzing()) return; mbClipNeedsUpdate=true; - - Point aPoints[] { Point(rRect.Left(), rRect.Top()), - Point(rRect.Right(), rRect.Top()), - Point(rRect.Right(), rRect.Bottom()), - Point(rRect.Left(), rRect.Bottom()) }; - tools::Polygon aPoly(4, aPoints); - aPoly = ImplMap( aPoly ); - aPoly.Optimize( PolyOptimizeFlags::CLOSE ); - tools::PolyPolygon aPolyPolyRect( aPoly ); + tools::Polygon aPoly( rRect ); + const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) ); maClipPath.excludeClip( aPolyPolyRect.getB2DPolyPolygon() ); } @@ -1390,6 +1377,20 @@ namespace emfio maActPos = aDest; } + void MtfTools::DrawRectWithBGColor(const tools::Rectangle& rRect) + { + WinMtfFillStyle aFillStyleBackup = maFillStyle; + bool aTransparentBackup = maLineStyle.bTransparent; + + const tools::Polygon aPoly( rRect ); + maLineStyle.bTransparent = true; + maFillStyle = maBkColor; + ImplSetNonPersistentLineColorTransparenz(); + DrawPolygon(aPoly, false); + maFillStyle = aFillStyleBackup; + maLineStyle.bTransparent = aTransparentBackup; + } + void MtfTools::DrawRect( const tools::Rectangle& rRect, bool bEdge ) { UpdateClipRegion(); diff --git a/emfio/source/reader/wmfreader.cxx b/emfio/source/reader/wmfreader.cxx index 2f0aafabfaa9..f14949135ad4 100644 --- a/emfio/source/reader/wmfreader.cxx +++ b/emfio/source/reader/wmfreader.cxx @@ -666,8 +666,9 @@ namespace emfio Point aPosition = ReadYX(); sal_uInt16 nLen = 0, nOptions = 0; mpInputStream->ReadUInt16( nLen ).ReadUInt16( nOptions ); - - if (nOptions & ETO_CLIPPED) + SAL_INFO( "emfio", "\t\t\t Pos: " << aPosition.getX() << ":" << aPosition.getY() << " String Length: " << nLen << " Options: " << nOptions ); + tools::Rectangle aRect; + if ( ( nOptions & ETO_CLIPPED ) || ( nOptions & ETO_OPAQUE ) ) { nNonStringLen += 2 * sizeof(sal_uInt16); @@ -676,10 +677,12 @@ namespace emfio SAL_WARN("emfio", "W_META_TEXTOUT too short"); break; } - - ReadPoint(); - ReadPoint(); - SAL_WARN("emfio", "clipping unsupported"); + const Point aTopLeft = ReadPoint(); + const Point aBottomRight = ReadPoint(); + aRect = tools::Rectangle( aTopLeft, aBottomRight ); + if ( nOptions & ETO_OPAQUE ) + DrawRectWithBGColor( aRect ); + SAL_INFO( "emfio", "\t\t\t Rectangle : " << aTopLeft.getX() << ":" << aTopLeft.getY() << ", " << aBottomRight.getX() << ":" << aBottomRight.getY() ); } ComplexTextLayoutFlags nTextLayoutMode = ComplexTextLayoutFlags::Default; @@ -709,6 +712,12 @@ namespace emfio // dxAry will not fit if ( nNewTextLen ) { + if ( nOptions & ETO_CLIPPED ) + { + Push(); // Save the current clip. It will be restored after text drawing + IntersectClipRect( aRect ); + } + SAL_INFO( "emfio", "\t\t\t Text : " << aText ); std::unique_ptr<tools::Long[]> pDXAry, pDYAry; auto nDxArySize = nMaxStreamPos - mpInputStream->Tell(); auto nDxAryEntries = nDxArySize >> 1; @@ -768,6 +777,8 @@ namespace emfio DrawText( aPosition, aText, pDXAry.get(), pDYAry.get() ); else DrawText( aPosition, aText ); + if ( nOptions & ETO_CLIPPED ) + Pop(); } } } |