compilerplugins/clang/redundantfcast.cxx | 3 drawinglayer/source/tools/emfpcustomlinecap.cxx | 9 - drawinglayer/source/tools/emfpcustomlinecap.hxx | 2 drawinglayer/source/tools/emfphelperdata.cxx | 62 +----------- drawinglayer/source/tools/emfppen.cxx | 70 +++++++++++--- drawinglayer/source/tools/emfppen.hxx | 5 - drawinglayer/source/tools/primitive2dxmldump.cxx | 32 ++++-- emfio/qa/cppunit/emf/EmfImportTest.cxx | 34 ++++++ emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithDash.emf |binary svx/qa/unit/svdraw.cxx | 3 10 files changed, 127 insertions(+), 93 deletions(-)
New commits: commit 547ac0aa23a25f03a17014cb474eedc1fd88017f Author: Bartosz Kosiorek <gan...@poczta.onet.pl> AuthorDate: Wed Apr 20 19:44:05 2022 +0200 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Fri Apr 22 16:13:54 2022 +0200 tdf#55058 tdf#143875 EMF+ Fix display of dashed lines and line joints With previous implementation, empty spaces between dashes were too long. Additionally line joints were not working correctly, after EMF+ reworking: tdf#111486 This commit fixes all these issues and additionally it is covering it with tests. Change-Id: I9404e566d2d7d3405ab817268ad9b1f538c200eb Change-Id: I523f92a928ab592ff175d0d01c1ad1a3bc22e324 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133207 Tested-by: Jenkins Reviewed-by: Bartosz Kosiorek <gan...@poczta.onet.pl> (cherry picked from commit 80c856336668e35837667323957fa3ad4172f3c0) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133152 diff --git a/compilerplugins/clang/redundantfcast.cxx b/compilerplugins/clang/redundantfcast.cxx index 67ff2c56edef..85386eb4ea8e 100644 --- a/compilerplugins/clang/redundantfcast.cxx +++ b/compilerplugins/clang/redundantfcast.cxx @@ -334,6 +334,9 @@ public: // tdf#145203: FIREBIRD cannot create a table if (fn == SRCDIR "/connectivity/source/drivers/firebird/DatabaseMetaData.cxx") return false; + // false positive during using contructor drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw }) + if (fn == SRCDIR "/drawinglayer/source/tools/emfppen.cxx") + return false; return true; } diff --git a/drawinglayer/source/tools/emfpcustomlinecap.cxx b/drawinglayer/source/tools/emfpcustomlinecap.cxx index e24cbcc32cb1..49cc912ed33c 100644 --- a/drawinglayer/source/tools/emfpcustomlinecap.cxx +++ b/drawinglayer/source/tools/emfpcustomlinecap.cxx @@ -43,15 +43,6 @@ namespace emfplushelper { } - void EMFPCustomLineCap::SetAttributes(rendering::StrokeAttributes& aAttributes) - { - aAttributes.StartCapType = EMFPPen::lcl_convertStrokeCap(strokeStartCap); - aAttributes.EndCapType = EMFPPen::lcl_convertStrokeCap(strokeEndCap); - aAttributes.JoinType = EMFPPen::lcl_convertLineJoinType(strokeJoin); - - aAttributes.MiterLimit = miterLimit; - } - void EMFPCustomLineCap::ReadPath(SvStream& s, EmfPlusHelperData const & rR, bool bFill) { sal_Int32 pathLength; diff --git a/drawinglayer/source/tools/emfpcustomlinecap.hxx b/drawinglayer/source/tools/emfpcustomlinecap.hxx index a42e0ab4ef46..e6202ae98179 100644 --- a/drawinglayer/source/tools/emfpcustomlinecap.hxx +++ b/drawinglayer/source/tools/emfpcustomlinecap.hxx @@ -19,7 +19,6 @@ #pragma once -#include <com/sun/star/rendering/StrokeAttributes.hpp> #include "emfphelperdata.hxx" namespace emfplushelper @@ -34,7 +33,6 @@ namespace emfplushelper EMFPCustomLineCap(); - void SetAttributes(com::sun::star::rendering::StrokeAttributes& aAttributes); void ReadPath(SvStream& s, EmfPlusHelperData const & rR, bool bFill); void Read(SvStream& s, EmfPlusHelperData const & rR); }; diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx index 447686167837..79ced761e8a2 100644 --- a/drawinglayer/source/tools/emfphelperdata.cxx +++ b/drawinglayer/source/tools/emfphelperdata.cxx @@ -509,13 +509,6 @@ namespace emfplushelper if (!(pen && polygon.count())) return; - // we need a line join attribute - basegfx::B2DLineJoin lineJoin = basegfx::B2DLineJoin::Round; - if (pen->penDataFlags & EmfPlusPenDataJoin) // additional line join information - { - lineJoin = static_cast<basegfx::B2DLineJoin>(EMFPPen::lcl_convertLineJoinType(pen->lineJoin)); - } - // we need a line cap attribute css::drawing::LineCap lineCap = css::drawing::LineCap_BUTT; if (pen->penDataFlags & EmfPlusPenDataStartCap) // additional line cap information @@ -527,57 +520,16 @@ namespace emfplushelper const double transformedPenWidth = maMapTransform.get(0, 0) * pen->penWidth; drawinglayer::attribute::LineAttribute lineAttribute(pen->GetColor().getBColor(), transformedPenWidth, - lineJoin, - lineCap); - - drawinglayer::attribute::StrokeAttribute aStrokeAttribute; - if (pen->penDataFlags & EmfPlusPenDataLineStyle && pen->dashStyle != EmfPlusLineStyleCustom) // pen has a predefined line style - { - // short writing - const double pw = maMapTransform.get(1, 1) * pen->penWidth; - // taken from the old cppcanvas implementation and multiplied with pen width - const std::vector<double> dash = { 3*pw, 3*pw }; - const std::vector<double> dot = { pw, 3*pw }; - const std::vector<double> dashdot = { 3*pw, 3*pw, pw, 3*pw }; - const std::vector<double> dashdotdot = { 3*pw, 3*pw, pw, 3*pw, pw, 3*pw }; - - switch (pen->dashStyle) - { - case EmfPlusLineStyleSolid: // do nothing special, use default stroke attribute - break; - case EmfPlusLineStyleDash: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::vector(dash)); - break; - case EmfPlusLineStyleDot: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::vector(dot)); - break; - case EmfPlusLineStyleDashDot: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::vector(dashdot)); - break; - case EmfPlusLineStyleDashDotDot: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::vector(dashdotdot)); - break; - } - } - else if (pen->penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line - { - // StrokeAttribute needs a double vector while the pen provides a float vector - std::vector<double> aPattern(pen->dashPattern.size()); - for (size_t i=0; i<aPattern.size(); i++) - { - // convert from float to double and multiply with the adjusted pen width - aPattern[i] = maMapTransform.get(1, 1) * pen->penWidth * pen->dashPattern[i]; - } - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::move(aPattern)); - } - + pen->GetLineJoinType(), + lineCap, + basegfx::deg2rad(15.0)); // TODO Add MiterLimit support if (!pen->GetColor().IsTransparent()) { mrTargetHolders.Current().append( new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( polygon, lineAttribute, - aStrokeAttribute)); + pen->GetStrokeAttribute(maMapTransform.get(1, 1)))); } else { @@ -585,7 +537,7 @@ namespace emfplushelper new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( polygon, lineAttribute, - aStrokeAttribute)); + pen->GetStrokeAttribute(maMapTransform.get(1, 1)))); mrTargetHolders.Current().append( new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( @@ -634,7 +586,7 @@ namespace emfplushelper new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( startCapPolygon, lineAttribute, - aStrokeAttribute)); + pen->GetStrokeAttribute(maMapTransform.get(1, 1)))); } } @@ -679,7 +631,7 @@ namespace emfplushelper new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( endCapPolygon, lineAttribute, - aStrokeAttribute)); + pen->GetStrokeAttribute(maMapTransform.get(1, 1)))); } } diff --git a/drawinglayer/source/tools/emfppen.cxx b/drawinglayer/source/tools/emfppen.cxx index d41dca676d4e..f348391b0519 100644 --- a/drawinglayer/source/tools/emfppen.cxx +++ b/drawinglayer/source/tools/emfppen.cxx @@ -18,7 +18,6 @@ */ #include <com/sun/star/rendering/PathCapType.hpp> -#include <com/sun/star/rendering/PathJoinType.hpp> #include <o3tl/safeint.hxx> #include <sal/log.hxx> #include <rtl/ustrbuf.hxx> @@ -179,27 +178,70 @@ namespace emfplushelper { switch (nEmfStroke) { - case EmfPlusLineCapTypeSquare: return rendering::PathCapType::SQUARE; - case EmfPlusLineCapTypeRound: return rendering::PathCapType::ROUND; + case EmfPlusLineCapTypeSquare: + return rendering::PathCapType::SQUARE; + // we have no mapping for EmfPlusLineCapTypeTriangle, + // but it is similar to Round + case EmfPlusLineCapTypeTriangle: // fall-through + case EmfPlusLineCapTypeRound: + return rendering::PathCapType::ROUND; } - // we have no mapping for EmfPlusLineCapTypeTriangle = 0x00000003, - // so return BUTT always return rendering::PathCapType::BUTT; } - sal_Int8 EMFPPen::lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin) + basegfx::B2DLineJoin EMFPPen::GetLineJoinType() const { - switch (nEmfLineJoin) + if (penDataFlags & EmfPlusPenDataJoin) // additional line join information { - case EmfPlusLineJoinTypeMiter: // fall-through - case EmfPlusLineJoinTypeMiterClipped: return rendering::PathJoinType::MITER; - case EmfPlusLineJoinTypeBevel: return rendering::PathJoinType::BEVEL; - case EmfPlusLineJoinTypeRound: return rendering::PathJoinType::ROUND; + switch (lineJoin) + { + case EmfPlusLineJoinTypeMiter: // fall-through + case EmfPlusLineJoinTypeMiterClipped: + return basegfx::B2DLineJoin::Miter; + case EmfPlusLineJoinTypeBevel: + return basegfx::B2DLineJoin::Bevel; + case EmfPlusLineJoinTypeRound: + return basegfx::B2DLineJoin::Round; + } } + // If nothing set, then miter applied with no limit + return basegfx::B2DLineJoin::Miter; + } - assert(false); // Line Join type isn't in specification. - return 0; + drawinglayer::attribute::StrokeAttribute + EMFPPen::GetStrokeAttribute(const double aTransformation) const + { + if (penDataFlags & EmfPlusPenDataLineStyle // pen has a predefined line style + && dashStyle != EmfPlusLineStyleCustom) + { + const double pw = aTransformation * penWidth; + switch (dashStyle) + { + case EmfPlusLineStyleDash: + return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw }); + case EmfPlusLineStyleDot: + return drawinglayer::attribute::StrokeAttribute({ pw, pw }); + case EmfPlusLineStyleDashDot: + return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw }); + case EmfPlusLineStyleDashDotDot: + return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw, pw, pw }); + } + } + else if (penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line + { + const double pw = aTransformation * penWidth; + // StrokeAttribute needs a double vector while the pen provides a float vector + std::vector<double> aPattern(dashPattern.size()); + for (size_t i = 0; i < aPattern.size(); i++) + { + // convert from float to double and multiply with the adjusted pen width + aPattern[i] = pw * dashPattern[i]; + } + return drawinglayer::attribute::StrokeAttribute(std::move(aPattern)); + } + // EmfPlusLineStyleSolid: - do nothing special, use default stroke attribute + return drawinglayer::attribute::StrokeAttribute(); } void EMFPPen::Read(SvStream& s, EmfPlusHelperData const & rR) @@ -249,7 +291,7 @@ namespace emfplushelper if (penDataFlags & PenDataJoin) { s.ReadInt32(lineJoin); - SAL_WARN("drawinglayer.emf", "EMF+\t\tTODO PenDataJoin: " << LineJoinTypeToString(lineJoin) << " (0x" << std::hex << lineJoin << ")"); + SAL_WARN("drawinglayer.emf", "EMF+\t\t LineJoin: " << LineJoinTypeToString(lineJoin) << " (0x" << std::hex << lineJoin << ")"); } else { diff --git a/drawinglayer/source/tools/emfppen.hxx b/drawinglayer/source/tools/emfppen.hxx index 05b2fc376d7d..29ece63ecf5d 100644 --- a/drawinglayer/source/tools/emfppen.hxx +++ b/drawinglayer/source/tools/emfppen.hxx @@ -19,6 +19,7 @@ #pragma once +#include <drawinglayer/attribute/strokeattribute.hxx> #include "emfpbrush.hxx" #include <vector> @@ -26,6 +27,7 @@ namespace emfplushelper { const sal_uInt32 EmfPlusLineCapTypeSquare = 0x00000001; const sal_uInt32 EmfPlusLineCapTypeRound = 0x00000002; + const sal_uInt32 EmfPlusLineCapTypeTriangle = 0x00000003; const sal_uInt32 EmfPlusLineJoinTypeMiter = 0x00000000; const sal_uInt32 EmfPlusLineJoinTypeBevel = 0x00000001; @@ -122,7 +124,8 @@ namespace emfplushelper void Read(SvStream& s, EmfPlusHelperData const & rR); static sal_Int8 lcl_convertStrokeCap(sal_uInt32 nEmfStroke); - static sal_Int8 lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin); + drawinglayer::attribute::StrokeAttribute GetStrokeAttribute(const double aTransformation) const; + basegfx::B2DLineJoin GetLineJoinType() const; }; } diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx b/drawinglayer/source/tools/primitive2dxmldump.cxx index 2e2bb887a3d4..a1fddf900543 100644 --- a/drawinglayer/source/tools/primitive2dxmldump.cxx +++ b/drawinglayer/source/tools/primitive2dxmldump.cxx @@ -13,6 +13,7 @@ #include <tools/stream.hxx> #include <tools/XmlWriter.hxx> +#include <math.h> #include <memory> #include <sal/log.hxx> @@ -132,6 +133,24 @@ void writePolyPolygon(::tools::XmlWriter& rWriter, const basegfx::B2DPolyPolygon rWriter.endElement(); } +void writeStrokeAttribute(::tools::XmlWriter& rWriter, + const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute) +{ + if (!rStrokeAttribute.getDotDashArray().empty()) + { + rWriter.startElement("stroke"); + + OUString sDotDash; + for (double fDotDash : rStrokeAttribute.getDotDashArray()) + { + sDotDash += OUString::number(round(100.0 * fDotDash)) + " "; + } + rWriter.attribute("dotDashArray", sDotDash); + rWriter.attribute("fullDotDashLength", rStrokeAttribute.getFullDotDashLen()); + rWriter.endElement(); + } +} + void writeLineAttribute(::tools::XmlWriter& rWriter, const drawinglayer::attribute::LineAttribute& rLineAttribute) { @@ -718,14 +737,7 @@ void Primitive2dXmlDump::decomposeAndWrite( rWriter.endElement(); writeLineAttribute(rWriter, rPolygonStrokePrimitive2D.getLineAttribute()); - - rWriter.startElement("stroke"); - const drawinglayer::attribute::StrokeAttribute& aStrokeAttribute - = rPolygonStrokePrimitive2D.getStrokeAttribute(); - rWriter.attribute("fulldotdashlen", aStrokeAttribute.getFullDotDashLen()); - //rWriter.attribute("dotdasharray", aStrokeAttribute.getDotDashArray()); - rWriter.endElement(); - + writeStrokeAttribute(rWriter, rPolygonStrokePrimitive2D.getStrokeAttribute()); rWriter.endElement(); } break; @@ -736,9 +748,7 @@ void Primitive2dXmlDump::decomposeAndWrite( rWriter.startElement("polypolygonstroke"); writeLineAttribute(rWriter, rPolyPolygonStrokePrimitive2D.getLineAttribute()); - - //getStrokeAttribute() - + writeStrokeAttribute(rWriter, rPolyPolygonStrokePrimitive2D.getStrokeAttribute()); writePolyPolygon(rWriter, rPolyPolygonStrokePrimitive2D.getB2DPolyPolygon()); rWriter.endElement(); diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx index 799f8eef9c46..63661c9c73c2 100644 --- a/emfio/qa/cppunit/emf/EmfImportTest.cxx +++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx @@ -52,6 +52,7 @@ class Test : public test::BootstrapFixture, public XmlTestTools, public unotest: void TestDrawStringTransparent(); void TestDrawStringWithBrush(); void TestDrawLine(); + void TestDrawLineWithDash(); void TestLinearGradient(); void TestTextMapMode(); void TestEnglishMapMode(); @@ -95,6 +96,7 @@ public: CPPUNIT_TEST(TestDrawStringTransparent); CPPUNIT_TEST(TestDrawStringWithBrush); CPPUNIT_TEST(TestDrawLine); + CPPUNIT_TEST(TestDrawLineWithDash); CPPUNIT_TEST(TestLinearGradient); CPPUNIT_TEST(TestTextMapMode); CPPUNIT_TEST(TestEnglishMapMode); @@ -390,6 +392,38 @@ void Test::TestDrawLine() assertXPath(pDocument, aXPathPrefix + "polypolygonstroke/line", "width", "33"); } +void Test::TestDrawLineWithDash() +{ + // EMF+ with records: DrawLine + // The lines with different dash styles + Primitive2DSequence aSequence + = parseEmf(u"/emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithDash.emf"); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength())); + drawinglayer::Primitive2dXmlDump dumper; + xmlDocUniquePtr pDocument + = dumper.dumpAndParse(comphelper::sequenceToContainer<Primitive2DContainer>(aSequence)); + CPPUNIT_ASSERT(pDocument); + + // check correct import of the DrawLine: color and width of the line + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke", 10); + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke[1]/line", "color", "#000000"); + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke[1]/line", "width", "132"); + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke[1]/stroke", 0); + + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke[2]/line", "width", "132"); + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke[2]/stroke", "dotDashArray", + "13225 13225 "); + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke[3]/stroke", "dotDashArray", + "39674 13225 "); + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke[4]/stroke", "dotDashArray", + "39674 13225 13225 13225 "); + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke[5]/stroke", "dotDashArray", + "39674 13225 13225 13225 13225 13225 "); + //TODO polypolygonstroke[6-9]/stroke add support for PenDataDashedLineOffset + assertXPath(pDocument, aXPathPrefix + "polypolygonstroke[10]/stroke", "dotDashArray", + "66124 26450 198372 52899 "); +} + void Test::TestLinearGradient() { // EMF+ file with LinearGradient brush diff --git a/emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithDash.emf b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithDash.emf new file mode 100644 index 000000000000..dc5af59e3f66 Binary files /dev/null and b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithDash.emf differ diff --git a/svx/qa/unit/svdraw.cxx b/svx/qa/unit/svdraw.cxx index cab6b56b0ae7..ae673af298b3 100644 --- a/svx/qa/unit/svdraw.cxx +++ b/svx/qa/unit/svdraw.cxx @@ -338,7 +338,8 @@ CPPUNIT_TEST_FIXTURE(SvdrawTest, testRectangleObject) assertXPathContent(pXmlDoc, aBasePath + "/polygon", "49.5,99 0,99 0,0 99,0 99,99"); - assertXPath(pXmlDoc, aBasePath + "/stroke", "fulldotdashlen", "0"); + // If solid line, then there is no line stroke information + assertXPath(pXmlDoc, aBasePath + "/stroke", 0); pPage->RemoveObject(0);