EMF+ tdf#142975 Add brush support to DrawString record

Change-Id: Icfcb4199dcd755fb20e14a8166571b6d6e763f2e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/117671
Tested-by: Jenkins
Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx
index b56e510..b052c15 100644
--- a/drawinglayer/source/tools/emfphelperdata.cxx
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -456,12 +456,14 @@ namespace emfplushelper
            color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff,
                          (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
        }
        else // we use a pen
        else // we use a brush
        {
            const EMFPPen* pen = static_cast<EMFPPen*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
            if (pen)
            const EMFPBrush* brush = static_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
            if (brush)
            {
                color = pen->GetColor();
                color = brush->GetColor();
                if (brush->type != BrushTypeSolidColor)
                    SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Brush other than solid color is not supported");
            }
        }
        return color;
@@ -1525,161 +1527,152 @@ namespace emfplushelper
                    }
                    case EmfPlusRecordTypeDrawString:
                    {
                        sal_uInt32 brushId;
                        sal_uInt32 formatId;
                        sal_uInt32 stringLength;
                        sal_uInt32 brushId, formatId, stringLength;
                        rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength);
                        SAL_INFO("drawinglayer.emf", "EMF+\t FontId: " << OUString::number(flags & 0xFF));
                        SAL_INFO("drawinglayer.emf", "EMF+\t BrushId: " << BrushIDToString(flags, brushId));
                        SAL_INFO("drawinglayer.emf", "EMF+\t FormatId: " << formatId);
                        SAL_INFO("drawinglayer.emf", "EMF+\t Length: " << stringLength);

                        if (flags & 0x8000)
                        // read the layout rectangle
                        float lx, ly, lw, lh;
                        rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh);

                        SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
                        // parse the string
                        const OUString text = read_uInt16s_ToOUString(rMS, stringLength);
                        SAL_INFO("drawinglayer.emf", "EMF+\t DrawString string: " << text);
                        // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr )
                        const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get());
                        // get the font from the flags
                        const EMFPFont *font = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() );
                        if (!font)
                        {
                            // read the layout rectangle
                            float lx, ly, lw, lh;
                            rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh);
                            break;
                        }
                        mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize)));

                            SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
                            // parse the string
                            const OUString text = read_uInt16s_ToOUString(rMS, stringLength);
                            SAL_INFO("drawinglayer.emf", "EMF+\t DrawString string: " << text);
                            // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr )
                            const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get());
                            // get the font from the flags
                            const EMFPFont *font = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() );
                            if (!font)
                        drawinglayer::attribute::FontAttribute fontAttribute(
                            font->family,                                          // font family
                            "",                                                    // (no) font style
                            font->Bold() ? 8u : 1u,                                // weight: 8 = bold
                            font->family == "SYMBOL",                              // symbol
                            stringFormat && stringFormat->DirectionVertical(),     // vertical
                            font->Italic(),                                        // italic
                            false,                                                 // monospaced
                            false,                                                 // outline = false, no such thing in MS-EMFPLUS
                            stringFormat && stringFormat->DirectionRightToLeft(),  // right-to-left
                            false);                                                // BiDiStrong

                        css::lang::Locale locale;
                        double stringAlignmentHorizontalOffset = 0.0;
                        if (stringFormat)
                        {
                            SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf", "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right.");
                            if (stringFormat->stringAlignment == StringAlignmentNear)
                            // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh)
                            {
                                break;
                            }
                            mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize)));

                            drawinglayer::attribute::FontAttribute fontAttribute(
                                font->family,                                          // font family
                                "",                                                    // (no) font style
                                font->Bold() ? 8u : 1u,                                // weight: 8 = bold
                                font->family == "SYMBOL",                              // symbol
                                stringFormat && stringFormat->DirectionVertical(),     // vertical
                                font->Italic(),                                        // italic
                                false,                                                 // monospaced
                                false,                                                 // outline = false, no such thing in MS-EMFPLUS
                                stringFormat && stringFormat->DirectionRightToLeft(),  // right-to-left
                                false);                                                // BiDiStrong

                            css::lang::Locale locale;
                            double stringAlignmentHorizontalOffset = 0.0;
                            if (stringFormat)
                                stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize;
                            } else if (stringFormat->stringAlignment == StringAlignmentCenter)
                            // Alignment is centered between the origin and extent of the layout rectangle
                            {
                                SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf", "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right.");
                                if (stringFormat->stringAlignment == StringAlignmentNear)
                                // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh)
                                {
                                    stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize;
                                } else if (stringFormat->stringAlignment == StringAlignmentCenter)
                                // Alignment is centered between the origin and extent of the layout rectangle
                                {
                                    stringAlignmentHorizontalOffset = 0.5 * lw + stringFormat->leadingMargin * font->emSize - 0.3 * font->emSize * stringLength;
                                } else if (stringFormat->stringAlignment == StringAlignmentFar)
                                // Alignment is to the right side of the layout rectangle
                                {
                                    stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - 0.6 * font->emSize * stringLength;
                                }

                                LanguageTag aLanguageTag(static_cast< LanguageType >(stringFormat->language));
                                locale = aLanguageTag.getLocale();
                            }
                            else
                                stringAlignmentHorizontalOffset = 0.5 * lw + stringFormat->leadingMargin * font->emSize - 0.3 * font->emSize * stringLength;
                            } else if (stringFormat->stringAlignment == StringAlignmentFar)
                            // Alignment is to the right side of the layout rectangle
                            {
                                // By default LeadingMargin is 1/6 inch
                                // TODO for typographic fonts set value to 0.
                                stringAlignmentHorizontalOffset = 16.0;

                                // use system default
                                locale = Application::GetSettings().GetLanguageTag().getLocale();
                                stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - 0.6 * font->emSize * stringLength;
                            }

                            const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
                                        ::basegfx::B2DSize(font->emSize, font->emSize),
                                        ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset, ly + font->emSize));

                            Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId);
                            Color color;

                            if (mbSetTextContrast)
                            {
                                const auto gammaVal = mnTextContrast / 1000;
                                const basegfx::BColorModifier_gamma gamma(gammaVal);

                                // gamma correct transparency color
                                sal_uInt16 alpha = uncorrectedColor.GetAlpha();
                                alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255;

                                basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor()));
                                color.SetRed(modifiedColor.getRed() * 255);
                                color.SetGreen(modifiedColor.getGreen() * 255);
                                color.SetBlue(modifiedColor.getBlue() * 255);
                                color.SetAlpha(alpha);
                            }
                            else
                            {
                                color = uncorrectedColor;
                            }

                            mrPropertyHolders.Current().setTextColor(color.getBColor());
                            mrPropertyHolders.Current().setTextColorActive(true);

                            if (color.GetAlpha() > 0)
                            {
                                std::vector<double> emptyVector;
                                rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
                                if (font->Underline() || font->Strikeout())
                                {
                                    pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
                                                transformMatrix,
                                                text,
                                                0,             // text always starts at 0
                                                stringLength,
                                                emptyVector,   // EMF-PLUS has no DX-array
                                                fontAttribute,
                                                locale,
                                                color.getBColor(),
                                                COL_TRANSPARENT,
                                                color.getBColor(),
                                                color.getBColor(),
                                                drawinglayer::primitive2d::TEXT_LINE_NONE,
                                                font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
                                                false,
                                                font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
                                }
                                else
                                {
                                    pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                                                transformMatrix,
                                                text,
                                                0,             // text always starts at 0
                                                stringLength,
                                                emptyVector,   // EMF-PLUS has no DX-array
                                                fontAttribute,
                                                locale,
                                                color.getBColor());
                                }
                                drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
                                if (color.IsTransparent())
                                {
                                    aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
                                                drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
                                                (255 - color.GetAlpha()) / 255.0);
                                }

                                mrTargetHolders.Current().append(
                                            new drawinglayer::primitive2d::TransformPrimitive2D(
                                                maMapTransform,
                                                drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
                            }
                            LanguageTag aLanguageTag(static_cast< LanguageType >(stringFormat->language));
                            locale = aLanguageTag.getLocale();
                        }
                        else
                        {
                            SAL_WARN("drawinglayer.emf", "EMF+\t DrawString TODO - drawing with brush not yet supported");
                            // By default LeadingMargin is 1/6 inch
                            // TODO for typographic fonts set value to 0.
                            stringAlignmentHorizontalOffset = 16.0;

                            // use system default
                            locale = Application::GetSettings().GetLanguageTag().getLocale();
                        }

                        const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
                                    ::basegfx::B2DSize(font->emSize, font->emSize),
                                    ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset, ly + font->emSize));

                        Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId);
                        Color color;

                        if (mbSetTextContrast)
                        {
                            const auto gammaVal = mnTextContrast / 1000;
                            const basegfx::BColorModifier_gamma gamma(gammaVal);

                            // gamma correct transparency color
                            sal_uInt16 alpha = uncorrectedColor.GetAlpha();
                            alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255;

                            basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor()));
                            color.SetRed(modifiedColor.getRed() * 255);
                            color.SetGreen(modifiedColor.getGreen() * 255);
                            color.SetBlue(modifiedColor.getBlue() * 255);
                            color.SetAlpha(alpha);
                        }
                        else
                        {
                            color = uncorrectedColor;
                        }

                        mrPropertyHolders.Current().setTextColor(color.getBColor());
                        mrPropertyHolders.Current().setTextColorActive(true);

                        if (color.GetAlpha() > 0)
                        {
                            std::vector<double> emptyVector;
                            rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
                            if (font->Underline() || font->Strikeout())
                            {
                                pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
                                            transformMatrix,
                                            text,
                                            0,             // text always starts at 0
                                            stringLength,
                                            emptyVector,   // EMF-PLUS has no DX-array
                                            fontAttribute,
                                            locale,
                                            color.getBColor(), // Font Color
                                            COL_TRANSPARENT,   // Fill Color
                                            color.getBColor(), // OverlineColor
                                            color.getBColor(), // TextlineColor
                                            drawinglayer::primitive2d::TEXT_LINE_NONE,
                                            font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
                                            false,
                                            font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
                            }
                            else
                            {
                                pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                                            transformMatrix,
                                            text,
                                            0,             // text always starts at 0
                                            stringLength,
                                            emptyVector,   // EMF-PLUS has no DX-array
                                            fontAttribute,
                                            locale,
                                            color.getBColor());
                            }
                            drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
                            if (color.IsTransparent())
                            {
                                aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
                                            drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
                                            (255 - color.GetAlpha()) / 255.0);
                            }

                            mrTargetHolders.Current().append(
                                        new drawinglayer::primitive2d::TransformPrimitive2D(
                                            maMapTransform,
                                            drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
                        }
                        break;
                    }
diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx b/drawinglayer/source/tools/primitive2dxmldump.cxx
index 9ed0e79..4b0c47f 100644
--- a/drawinglayer/source/tools/primitive2dxmldump.cxx
+++ b/drawinglayer/source/tools/primitive2dxmldump.cxx
@@ -25,6 +25,8 @@
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
#include <primitive2d/textlineprimitive2d.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
@@ -56,6 +58,19 @@ OUString convertColorToString(const basegfx::BColor& rColor)
    return "#" + aRGBString;
}

void writeMatrix(::tools::XmlWriter& rWriter, const basegfx::B2DHomMatrix& rMatrix)
{
    rWriter.attribute("xy11", rMatrix.get(0,0));
    rWriter.attribute("xy12", rMatrix.get(0,1));
    rWriter.attribute("xy13", rMatrix.get(0,2));
    rWriter.attribute("xy21", rMatrix.get(1,0));
    rWriter.attribute("xy22", rMatrix.get(1,1));
    rWriter.attribute("xy23", rMatrix.get(1,2));
    rWriter.attribute("xy31", rMatrix.get(2,0));
    rWriter.attribute("xy32", rMatrix.get(2,1));
    rWriter.attribute("xy33", rMatrix.get(2,2));
}

void writePolyPolygon(::tools::XmlWriter& rWriter, const basegfx::B2DPolyPolygon& rB2DPolyPolygon)
{
    rWriter.startElement("polypolygon");
@@ -211,17 +226,7 @@ void Primitive2dXmlDump::decomposeAndWrite(
            {
                const BitmapPrimitive2D& rBitmapPrimitive2D = dynamic_cast<const BitmapPrimitive2D&>(*pBasePrimitive);
                rWriter.startElement("bitmap");

                basegfx::B2DHomMatrix const & rMatrix = rBitmapPrimitive2D.getTransform();
                rWriter.attribute("xy11", rMatrix.get(0,0));
                rWriter.attribute("xy12", rMatrix.get(0,1));
                rWriter.attribute("xy13", rMatrix.get(0,2));
                rWriter.attribute("xy21", rMatrix.get(1,0));
                rWriter.attribute("xy22", rMatrix.get(1,1));
                rWriter.attribute("xy23", rMatrix.get(1,2));
                rWriter.attribute("xy31", rMatrix.get(2,0));
                rWriter.attribute("xy32", rMatrix.get(2,1));
                rWriter.attribute("xy33", rMatrix.get(2,2));
                writeMatrix(rWriter, rBitmapPrimitive2D.getTransform());

                const BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapPrimitive2D.getXBitmap()));
                const Size& rSizePixel(aBitmapEx.GetSizePixel());
@@ -260,18 +265,7 @@ void Primitive2dXmlDump::decomposeAndWrite(
            {
                const TransformPrimitive2D& rTransformPrimitive2D = dynamic_cast<const TransformPrimitive2D&>(*pBasePrimitive);
                rWriter.startElement("transform");

                basegfx::B2DHomMatrix const & rMatrix = rTransformPrimitive2D.getTransformation();
                rWriter.attributeDouble("xy11", rMatrix.get(0,0));
                rWriter.attributeDouble("xy12", rMatrix.get(0,1));
                rWriter.attributeDouble("xy13", rMatrix.get(0,2));
                rWriter.attributeDouble("xy21", rMatrix.get(1,0));
                rWriter.attributeDouble("xy22", rMatrix.get(1,1));
                rWriter.attributeDouble("xy23", rMatrix.get(1,2));
                rWriter.attributeDouble("xy31", rMatrix.get(2,0));
                rWriter.attributeDouble("xy32", rMatrix.get(2,1));
                rWriter.attributeDouble("xy33", rMatrix.get(2,2));

                writeMatrix(rWriter, rTransformPrimitive2D.getTransformation());
                decomposeAndWrite(rTransformPrimitive2D.getChildren(), rWriter);
                rWriter.endElement();
            }
@@ -355,7 +349,35 @@ void Primitive2dXmlDump::decomposeAndWrite(
                rWriter.content(basegfx::utils::exportToSvgPoints(rPolygonHairlinePrimitive2D.getB2DPolygon()));
                rWriter.endElement();

                rWriter.endElement();
            }
            break;

            case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D:
            {
                const TextDecoratedPortionPrimitive2D& rTextDecoratedPortionPrimitive2D = dynamic_cast<const TextDecoratedPortionPrimitive2D&>(*pBasePrimitive);
                rWriter.startElement("textdecoratedportion");
                writeMatrix(rWriter, rTextDecoratedPortionPrimitive2D.getTextTransform());

                rWriter.attribute("text", rTextDecoratedPortionPrimitive2D.getText());
                rWriter.attribute("fontcolor", convertColorToString(rTextDecoratedPortionPrimitive2D.getFontColor()));

                const drawinglayer::attribute::FontAttribute& aFontAttribute = rTextDecoratedPortionPrimitive2D.getFontAttribute();
                rWriter.attribute("familyname", aFontAttribute.getFamilyName());
                rWriter.endElement();
            }
            break;

            case PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D:
            {
                const TextLinePrimitive2D& rTextLinePrimitive2D = dynamic_cast<const TextLinePrimitive2D&>(*pBasePrimitive);
                rWriter.startElement("textline");
                writeMatrix(rWriter, rTextLinePrimitive2D.getObjectTransformation());

                rWriter.attribute("width", rTextLinePrimitive2D.getWidth());
                rWriter.attribute("offset", rTextLinePrimitive2D.getOffset());
                rWriter.attribute("height", rTextLinePrimitive2D.getHeight());
                rWriter.attribute("color", convertColorToString(rTextLinePrimitive2D.getLineColor()));
                rWriter.endElement();
            }
            break;
@@ -406,9 +428,8 @@ void Primitive2dXmlDump::decomposeAndWrite(
            {
                const UnifiedTransparencePrimitive2D& rUnifiedTransparencePrimitive2D = dynamic_cast<const UnifiedTransparencePrimitive2D&>(*pBasePrimitive);
                rWriter.startElement("unifiedtransparence");
                rWriter.attribute("transparence", OString::number(rUnifiedTransparencePrimitive2D.getTransparence()));
                rWriter.attribute("transparence", std::lround(100 * rUnifiedTransparencePrimitive2D.getTransparence()));
                decomposeAndWrite(rUnifiedTransparencePrimitive2D.getChildren(), rWriter);

                rWriter.endElement();
            }
            break;
@@ -452,16 +473,7 @@ void Primitive2dXmlDump::decomposeAndWrite(
                rWriter.attributeDouble("opacity", rSvgLinearGradientPrimitive2D.getGradientEntries().front().getOpacity());

                rWriter.startElement("transform");
                basegfx::B2DHomMatrix const & rMatrix = rSvgLinearGradientPrimitive2D.getGradientTransform();
                rWriter.attributeDouble("xy11", rMatrix.get(0,0));
                rWriter.attributeDouble("xy12", rMatrix.get(0,1));
                rWriter.attributeDouble("xy13", rMatrix.get(0,2));
                rWriter.attributeDouble("xy21", rMatrix.get(1,0));
                rWriter.attributeDouble("xy22", rMatrix.get(1,1));
                rWriter.attributeDouble("xy23", rMatrix.get(1,2));
                rWriter.attributeDouble("xy31", rMatrix.get(2,0));
                rWriter.attributeDouble("xy32", rMatrix.get(2,1));
                rWriter.attributeDouble("xy33", rMatrix.get(2,2));
                writeMatrix(rWriter, rSvgLinearGradientPrimitive2D.getGradientTransform());
                rWriter.endElement();

                writePolyPolygon(rWriter, rSvgLinearGradientPrimitive2D.getPolyPolygon());
diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx
index 1383b5f..34288bc 100644
--- a/emfio/qa/cppunit/emf/EmfImportTest.cxx
+++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx
@@ -48,6 +48,7 @@ class Test : public test::BootstrapFixture, public XmlTestTools, public unotest:
    void testPolyPolygon();
    void TestDrawString();
    void TestDrawStringTransparent();
    void TestDrawStringWithBrush();
    void TestDrawLine();
    void TestLinearGradient();
    void TestTextMapMode();
@@ -87,6 +88,7 @@ public:
    CPPUNIT_TEST(testPolyPolygon);
    CPPUNIT_TEST(TestDrawString);
    CPPUNIT_TEST(TestDrawStringTransparent);
    CPPUNIT_TEST(TestDrawStringWithBrush);
    CPPUNIT_TEST(TestDrawLine);
    CPPUNIT_TEST(TestLinearGradient);
    CPPUNIT_TEST(TestTextMapMode);
@@ -189,10 +191,9 @@ void Test::testPolyPolygon()
void Test::TestDrawString()
{
#if HAVE_MORE_FONTS
    // This unit checks for a correct import of an EMF+ file with only one DrawString Record
    // EMF+ file with only one DrawString Record
    // Since the text is undecorated the optimal choice is a simpletextportion primitive

    // first, get the sequence of primitives and dump it
    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/TestDrawString.emf");
    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
    drawinglayer::Primitive2dXmlDump dumper;
@@ -212,17 +213,15 @@ void Test::TestDrawString()
void Test::TestDrawStringTransparent()
{
#if HAVE_MORE_FONTS
    // This unit checks for a correct import of an EMF+ file with one DrawString Record with transparency
    // EMF+ file with one DrawString Record with transparency

    // first, get the sequence of primitives and dump it
    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/TestDrawStringTransparent.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/mask/transform/unifiedtransparence", "transparence", "0.498039215686275");

    assertXPath(pDocument, "/primitive2D/metafile/transform/mask/transform/unifiedtransparence", "transparence", "50");
    assertXPath(pDocument, "/primitive2D/metafile/transform/mask/transform/unifiedtransparence/textsimpleportion", "height", "24");
    assertXPath(pDocument, "/primitive2D/metafile/transform/mask/transform/unifiedtransparence/textsimpleportion", "x", "66");
    assertXPath(pDocument, "/primitive2D/metafile/transform/mask/transform/unifiedtransparence/textsimpleportion", "y", "74");
@@ -232,12 +231,27 @@ void Test::TestDrawStringTransparent()
#endif
}

void Test::TestDrawStringWithBrush()
{
    // tdf#142975 EMF+ with records: DrawString, Brush and Font
    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/TestDrawStringWithBrush.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/transform/textdecoratedportion", "xy11", "20");
    assertXPath(pDocument, "/primitive2D/metafile/transform/transform/textdecoratedportion", "xy13", "16");
    assertXPath(pDocument, "/primitive2D/metafile/transform/transform/textdecoratedportion", "xy22", "20");
    assertXPath(pDocument, "/primitive2D/metafile/transform/transform/textdecoratedportion", "xy33", "1");
    assertXPath(pDocument, "/primitive2D/metafile/transform/transform/textdecoratedportion", "text", "0123456789ABCDEF");
    assertXPath(pDocument, "/primitive2D/metafile/transform/transform/textdecoratedportion", "fontcolor", "#a50021");
    assertXPath(pDocument, "/primitive2D/metafile/transform/transform/textdecoratedportion", "familyname", "TIMES NEW ROMAN");
}

void Test::TestDrawLine()
{
    // This unit checks for a correct import of an EMF+ file with only one DrawLine Record
    // EMF+ with records: DrawLine
    // The line is colored and has a specified width, therefore a polypolygonstroke primitive is the optimal choice

    // first, get the sequence of primitives and dump it
    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/TestDrawLine.emf");
    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
    drawinglayer::Primitive2dXmlDump dumper;
@@ -251,18 +265,18 @@ void Test::TestDrawLine()

void Test::TestLinearGradient()
{
    // This unit checks for a correct import of an EMF+ file with LinearGradient brush
    // EMF+ file with LinearGradient brush
    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/TestLinearGradient.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", "xy11", "1.0000656512605");
    assertXPath(pDocument, "/primitive2D/metafile/transform", "xy11", "1");
    assertXPath(pDocument, "/primitive2D/metafile/transform", "xy12", "0");
    assertXPath(pDocument, "/primitive2D/metafile/transform", "xy13", "0");
    assertXPath(pDocument, "/primitive2D/metafile/transform", "xy21", "0");
    assertXPath(pDocument, "/primitive2D/metafile/transform", "xy22", "1.00013140604468");
    assertXPath(pDocument, "/primitive2D/metafile/transform", "xy22", "1");
    assertXPath(pDocument, "/primitive2D/metafile/transform", "xy23", "0");
    assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygon", "height", "7610");
    assertXPath(pDocument, "/primitive2D/metafile/transform/mask/polypolygon", "width", "15232");
@@ -284,7 +298,7 @@ void Test::TestLinearGradient()

void Test::TestTextMapMode()
{
    // Check import of EMF image with records: SETMAPMODE with MM_TEXT MapMode, POLYLINE16, EXTCREATEPEN, EXTTEXTOUTW
    // EMF 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(u"/emfio/qa/cppunit/emf/data/TextMapMode.emf");
    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
@@ -318,7 +332,7 @@ void Test::TestTextMapMode()

void Test::TestEnglishMapMode()
{
    // Check import of EMF image with records: SETMAPMODE with MM_ENGLISH MapMode, STROKEANDFILLPATH, EXTTEXTOUTW, SETTEXTALIGN, STRETCHDIBITS
    // EMF image with records: SETMAPMODE with MM_ENGLISH MapMode, STROKEANDFILLPATH, EXTTEXTOUTW, SETTEXTALIGN, STRETCHDIBITS
    // MM_LOENGLISH is mapped to 0.01 inch. Positive x is to the right; positive y is up.M
    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/test_mm_hienglish_ref.emf");
    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
@@ -351,7 +365,7 @@ void Test::TestEnglishMapMode()

void Test::TestRectangleWithModifyWorldTransform()
{
    // Check import of EMF image with records: EXTCREATEPEN, SELECTOBJECT, MODIFYWORLDTRANSFORM, RECTANGLE
    // EMF image with records: EXTCREATEPEN, SELECTOBJECT, MODIFYWORLDTRANSFORM, RECTANGLE

    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/TestRectangleWithModifyWorldTransform.emf");
    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
@@ -452,7 +466,7 @@ void Test::TestEllipseXformIntersectClipRect()

void Test::TestDrawPolyLine16WithClip()
{
    // Check import of EMF image with records:
    // EMF image with records:
    // CREATEBRUSHINDIRECT, FILLRGN, BEGINPATH, POLYGON16, SELECTCLIPPATH, MODIFYWORLDTRANSFORM, SELECTOBJECT
    Primitive2DSequence aSequence = parseEmf(u"/emfio/qa/cppunit/emf/data/TestDrawPolyLine16WithClip.emf");
    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
diff --git a/emfio/qa/cppunit/emf/data/TestDrawStringWithBrush.emf b/emfio/qa/cppunit/emf/data/TestDrawStringWithBrush.emf
new file mode 100644
index 0000000..3afb53e
--- /dev/null
+++ b/emfio/qa/cppunit/emf/data/TestDrawStringWithBrush.emf
Binary files differ
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx
index e089558..25baf76 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -381,7 +381,7 @@ void Test::testTdf79163()

    CPPUNIT_ASSERT (pDocument);

    assertXPath(pDocument, "/primitive2D/transform/unifiedtransparence", "transparence", "0.5");
    assertXPath(pDocument, "/primitive2D/transform/unifiedtransparence", "transparence", "50");
}

void Test::testTdf97542_1()
@@ -464,7 +464,7 @@ void Test::testRGBAColor()

    CPPUNIT_ASSERT (pDocument);

    assertXPath(pDocument, "/primitive2D/transform/unifiedtransparence", "transparence", "0.5");
    assertXPath(pDocument, "/primitive2D/transform/unifiedtransparence", "transparence", "50");
}

void Test::testNoneColor()
diff --git a/svx/qa/unit/sdr.cxx b/svx/qa/unit/sdr.cxx
index f6eb744..10f9901 100644
--- a/svx/qa/unit/sdr.cxx
+++ b/svx/qa/unit/sdr.cxx
@@ -86,15 +86,15 @@ CPPUNIT_TEST_FIXTURE(SdrTest, testShadowScaleOrigin)
    // Examine the created primitives.
    drawinglayer::Primitive2dXmlDump aDumper;
    xmlDocUniquePtr pDocument = aDumper.dumpAndParse(xPrimitiveSequence);
    double fShadowX = getXPath(pDocument, "//shadow/transform", "xy13").toDouble();
    double fShadowY = getXPath(pDocument, "//shadow/transform", "xy23").toDouble();
    sal_Int32 fShadowX = getXPath(pDocument, "//shadow/transform", "xy13").toInt32();
    sal_Int32 fShadowY = getXPath(pDocument, "//shadow/transform", "xy23").toInt32();
    // Without the accompanying fix in place, this test would have failed with:
    // - Expected: -705
    // - Actual  : -158
    // i.e. the shadow origin was not the top right corner for scaling (larger x position, so it was
    // visible on the right of the shape as well).
    CPPUNIT_ASSERT_EQUAL(-705., std::round(fShadowX));
    CPPUNIT_ASSERT_EQUAL(-685., std::round(fShadowY));
    CPPUNIT_ASSERT_EQUAL(sal_Int32(-705), fShadowX);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(-684), fShadowY);
}

CPPUNIT_TEST_FIXTURE(SdrTest, testZeroWidthTextWrap)
diff --git a/svx/qa/unit/svdraw.cxx b/svx/qa/unit/svdraw.cxx
index d6e43d6..fa344d8 100644
--- a/svx/qa/unit/svdraw.cxx
+++ b/svx/qa/unit/svdraw.cxx
@@ -110,9 +110,9 @@ CPPUNIT_TEST_FIXTURE(SvdrawTest, testSemiTransparentText)
    // - Actual  : 0
    // - XPath '//unifiedtransparence' number of nodes is incorrect
    // i.e. the text was just plain red, not semi-transparent.
    double fTransparence = getXPath(pDocument, "//unifiedtransparence", "transparence").toDouble();
    CPPUNIT_ASSERT_EQUAL(nTransparence,
                         static_cast<sal_Int16>(basegfx::fround(fTransparence * 100)));
    sal_Int16 fTransparence
        = getXPath(pDocument, "//unifiedtransparence", "transparence").toInt32();
    CPPUNIT_ASSERT_EQUAL(nTransparence, fTransparence);
}

CPPUNIT_TEST_FIXTURE(SvdrawTest, testHandlePathObjScale)