tdf#101122 DOCX custom shape export: remove bad fill

of (simplified export) of not filled custom shapes by
adding missing fill="none" to a:path.

Note: in OpenDocument, unfilled shape path is defined
by draw:enhanced-path command "F", see section 19.145
in ODF v1.2.

Co-authored-by: Szabolcs Tóth

Change-Id: I0be2aada3deb06828216e0441c91c389a673f87c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/104205
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx
index 11bf303e..a4ef6af 100644
--- a/include/oox/export/drawingml.hxx
+++ b/include/oox/export/drawingml.hxx
@@ -171,6 +171,7 @@ protected:

    void WriteGlowEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet);
    void WriteSoftEdgeEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet);
    bool HasEnhancedCustomShapeSegmentCommand(const css::uno::Reference<css::drawing::XShape>& rXShape, const sal_Int16 nCommand);

public:
    DrawingML( ::sax_fastparser::FSHelperPtr pFS, ::oox::core::XmlFilterBase* pFB, DocumentType eDocumentType = DOCUMENT_PPTX, DMLTextExport* pTextExport = nullptr )
@@ -275,7 +276,8 @@ public:
    static sal_Int32 GetCustomGeometryPointValue(
        const css::drawing::EnhancedCustomShapeParameter& rParam,
        const SdrObjCustomShape& rSdrObjCustomShape);
    void WritePolyPolygon( const tools::PolyPolygon& rPolyPolygon, const bool bClosed );
    void WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& rXShape,
                          const tools::PolyPolygon& rPolyPolygon, const bool bClosed);
    void WriteFill( const css::uno::Reference< css::beans::XPropertySet >& xPropSet );
    void WriteShapeStyle( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet );
    void WriteShapeEffects( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet );
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index c19b030..8b7c4ad 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -3582,7 +3582,8 @@ sal_Int32 DrawingML::GetCustomGeometryPointValue(
    return nValue;
}

void DrawingML::WritePolyPolygon( const tools::PolyPolygon& rPolyPolygon, const bool bClosed )
void DrawingML::WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& rXShape,
                                 const tools::PolyPolygon& rPolyPolygon, const bool bClosed)
{
    // In case of Writer, the parent element is <wps:spPr>, and there the
    // <a:custGeom> element is not optional.
@@ -3599,9 +3600,15 @@ void DrawingML::WritePolyPolygon( const tools::PolyPolygon& rPolyPolygon, const 

    const tools::Rectangle aRect( rPolyPolygon.GetBoundRect() );

    // tdf#101122
    std::optional<OString> sFill;
    if (HasEnhancedCustomShapeSegmentCommand(rXShape, css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL))
        sFill = "none"; // for possible values see ST_PathFillMode in OOXML standard

    // Put all polygons of rPolyPolygon in the same path element
    // to subtract the overlapped areas.
    mpFS->startElementNS( XML_a, XML_path,
            XML_fill, sFill,
            XML_w, OString::number(aRect.GetWidth()),
            XML_h, OString::number(aRect.GetHeight()) );

@@ -4191,6 +4198,44 @@ void DrawingML::WriteSoftEdgeEffect(const css::uno::Reference<css::beans::XPrope
    WriteShapeEffect("softEdge", aProps);
}

bool DrawingML::HasEnhancedCustomShapeSegmentCommand(
    const css::uno::Reference<css::drawing::XShape>& rXShape, const sal_Int16 nCommand)
{
    try
    {
        uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY_THROW);
        if (!GetProperty(xPropSet, "CustomShapeGeometry"))
            return false;
        Sequence<PropertyValue> aCustomShapeGeometryProps;
        mAny >>= aCustomShapeGeometryProps;
        for (const beans::PropertyValue& rGeomProp : std::as_const(aCustomShapeGeometryProps))
        {
            if (rGeomProp.Name == "Path")
            {
                uno::Sequence<beans::PropertyValue> aPathProps;
                rGeomProp.Value >>= aPathProps;
                for (const beans::PropertyValue& rPathProp : std::as_const(aPathProps))
                {
                    if (rPathProp.Name == "Segments")
                    {
                        uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
                        rPathProp.Value >>= aSegments;
                        for (const auto& rSegment : std::as_const(aSegments))
                        {
                            if (rSegment.Command == nCommand)
                                return true;
                        }
                    }
                }
            }
        }
    }
    catch (const ::uno::Exception&)
    {
    }
    return false;
}

void DrawingML::WriteShape3DEffects( const Reference< XPropertySet >& xPropSet )
{
    // check existence of the grab bag
diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx
index 00a44d3..2b12516 100644
--- a/oox/source/export/shapes.cxx
+++ b/oox/source/export/shapes.cxx
@@ -427,7 +427,7 @@ ShapeExport& ShapeExport::WritePolyPolygonShape( const Reference< XShape >& xSha
    // visual shape properties
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
    WriteTransformation( aRect, XML_a );
    WritePolyPolygon( aPolyPolygon, bClosed );
    WritePolyPolygon(xShape, aPolyPolygon, bClosed);
    Reference< XPropertySet > xProps( xShape, UNO_QUERY );
    if( xProps.is() ) {
        if( bClosed )
@@ -845,7 +845,7 @@ ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
        bool bInvertRotation = bFlipH != bFlipV;
        if (nRotation != 0)
            aPolyPolygon.Rotate(Point(0,0), static_cast<sal_uInt16>(bInvertRotation ? nRotation/10 : 3600-nRotation/10));
        WritePolyPolygon( aPolyPolygon, false );
        WritePolyPolygon(xShape, aPolyPolygon, false);
    }
    else if (bCustGeom)
    {
diff --git a/sw/qa/extras/ooxmlexport/data/tdf101122_noFillForCustomShape.odt b/sw/qa/extras/ooxmlexport/data/tdf101122_noFillForCustomShape.odt
new file mode 100644
index 0000000..8cc2a13
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf101122_noFillForCustomShape.odt
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index 6e456049..f26d757 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -1380,6 +1380,22 @@ DECLARE_OOXMLEXPORT_TEST(testTdf67207_MERGEFIELD_DATABASE, "tdf67207.docx")
    CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.text.fieldmaster.DataBase.database.Sheet1.c1"), sValue);
}

DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf101122_noFillForCustomShape, "tdf101122_noFillForCustomShape.odt")
{
    // tdf#101122 check whether the "F" (noFill) option has been exported to docx
    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");

    assertXPath(pXmlDoc,
                "/w:document/w:body/w:p/w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor/"
                "a:graphic/a:graphicData/wps:wsp/wps:spPr/a:custGeom/a:pathLst/a:path",
                "fill", "none");
    assertXPathNoAttribute(
        pXmlDoc,
        "/w:document/w:body/w:p/w:r/mc:AlternateContent[2]/mc:Choice/w:drawing/wp:anchor/a:graphic/"
        "a:graphicData/wps:wsp/wps:spPr/a:custGeom/a:pathLst/a:path",
        "fill");
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */