[Libreoffice-commits] core.git: include/oox oox/source sd/qa

Regina Henschel (via logerrit) logerrit at kemper.freedesktop.org
Thu Jul 9 22:58:06 UTC 2020


 include/oox/export/drawingml.hxx                       |   13 -
 include/oox/export/shapes.hxx                          |    1 
 oox/source/drawingml/customshapeproperties.cxx         |    3 
 oox/source/export/drawingml.cxx                        |  188 ++++++++++++-----
 oox/source/export/shapes.cxx                           |   53 +---
 sd/qa/unit/data/odp/tdf100348_FontworkBitmapFill.odp   |binary
 sd/qa/unit/data/odp/tdf100348_FontworkGradientGlow.odp |binary
 sd/qa/unit/export-tests-ooxml1.cxx                     |   37 +++
 8 files changed, 198 insertions(+), 97 deletions(-)

New commits:
commit 005f5db47b8e1bbd7ebddee92009be072e835fd5
Author:     Regina Henschel <rb.henschel at t-online.de>
AuthorDate: Sat Jul 4 15:11:03 2020 +0200
Commit:     Thorsten Behrens <Thorsten.Behrens at CIB.de>
CommitDate: Fri Jul 10 00:57:22 2020 +0200

    tdf#100348 add fill to fontwork in export to pptx
    
    This patch adds fill to the characters in a Fontwork shape in export
    to pptx. It does not contain export to docx and not import.
    
    Change-Id: Ie7c8a35380a845f513516636c4f60ee307eacd50
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/98187
    Tested-by: Jenkins
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx
index 439d817d0449..2760d2fe64a0 100644
--- a/include/oox/export/drawingml.hxx
+++ b/include/oox/export/drawingml.hxx
@@ -143,6 +143,7 @@ private:
     /// Parent exporter, used for text callback.
     DMLTextExport* mpTextExport;
 
+
 protected:
     css::uno::Any                             mAny;
     ::sax_fastparser::FSHelperPtr             mpFS;
@@ -166,6 +167,7 @@ protected:
     const char* GetRelationCompPrefix() const;
 
     static bool EqualGradients( css::awt::Gradient aGradient1, css::awt::Gradient aGradient2 );
+    bool IsFontworkShape(const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet);
 
     void WriteGlowEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet);
     void WriteSoftEdgeEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet);
@@ -244,17 +246,20 @@ public:
     void WriteTransformation(const tools::Rectangle& rRectangle,
                   sal_Int32 nXmlNamespace, bool bFlipH = false, bool bFlipV = false, sal_Int32 nRotation = 0, bool bIsGroupShape = false);
 
-    void WriteText( const css::uno::Reference< css::uno::XInterface >& rXIface, const OUString& presetWarp, bool bBodyPr, bool bText = true, sal_Int32 nXmlNamespace = 0);
+    void WriteText( const css::uno::Reference< css::uno::XInterface >& rXIface, bool bBodyPr, bool bText = true, sal_Int32 nXmlNamespace = 0);
     void WriteParagraph( const css::uno::Reference< css::text::XTextContent >& rParagraph,
-                         bool& rbOverridingCharHeight, sal_Int32& rnCharHeight );
+                         bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet);
     void WriteParagraphProperties(const css::uno::Reference< css::text::XTextContent >& rParagraph, float fFirstCharHeight);
     void WriteParagraphNumbering(const css::uno::Reference< css::beans::XPropertySet >& rXPropSet, float fFirstCharHeight,
                                   sal_Int16 nLevel );
     void WriteParagraphTabStops(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet);
     void WriteRun( const css::uno::Reference< css::text::XTextRange >& rRun,
-                   bool& rbOverridingCharHeight, sal_Int32& rnCharHeight );
+                   bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
+                   const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet);
     void WriteRunProperties( const css::uno::Reference< css::beans::XPropertySet >& rRun, bool bIsField, sal_Int32 nElement, bool bCheckDirect,
-                             bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, sal_Int16 nScriptType = css::i18n::ScriptType::LATIN);
+                             bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
+                             sal_Int16 nScriptType = css::i18n::ScriptType::LATIN,
+                             const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet = {});
 
     void WritePresetShape( const char* pShape , std::vector< std::pair<sal_Int32,sal_Int32>> & rAvList );
     void WritePresetShape( const char* pShape );
diff --git a/include/oox/export/shapes.hxx b/include/oox/export/shapes.hxx
index 53d505f168a2..e95af1eff83e 100644
--- a/include/oox/export/shapes.hxx
+++ b/include/oox/export/shapes.hxx
@@ -102,7 +102,6 @@ private:
 
     ShapeHashMap maShapeMap;
     ShapeHashMap* mpShapeMap;
-    OUString m_presetWarp;
 
 public:
 
diff --git a/oox/source/drawingml/customshapeproperties.cxx b/oox/source/drawingml/customshapeproperties.cxx
index 00ecf33368ae..1b4d6b4a59f6 100644
--- a/oox/source/drawingml/customshapeproperties.cxx
+++ b/oox/source/drawingml/customshapeproperties.cxx
@@ -157,6 +157,9 @@ void CustomShapeProperties::pushToPropSet(
         uno::Any aGeoPropSet = xPropSet->getPropertyValue( sCustomShapeGeometry );
         uno::Sequence< beans::PropertyValue > aGeoPropSeq;
 
+        // ToDo: Using sAdjustmentValues in this "if" looks nonsense.
+        // It was introduced in revision acd2c909, which introduced the property "PresetTextWarp"
+        // for interoperability with Word.
         if (aGeoPropSet >>= aGeoPropSeq)
         {
             for ( const auto& rGeoProp : std::as_const(aGeoPropSeq) )
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 052ea8f93090..847bee54ab97 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -115,6 +115,7 @@
 #include <svx/unoapi.hxx>
 #include <svx/unoshape.hxx>
 #include <svx/EnhancedCustomShape2d.hxx>
+#include <drawingml/presetgeometrynames.hxx>
 
 using namespace ::css;
 using namespace ::css::beans;
@@ -1694,8 +1695,9 @@ void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sa
             bFlipHWrite, bFlipVWrite, ExportRotateClockwisify(nRotation), IsGroupShape( rXShape ));
 }
 
-void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bIsField, sal_Int32 nElement, bool bCheckDirect,
-                                    bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, sal_Int16 nScriptType )
+void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bIsField, sal_Int32 nElement,
+                                    bool bCheckDirect,bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
+                                    sal_Int16 nScriptType, const Reference< XPropertySet >& rXShapePropSet)
 {
     Reference< XPropertySet > rXPropSet = rRun;
     Reference< XPropertyState > rXPropState( rRun, UNO_QUERY );
@@ -1899,31 +1901,42 @@ void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool
                           XML_baseline, sax_fastparser::UseIf(OString::number(nCharEscapement*1000), nCharEscapement != 0),
                           XML_cap, cap );
 
-    // mso doesn't like text color to be placed after typeface
-    if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharColor", eState)
-         && eState == beans::PropertyState_DIRECT_VALUE)
-        || GetProperty(rXPropSet, "CharColor"))
+    // Fontwork-shapes in LO have text outline and fill from shape stroke and shape fill
+    // PowerPoint has this as run properties
+    if (IsFontworkShape(rXShapePropSet))
     {
-        ::Color color( *o3tl::doAccess<sal_uInt32>(mAny) );
-        SAL_INFO("oox.shape", "run color: " << sal_uInt32(color) << " auto: " << sal_uInt32(COL_AUTO));
-
-        // WriteSolidFill() handles MAX_PERCENT as "no transparency".
-        sal_Int32 nTransparency = MAX_PERCENT;
-        if (rXPropSet->getPropertySetInfo()->hasPropertyByName("CharTransparence"))
+        WriteOutline(rXShapePropSet);
+        WriteBlipOrNormalFill(rXShapePropSet, "Graphic");
+        WriteShapeEffects(rXShapePropSet);
+    }
+    else
+    {
+        // mso doesn't like text color to be placed after typeface
+        if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharColor", eState)
+            && eState == beans::PropertyState_DIRECT_VALUE)
+            || GetProperty(rXPropSet, "CharColor"))
         {
-            rXPropSet->getPropertyValue("CharTransparence") >>= nTransparency;
-            // UNO scale is 0..100, OOXML scale is 0..100000; also UNO tracks transparency, OOXML
-            // tracks opacity.
-            nTransparency = MAX_PERCENT - (nTransparency * PER_PERCENT);
-        }
+            ::Color color( *o3tl::doAccess<sal_uInt32>(mAny) );
+            SAL_INFO("oox.shape", "run color: " << sal_uInt32(color) << " auto: " << sal_uInt32(COL_AUTO));
 
-        // tdf#104219 In LibreOffice and MS Office, there are two types of colors:
-        // Automatic and Fixed. OOXML is setting automatic color, by not providing color.
-        if( color != COL_AUTO )
-        {
-            color.SetTransparency(0);
-            // TODO: special handle embossed/engraved
-            WriteSolidFill(color, nTransparency);
+            // WriteSolidFill() handles MAX_PERCENT as "no transparency".
+            sal_Int32 nTransparency = MAX_PERCENT;
+            if (rXPropSet->getPropertySetInfo()->hasPropertyByName("CharTransparence"))
+            {
+                rXPropSet->getPropertyValue("CharTransparence") >>= nTransparency;
+                // UNO scale is 0..100, OOXML scale is 0..100000; also UNO tracks transparency, OOXML
+                // tracks opacity.
+                nTransparency = MAX_PERCENT - (nTransparency * PER_PERCENT);
+            }
+
+            // tdf#104219 In LibreOffice and MS Office, there are two types of colors:
+            // Automatic and Fixed. OOXML is setting automatic color, by not providing color.
+            if( color != COL_AUTO )
+            {
+                color.SetTransparency(0);
+                // TODO: special handle embossed/engraved
+                WriteSolidFill(color, nTransparency);
+            }
         }
     }
 
@@ -2136,7 +2149,8 @@ OUString DrawingML::GetFieldValue( const css::uno::Reference< css::text::XTextRa
 }
 
 void DrawingML::WriteRun( const Reference< XTextRange >& rRun,
-                          bool& rbOverridingCharHeight, sal_Int32& rnCharHeight)
+                          bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
+                          const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet)
 {
     Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
     sal_Int16 nLevel = -1;
@@ -2197,7 +2211,7 @@ void DrawingML::WriteRun( const Reference< XTextRange >& rRun,
 
         Reference< XPropertySet > xPropSet( rRun, uno::UNO_QUERY );
 
-        WriteRunProperties( xPropSet, bIsURLField, XML_rPr, true, rbOverridingCharHeight, rnCharHeight, GetScriptType(sText) );
+        WriteRunProperties( xPropSet, bIsURLField, XML_rPr, true, rbOverridingCharHeight, rnCharHeight, GetScriptType(sText), rXShapePropSet);
         mpFS->startElementNS(XML_a, XML_t);
         mpFS->writeEscaped( sText );
         mpFS->endElementNS( XML_a, XML_t );
@@ -2669,7 +2683,8 @@ void DrawingML::WriteParagraphProperties( const Reference< XTextContent >& rPara
 }
 
 void DrawingML::WriteParagraph( const Reference< XTextContent >& rParagraph,
-                                bool& rbOverridingCharHeight, sal_Int32& rnCharHeight )
+                                bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
+                                const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet)
 {
     Reference< XEnumerationAccess > access( rParagraph, UNO_QUERY );
     if( !access.is() )
@@ -2699,26 +2714,60 @@ void DrawingML::WriteParagraph( const Reference< XTextContent >& rParagraph,
                 WriteParagraphProperties( rParagraph, fFirstCharHeight );
                 bPropertiesWritten = true;
             }
-            WriteRun( run, rbOverridingCharHeight, rnCharHeight );
+            WriteRun( run, rbOverridingCharHeight, rnCharHeight, rXShapePropSet);
         }
     }
     Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
-    WriteRunProperties( rXPropSet, false, XML_endParaRPr, false, rbOverridingCharHeight, rnCharHeight );
+    sal_Int16 nDummy = -1;
+    WriteRunProperties(rXPropSet, false, XML_endParaRPr, false, rbOverridingCharHeight,
+                       rnCharHeight, nDummy, rXShapePropSet);
 
     mpFS->endElementNS( XML_a, XML_p );
 }
 
-void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUString& presetWarp, bool bBodyPr, bool bText, sal_Int32 nXmlNamespace )
+bool DrawingML::IsFontworkShape(const css::uno::Reference<css::beans::XPropertySet>& rXShapePropSet)
 {
-    Reference< XText > xXText( rXIface, UNO_QUERY );
-    Reference< XPropertySet > rXPropSet( rXIface, UNO_QUERY );
+    bool bResult(false);
+    if (rXShapePropSet.is())
+    {
+        Sequence<PropertyValue> aCustomShapeGeometryProps;
+        if (GetProperty(rXShapePropSet, "CustomShapeGeometry"))
+        {
+            mAny >>= aCustomShapeGeometryProps;
+            uno::Sequence<beans::PropertyValue> aTextPathSeq;
+            for (const auto& rProp : std::as_const(aCustomShapeGeometryProps))
+            {
+                if (rProp.Name == "TextPath")
+                {
+                    rProp.Value >>= aTextPathSeq;
+                    for (const auto& rTextPathItem : std::as_const(aTextPathSeq))
+                    {
+                        if (rTextPathItem.Name == "TextPath")
+                        {
+                            rTextPathItem.Value >>= bResult;
+                            break;
+                        }
+                    }
+                    break;
+                }
+            }
+        }
+    }
+    return bResult;
+}
 
+void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bool bText,
+                           sal_Int32 nXmlNamespace)
+{
+    // ToDo: Fontwork in DOCX
+    Reference< XText > xXText( rXIface, UNO_QUERY );
     if( !xXText.is() )
         return;
 
+    Reference< XPropertySet > rXPropSet( rXIface, UNO_QUERY );
+
     sal_Int32 nTextPreRotateAngle = 0;
     double nTextRotateAngle = 0;
-    bool bIsFontworkShape(presetWarp.startsWith("text") && (presetWarp != "textNoShape"));
 
 #define DEFLRINS 254
 #define DEFTBINS 127
@@ -2757,9 +2806,13 @@ void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUStrin
         }
     }
 
+    bool bIsFontworkShape(IsFontworkShape(rXPropSet));
     Sequence<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentSeq;
     uno::Sequence<beans::PropertyValue> aTextPathSeq;
     bool bScaleX(false);
+    OUString sShapeType("non-primitive");
+    // ToDo move to InteropGrabBag
+    OUString sMSWordPresetTextWarp;
 
     if (GetProperty(rXPropSet, "CustomShapeGeometry"))
     {
@@ -2780,29 +2833,35 @@ void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUStrin
                         sWritingMode = "vert270";
                         bVertical = true;
                     }
-                    if (!bIsFontworkShape)
-                        break;
                 }
                 else if (rProp.Name == "AdjustmentValues")
                     rProp.Value >>= aAdjustmentSeq;
                 else if( rProp.Name == "TextRotateAngle" )
                     rProp.Value >>= nTextRotateAngle;
+                else if (rProp.Name == "Type")
+                    rProp.Value >>= sShapeType;
                 else if (rProp.Name == "TextPath")
                 {
                     rProp.Value >>= aTextPathSeq;
-                    for (const auto& rTextPath : std::as_const(aTextPathSeq))
+                    for (const auto& rTextPathItem : std::as_const(aTextPathSeq))
                     {
-                        if (rTextPath.Name == "ScaleX")
-                            rTextPath.Value >>= bScaleX;
+                        if (rTextPathItem.Name == "ScaleX")
+                            rTextPathItem.Value >>= bScaleX;
                     }
                 }
+                else if (rProp.Name == "PresetTextWarp")
+                    rProp.Value >>= sMSWordPresetTextWarp;
             }
         }
     }
+    OUString sPresetWarp(PresetGeometryTypeNames::GetMsoName(sShapeType));
+    // ODF may have user defined TextPath, use "textPlain" as ersatz.
+    if (sPresetWarp.isEmpty())
+        sPresetWarp = bIsFontworkShape ? OUStringLiteral("textPlain") : OUStringLiteral("textNoShape");
 
     bool bFromWordArt = !bScaleX
-                        && ( presetWarp == "textArchDown" || presetWarp == "textArchUp"
-                            || presetWarp == "textButton" || presetWarp == "textCircle");
+                        && ( sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp"
+                            || sPresetWarp == "textButton" || sPresetWarp == "textCircle");
 
     TextHorizontalAdjust eHorizontalAlignment( TextHorizontalAdjust_CENTER );
     bool bHorizontalCenter = false;
@@ -2824,7 +2883,7 @@ void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUStrin
 
     if (bBodyPr)
     {
-        const char* pWrap = bHasWrap && !bWrap ? "none" : nullptr;
+        const char* pWrap = (bHasWrap && !bWrap) || bIsFontworkShape ? "none" : nullptr;
         if (GetDocumentType() == DOCUMENT_DOCX)
         {
             // In case of DOCX, if we want to have the same effect as
@@ -2849,11 +2908,16 @@ void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUStrin
         {
             if (aAdjustmentSeq.hasElements())
             {
-                mpFS->startElementNS(XML_a, XML_prstTxWarp, XML_prst, presetWarp);
+                mpFS->startElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp);
                 mpFS->startElementNS(XML_a, XML_avLst);
+                bool bHasTwoHandles(
+                    sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour"
+                    || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour"
+                    || sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1"
+                    || sPresetWarp == "textWave2" || sPresetWarp == "textWave4");
                 for (sal_Int32 i = 0, nElems = aAdjustmentSeq.getLength(); i < nElems; ++i )
                 {
-                    OString sName = "adj" + (( nElems > 1 ) ? OString::number(i + 1) : OString());
+                    OString sName = "adj" + (bHasTwoHandles ? OString::number(i + 1) : OString());
                     double fValue(0.0);
                     if (aAdjustmentSeq[i].Value.getValueTypeClass() == TypeClass_DOUBLE)
                         aAdjustmentSeq[i].Value >>= fValue;
@@ -2866,20 +2930,27 @@ void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUStrin
                     // Convert from binary coordinate system with viewBox "0 0 21600 21600" and simple degree
                     // to DrawingML with coordinate range 0..100000 and angle in 1/60000 degree.
                     // Reverse to conversion in lcl_createPresetShape in drawingml/shape.cxx on import.
-                    if (presetWarp == "textArchDown" || presetWarp == "textArchUp"
-                        || presetWarp == "textButton" || presetWarp == "textCircle"
-                        || ((i == 0) && (presetWarp == "textArchDownPour" || presetWarp == "textArchUpPour"
-                        || presetWarp == "textButtonPour" || presetWarp == "textCirclePour")))
+                    if (sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp"
+                        || sPresetWarp == "textButton" || sPresetWarp == "textCircle"
+                        || ((i == 0)
+                            && (sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour"
+                                || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour")))
                     {
                         fValue *= 60000.0;
+                        if (fValue < 0)
+                            fValue += 21600000;
                     }
-                    else if ((i == 1) && (presetWarp == "textDoubleWave1" || presetWarp == "textWave1"
-                            || presetWarp == "textWave2" || presetWarp == "textWave4"))
+                    else if ((i == 1)
+                             && (sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1"
+                            || sPresetWarp == "textWave2" || sPresetWarp == "textWave4"))
                     {
                         fValue = fValue / 0.216 - 50000.0;
                     }
-                    else if ((i == 1) && (presetWarp == "textArchDownPour" || presetWarp == "textArchUpPour"
-                        || presetWarp == "textButtonPour" || presetWarp == "textCirclePour"))
+                    else if ((i == 1)
+                             && (sPresetWarp == "textArchDownPour"
+                                 || sPresetWarp == "textArchUpPour"
+                                 || sPresetWarp == "textButtonPour"
+                                 || sPresetWarp == "textCirclePour"))
                     {
                         fValue /= 0.108;
                     }
@@ -2889,15 +2960,24 @@ void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUStrin
                     }
                     OString sFmla = "val " + OString::number(std::lround(fValue));
                     mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
+                    // There exists faulty Favorite shapes with one handle but two adjustment values.
+                    if (!bHasTwoHandles)
+                        break;
                 }
-                mpFS->endElementNS( XML_a, XML_avLst );
+                mpFS->endElementNS(XML_a, XML_avLst);
                 mpFS->endElementNS(XML_a, XML_prstTxWarp);
             }
             else
             {
-                mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, presetWarp);
+                mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp);
             }
         }
+        else if (GetDocumentType() == DOCUMENT_DOCX)
+        {
+            // interim solution for fdo#80897, roundtrip DOCX > LO > DOCX
+            if (!sMSWordPresetTextWarp.isEmpty())
+                mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sMSWordPresetTextWarp);
+        }
 
         if (GetDocumentType() == DOCUMENT_DOCX || GetDocumentType() == DOCUMENT_XLSX)
         {
@@ -2995,7 +3075,7 @@ void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUStrin
         Any any ( enumeration->nextElement() );
 
         if( any >>= paragraph)
-            WriteParagraph( paragraph, bOverridingCharHeight, nCharHeight );
+            WriteParagraph( paragraph, bOverridingCharHeight, nCharHeight, rXPropSet );
     }
 }
 
diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx
index 5729c9d27a66..c1982961537b 100644
--- a/oox/source/export/shapes.cxx
+++ b/oox/source/export/shapes.cxx
@@ -686,11 +686,16 @@ static sal_Int32 lcl_CircleAngle2CustomShapeEllipseAngleOOX(const sal_Int32 nInt
 
 ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
 {
-    // First check, if this is a Fontwork-shape. For DrawingML, such a shape is a
-    // TextBox shape with body property prstTxWarp.
     SAL_INFO("oox.shape", "write custom shape");
     Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
-    bool bIsFontworkShape(false);
+    // First check, if this is a Fontwork-shape. For DrawingML, such a shape is a
+    // TextBox shape with body property prstTxWarp.
+    if (IsFontworkShape(rXPropSet))
+    {
+        ShapeExport::WriteTextShape(xShape); // qualifier to prevent PowerPointShapeExport
+        return *this;
+    }
+
     bool bHasGeometrySeq(false);
     Sequence< PropertyValue > aGeometrySeq;
     OUString sShapeType;
@@ -704,32 +709,11 @@ ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
             for (const PropertyValue& rProp : std::as_const(aGeometrySeq))
             {
                 SAL_INFO("oox.shape", "geometry property: " << rProp.Name);
-                if (rProp.Name == "TextPath")
-                {
-                    uno::Sequence<beans::PropertyValue> aTextPathSeq;
-                    rProp.Value >>= aTextPathSeq;
-                    for (const PropertyValue& rTextProp : std::as_const(aTextPathSeq))
-                    {
-                        if (rTextProp.Name == "TextPath")
-                        {
-                            rTextProp.Value >>= bIsFontworkShape;
-                        }
-                    }
-                }
-                else if (rProp.Name == "Type")
+                if (rProp.Name == "Type")
                     rProp.Value >>= sShapeType;
             }
         }
     }
-    if (bIsFontworkShape)
-    {
-        // write the correct type to m_presetWarp, WriteTextShape() needs it
-        // to set TextWarp.
-        m_presetWarp = PresetGeometryTypeNames::GetMsoName(sShapeType);
-        ShapeExport::WriteTextShape(xShape); // qualifier to prevent PowerPointShapeExport
-        return *this;
-    }
-
 
     bool bPredefinedHandlesUsed = true;
     bool bHasHandles = false;
@@ -752,9 +736,6 @@ ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
     bool bFlipH = false;
     bool bFlipV = false;
 
-    // Avoid interference of preset type to the next shape
-    m_presetWarp = "";
-
     if (bHasGeometrySeq)
     {
         for (int i = 0; i < aGeometrySeq.getLength(); i++)
@@ -778,10 +759,6 @@ ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
                         bPredefinedHandlesUsed = false;
                     // TODO: update nAdjustmentsWhichNeedsToBeConverted here
                 }
-                else if ( rProp.Name == "PresetTextWarp" )
-                {
-                    rProp.Value >>= m_presetWarp;
-                }
                 else if ( rProp.Name == "ViewBox" )
                     rProp.Value >>= aViewBox;
         }
@@ -1556,7 +1533,7 @@ ShapeExport& ShapeExport::WriteTextBox( const Reference< XInterface >& xIface, s
             if (xPropertySetInfo->hasPropertyByName("TextBox") && xPropertySet->getPropertyValue("TextBox").get<bool>())
             {
                 GetTextExport()->WriteTextBox(uno::Reference<drawing::XShape>(xIface, uno::UNO_QUERY_THROW));
-                WriteText( xIface, m_presetWarp, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
+                WriteText( xIface, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
                 return *this;
             }
         }
@@ -1569,10 +1546,10 @@ ShapeExport& ShapeExport::WriteTextBox( const Reference< XInterface >& xIface, s
 
         pFS->startElementNS(nXmlNamespace,
                             (GetDocumentType() != DOCUMENT_DOCX ? XML_txBody : XML_txbx));
-        WriteText( xIface, m_presetWarp, /*bBodyPr=*/(GetDocumentType() != DOCUMENT_DOCX) );
+        WriteText( xIface, /*bBodyPr=*/(GetDocumentType() != DOCUMENT_DOCX) );
         pFS->endElementNS( nXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_txBody : XML_txbx) );
         if (GetDocumentType() == DOCUMENT_DOCX)
-            WriteText( xIface, m_presetWarp, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
+            WriteText( xIface, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
     }
     else if (GetDocumentType() == DOCUMENT_DOCX)
         mpFS->singleElementNS(nXmlNamespace, XML_bodyPr);
@@ -1859,9 +1836,9 @@ ShapeExport& ShapeExport::WriteTableShape( const Reference< XShape >& xShape )
 
 ShapeExport& ShapeExport::WriteTextShape( const Reference< XShape >& xShape )
 {
-    bool bIsFontworkShape(m_presetWarp.startsWith("text") && m_presetWarp != "textNoShape");
     FSHelperPtr pFS = GetFS();
     Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
+
     pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp));
 
     // non visual shape properties
@@ -1898,12 +1875,12 @@ ShapeExport& ShapeExport::WriteTextShape( const Reference< XShape >& xShape )
     WriteShapeTransformation( xShape, XML_a );
     WritePresetShape( "rect" );
     uno::Reference<beans::XPropertySet> xPropertySet(xShape, UNO_QUERY);
-    if (!bIsFontworkShape) // Fontwork needs fill and outline on char instead.
+    if (!IsFontworkShape(xShapeProps)) // Fontwork needs fill and outline in run properties instead.
     {
         WriteBlipOrNormalFill(xPropertySet, "Graphic");
         WriteOutline(xPropertySet);
+        WriteShapeEffects(xPropertySet);
     }
-    WriteShapeEffects(xPropertySet);
     pFS->endElementNS( mnXmlNamespace, XML_spPr );
 
     WriteTextBox( xShape, mnXmlNamespace );
diff --git a/sd/qa/unit/data/odp/tdf100348_FontworkBitmapFill.odp b/sd/qa/unit/data/odp/tdf100348_FontworkBitmapFill.odp
new file mode 100644
index 000000000000..be3d17c21c12
Binary files /dev/null and b/sd/qa/unit/data/odp/tdf100348_FontworkBitmapFill.odp differ
diff --git a/sd/qa/unit/data/odp/tdf100348_FontworkGradientGlow.odp b/sd/qa/unit/data/odp/tdf100348_FontworkGradientGlow.odp
new file mode 100644
index 000000000000..e2c5f1e887f9
Binary files /dev/null and b/sd/qa/unit/data/odp/tdf100348_FontworkGradientGlow.odp differ
diff --git a/sd/qa/unit/export-tests-ooxml1.cxx b/sd/qa/unit/export-tests-ooxml1.cxx
index f7cec3010dc7..57112261a4af 100644
--- a/sd/qa/unit/export-tests-ooxml1.cxx
+++ b/sd/qa/unit/export-tests-ooxml1.cxx
@@ -91,6 +91,8 @@ public:
     void testRoundtripPrstDash();
     void testDashOnHairline();
     void testCustomshapeBitmapfillSrcrect();
+    void testTdf100348FontworkBitmapFill();
+    void testTdf100348FontworkGradientGlow();
 
     CPPUNIT_TEST_SUITE(SdOOXMLExportTest1);
 
@@ -131,6 +133,8 @@ public:
     CPPUNIT_TEST(testRoundtripPrstDash);
     CPPUNIT_TEST(testDashOnHairline);
     CPPUNIT_TEST(testCustomshapeBitmapfillSrcrect);
+    CPPUNIT_TEST(testTdf100348FontworkBitmapFill);
+    CPPUNIT_TEST(testTdf100348FontworkGradientGlow);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -1095,6 +1099,39 @@ void SdOOXMLExportTest1::testCustomshapeBitmapfillSrcrect()
     CPPUNIT_ASSERT_EQUAL(4.0, fRightPercent);
 }
 
+void SdOOXMLExportTest1::testTdf100348FontworkBitmapFill()
+{
+    ::sd::DrawDocShellRef xDocShRef
+        = loadURL(m_directories.getURLFromSrc("sd/qa/unit/data/odp/tdf100348_FontworkBitmapFill.odp"), ODP);
+    utl::TempFile tempFile;
+    xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile);
+    xDocShRef->DoClose();
+
+    // Make sure the fontwork shape has a blip bitmap fill and a colored outline.
+    // Without the patch, fill and outline were black.
+    xmlDocUniquePtr pXmlDoc = parseExport(tempFile, "ppt/slides/slide1.xml");
+    const OString sPathStart("//p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr");
+    assertXPath(pXmlDoc, sPathStart + "/a:blipFill/a:blip", 1);
+    assertXPath(pXmlDoc, sPathStart + "/a:ln/a:solidFill/a:srgbClr", "val", "ffbf00");
+}
+
+void SdOOXMLExportTest1::testTdf100348FontworkGradientGlow()
+{
+    ::sd::DrawDocShellRef xDocShRef
+        = loadURL(m_directories.getURLFromSrc("sd/qa/unit/data/odp/tdf100348_FontworkGradientGlow.odp"), ODP);
+    utl::TempFile tempFile;
+    xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile);
+    xDocShRef->DoClose();
+
+    // Make sure the fontwork shape has a gradient fill and a colored glow.
+    // Without the patch, fill was black and no glow applied.
+    xmlDocUniquePtr pXmlDoc = parseExport(tempFile, "ppt/slides/slide1.xml");
+    const OString sPathStart("//p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr");
+    assertXPath(pXmlDoc, sPathStart + "/a:gradFill/a:gsLst/a:gs[1]/a:srgbClr", "val", "8d281e");
+    assertXPath(pXmlDoc, sPathStart + "/a:effectLst/a:glow", "rad", "63360");
+    assertXPath(pXmlDoc, sPathStart + "/a:effectLst/a:glow/a:srgbClr", "val", "ff4500");
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SdOOXMLExportTest1);
 
 CPPUNIT_PLUGIN_IMPLEMENT();


More information about the Libreoffice-commits mailing list