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& rXPropSet); void WriteSoftEdgeEffect(const css::uno::Reference& 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& 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> & 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 #include #include +#include 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(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(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& rXShapePropSet) { - Reference< XText > xXText( rXIface, UNO_QUERY ); - Reference< XPropertySet > rXPropSet( rXIface, UNO_QUERY ); + bool bResult(false); + if (rXShapePropSet.is()) + { + Sequence aCustomShapeGeometryProps; + if (GetProperty(rXShapePropSet, "CustomShapeGeometry")) + { + mAny >>= aCustomShapeGeometryProps; + uno::Sequence 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& 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 aAdjustmentSeq; uno::Sequence 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 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()) { GetTextExport()->WriteTextBox(uno::Reference(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 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 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();