tdf#35986 tdf#92315 tdf#116335 tdf#116622 Add support for MapMode TEXT

To properly import some EMF files, the proper implementation of MapMode Text
needs to be done according to MS documentation.
I have also added regression tests.

Change-Id: Id788294a498b93bebb62118d13ea545f80a60f01
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/105771
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
This commit is contained in:
Bartosz Kosiorek
2020-11-12 21:48:44 +01:00
committed by Tomaž Vajngerl
parent fc351e37d6
commit b0b78838e7
4 changed files with 137 additions and 24 deletions

View File

@@ -16,6 +16,7 @@
#include <memory>
#include <sal/log.hxx>
#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/Tools.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
@@ -206,7 +207,50 @@ void Primitive2dXmlDump::decomposeAndWrite(
rWriter.endElement();
}
break;
case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
{
const PointArrayPrimitive2D& rPointArrayPrimitive2D = dynamic_cast<const PointArrayPrimitive2D&>(*pBasePrimitive);
rWriter.startElement("pointarray");
rWriter.attribute("color", convertColorToString(rPointArrayPrimitive2D.getRGBColor()));
const std::vector< basegfx::B2DPoint > aPositions = rPointArrayPrimitive2D.getPositions();
for (std::vector<basegfx::B2DPoint>::const_iterator iter = aPositions.begin(); iter != aPositions.end(); ++iter)
{
rWriter.startElement("point");
rWriter.attribute("x", OUString::number(iter->getX()));
rWriter.attribute("y", OUString::number(iter->getY()));
rWriter.endElement();
}
rWriter.endElement();
}
break;
case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
{
const PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D = dynamic_cast<const PolygonStrokePrimitive2D&>(*pBasePrimitive);
rWriter.startElement("polygonstroke");
rWriter.startElement("polygon");
rWriter.content(basegfx::utils::exportToSvgPoints(rPolygonStrokePrimitive2D.getB2DPolygon()));
rWriter.endElement();
rWriter.startElement("line");
const drawinglayer::attribute::LineAttribute& aLineAttribute = rPolygonStrokePrimitive2D.getLineAttribute();
rWriter.attribute("color", convertColorToString(aLineAttribute.getColor()));
rWriter.attribute("width", aLineAttribute.getWidth());
rWriter.endElement();
rWriter.startElement("stroke");
const drawinglayer::attribute::StrokeAttribute& aStrokeAttribute = rPolygonStrokePrimitive2D.getStrokeAttribute();
rWriter.attribute("fulldotdashlen", aStrokeAttribute.getFullDotDashLen());
//rWriter.attribute("dotdasharray", aStrokeAttribute.getDotDashArray());
rWriter.endElement();
rWriter.endElement();
}
break;
case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D:
{
const PolyPolygonStrokePrimitive2D& rPolyPolygonStrokePrimitive2D = dynamic_cast<const PolyPolygonStrokePrimitive2D&>(*pBasePrimitive);
@@ -434,6 +478,7 @@ void Primitive2dXmlDump::decomposeAndWrite(
{
rWriter.startElement("unhandled");
rWriter.attribute("id", OUStringToOString(sCurrentElementTag, RTL_TEXTENCODING_UTF8));
rWriter.attribute("idNumber", nId);
drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
drawinglayer::geometry::ViewInformation2D());

View File

@@ -44,13 +44,12 @@ class Test : public test::BootstrapFixture, public XmlTestTools, public unotest:
{
uno::Reference<lang::XComponent> mxComponent;
void checkRectPrimitive(Primitive2DSequence const & rPrimitive);
void testWorking();
void testPolyPolygon();
void TestDrawString();
void TestDrawStringTransparent();
void TestDrawLine();
void TestLinearGradient();
void TestTextMapMode();
void TestPdfInEmf();
Primitive2DSequence parseEmf(const OUString& aSource);
@@ -61,11 +60,12 @@ public:
uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
CPPUNIT_TEST_SUITE(Test);
CPPUNIT_TEST(testWorking);
CPPUNIT_TEST(testPolyPolygon);
CPPUNIT_TEST(TestDrawString);
CPPUNIT_TEST(TestDrawStringTransparent);
CPPUNIT_TEST(TestDrawLine);
CPPUNIT_TEST(TestLinearGradient);
CPPUNIT_TEST(TestTextMapMode);
CPPUNIT_TEST(TestPdfInEmf);
CPPUNIT_TEST_SUITE_END();
};
@@ -105,24 +105,39 @@ Primitive2DSequence Test::parseEmf(const OUString& aSource)
return xEmfParser->getDecomposition(aInputStream, aPath, aEmptyValues);
}
void Test::checkRectPrimitive(Primitive2DSequence const & rPrimitive)
void Test::testPolyPolygon()
{
Primitive2DSequence aSequence = parseEmf("/emfio/qa/cppunit/emf/data/fdo79679-2.emf");
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
drawinglayer::Primitive2dXmlDump dumper;
xmlDocUniquePtr pDocument = dumper.dumpAndParse(comphelper::sequenceToContainer<Primitive2DContainer>(rPrimitive));
xmlDocUniquePtr pDocument = dumper.dumpAndParse(comphelper::sequenceToContainer<Primitive2DContainer>(aSequence));
CPPUNIT_ASSERT (pDocument);
// emfio: add examples (later)
// assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor", "color", "#00cc00"); // rect background color
// assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor", "height", "100"); // rect background height
// assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor", "width", "100"); // rect background width
}
// Chart axis
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygon", "path", "m0 0h19746v14817h-19746z");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor", 2);
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor[1]", "color", "#ffffff");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor[1]/polypolygon", "path", "m0 0h19780v14851h-19780z");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor[2]/polypolygon", "path", "m2574 13194v-12065h15303v12065z");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polygonstroke", 116);
assertXPathContent(pDocument, "/primitive2D/metafile/transform/mask/polygonstroke[1]/polygon", "2574,13194 2574,1129 17877,1129 17877,13194");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polygonstroke[1]/line", "color", "#ffffff");
assertXPathContent(pDocument, "/primitive2D/metafile/transform/mask/polygonstroke[10]/polygon", "8674,13194 8674,1129");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polygonstroke[10]/line", "color", "#000000");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/textsimpleportion", 28);
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/textsimpleportion[6]", "width", "459");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/textsimpleportion[6]", "x", "9908");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/textsimpleportion[6]", "text", "0.5");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/textsimpleportion[6]", "fontcolor", "#000000");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/pointarray", 98);
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/pointarray[1]", "color", "#000000");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/pointarray[1]/point", "x", "2574");
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/pointarray[1]/point", "y", "1129");
void Test::testWorking()
{
Primitive2DSequence aSequenceRect = parseEmf("/emfio/qa/cppunit/emf/data/fdo79679-2.emf");
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequenceRect.getLength()));
checkRectPrimitive(aSequenceRect);
}
void Test::TestDrawString()
@@ -221,6 +236,40 @@ void Test::TestLinearGradient()
assertXPath(pDocument, "/primitive2D/metafile/transform/mask/svglineargradient[2]/polypolygon", "path", "m7615.75822989746 0.216110019646294h7615.75822989746v7610.21611001965h-7615.75822989746z");
}
void Test::TestTextMapMode()
{
// Check import of EMF image with records: SETMAPMODE with MM_TEXT MapMode, POLYLINE16, EXTCREATEPEN, EXTTEXTOUTW
// MM_TEXT is mapped to one device pixel. Positive x is to the right; positive y is down.
Primitive2DSequence aSequence = parseEmf("/emfio/qa/cppunit/emf/data/TextMapMode.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", 2);
assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[1]", "color", "#ffffff");
assertXPath(pDocument, "/primitive2D/metafile/transform/polypolygoncolor[1]/polypolygon", "path", "m0 0h3542v4647h-3542z");
assertXPath(pDocument, "/primitive2D/metafile/transform/textsimpleportion", 20);
assertXPath(pDocument, "/primitive2D/metafile/transform/textsimpleportion[1]", "text", "N");
assertXPath(pDocument, "/primitive2D/metafile/transform/textsimpleportion[1]", "fontcolor", "#4a70e3");
assertXPath(pDocument, "/primitive2D/metafile/transform/textsimpleportion[1]", "x", "2099");
assertXPath(pDocument, "/primitive2D/metafile/transform/textsimpleportion[1]", "y", "1859");
assertXPath(pDocument, "/primitive2D/metafile/transform/polygonstroke", 138);
assertXPathContent(pDocument, "/primitive2D/metafile/transform/polygonstroke[1]/polygon", "2142,1638 2142,1489");
assertXPath(pDocument, "/primitive2D/metafile/transform/polygonstroke[1]/line", "color", "#4a70e3");
assertXPath(pDocument, "/primitive2D/metafile/transform/polygonstroke[1]/line", "width", "11");
assertXPathContent(pDocument, "/primitive2D/metafile/transform/polygonstroke[10]/polygon", "1967,1029 1869,952");
assertXPath(pDocument, "/primitive2D/metafile/transform/polygonstroke[10]/line", "color", "#4a70e3");
assertXPath(pDocument, "/primitive2D/metafile/transform/polygonstroke[10]/line", "width", "11");
assertXPathContent(pDocument, "/primitive2D/metafile/transform/polygonstroke[20]/polygon", "2710,1113 2873,1330");
assertXPath(pDocument, "/primitive2D/metafile/transform/polygonstroke[20]/line", "color", "#666666");
assertXPath(pDocument, "/primitive2D/metafile/transform/polygonstroke[20]/line", "width", "11");
}
void Test::TestPdfInEmf()
{
// Load a PPTX file, which has a shape, with a bitmap fill, which is an EMF, containing a PDF.

Binary file not shown.

View File

@@ -374,10 +374,13 @@ namespace emfio
{
fX2 -= mnWinOrgX;
fY2 -= mnWinOrgY;
fX2 /= mnWinExtX;
fY2 /= mnWinExtY;
fX2 *= mnDevWidth;
fY2 *= mnDevHeight;
if ( mnMapMode != MM_TEXT )
{
fX2 /= mnWinExtX;
fY2 /= mnWinExtY;
fX2 *= mnDevWidth;
fY2 *= mnDevHeight;
}
fX2 += mnDevOrgX;
fY2 += mnDevOrgY; // fX2, fY2 now in device units
fX2 *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
@@ -464,10 +467,13 @@ namespace emfio
}
else
{
fWidth /= mnWinExtX;
fHeight /= mnWinExtY;
fWidth *= mnDevWidth;
fHeight *= mnDevHeight;
if ( mnMapMode != MM_TEXT )
{
fWidth /= mnWinExtX;
fHeight /= mnWinExtY;
fWidth *= mnDevWidth;
fHeight *= mnDevHeight;
}
fWidth *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
fHeight *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
}
@@ -2131,6 +2137,12 @@ namespace emfio
pSave->maPathObj = maPathObj;
pSave->maClipPath = maClipPath;
SAL_INFO("emfio", "\t\t GfxMode: " << mnGfxMode);
SAL_INFO("emfio", "\t\t MapMode: " << mnMapMode);
SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY);
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);
mvSaveStack.push_back( pSave );
}
@@ -2180,6 +2192,13 @@ namespace emfio
mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
meLatestRasterOp = meRasterOp;
}
SAL_INFO("emfio", "\t\t GfxMode: " << mnGfxMode);
SAL_INFO("emfio", "\t\t MapMode: " << mnMapMode);
SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY);
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);
mvSaveStack.pop_back();
}