tdf#142770 tdf#143031 EMF+ Implement CustomLineCap
Change-Id: I9fae1d259ecdca37a1babac8a8a0e503b2dc0118
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135960
Tested-by: Jenkins
Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
diff --git a/drawinglayer/source/tools/emfpcustomlinecap.cxx b/drawinglayer/source/tools/emfpcustomlinecap.cxx
index 49cc912..e457a36 100644
--- a/drawinglayer/source/tools/emfpcustomlinecap.cxx
+++ b/drawinglayer/source/tools/emfpcustomlinecap.cxx
@@ -21,6 +21,7 @@
#include "emfpcustomlinecap.hxx"
#include "emfppath.hxx"
#include "emfppen.hxx"
#include <basegfx/matrix/b2dhommatrixtools.hxx>
using namespace ::com::sun::star;
using namespace ::basegfx;
@@ -39,6 +40,7 @@ namespace emfplushelper
, strokeEndCap(0)
, strokeJoin(0)
, miterLimit(0.0)
, widthScale(0.0)
, mbIsFilled(false)
{
}
@@ -57,6 +59,8 @@ namespace emfplushelper
EMFPPath path(pathPoints);
path.Read(s, pathFlags);
polygon = path.GetPolygon(rR, false);
// rotate polygon by 180 degrees
polygon.transform(basegfx::utils::createRotateB2DHomMatrix(M_PI));
mbIsFilled = bFill;
}
@@ -71,7 +75,6 @@ namespace emfplushelper
{
sal_uInt32 customLineCapDataFlags, baseCap;
float baseInset;
float widthScale;
float fillHotSpotX, fillHotSpotY, strokeHotSpotX, strokeHotSpotY;
s.ReadUInt32(customLineCapDataFlags).ReadUInt32(baseCap).ReadFloat(baseInset)
@@ -82,11 +85,6 @@ namespace emfplushelper
SAL_INFO("drawinglayer.emf", "EMF+\t\tcustomLineCapDataFlags: 0x" << std::hex << customLineCapDataFlags);
SAL_INFO("drawinglayer.emf", "EMF+\t\tbaseCap: 0x" << std::hex << baseCap);
SAL_INFO("drawinglayer.emf", "EMF+\t\tbaseInset: " << baseInset);
SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeStartCap: 0x" << std::hex << strokeStartCap);
SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeEndCap: 0x" << std::hex << strokeEndCap);
SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeJoin: 0x" << std::hex << strokeJoin);
SAL_INFO("drawinglayer.emf", "EMF+\t\tmiterLimit: " << miterLimit);
SAL_INFO("drawinglayer.emf", "EMF+\t\twidthScale: " << widthScale);
if (customLineCapDataFlags & EmfPlusCustomLineCapDataFillPath)
{
@@ -103,16 +101,20 @@ namespace emfplushelper
// TODO only reads the data, does not use them [I've had
// no test document to be able to implement it]
sal_Int32 width, height, middleInset, fillState, lineStartCap;
sal_Int32 lineEndCap, lineJoin, widthScale;
float fillHotSpotX, fillHotSpotY, lineHotSpotX, lineHotSpotY;
sal_Int32 fillState;
float width, height, middleInset, unusedHotSpot;
s.ReadInt32(width).ReadInt32(height).ReadInt32(middleInset).ReadInt32(fillState).ReadInt32(lineStartCap)
.ReadInt32(lineEndCap).ReadInt32(lineJoin).ReadFloat(miterLimit).ReadInt32(widthScale)
.ReadFloat(fillHotSpotX).ReadFloat(fillHotSpotY).ReadFloat(lineHotSpotX).ReadFloat(lineHotSpotY);
s.ReadFloat(width).ReadFloat(height).ReadFloat(middleInset).ReadInt32(fillState).ReadUInt32(strokeStartCap)
.ReadUInt32(strokeEndCap).ReadUInt32(strokeJoin).ReadFloat(miterLimit).ReadFloat(widthScale)
.ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot);
SAL_INFO("drawinglayer.emf", "EMF+\t\tTODO - actually read EmfPlusCustomLineCapArrowData object (section 2.2.2.12)");
}
SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeStartCap: 0x" << std::hex << strokeStartCap);
SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeEndCap: 0x" << std::hex << strokeEndCap);
SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeJoin: 0x" << std::hex << strokeJoin);
SAL_INFO("drawinglayer.emf", "EMF+\t\tmiterLimit: " << miterLimit);
SAL_INFO("drawinglayer.emf", "EMF+\t\twidthScale: " << widthScale);
}
}
diff --git a/drawinglayer/source/tools/emfpcustomlinecap.hxx b/drawinglayer/source/tools/emfpcustomlinecap.hxx
index e6202ae..22ed6be 100644
--- a/drawinglayer/source/tools/emfpcustomlinecap.hxx
+++ b/drawinglayer/source/tools/emfpcustomlinecap.hxx
@@ -27,7 +27,7 @@ namespace emfplushelper
{
sal_uInt32 type;
sal_uInt32 strokeStartCap, strokeEndCap, strokeJoin;
float miterLimit;
float miterLimit, widthScale;
basegfx::B2DPolyPolygon polygon;
bool mbIsFilled;
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx
index 9e0c78c..5422595 100644
--- a/drawinglayer/source/tools/emfphelperdata.cxx
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -603,11 +603,29 @@ namespace emfplushelper
drawinglayer::attribute::LineStartEndAttribute aStart;
if (pen->penDataFlags & EmfPlusPenDataStartCap)
aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth);
{
if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap)
&& (pen->customStartCap->polygon.begin()->count() > 1))
aStart = drawinglayer::attribute::LineStartEndAttribute(
pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
* pen->customStartCap->widthScale * pen->penWidth,
pen->customStartCap->polygon, false);
else
aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth);
}
drawinglayer::attribute::LineStartEndAttribute aEnd;
if (pen->penDataFlags & EmfPlusPenDataEndCap)
aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth);
{
if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap)
&& (pen->customEndCap->polygon.begin()->count() > 1))
aEnd = drawinglayer::attribute::LineStartEndAttribute(
pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
* pen->customEndCap->widthScale * pen->penWidth,
pen->customEndCap->polygon, false);
else
aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth);
}
if (pen->GetColor().IsTransparent())
{
@@ -644,97 +662,6 @@ namespace emfplushelper
pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
}
}
if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap) && (pen->customStartCap->polygon.begin()->count() > 1))
{
SAL_WARN("drawinglayer.emf", "EMF+\tCustom Start Line Cap");
::basegfx::B2DPolyPolygon startCapPolygon(pen->customStartCap->polygon);
// get the gradient of the first line in the polypolygon
double x1 = polygon.begin()->getB2DPoint(0).getX();
double y1 = polygon.begin()->getB2DPoint(0).getY();
double x2 = polygon.begin()->getB2DPoint(1).getX();
double y2 = polygon.begin()->getB2DPoint(1).getY();
if ((x2 - x1) != 0)
{
double gradient = (y2 - y1) / (x2 - x1);
// now we get the angle that we need to rotate the arrow by
double angle = (M_PI / 2) - atan(gradient);
// rotate the arrow
startCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle));
}
startCapPolygon.transform(maMapTransform);
basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(0).getX(),
0.0, pen->penWidth, polygon.begin()->getB2DPoint(0).getY());
startCapPolygon.transform(tran);
if (pen->customStartCap->mbIsFilled)
{
mrTargetHolders.Current().append(
new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
std::move(startCapPolygon),
pen->GetColor().getBColor()));
}
else
{
mrTargetHolders.Current().append(
new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
std::move(startCapPolygon),
lineAttribute,
pen->GetStrokeAttribute(mdExtractedXScale)));
}
}
if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap) && (pen->customEndCap->polygon.begin()->count() > 1))
{
SAL_WARN("drawinglayer.emf", "EMF+\tCustom End Line Cap");
::basegfx::B2DPolyPolygon endCapPolygon(pen->customEndCap->polygon);
// get the gradient of the first line in the polypolygon
double x1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX();
double y1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY();
double x2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getX();
double y2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getY();
if ((x2 - x1) != 0)
{
double gradient = (y2 - y1) / (x2 - x1);
// now we get the angle that we need to rotate the arrow by
double angle = (M_PI / 2) - atan(gradient);
// rotate the arrow
endCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle));
}
endCapPolygon.transform(maMapTransform);
basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX(),
0.0, pen->penWidth, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY());
endCapPolygon.transform(tran);
if (pen->customEndCap->mbIsFilled)
{
mrTargetHolders.Current().append(
new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
std::move(endCapPolygon),
pen->GetColor().getBColor()));
}
else
{
mrTargetHolders.Current().append(
new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
std::move(endCapPolygon),
lineAttribute,
pen->GetStrokeAttribute(mdExtractedXScale)));
}
}
mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor());
mrPropertyHolders.Current().setLineColorActive(true);
mrPropertyHolders.Current().setFillColorActive(false);
diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx
index 40db85d..b423eae 100644
--- a/emfio/qa/cppunit/emf/EmfImportTest.cxx
+++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx
@@ -68,6 +68,7 @@ class Test : public test::BootstrapFixture, public XmlTestTools, public unotest:
void TestEmfPlusBrushPathGradientWithBlendColors();
void TestEmfPlusGetDC();
void TestEmfPlusSave();
void TestEmfPlusDrawPathWithCustomCap();
void TestEmfPlusDrawPathWithMiterLimit();
void TestEmfPlusFillClosedCurve();
void TestExtTextOutOpaqueAndClipTransform();
@@ -119,6 +120,7 @@ public:
CPPUNIT_TEST(TestEmfPlusBrushPathGradientWithBlendColors);
CPPUNIT_TEST(TestEmfPlusGetDC);
CPPUNIT_TEST(TestEmfPlusSave);
CPPUNIT_TEST(TestEmfPlusDrawPathWithCustomCap);
CPPUNIT_TEST(TestEmfPlusDrawPathWithMiterLimit);
CPPUNIT_TEST(TestEmfPlusFillClosedCurve);
CPPUNIT_TEST(TestExtTextOutOpaqueAndClipTransform);
@@ -1046,6 +1048,30 @@ void Test::TestEmfPlusSave()
"12832.6557236512,4907.54325697157");
}
void Test::TestEmfPlusDrawPathWithCustomCap()
{
// tdf#142261 EMF+ records: DrawPath, SetWorldTransform, Object (Brush, Pen, Path)
// Check if CustomEndCap is displayed correctly
Primitive2DSequence aSequence
= parseEmf(u"emfio/qa/cppunit/emf/data/TestEmfPlusDrawPathWithCustomCap.emf");
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
drawinglayer::Primitive2dXmlDump dumper;
xmlDocUniquePtr pDocument = dumper.dumpAndParse(Primitive2DContainer(aSequence));
CPPUNIT_ASSERT(pDocument);
assertXPathContent(pDocument, aXPathPrefix + "polygonstrokearrow/polygon",
"1423.297394625,1268.98481214025 830.006276132353,558.656004112967");
assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow/line", "color", "#cc0000");
assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow/line", "width", "96");
assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow/line", "linecap", "BUTT");
assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow/stroke", 0);
assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow/linestartattribute", 0);
assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow/lineendattribute", "centered", "0");
assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow/lineendattribute/polypolygon", "path",
"m-1.5 3 1.5-3 1.5 3z");
}
void Test::TestEmfPlusDrawPathWithMiterLimit()
{
// tdf#142261 EMF+ records: DrawPath, TranslateWorldTransform, Object (Brush, Pen, Path)
diff --git a/emfio/qa/cppunit/emf/data/TestEmfPlusDrawPathWithCustomCap.emf b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawPathWithCustomCap.emf
new file mode 100644
index 0000000..e9498643
--- /dev/null
+++ b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawPathWithCustomCap.emf
Binary files differ