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 <memory>
#include <sal/log.hxx> #include <sal/log.hxx>
#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/Tools.hxx> #include <drawinglayer/primitive2d/Tools.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
@@ -206,7 +207,50 @@ void Primitive2dXmlDump::decomposeAndWrite(
rWriter.endElement(); rWriter.endElement();
} }
break; 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: case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D:
{ {
const PolyPolygonStrokePrimitive2D& rPolyPolygonStrokePrimitive2D = dynamic_cast<const PolyPolygonStrokePrimitive2D&>(*pBasePrimitive); const PolyPolygonStrokePrimitive2D& rPolyPolygonStrokePrimitive2D = dynamic_cast<const PolyPolygonStrokePrimitive2D&>(*pBasePrimitive);
@@ -434,6 +478,7 @@ void Primitive2dXmlDump::decomposeAndWrite(
{ {
rWriter.startElement("unhandled"); rWriter.startElement("unhandled");
rWriter.attribute("id", OUStringToOString(sCurrentElementTag, RTL_TEXTENCODING_UTF8)); rWriter.attribute("id", OUStringToOString(sCurrentElementTag, RTL_TEXTENCODING_UTF8));
rWriter.attribute("idNumber", nId);
drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
pBasePrimitive->get2DDecomposition(aPrimitiveContainer, pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
drawinglayer::geometry::ViewInformation2D()); drawinglayer::geometry::ViewInformation2D());

View File

@@ -44,13 +44,12 @@ class Test : public test::BootstrapFixture, public XmlTestTools, public unotest:
{ {
uno::Reference<lang::XComponent> mxComponent; uno::Reference<lang::XComponent> mxComponent;
void checkRectPrimitive(Primitive2DSequence const & rPrimitive); void testPolyPolygon();
void testWorking();
void TestDrawString(); void TestDrawString();
void TestDrawStringTransparent(); void TestDrawStringTransparent();
void TestDrawLine(); void TestDrawLine();
void TestLinearGradient(); void TestLinearGradient();
void TestTextMapMode();
void TestPdfInEmf(); void TestPdfInEmf();
Primitive2DSequence parseEmf(const OUString& aSource); Primitive2DSequence parseEmf(const OUString& aSource);
@@ -61,11 +60,12 @@ public:
uno::Reference<lang::XComponent>& getComponent() { return mxComponent; } uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
CPPUNIT_TEST_SUITE(Test); CPPUNIT_TEST_SUITE(Test);
CPPUNIT_TEST(testWorking); CPPUNIT_TEST(testPolyPolygon);
CPPUNIT_TEST(TestDrawString); CPPUNIT_TEST(TestDrawString);
CPPUNIT_TEST(TestDrawStringTransparent); CPPUNIT_TEST(TestDrawStringTransparent);
CPPUNIT_TEST(TestDrawLine); CPPUNIT_TEST(TestDrawLine);
CPPUNIT_TEST(TestLinearGradient); CPPUNIT_TEST(TestLinearGradient);
CPPUNIT_TEST(TestTextMapMode);
CPPUNIT_TEST(TestPdfInEmf); CPPUNIT_TEST(TestPdfInEmf);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
}; };
@@ -105,24 +105,39 @@ Primitive2DSequence Test::parseEmf(const OUString& aSource)
return xEmfParser->getDecomposition(aInputStream, aPath, aEmptyValues); 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; drawinglayer::Primitive2dXmlDump dumper;
xmlDocUniquePtr pDocument = dumper.dumpAndParse(comphelper::sequenceToContainer<Primitive2DContainer>(rPrimitive)); xmlDocUniquePtr pDocument = dumper.dumpAndParse(comphelper::sequenceToContainer<Primitive2DContainer>(aSequence));
CPPUNIT_ASSERT (pDocument); CPPUNIT_ASSERT (pDocument);
// emfio: add examples (later) // Chart axis
// assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor", "color", "#00cc00"); // rect background color assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygon", "path", "m0 0h19746v14817h-19746z");
// assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor", "height", "100"); // rect background height assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygoncolor", 2);
// assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor", "width", "100"); // rect background width 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() 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"); 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() void Test::TestPdfInEmf()
{ {
// Load a PPTX file, which has a shape, with a bitmap fill, which is an EMF, containing a PDF. // 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; fX2 -= mnWinOrgX;
fY2 -= mnWinOrgY; fY2 -= mnWinOrgY;
fX2 /= mnWinExtX; if ( mnMapMode != MM_TEXT )
fY2 /= mnWinExtY; {
fX2 *= mnDevWidth; fX2 /= mnWinExtX;
fY2 *= mnDevHeight; fY2 /= mnWinExtY;
fX2 *= mnDevWidth;
fY2 *= mnDevHeight;
}
fX2 += mnDevOrgX; fX2 += mnDevOrgX;
fY2 += mnDevOrgY; // fX2, fY2 now in device units fY2 += mnDevOrgY; // fX2, fY2 now in device units
fX2 *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); fX2 *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
@@ -464,10 +467,13 @@ namespace emfio
} }
else else
{ {
fWidth /= mnWinExtX; if ( mnMapMode != MM_TEXT )
fHeight /= mnWinExtY; {
fWidth *= mnDevWidth; fWidth /= mnWinExtX;
fHeight *= mnDevHeight; fHeight /= mnWinExtY;
fWidth *= mnDevWidth;
fHeight *= mnDevHeight;
}
fWidth *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); fWidth *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
fHeight *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); fHeight *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
} }
@@ -2131,6 +2137,12 @@ namespace emfio
pSave->maPathObj = maPathObj; pSave->maPathObj = maPathObj;
pSave->maClipPath = maClipPath; 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 ); mvSaveStack.push_back( pSave );
} }
@@ -2180,6 +2192,13 @@ namespace emfio
mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) ); mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
meLatestRasterOp = 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(); mvSaveStack.pop_back();
} }