drawinglayer/source/tools/emfphelperdata.cxx              |  144 ++++++++++----
 drawinglayer/source/tools/emfphelperdata.hxx              |    4 
 drawinglayer/source/tools/emfppen.cxx                     |   17 -
 drawinglayer/source/tools/emfppen.hxx                     |    1 
 drawinglayer/source/tools/primitive2dxmldump.cxx          |   44 ++++
 emfio/qa/cppunit/emf/EmfImportTest.cxx                    |  116 +++++++++--
 emfio/qa/cppunit/emf/data/TestDrawLine.emf                |binary
 emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithCaps.emf |binary
 8 files changed, 260 insertions(+), 66 deletions(-)

New commits:
commit 1440ab87386bb5d1ad3634082577bf27f279e066
Author:     Bartosz Kosiorek <gan...@poczta.onet.pl>
AuthorDate: Sun Apr 24 02:29:59 2022 +0200
Commit:     Bartosz Kosiorek <gan...@poczta.onet.pl>
CommitDate: Mon May 9 20:50:37 2022 +0200

    tdf#143875 tdf#55058 EMF+ Add support for individual line endings
    
    EMF+ is allowing different caps and arrows on both ends
    It is not possible to implement that with css::drawing::LineCap,
    as it is set line endings on both line start and line end.
    Additionally when the Dash Pattern is used, the css::drawing::LineCap
    is also applied there.
    
    To resolve that limitation, the Cap needs to be implemented
    independetly by using PolygonStrokeArrowPrimitive2D, and
    the css::drawing::LineCap inside drawinglayer::attribute::LineAttribute
    always set to css::drawing::LineCap_BUTT
    
    Change-Id: I4be76e2dbefcb34154a1404c3b57dc4b7f7ada85
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133299
    Tested-by: Jenkins
    Reviewed-by: Bartosz Kosiorek <gan...@poczta.onet.pl>

diff --git a/drawinglayer/source/tools/emfphelperdata.cxx 
b/drawinglayer/source/tools/emfphelperdata.cxx
index c405a4c4876b..c5b282998d71 100644
--- a/drawinglayer/source/tools/emfphelperdata.cxx
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -29,6 +29,7 @@
 #include "emfpstringformat.hxx"
 #include <basegfx/curve/b2dcubicbezier.hxx>
 #include <wmfemfhelper.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
 #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
@@ -519,7 +520,72 @@ namespace emfplushelper
         }
     }
 
-    void EmfPlusHelperData::EMFPPlusDrawPolygon(const 
::basegfx::B2DPolyPolygon& polygon, sal_uInt32 penIndex)
+    drawinglayer::attribute::LineStartEndAttribute
+    EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float 
aPenWidth) const
+    {
+        const double pw = mdExtractedYScale * aPenWidth;
+        if (aCap == LineCapTypeSquare)
+        {
+            basegfx::B2DPolygon aCapPolygon(
+                { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+            aCapPolygon.setClosed(true);
+            return drawinglayer::attribute::LineStartEndAttribute(
+                pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+        }
+        else if (aCap == LineCapTypeRound)
+        {
+            basegfx::B2DPolygon aCapPolygon(
+                { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827},
+                  {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, 
-0.9236},
+                  {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} });
+            aCapPolygon.setClosed(true);
+            return drawinglayer::attribute::LineStartEndAttribute(
+                pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+        }
+        else if (aCap == LineCapTypeTriangle)
+        {
+            basegfx::B2DPolygon aCapPolygon(
+                { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 
0.0} });
+            aCapPolygon.setClosed(true);
+            return drawinglayer::attribute::LineStartEndAttribute(
+                pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+        }
+        else if (aCap == LineCapTypeSquareAnchor)
+        {
+            basegfx::B2DPolygon aCapPolygon(
+                { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+            aCapPolygon.setClosed(true);
+            return drawinglayer::attribute::LineStartEndAttribute(
+                1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+        }
+        else if (aCap == LineCapTypeRoundAnchor)
+        {
+            const basegfx::B2DPolygon aCapPolygon
+                = 
::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 
1.0);
+            return drawinglayer::attribute::LineStartEndAttribute(
+                2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+        }
+        else if (aCap == LineCapTypeDiamondAnchor)
+        {
+            basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 
0.5},
+                                              {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 
0.5},
+                                              {-1.0, 0.0} });
+            aCapPolygon.setClosed(true);
+            return drawinglayer::attribute::LineStartEndAttribute(
+                2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+        }
+        else if (aCap == LineCapTypeArrowAnchor)
+        {
+            basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 
1.0} });
+            aCapPolygon.setClosed(true);
+            return drawinglayer::attribute::LineStartEndAttribute(
+                2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+        }
+        return drawinglayer::attribute::LineStartEndAttribute();
+    }
+
+    void EmfPlusHelperData::EMFPPlusDrawPolygon(const 
::basegfx::B2DPolyPolygon& polygon,
+                                                sal_uInt32 penIndex)
     {
         const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 
0xff].get());
         SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen");
@@ -527,40 +593,56 @@ namespace emfplushelper
         if (!(pen && polygon.count()))
             return;
 
-        // we need a line cap attribute
-        css::drawing::LineCap lineCap = css::drawing::LineCap_BUTT;
-        if (pen->penDataFlags & EmfPlusPenDataStartCap) // additional line cap 
information
-        {
-            lineCap = 
static_cast<css::drawing::LineCap>(EMFPPen::lcl_convertStrokeCap(pen->startCap));
-            SAL_WARN_IF(pen->startCap != pen->endCap, "drawinglayer.emf", 
"emf+ pen uses different start and end cap");
-        }
-
         const double transformedPenWidth = mdExtractedYScale * pen->penWidth;
-        drawinglayer::attribute::LineAttribute 
lineAttribute(pen->GetColor().getBColor(),
-                                                             
transformedPenWidth,
-                                                             
pen->GetLineJoinType(),
-                                                             lineCap,
-                                                             
basegfx::deg2rad(15.0)); // TODO Add MiterLimit support
-        if (!pen->GetColor().IsTransparent())
+        drawinglayer::attribute::LineAttribute lineAttribute(
+            pen->GetColor().getBColor(), transformedPenWidth, 
pen->GetLineJoinType(),
+            css::drawing::LineCap_BUTT,
+            basegfx::deg2rad(15.0)); // TODO Add MiterLimit support
+
+        drawinglayer::attribute::LineStartEndAttribute aStart;
+        if (pen->penDataFlags & EmfPlusPenDataStartCap)
+            aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, 
pen->penWidth);
+
+        drawinglayer::attribute::LineStartEndAttribute aEnd;
+        if (pen->penDataFlags & EmfPlusPenDataEndCap)
+            aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, 
pen->penWidth);
+
+        if (pen->GetColor().IsTransparent())
         {
+            drawinglayer::primitive2d::Primitive2DContainer aContainer;
+            if ((pen->penDataFlags & EmfPlusPenDataStartCap)
+                || (pen->penDataFlags & EmfPlusPenDataEndCap))
+            {
+                aContainer.resize(polygon.count());
+                for (sal_uInt32 i = 0; i < polygon.count(); i++)
+                    aContainer[i] = 
drawinglayer::primitive2d::Primitive2DReference(
+                        new 
drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
+                            polygon.getB2DPolygon(i), lineAttribute,
+                            pen->GetStrokeAttribute(mdExtractedXScale), 
aStart, aEnd));
+            }
+            else
+                
aContainer.append(drawinglayer::primitive2d::Primitive2DReference(
+                    new 
drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+                        polygon, lineAttribute, 
pen->GetStrokeAttribute(mdExtractedXScale))));
             mrTargetHolders.Current().append(
-                new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
-                    polygon,
-                    lineAttribute,
-                    pen->GetStrokeAttribute(mdExtractedXScale)));
+                new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+                    std::move(aContainer), (255 - pen->GetColor().GetAlpha()) 
/ 255.0));
         }
         else
         {
-            const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
-                        new 
drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
-                            polygon,
-                            lineAttribute,
-                            pen->GetStrokeAttribute(mdExtractedXScale)));
-
-            mrTargetHolders.Current().append(
-                        new 
drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
-                            drawinglayer::primitive2d::Primitive2DContainer { 
aPrimitive },
-                            (255 - pen->GetColor().GetAlpha()) / 255.0));
+            if ((pen->penDataFlags & EmfPlusPenDataStartCap)
+                || (pen->penDataFlags & EmfPlusPenDataEndCap))
+                for (sal_uInt32 i = 0; i < polygon.count(); i++)
+                {
+                    mrTargetHolders.Current().append(
+                        new 
drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
+                            polygon.getB2DPolygon(i), lineAttribute,
+                            pen->GetStrokeAttribute(mdExtractedXScale), 
aStart, aEnd));
+                }
+            else
+                mrTargetHolders.Current().append(
+                    new 
drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+                        polygon, lineAttribute, 
pen->GetStrokeAttribute(mdExtractedXScale)));
         }
 
         if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap) && 
(pen->customStartCap->polygon.begin()->count() > 1))
@@ -604,7 +686,7 @@ namespace emfplushelper
                             new 
drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
                                 startCapPolygon,
                                 lineAttribute,
-                                pen->GetStrokeAttribute(maMapTransform.get(1, 
1))));
+                                pen->GetStrokeAttribute(mdExtractedXScale)));
             }
         }
 
@@ -649,7 +731,7 @@ namespace emfplushelper
                             new 
drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
                                 endCapPolygon,
                                 lineAttribute,
-                                pen->GetStrokeAttribute(maMapTransform.get(1, 
1))));
+                                pen->GetStrokeAttribute(mdExtractedXScale)));
             }
         }
 
diff --git a/drawinglayer/source/tools/emfphelperdata.hxx 
b/drawinglayer/source/tools/emfphelperdata.hxx
index 600f666145af..796cbff03387 100644
--- a/drawinglayer/source/tools/emfphelperdata.hxx
+++ b/drawinglayer/source/tools/emfphelperdata.hxx
@@ -21,6 +21,7 @@
 
 #include <wmfemfhelper.hxx>
 #include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/attribute/linestartendattribute.hxx>
 #include <tools/stream.hxx>
 #include <basegfx/point/b2dpoint.hxx>
 #include <map>
@@ -232,6 +233,9 @@ namespace emfplushelper
         void GraphicStatePush(GraphicStateMap& map, sal_Int32 index);
         void GraphicStatePop(GraphicStateMap& map, sal_Int32 index);
 
+        drawinglayer::attribute::LineStartEndAttribute CreateLineEnd(const 
sal_Int32 aCap,
+                                                                     const 
float aPenWidth) const;
+
         // primitive creators
         void EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, 
sal_uInt32 penIndex);
         void EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, 
const bool isColor, const sal_uInt32 brushIndexOrColor);
diff --git a/drawinglayer/source/tools/emfppen.cxx 
b/drawinglayer/source/tools/emfppen.cxx
index b927ac186174..b0408f8d0e80 100644
--- a/drawinglayer/source/tools/emfppen.cxx
+++ b/drawinglayer/source/tools/emfppen.cxx
@@ -173,23 +173,6 @@ namespace emfplushelper
         return "";
     }
 
-    /// Convert stroke caps between EMF+ and rendering API
-    sal_Int8 EMFPPen::lcl_convertStrokeCap(sal_uInt32 nEmfStroke)
-    {
-        switch (nEmfStroke)
-        {
-            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;
-        }
-
-        return rendering::PathCapType::BUTT;
-    }
-
     basegfx::B2DLineJoin EMFPPen::GetLineJoinType() const
     {
         if (penDataFlags & EmfPlusPenDataJoin) // additional line join 
information
diff --git a/drawinglayer/source/tools/emfppen.hxx 
b/drawinglayer/source/tools/emfppen.hxx
index a22ae14fc247..cad849e4f278 100644
--- a/drawinglayer/source/tools/emfppen.hxx
+++ b/drawinglayer/source/tools/emfppen.hxx
@@ -123,7 +123,6 @@ namespace emfplushelper
 
         void Read(SvStream& s, EmfPlusHelperData const & rR);
 
-        static sal_Int8 lcl_convertStrokeCap(sal_uInt32 nEmfStroke);
         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 3074ad30690a..886ffbf7fdd3 100644
--- a/drawinglayer/source/tools/primitive2dxmldump.cxx
+++ b/drawinglayer/source/tools/primitive2dxmldump.cxx
@@ -23,6 +23,7 @@
 #include <drawinglayer/primitive2d/Tools.hxx>
 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
 #include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
 #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
@@ -722,6 +723,49 @@ void Primitive2dXmlDump::decomposeAndWrite(
                 rWriter.endElement();
             }
             break;
+
+            case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D:
+            {
+                const PolygonStrokeArrowPrimitive2D& 
rPolygonStrokeArrowPrimitive2D
+                    = dynamic_cast<const 
PolygonStrokeArrowPrimitive2D&>(*pBasePrimitive);
+                rWriter.startElement("polygonstrokearrow");
+
+                rWriter.startElement("polygon");
+                rWriter.content(basegfx::utils::exportToSvgPoints(
+                    rPolygonStrokeArrowPrimitive2D.getB2DPolygon()));
+                rWriter.endElement();
+
+                if 
(rPolygonStrokeArrowPrimitive2D.getStart().getB2DPolyPolygon().count())
+                {
+                    rWriter.startElement("linestartattribute");
+                    rWriter.attribute("width",
+                                      
rPolygonStrokeArrowPrimitive2D.getStart().getWidth());
+                    rWriter.attribute("centered",
+                                      static_cast<sal_Int32>(
+                                          
rPolygonStrokeArrowPrimitive2D.getStart().isCentered()));
+                    writePolyPolygon(rWriter,
+                                     
rPolygonStrokeArrowPrimitive2D.getStart().getB2DPolyPolygon());
+                    rWriter.endElement();
+                }
+
+                if 
(rPolygonStrokeArrowPrimitive2D.getEnd().getB2DPolyPolygon().count())
+                {
+                    rWriter.startElement("lineendattribute");
+                    rWriter.attribute("width", 
rPolygonStrokeArrowPrimitive2D.getEnd().getWidth());
+                    rWriter.attribute("centered",
+                                      static_cast<sal_Int32>(
+                                          
rPolygonStrokeArrowPrimitive2D.getEnd().isCentered()));
+                    writePolyPolygon(rWriter,
+                                     
rPolygonStrokeArrowPrimitive2D.getEnd().getB2DPolyPolygon());
+                    rWriter.endElement();
+                }
+
+                writeLineAttribute(rWriter, 
rPolygonStrokeArrowPrimitive2D.getLineAttribute());
+                writeStrokeAttribute(rWriter, 
rPolygonStrokeArrowPrimitive2D.getStrokeAttribute());
+                rWriter.endElement();
+            }
+            break;
+
             case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
             {
                 const PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D
diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx 
b/emfio/qa/cppunit/emf/EmfImportTest.cxx
index 5891799dd607..f83688745e02 100644
--- a/emfio/qa/cppunit/emf/EmfImportTest.cxx
+++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx
@@ -51,6 +51,7 @@ class Test : public test::BootstrapFixture, public 
XmlTestTools, public unotest:
     void TestDrawStringTransparent();
     void TestDrawStringWithBrush();
     void TestDrawLine();
+    void TestDrawLineWithCaps();
     void TestDrawLineWithDash();
     void TestLinearGradient();
     void TestTextMapMode();
@@ -98,6 +99,7 @@ public:
     CPPUNIT_TEST(TestDrawStringTransparent);
     CPPUNIT_TEST(TestDrawStringWithBrush);
     CPPUNIT_TEST(TestDrawLine);
+    CPPUNIT_TEST(TestDrawLineWithCaps);
     CPPUNIT_TEST(TestDrawLineWithDash);
     CPPUNIT_TEST(TestLinearGradient);
     CPPUNIT_TEST(TestTextMapMode);
@@ -155,6 +157,7 @@ Primitive2DSequence Test::parseEmf(std::u16string_view 
aSource)
 
     SvFileStream aFileStream(aUrl, StreamMode::READ);
     std::size_t nSize = aFileStream.remainingSize();
+    CPPUNIT_ASSERT_MESSAGE("Unable to open file", nSize);
     std::unique_ptr<sal_Int8[]> pBuffer(new sal_Int8[nSize + 1]);
     aFileStream.ReadBytes(pBuffer.get(), nSize);
     pBuffer[nSize] = 0;
@@ -379,30 +382,91 @@ void Test::TestDrawLine()
 {
     // EMF+ with records: DrawLine
     // The line is colored and has a specified width, therefore a 
polypolygonstroke primitive is the optimal choice
-    Primitive2DSequence aSequence = 
parseEmf(u"/emfio/qa/cppunit/emf/data/TestDrawLine.emf");
+    Primitive2DSequence aSequence = 
parseEmf(u"emfio/qa/cppunit/emf/data/TestDrawLine.emf");
     CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
     drawinglayer::Primitive2dXmlDump dumper;
     xmlDocUniquePtr pDocument = 
dumper.dumpAndParse(Primitive2DContainer(aSequence));
     CPPUNIT_ASSERT(pDocument);
 
     // check correct import of the DrawLine: color and width of the line
-    assertXPath(pDocument, aXPathPrefix + "polypolygonstroke/line", "color", 
"#000000");
-    assertXPath(pDocument, aXPathPrefix + "polypolygonstroke/line", "width", 
"23");
+    assertXPath(pDocument, aXPathPrefix + "mask/unifiedtransparence", 
"transparence", "14");
+    assertXPath(pDocument, aXPathPrefix + 
"mask/unifiedtransparence/polypolygonstroke/line",
+                "color", "#c01002");
+    assertXPath(pDocument, aXPathPrefix + 
"mask/unifiedtransparence/polypolygonstroke/line",
+                "width", "115");
+    assertXPath(pDocument, aXPathPrefix + 
"mask/unifiedtransparence/polypolygonstroke/line",
+                "linecap", "BUTT");
+    assertXPath(pDocument, aXPathPrefix + 
"mask/unifiedtransparence/polypolygonstroke/polypolygon",
+                "path", "m55.5192348773662 403.573503917507 
874.352660545936-345.821325648415");
+}
+
+void Test::TestDrawLineWithCaps()
+{
+    // EMF+ with records: DrawLine
+    // Test lines with different caps styles and arrows
+    Primitive2DSequence aSequence
+        = 
parseEmf(u"emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithCaps.emf");
+    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
+    drawinglayer::Primitive2dXmlDump dumper;
+    xmlDocUniquePtr pDocument = 
dumper.dumpAndParse(Primitive2DContainer(aSequence));
+    CPPUNIT_ASSERT(pDocument);
+    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow", 3);
+
+    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow[1]/line", 
"width", "211");
+    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow[1]/stroke", 0);
+    assertXPath(pDocument, aXPathPrefix + 
"polygonstrokearrow[1]/linestartattribute/polypolygon",
+                "path", "m0-1 1 2h-2z");
+    assertXPath(pDocument, aXPathPrefix + 
"polygonstrokearrow[1]/lineendattribute", 0);
+
+    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow[2]/line", 
"width", "211");
+    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow[2]/stroke", 0);
+    assertXPath(pDocument, aXPathPrefix + 
"polygonstrokearrow[2]/linestartattribute/polypolygon",
+                "path", "m0-1 1 2h-2z");
+    assertXPath(pDocument, aXPathPrefix + 
"polygonstrokearrow[2]/lineendattribute", 0);
+
+    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow[3]/line", 
"width", "423");
+    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow[3]/stroke", 0);
+    assertXPath(pDocument, aXPathPrefix + 
"polygonstrokearrow[3]/linestartattribute", 0);
+    assertXPath(pDocument, aXPathPrefix + 
"polygonstrokearrow[3]/lineendattribute/polypolygon",
+                "path", "m-1-1h2v2h-2z");
+
+    assertXPath(pDocument, aXPathPrefix + "unifiedtransparence", 3);
+    assertXPath(pDocument, aXPathPrefix + "unifiedtransparence[1]", 
"transparence", "39");
+    assertXPath(pDocument,
+                aXPathPrefix
+                    + 
"unifiedtransparence[1]/polygonstrokearrow/linestartattribute/polypolygon",
+                "path",
+                "m-1 
1h2v-1l-0.0764-0.3827-0.2165-0.3244-0.3244-0.2165-0.3827-0.0764-0.3827 "
+                "0.0764-0.3244 0.2165-0.2165 0.3244-0.0764 0.3827z");
+    assertXPath(pDocument,
+                aXPathPrefix
+                    + 
"unifiedtransparence[1]/polygonstrokearrow/lineendattribute/polypolygon",
+                "path", "m-1 1h2v-1l-1-1-1 1z");
+    assertXPath(pDocument,
+                aXPathPrefix + 
"unifiedtransparence[2]/polygonstrokearrow/linestartattribute", 0);
+    assertXPath(pDocument,
+                aXPathPrefix
+                    + 
"unifiedtransparence[2]/polygonstrokearrow/lineendattribute/polypolygon",
+                "path", "m-1-1h2v2h-2z");
+    assertXPath(pDocument,
+                aXPathPrefix
+                    + 
"unifiedtransparence[3]/polygonstrokearrow/lineendattribute/polypolygon",
+                "path", "m0-1 1 1-0.5 0.5v0.5h-1v-0.5l-0.5-0.5z");
 }
 
 void Test::TestDrawLineWithDash()
 {
     // EMF+ with records: DrawLine, ScaleWorldTransform, RotateWorldTransform
-    // Test lines with different dash styles and different World Rotation
+    // Test lines with different dash styles, different line arrows and 
different World Rotation
     Primitive2DSequence aSequence
-        = 
parseEmf(u"/emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithDash.emf");
+        = 
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(Primitive2DContainer(aSequence));
     CPPUNIT_ASSERT(pDocument);
 
     // check correct import of the DrawLine: color and width of the line
-    assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke", 12);
+    assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke", 10);
     assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[1]/line", 
"color", "#000000");
     assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[1]/line", 
"width", "185");
     assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[1]/stroke", 
0);
@@ -419,14 +483,30 @@ void Test::TestDrawLineWithDash()
     assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[5]/line", 
"width", "370");
     assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[5]/stroke", 
"dotDashArray",
                 "556 185 185 185 185 185 ");
+
+    assertXPath(pDocument, aXPathPrefix + "mask/polygonstrokearrow", 2);
     //TODO polypolygonstroke[6-9]/stroke add support for 
PenDataDashedLineOffset
-    assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[10]/line", 
"width", "370");
-    assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[10]/stroke", 
"dotDashArray",
-                "1851 741 5554 1481 ");
-    assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[11]/line", 
"width", "370");
-    assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[11]/stroke", 
"dotDashArray",
+    assertXPath(pDocument, aXPathPrefix + "mask/polygonstrokearrow[1]/line", 
"width", "370");
+    assertXPath(pDocument, aXPathPrefix + "mask/polygonstrokearrow[1]/stroke", 
"dotDashArray",
                 "1851 741 5554 1481 ");
-    assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke[12]/line", 
"width", "370");
+    // Arrows on both ends
+    assertXPath(pDocument,
+                aXPathPrefix + 
"mask/polygonstrokearrow[1]/linestartattribute/polypolygon", "path",
+                "m0-1 1 2h-2z");
+    assertXPath(pDocument, aXPathPrefix + 
"mask/polygonstrokearrow[1]/lineendattribute/polypolygon",
+                "path", "m0-1 1 2h-2z");
+
+    assertXPath(pDocument, aXPathPrefix + "mask/polygonstrokearrow[2]/line", 
"width", "370");
+    assertXPath(pDocument, aXPathPrefix + "mask/polygonstrokearrow[2]/stroke", 
"dotDashArray",
+                "1852 741 5555 1481 ");
+    assertXPath(pDocument,
+                aXPathPrefix + 
"mask/polygonstrokearrow[2]/linestartattribute/polypolygon", "path",
+                "m-1 
1h2v-1l-0.0764-0.3827-0.2165-0.3244-0.3244-0.2165-0.3827-0.0764-0.3827 "
+                "0.0764-0.3244 0.2165-0.2165 0.3244-0.0764 0.3827z");
+    assertXPath(pDocument, aXPathPrefix + 
"mask/polygonstrokearrow[2]/lineendattribute/polypolygon",
+                "path",
+                "m-1 
1h2v-1l-0.0764-0.3827-0.2165-0.3244-0.3244-0.2165-0.3827-0.0764-0.3827 "
+                "0.0764-0.3244 0.2165-0.2165 0.3244-0.0764 0.3827z");
 }
 
 void Test::TestLinearGradient()
@@ -878,7 +958,7 @@ void Test::TestPolylinetoCloseStroke()
 void Test::TestEmfPlusGetDC()
 {
     // tdf#147818 EMF+ records: GetDC, DrawPath, FillRects
-    Primitive2DSequence aSequence = 
parseEmf(u"/emfio/qa/cppunit/emf/data/TestEmfPlusGetDC.emf");
+    Primitive2DSequence aSequence = 
parseEmf(u"emfio/qa/cppunit/emf/data/TestEmfPlusGetDC.emf");
     CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
     drawinglayer::Primitive2dXmlDump dumper;
     xmlDocUniquePtr pDocument = 
dumper.dumpAndParse(Primitive2DContainer(aSequence));
@@ -910,7 +990,8 @@ void Test::TestEmfPlusGetDC()
                 "m19428.4895833333 
6632.22222222222h317.34375v-2398.88888888889h-317.34375z");
     assertXPath(pDocument, aXPathPrefix + "polypolygoncolor[6]", "color", 
"#fcf2e3");
 
-    assertXPath(pDocument, aXPathPrefix + "polypolygonstroke", 15);
+    assertXPath(pDocument, aXPathPrefix + "polypolygonstroke", 4);
+    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow", 13);
 }
 
 void Test::TestEmfPlusSave()
@@ -932,9 +1013,10 @@ void Test::TestEmfPlusSave()
                 "m10853.4145539602 
7321.41354709201h41952690v29630720h-41952690z");
     assertXPath(pDocument, aXPathPrefix + "mask/polypolygoncolor", "color", 
"#00ffad");
 
-    assertXPath(pDocument, aXPathPrefix + "mask/polypolygonstroke/line", 
"color", "#000000");
-    assertXPath(pDocument, aXPathPrefix + 
"mask/polypolygonstroke/polypolygon", "path",
-                "m10853.4145539602 
7321.41354709201v-2413.87029012044h1979.24116969109");
+    assertXPath(pDocument, aXPathPrefix + "mask/polygonstrokearrow/line", 
"color", "#000000");
+    assertXPathContent(pDocument, aXPathPrefix + 
"mask/polygonstrokearrow/polygon",
+                       "10853.4145539602,7321.41354709201 
10853.4145539602,4907.54325697157 "
+                       "12832.6557236512,4907.54325697157");
 }
 
 void Test::TestExtTextOutOpaqueAndClipTransform()
diff --git a/emfio/qa/cppunit/emf/data/TestDrawLine.emf 
b/emfio/qa/cppunit/emf/data/TestDrawLine.emf
index 89946c523f98..8d8c620b2c81 100644
Binary files a/emfio/qa/cppunit/emf/data/TestDrawLine.emf and 
b/emfio/qa/cppunit/emf/data/TestDrawLine.emf differ
diff --git a/emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithCaps.emf 
b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithCaps.emf
new file mode 100644
index 000000000000..1b4cedb63514
Binary files /dev/null and 
b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawLineWithCaps.emf differ

Reply via email to