1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <config_features.h> 21 22 #include <config_folders.h> 23 #include <rtl/bootstrap.hxx> 24 #include <sal/log.hxx> 25 #include <oox/core/xmlfilterbase.hxx> 26 #include <oox/export/drawingml.hxx> 27 #include <oox/export/utils.hxx> 28 #include <oox/helper/propertyset.hxx> 29 #include <oox/drawingml/color.hxx> 30 #include <drawingml/fillproperties.hxx> 31 #include <drawingml/textparagraph.hxx> 32 #include <oox/token/namespaces.hxx> 33 #include <oox/token/properties.hxx> 34 #include <oox/token/relationship.hxx> 35 #include <oox/token/tokens.hxx> 36 #include <oox/drawingml/drawingmltypes.hxx> 37 #include <svtools/unitconv.hxx> 38 #include <sax/fastattribs.hxx> 39 #include <tools/diagnose_ex.h> 40 #include <comphelper/processfactory.hxx> 41 #include <i18nlangtag/languagetag.hxx> 42 43 #include <numeric> 44 #include <string_view> 45 46 #include <com/sun/star/awt/CharSet.hpp> 47 #include <com/sun/star/awt/FontDescriptor.hpp> 48 #include <com/sun/star/awt/FontSlant.hpp> 49 #include <com/sun/star/awt/FontStrikeout.hpp> 50 #include <com/sun/star/awt/FontWeight.hpp> 51 #include <com/sun/star/awt/FontUnderline.hpp> 52 #include <com/sun/star/awt/Gradient.hpp> 53 #include <com/sun/star/beans/XPropertySet.hpp> 54 #include <com/sun/star/beans/XPropertyState.hpp> 55 #include <com/sun/star/beans/XPropertySetInfo.hpp> 56 #include <com/sun/star/container/XEnumerationAccess.hpp> 57 #include <com/sun/star/container/XIndexAccess.hpp> 58 #include <com/sun/star/container/XNameAccess.hpp> 59 #include <com/sun/star/drawing/BitmapMode.hpp> 60 #include <com/sun/star/drawing/ColorMode.hpp> 61 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp> 62 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp> 63 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp> 64 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp> 65 #include <com/sun/star/drawing/Hatch.hpp> 66 #include <com/sun/star/drawing/LineDash.hpp> 67 #include <com/sun/star/drawing/LineJoint.hpp> 68 #include <com/sun/star/drawing/LineStyle.hpp> 69 #include <com/sun/star/drawing/TextFitToSizeType.hpp> 70 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp> 71 #include <com/sun/star/drawing/TextVerticalAdjust.hpp> 72 #include <com/sun/star/drawing/XShape.hpp> 73 #include <com/sun/star/drawing/XShapes.hpp> 74 #include <com/sun/star/drawing/FillStyle.hpp> 75 #include <com/sun/star/frame/XModel.hpp> 76 #include <com/sun/star/graphic/XGraphic.hpp> 77 #include <com/sun/star/i18n/ScriptType.hpp> 78 #include <com/sun/star/i18n/BreakIterator.hpp> 79 #include <com/sun/star/i18n/XBreakIterator.hpp> 80 #include <com/sun/star/io/XOutputStream.hpp> 81 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 82 #include <com/sun/star/style/LineSpacing.hpp> 83 #include <com/sun/star/style/LineSpacingMode.hpp> 84 #include <com/sun/star/text/WritingMode.hpp> 85 #include <com/sun/star/text/WritingMode2.hpp> 86 #include <com/sun/star/text/GraphicCrop.hpp> 87 #include <com/sun/star/text/XText.hpp> 88 #include <com/sun/star/text/XTextContent.hpp> 89 #include <com/sun/star/text/XTextField.hpp> 90 #include <com/sun/star/text/XTextRange.hpp> 91 #include <com/sun/star/text/XTextFrame.hpp> 92 #include <com/sun/star/style/CaseMap.hpp> 93 #include <com/sun/star/xml/dom/XNodeList.hpp> 94 #include <com/sun/star/xml/sax/Writer.hpp> 95 #include <com/sun/star/xml/sax/XSAXSerializable.hpp> 96 #include <com/sun/star/container/XNamed.hpp> 97 #include <com/sun/star/drawing/XDrawPages.hpp> 98 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp> 99 100 #include <comphelper/random.hxx> 101 #include <comphelper/seqstream.hxx> 102 #include <comphelper/storagehelper.hxx> 103 #include <comphelper/xmltools.hxx> 104 #include <o3tl/any.hxx> 105 #include <o3tl/safeint.hxx> 106 #include <tools/stream.hxx> 107 #include <unotools/fontdefs.hxx> 108 #include <vcl/cvtgrf.hxx> 109 #include <vcl/graph.hxx> 110 #include <vcl/svapp.hxx> 111 #include <rtl/strbuf.hxx> 112 #include <filter/msfilter/escherex.hxx> 113 #include <filter/msfilter/util.hxx> 114 #include <editeng/outlobj.hxx> 115 #include <editeng/svxenum.hxx> 116 #include <editeng/unonames.hxx> 117 #include <editeng/unoprnms.hxx> 118 #include <editeng/flditem.hxx> 119 #include <svx/svdoashp.hxx> 120 #include <svx/svdomedia.hxx> 121 #include <svx/unoapi.hxx> 122 #include <svx/unoshape.hxx> 123 #include <svx/EnhancedCustomShape2d.hxx> 124 #include <drawingml/presetgeometrynames.hxx> 125 126 using namespace ::css; 127 using namespace ::css::beans; 128 using namespace ::css::drawing; 129 using namespace ::css::i18n; 130 using namespace ::css::style; 131 using namespace ::css::text; 132 using namespace ::css::uno; 133 using namespace ::css::container; 134 135 using ::css::io::XOutputStream; 136 using ::sax_fastparser::FSHelperPtr; 137 using ::sax_fastparser::FastSerializerHelper; 138 139 namespace 140 { 141 /// Extracts start or end alpha information from a transparency gradient. 142 sal_Int32 GetAlphaFromTransparenceGradient(const awt::Gradient& rGradient, bool bStart) 143 { 144 // Our alpha is a gray color value. 145 sal_uInt8 nRed = ::Color(ColorTransparency, bStart ? rGradient.StartColor : rGradient.EndColor).GetRed(); 146 // drawingML alpha is a percentage on a 0..100000 scale. 147 return (255 - nRed) * oox::drawingml::MAX_PERCENT / 255; 148 } 149 } 150 151 namespace oox::drawingml { 152 153 URLTransformer::~URLTransformer() 154 { 155 } 156 157 OUString URLTransformer::getTransformedString(const OUString& rString) const 158 { 159 return rString; 160 } 161 162 bool URLTransformer::isExternalURL(const OUString& rURL) const 163 { 164 bool bExternal = true; 165 if (rURL.startsWith("#")) 166 bExternal = false; 167 return bExternal; 168 } 169 170 static css::uno::Any getLineDash( const css::uno::Reference<css::frame::XModel>& xModel, const OUString& rDashName ) 171 { 172 css::uno::Reference<css::lang::XMultiServiceFactory> xFact(xModel, css::uno::UNO_QUERY); 173 css::uno::Reference<css::container::XNameAccess> xNameAccess( 174 xFact->createInstance("com.sun.star.drawing.DashTable"), 175 css::uno::UNO_QUERY ); 176 if(xNameAccess.is()) 177 { 178 if (!xNameAccess->hasByName(rDashName)) 179 return css::uno::Any(); 180 181 return xNameAccess->getByName(rDashName); 182 } 183 184 return css::uno::Any(); 185 } 186 187 namespace 188 { 189 void WriteGradientPath(const awt::Gradient& rGradient, const FSHelperPtr& pFS, const bool bCircle) 190 { 191 pFS->startElementNS(XML_a, XML_path, XML_path, bCircle ? "circle" : "rect"); 192 193 // Write the focus rectangle. Work with the focus point, and assume 194 // that it extends 50% in all directions. The below 195 // left/top/right/bottom values are percentages, where 0 means the 196 // edge of the tile rectangle and 100% means the center of it. 197 rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList( 198 sax_fastparser::FastSerializerHelper::createAttrList()); 199 sal_Int32 nLeftPercent = rGradient.XOffset; 200 pAttributeList->add(XML_l, OString::number(nLeftPercent * PER_PERCENT)); 201 sal_Int32 nTopPercent = rGradient.YOffset; 202 pAttributeList->add(XML_t, OString::number(nTopPercent * PER_PERCENT)); 203 sal_Int32 nRightPercent = 100 - rGradient.XOffset; 204 pAttributeList->add(XML_r, OString::number(nRightPercent * PER_PERCENT)); 205 sal_Int32 nBottomPercent = 100 - rGradient.YOffset; 206 pAttributeList->add(XML_b, OString::number(nBottomPercent * PER_PERCENT)); 207 pFS->singleElementNS(XML_a, XML_fillToRect, pAttributeList); 208 209 pFS->endElementNS(XML_a, XML_path); 210 } 211 } 212 213 // not thread safe 214 int DrawingML::mnImageCounter = 1; 215 int DrawingML::mnWdpImageCounter = 1; 216 std::map<OUString, OUString> DrawingML::maWdpCache; 217 sal_Int32 DrawingML::mnDrawingMLCount = 0; 218 sal_Int32 DrawingML::mnVmlCount = 0; 219 220 sal_Int16 DrawingML::GetScriptType(const OUString& rStr) 221 { 222 if (rStr.getLength() > 0) 223 { 224 static Reference<css::i18n::XBreakIterator> xBreakIterator = 225 css::i18n::BreakIterator::create(comphelper::getProcessComponentContext()); 226 227 sal_Int16 nScriptType = xBreakIterator->getScriptType(rStr, 0); 228 229 if (nScriptType == css::i18n::ScriptType::WEAK) 230 { 231 sal_Int32 nPos = xBreakIterator->nextScript(rStr, 0, nScriptType); 232 if (nPos < rStr.getLength()) 233 nScriptType = xBreakIterator->getScriptType(rStr, nPos); 234 235 } 236 237 if (nScriptType != css::i18n::ScriptType::WEAK) 238 return nScriptType; 239 } 240 241 return css::i18n::ScriptType::LATIN; 242 } 243 244 void DrawingML::ResetCounters() 245 { 246 mnImageCounter = 1; 247 mnWdpImageCounter = 1; 248 maWdpCache.clear(); 249 } 250 251 void DrawingML::ResetMlCounters() 252 { 253 mnDrawingMLCount = 0; 254 mnVmlCount = 0; 255 } 256 257 bool DrawingML::GetProperty( const Reference< XPropertySet >& rXPropertySet, const OUString& aName ) 258 { 259 try 260 { 261 mAny = rXPropertySet->getPropertyValue(aName); 262 if (mAny.hasValue()) 263 return true; 264 } 265 catch( const Exception& ) 266 { 267 /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */ 268 } 269 return false; 270 } 271 272 bool DrawingML::GetPropertyAndState( const Reference< XPropertySet >& rXPropertySet, const Reference< XPropertyState >& rXPropertyState, const OUString& aName, PropertyState& eState ) 273 { 274 try 275 { 276 mAny = rXPropertySet->getPropertyValue(aName); 277 if (mAny.hasValue()) 278 { 279 eState = rXPropertyState->getPropertyState(aName); 280 return true; 281 } 282 } 283 catch( const Exception& ) 284 { 285 /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */ 286 } 287 return false; 288 } 289 290 namespace 291 { 292 /// Gets hexa value of color on string format. 293 OString getColorStr(const ::Color nColor) 294 { 295 // Transparency is a separate element. 296 OString sColor = OString::number(sal_uInt32(nColor) & 0x00FFFFFF, 16); 297 if (sColor.getLength() < 6) 298 { 299 OStringBuffer sBuf("0"); 300 int remains = 5 - sColor.getLength(); 301 302 while (remains > 0) 303 { 304 sBuf.append("0"); 305 remains--; 306 } 307 308 sBuf.append(sColor); 309 310 sColor = sBuf.getStr(); 311 } 312 return sColor; 313 } 314 } 315 316 void DrawingML::WriteColor( ::Color nColor, sal_Int32 nAlpha ) 317 { 318 const auto sColor = getColorStr(nColor); 319 if( nAlpha < MAX_PERCENT ) 320 { 321 mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor); 322 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha)); 323 mpFS->endElementNS( XML_a, XML_srgbClr ); 324 325 } 326 else 327 { 328 mpFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor); 329 } 330 } 331 332 void DrawingML::WriteColor( const OUString& sColorSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha ) 333 { 334 // prevent writing a tag with empty val attribute 335 if( sColorSchemeName.isEmpty() ) 336 return; 337 338 if( aTransformations.hasElements() ) 339 { 340 mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName); 341 WriteColorTransformations( aTransformations, nAlpha ); 342 mpFS->endElementNS( XML_a, XML_schemeClr ); 343 } 344 else if(nAlpha < MAX_PERCENT) 345 { 346 mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName); 347 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha)); 348 mpFS->endElementNS( XML_a, XML_schemeClr ); 349 } 350 else 351 { 352 mpFS->singleElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName); 353 } 354 } 355 356 void DrawingML::WriteColor( const ::Color nColor, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha ) 357 { 358 const auto sColor = getColorStr(nColor); 359 if( aTransformations.hasElements() ) 360 { 361 mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor); 362 WriteColorTransformations(aTransformations, nAlpha); 363 mpFS->endElementNS(XML_a, XML_srgbClr); 364 } 365 else if(nAlpha < MAX_PERCENT) 366 { 367 mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor); 368 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha)); 369 mpFS->endElementNS(XML_a, XML_srgbClr); 370 } 371 else 372 { 373 mpFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor); 374 } 375 } 376 377 void DrawingML::WriteColorTransformations( const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha ) 378 { 379 for( const auto& rTransformation : aTransformations ) 380 { 381 sal_Int32 nToken = Color::getColorTransformationToken( rTransformation.Name ); 382 if( nToken != XML_TOKEN_INVALID && rTransformation.Value.hasValue() ) 383 { 384 if(nToken == XML_alpha && nAlpha < MAX_PERCENT) 385 { 386 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nAlpha)); 387 } 388 else 389 { 390 sal_Int32 nValue = rTransformation.Value.get<sal_Int32>(); 391 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nValue)); 392 } 393 } 394 } 395 } 396 397 void DrawingML::WriteSolidFill( ::Color nColor, sal_Int32 nAlpha ) 398 { 399 mpFS->startElementNS(XML_a, XML_solidFill); 400 WriteColor( nColor, nAlpha ); 401 mpFS->endElementNS( XML_a, XML_solidFill ); 402 } 403 404 void DrawingML::WriteSolidFill( const OUString& sSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha ) 405 { 406 mpFS->startElementNS(XML_a, XML_solidFill); 407 WriteColor( sSchemeName, aTransformations, nAlpha ); 408 mpFS->endElementNS( XML_a, XML_solidFill ); 409 } 410 411 void DrawingML::WriteSolidFill( const ::Color nColor, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha ) 412 { 413 mpFS->startElementNS(XML_a, XML_solidFill); 414 WriteColor(nColor, aTransformations, nAlpha); 415 mpFS->endElementNS(XML_a, XML_solidFill); 416 } 417 418 void DrawingML::WriteSolidFill( const Reference< XPropertySet >& rXPropSet ) 419 { 420 // get fill color 421 if ( !GetProperty( rXPropSet, "FillColor" ) ) 422 return; 423 sal_uInt32 nFillColor = mAny.get<sal_uInt32>(); 424 425 // get InteropGrabBag and search the relevant attributes 426 OUString sColorFillScheme; 427 sal_uInt32 nOriginalColor = 0; 428 Sequence< PropertyValue > aStyleProperties, aTransformations; 429 if ( GetProperty( rXPropSet, "InteropGrabBag" ) ) 430 { 431 Sequence< PropertyValue > aGrabBag; 432 mAny >>= aGrabBag; 433 for( const auto& rProp : std::as_const(aGrabBag) ) 434 { 435 if( rProp.Name == "SpPrSolidFillSchemeClr" ) 436 rProp.Value >>= sColorFillScheme; 437 else if( rProp.Name == "OriginalSolidFillClr" ) 438 rProp.Value >>= nOriginalColor; 439 else if( rProp.Name == "StyleFillRef" ) 440 rProp.Value >>= aStyleProperties; 441 else if( rProp.Name == "SpPrSolidFillSchemeClrTransformations" ) 442 rProp.Value >>= aTransformations; 443 } 444 } 445 446 sal_Int32 nAlpha = MAX_PERCENT; 447 if( GetProperty( rXPropSet, "FillTransparence" ) ) 448 { 449 sal_Int32 nTransparency = 0; 450 mAny >>= nTransparency; 451 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency()) 452 nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) ); 453 } 454 455 // OOXML has no separate transparence gradient but uses transparency in the gradient stops. 456 // So we merge transparency and color and use gradient fill in such case. 457 awt::Gradient aTransparenceGradient; 458 bool bNeedGradientFill(false); 459 if (GetProperty(rXPropSet, "FillTransparenceGradient")) 460 { 461 mAny >>= aTransparenceGradient; 462 if (aTransparenceGradient.StartColor != aTransparenceGradient.EndColor) 463 bNeedGradientFill = true; 464 else if (aTransparenceGradient.StartColor != 0) 465 nAlpha = GetAlphaFromTransparenceGradient(aTransparenceGradient, true); 466 } 467 468 // write XML 469 if (bNeedGradientFill) 470 { 471 awt::Gradient aPseudoColorGradient; 472 aPseudoColorGradient.XOffset = aTransparenceGradient.XOffset; 473 aPseudoColorGradient.YOffset = aTransparenceGradient.YOffset; 474 aPseudoColorGradient.StartIntensity = 100; 475 aPseudoColorGradient.EndIntensity = 100; 476 aPseudoColorGradient.Angle = aTransparenceGradient.Angle; 477 aPseudoColorGradient.Border = aTransparenceGradient.Border; 478 aPseudoColorGradient.Style = aTransparenceGradient.Style; 479 aPseudoColorGradient.StartColor = nFillColor; 480 aPseudoColorGradient.EndColor = nFillColor; 481 aPseudoColorGradient.StepCount = aTransparenceGradient.StepCount; 482 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0"); 483 WriteGradientFill(aPseudoColorGradient, aTransparenceGradient); 484 mpFS->endElementNS( XML_a, XML_gradFill ); 485 } 486 else if ( nFillColor != nOriginalColor ) 487 { 488 // the user has set a different color for the shape 489 WriteSolidFill( ::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha ); 490 } 491 else if ( !sColorFillScheme.isEmpty() ) 492 { 493 // the shape had a scheme color and the user didn't change it 494 WriteSolidFill( sColorFillScheme, aTransformations, nAlpha ); 495 } 496 else 497 { 498 // the shape had a custom color and the user didn't change it 499 // tdf#124013 500 WriteSolidFill( ::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha ); 501 } 502 } 503 504 void DrawingML::WriteGradientStop(sal_uInt16 nStop, ::Color nColor, sal_Int32 nAlpha) 505 { 506 mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nStop * 1000)); 507 WriteColor(nColor, nAlpha); 508 mpFS->endElementNS( XML_a, XML_gs ); 509 } 510 511 ::Color DrawingML::ColorWithIntensity( sal_uInt32 nColor, sal_uInt32 nIntensity ) 512 { 513 return ::Color(ColorTransparency, ( ( ( nColor & 0xff ) * nIntensity ) / 100 ) 514 | ( ( ( ( ( nColor & 0xff00 ) >> 8 ) * nIntensity ) / 100 ) << 8 ) 515 | ( ( ( ( ( nColor & 0xff0000 ) >> 8 ) * nIntensity ) / 100 ) << 8 )); 516 } 517 518 bool DrawingML::EqualGradients( awt::Gradient aGradient1, awt::Gradient aGradient2 ) 519 { 520 return aGradient1.Style == aGradient2.Style && 521 aGradient1.StartColor == aGradient2.StartColor && 522 aGradient1.EndColor == aGradient2.EndColor && 523 aGradient1.Angle == aGradient2.Angle && 524 aGradient1.Border == aGradient2.Border && 525 aGradient1.XOffset == aGradient2.XOffset && 526 aGradient1.YOffset == aGradient2.YOffset && 527 aGradient1.StartIntensity == aGradient2.StartIntensity && 528 aGradient1.EndIntensity == aGradient2.EndIntensity && 529 aGradient1.StepCount == aGradient2.StepCount; 530 } 531 532 void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet ) 533 { 534 awt::Gradient aGradient; 535 if (!GetProperty(rXPropSet, "FillGradient")) 536 return; 537 538 aGradient = *o3tl::doAccess<awt::Gradient>(mAny); 539 540 // get InteropGrabBag and search the relevant attributes 541 awt::Gradient aOriginalGradient; 542 Sequence< PropertyValue > aGradientStops; 543 if ( GetProperty( rXPropSet, "InteropGrabBag" ) ) 544 { 545 Sequence< PropertyValue > aGrabBag; 546 mAny >>= aGrabBag; 547 for( const auto& rProp : std::as_const(aGrabBag) ) 548 if( rProp.Name == "GradFillDefinition" ) 549 rProp.Value >>= aGradientStops; 550 else if( rProp.Name == "OriginalGradFill" ) 551 rProp.Value >>= aOriginalGradient; 552 } 553 554 // check if an ooxml gradient had been imported and if the user has modified it 555 // Gradient grab-bag depends on theme grab-bag, which is implemented 556 // only for DOCX. 557 if( EqualGradients( aOriginalGradient, aGradient ) && GetDocumentType() == DOCUMENT_DOCX) 558 { 559 // If we have no gradient stops that means original gradient were defined by a theme. 560 if( aGradientStops.hasElements() ) 561 { 562 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0"); 563 WriteGrabBagGradientFill(aGradientStops, aGradient); 564 mpFS->endElementNS( XML_a, XML_gradFill ); 565 } 566 } 567 else 568 { 569 awt::Gradient aTransparenceGradient; 570 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0"); 571 OUString sFillTransparenceGradientName; 572 if (GetProperty(rXPropSet, "FillTransparenceGradientName") 573 && (mAny >>= sFillTransparenceGradientName) 574 && !sFillTransparenceGradientName.isEmpty()) 575 { 576 if (GetProperty(rXPropSet, "FillTransparenceGradient")) 577 aTransparenceGradient = *o3tl::doAccess<awt::Gradient>(mAny); 578 } 579 else if (GetProperty(rXPropSet, "FillTransparence")) 580 { 581 // currently only StartColor and EndColor are evaluated in WriteGradientFill() 582 sal_Int32 nTransparency = 0; 583 mAny >>= nTransparency; 584 // convert percent to gray color 585 nTransparency = nTransparency * 255/100; 586 const sal_Int32 aGrayColor = static_cast<sal_Int32>( nTransparency | nTransparency << 8 | nTransparency << 16 ); 587 aTransparenceGradient.StartColor = aGrayColor; 588 aTransparenceGradient.EndColor = aGrayColor; 589 } 590 WriteGradientFill(aGradient, aTransparenceGradient); 591 mpFS->endElementNS(XML_a, XML_gradFill); 592 } 593 } 594 595 void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGradientStops, awt::Gradient rGradient ) 596 { 597 // write back the original gradient 598 mpFS->startElementNS(XML_a, XML_gsLst); 599 600 // get original stops and write them 601 for( const auto& rGradientStop : aGradientStops ) 602 { 603 Sequence< PropertyValue > aGradientStop; 604 rGradientStop.Value >>= aGradientStop; 605 606 // get values 607 OUString sSchemeClr; 608 double nPos = 0; 609 sal_Int16 nTransparency = 0; 610 ::Color nRgbClr; 611 Sequence< PropertyValue > aTransformations; 612 for( const auto& rProp : std::as_const(aGradientStop) ) 613 { 614 if( rProp.Name == "SchemeClr" ) 615 rProp.Value >>= sSchemeClr; 616 else if( rProp.Name == "RgbClr" ) 617 rProp.Value >>= nRgbClr; 618 else if( rProp.Name == "Pos" ) 619 rProp.Value >>= nPos; 620 else if( rProp.Name == "Transparency" ) 621 rProp.Value >>= nTransparency; 622 else if( rProp.Name == "Transformations" ) 623 rProp.Value >>= aTransformations; 624 } 625 // write stop 626 mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nPos * 100000.0).getStr()); 627 if( sSchemeClr.isEmpty() ) 628 { 629 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency()) 630 sal_Int32 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency ); 631 WriteColor( nRgbClr, nAlpha ); 632 } 633 else 634 { 635 WriteColor( sSchemeClr, aTransformations ); 636 } 637 mpFS->endElementNS( XML_a, XML_gs ); 638 } 639 mpFS->endElementNS( XML_a, XML_gsLst ); 640 641 switch (rGradient.Style) 642 { 643 default: 644 mpFS->singleElementNS( 645 XML_a, XML_lin, XML_ang, 646 OString::number(((3600 - rGradient.Angle + 900) * 6000) % 21600000)); 647 break; 648 case awt::GradientStyle_RADIAL: 649 WriteGradientPath(rGradient, mpFS, true); 650 break; 651 } 652 } 653 654 void DrawingML::WriteGradientFill(awt::Gradient rGradient, awt::Gradient rTransparenceGradient, 655 const uno::Reference<beans::XPropertySet>& rXPropSet) 656 { 657 sal_Int32 nStartAlpha; 658 sal_Int32 nEndAlpha; 659 if( rXPropSet.is() && GetProperty(rXPropSet, "FillTransparence") ) 660 { 661 sal_Int32 nTransparency = 0; 662 mAny >>= nTransparency; 663 nStartAlpha = nEndAlpha = (MAX_PERCENT - (PER_PERCENT * nTransparency)); 664 } 665 else 666 { 667 nStartAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, true); 668 nEndAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, false); 669 } 670 switch( rGradient.Style ) 671 { 672 default: 673 case awt::GradientStyle_LINEAR: 674 { 675 mpFS->startElementNS(XML_a, XML_gsLst); 676 WriteGradientStop(rGradient.Border, ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity), 677 nStartAlpha); 678 WriteGradientStop(100, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), 679 nEndAlpha); 680 mpFS->endElementNS( XML_a, XML_gsLst ); 681 mpFS->singleElementNS( 682 XML_a, XML_lin, XML_ang, 683 OString::number(((3600 - rGradient.Angle + 900) * 6000) % 21600000)); 684 break; 685 } 686 687 case awt::GradientStyle_AXIAL: 688 { 689 mpFS->startElementNS(XML_a, XML_gsLst); 690 WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), 691 nEndAlpha); 692 if (rGradient.Border > 0 && rGradient.Border < 100) 693 { 694 WriteGradientStop(rGradient.Border/2, 695 ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), 696 nEndAlpha); 697 } 698 WriteGradientStop(50, ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity), 699 nStartAlpha); 700 if (rGradient.Border > 0 && rGradient.Border < 100) 701 { 702 WriteGradientStop(100 - rGradient.Border/2, 703 ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), 704 nEndAlpha); 705 } 706 WriteGradientStop(100, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), 707 nEndAlpha); 708 mpFS->endElementNS(XML_a, XML_gsLst); 709 mpFS->singleElementNS( 710 XML_a, XML_lin, XML_ang, 711 OString::number(((3600 - rGradient.Angle + 900) * 6000) % 21600000)); 712 break; 713 } 714 715 case awt::GradientStyle_RADIAL: 716 case awt::GradientStyle_ELLIPTICAL: 717 case awt::GradientStyle_RECT: 718 case awt::GradientStyle_SQUARE: 719 { 720 mpFS->startElementNS(XML_a, XML_gsLst); 721 WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), 722 nEndAlpha); 723 if (rGradient.Border > 0 && rGradient.Border < 100) 724 { 725 // Map border to an additional gradient stop, which has the 726 // same color as the final stop. 727 WriteGradientStop(100 - rGradient.Border, 728 ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity), 729 nStartAlpha); 730 } 731 WriteGradientStop(100, 732 ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity), 733 nStartAlpha); 734 mpFS->endElementNS(XML_a, XML_gsLst); 735 736 WriteGradientPath(rGradient, mpFS, rGradient.Style == awt::GradientStyle_RADIAL || rGradient.Style == awt::GradientStyle_ELLIPTICAL); 737 break; 738 } 739 } 740 } 741 742 void DrawingML::WriteLineArrow( const Reference< XPropertySet >& rXPropSet, bool bLineStart ) 743 { 744 ESCHER_LineEnd eLineEnd; 745 sal_Int32 nArrowLength; 746 sal_Int32 nArrowWidth; 747 748 if ( !EscherPropertyContainer::GetLineArrow( bLineStart, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) ) 749 return; 750 751 const char* len; 752 const char* type; 753 const char* width; 754 755 switch( nArrowLength ) 756 { 757 case ESCHER_LineShortArrow: 758 len = "sm"; 759 break; 760 default: 761 case ESCHER_LineMediumLenArrow: 762 len = "med"; 763 break; 764 case ESCHER_LineLongArrow: 765 len = "lg"; 766 break; 767 } 768 769 switch( eLineEnd ) 770 { 771 default: 772 case ESCHER_LineNoEnd: 773 type = "none"; 774 break; 775 case ESCHER_LineArrowEnd: 776 type = "triangle"; 777 break; 778 case ESCHER_LineArrowStealthEnd: 779 type = "stealth"; 780 break; 781 case ESCHER_LineArrowDiamondEnd: 782 type = "diamond"; 783 break; 784 case ESCHER_LineArrowOvalEnd: 785 type = "oval"; 786 break; 787 case ESCHER_LineArrowOpenEnd: 788 type = "arrow"; 789 break; 790 } 791 792 switch( nArrowWidth ) 793 { 794 case ESCHER_LineNarrowArrow: 795 width = "sm"; 796 break; 797 default: 798 case ESCHER_LineMediumWidthArrow: 799 width = "med"; 800 break; 801 case ESCHER_LineWideArrow: 802 width = "lg"; 803 break; 804 } 805 806 mpFS->singleElementNS( XML_a, bLineStart ? XML_headEnd : XML_tailEnd, 807 XML_len, len, 808 XML_type, type, 809 XML_w, width ); 810 } 811 812 void DrawingML::WriteOutline( const Reference<XPropertySet>& rXPropSet, Reference< frame::XModel > const & xModel ) 813 { 814 drawing::LineStyle aLineStyle( drawing::LineStyle_NONE ); 815 if (GetProperty(rXPropSet, "LineStyle")) 816 mAny >>= aLineStyle; 817 818 const LineCap aLineCap = GetProperty(rXPropSet, "LineCap") ? mAny.get<drawing::LineCap>() : LineCap_BUTT; 819 820 sal_uInt32 nLineWidth = 0; 821 sal_uInt32 nEmuLineWidth = 0; 822 ::Color nColor; 823 sal_Int32 nColorAlpha = MAX_PERCENT; 824 bool bColorSet = false; 825 const char* cap = nullptr; 826 drawing::LineDash aLineDash; 827 bool bDashSet = false; 828 bool bNoFill = false; 829 830 831 // get InteropGrabBag and search the relevant attributes 832 OUString sColorFillScheme; 833 ::Color aResolvedColorFillScheme; 834 835 ::Color nOriginalColor; 836 ::Color nStyleColor; 837 sal_uInt32 nStyleLineWidth = 0; 838 839 Sequence<PropertyValue> aStyleProperties; 840 Sequence<PropertyValue> aTransformations; 841 842 drawing::LineStyle aStyleLineStyle(drawing::LineStyle_NONE); 843 drawing::LineJoint aStyleLineJoint(drawing::LineJoint_NONE); 844 845 if (GetProperty(rXPropSet, "InteropGrabBag")) 846 { 847 Sequence<PropertyValue> aGrabBag; 848 mAny >>= aGrabBag; 849 850 for (const auto& rProp : std::as_const(aGrabBag)) 851 { 852 if( rProp.Name == "SpPrLnSolidFillSchemeClr" ) 853 rProp.Value >>= sColorFillScheme; 854 if( rProp.Name == "SpPrLnSolidFillResolvedSchemeClr" ) 855 rProp.Value >>= aResolvedColorFillScheme; 856 else if( rProp.Name == "OriginalLnSolidFillClr" ) 857 rProp.Value >>= nOriginalColor; 858 else if( rProp.Name == "StyleLnRef" ) 859 rProp.Value >>= aStyleProperties; 860 else if( rProp.Name == "SpPrLnSolidFillSchemeClrTransformations" ) 861 rProp.Value >>= aTransformations; 862 else if( rProp.Name == "EmuLineWidth" ) 863 rProp.Value >>= nEmuLineWidth; 864 } 865 for (const auto& rStyleProp : std::as_const(aStyleProperties)) 866 { 867 if( rStyleProp.Name == "Color" ) 868 rStyleProp.Value >>= nStyleColor; 869 else if( rStyleProp.Name == "LineStyle" ) 870 rStyleProp.Value >>= aStyleLineStyle; 871 else if( rStyleProp.Name == "LineJoint" ) 872 rStyleProp.Value >>= aStyleLineJoint; 873 else if( rStyleProp.Name == "LineWidth" ) 874 rStyleProp.Value >>= nStyleLineWidth; 875 } 876 } 877 878 if (GetProperty(rXPropSet, "LineWidth")) 879 mAny >>= nLineWidth; 880 881 switch (aLineStyle) 882 { 883 case drawing::LineStyle_NONE: 884 bNoFill = true; 885 break; 886 case drawing::LineStyle_DASH: 887 if (GetProperty(rXPropSet, "LineDash")) 888 { 889 aLineDash = mAny.get<drawing::LineDash>(); 890 //this query is good for shapes, but in the case of charts it returns 0 values 891 if (aLineDash.Dots == 0 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 0) { 892 OUString aLineDashName; 893 if (GetProperty(rXPropSet, "LineDashName")) 894 mAny >>= aLineDashName; 895 if (!aLineDashName.isEmpty() && xModel) { 896 css::uno::Any aAny = getLineDash(xModel, aLineDashName); 897 aAny >>= aLineDash; 898 } 899 } 900 } 901 else 902 { 903 //export the linestyle of chart wall (plot area) and chart page 904 OUString aLineDashName; 905 if (GetProperty(rXPropSet, "LineDashName")) 906 mAny >>= aLineDashName; 907 if (!aLineDashName.isEmpty() && xModel) { 908 css::uno::Any aAny = getLineDash(xModel, aLineDashName); 909 aAny >>= aLineDash; 910 } 911 } 912 bDashSet = true; 913 if (aLineDash.Style == DashStyle_ROUND || aLineDash.Style == DashStyle_ROUNDRELATIVE) 914 { 915 cap = "rnd"; 916 } 917 918 SAL_INFO("oox.shape", "dash dots: " << aLineDash.Dots << " dashes: " << aLineDash.Dashes 919 << " dotlen: " << aLineDash.DotLen << " dashlen: " << aLineDash.DashLen << " distance: " << aLineDash.Distance); 920 921 [[fallthrough]]; 922 case drawing::LineStyle_SOLID: 923 default: 924 if (GetProperty(rXPropSet, "LineColor")) 925 { 926 nColor = ::Color(ColorTransparency, mAny.get<sal_uInt32>() & 0xffffff); 927 bColorSet = true; 928 } 929 if (GetProperty(rXPropSet, "LineTransparence")) 930 { 931 nColorAlpha = MAX_PERCENT - (mAny.get<sal_Int16>() * PER_PERCENT); 932 } 933 if (aLineCap == LineCap_ROUND) 934 cap = "rnd"; 935 else if (aLineCap == LineCap_SQUARE) 936 cap = "sq"; 937 break; 938 } 939 940 // if the line-width was not modified after importing then the original EMU value will be exported to avoid unexpected conversion (rounding) error 941 if (nEmuLineWidth == 0 || static_cast<sal_uInt32>(oox::drawingml::convertEmuToHmm(nEmuLineWidth)) != nLineWidth) 942 nEmuLineWidth = oox::drawingml::convertHmmToEmu(nLineWidth); 943 mpFS->startElementNS( XML_a, XML_ln, 944 XML_cap, cap, 945 XML_w, sax_fastparser::UseIf(OString::number(nEmuLineWidth), 946 nLineWidth == 0 || (nLineWidth > 1 && nStyleLineWidth != nLineWidth)) ); 947 948 if( bColorSet ) 949 { 950 if( nColor != nOriginalColor ) 951 { 952 // the user has set a different color for the line 953 WriteSolidFill( nColor, nColorAlpha ); 954 } 955 else if( !sColorFillScheme.isEmpty() ) 956 { 957 // the line had a scheme color and the user didn't change it 958 WriteSolidFill( aResolvedColorFillScheme, aTransformations ); 959 } 960 else 961 { 962 WriteSolidFill( nColor, nColorAlpha ); 963 } 964 } 965 966 if( bDashSet && aStyleLineStyle != drawing::LineStyle_DASH ) 967 { 968 // Try to detect if it might come from ms preset line style import. 969 // MS Office styles are always relative, both binary and OOXML. 970 // "dot" is always the first dash and "dash" the second one. All OOXML presets linestyles 971 // start with the longer one. Definitions are in OOXML part 1, 20.1.10.49 972 // The tests are strict, for to not catch styles from standard.sod (as of Aug 2019). 973 bool bIsConverted = false; 974 975 bool bIsRelative(aLineDash.Style == DashStyle_RECTRELATIVE || aLineDash.Style == DashStyle_ROUNDRELATIVE); 976 if ( bIsRelative && aLineDash.Dots == 1) 977 { // The length were tweaked on import in case of prstDash. Revert it here. 978 sal_uInt32 nDotLen = aLineDash.DotLen; 979 sal_uInt32 nDashLen = aLineDash.DashLen; 980 sal_uInt32 nDistance = aLineDash.Distance; 981 if (aLineCap != LineCap_BUTT && nDistance >= 99) 982 { 983 nDistance -= 99; 984 nDotLen += 99; 985 if (nDashLen > 0) 986 nDashLen += 99; 987 } 988 // LO uses length 0 for 100%, if the attribute is missing in ODF. 989 // Other applications might write 100%. Make is unique for the conditions. 990 if (nDotLen == 0) 991 nDotLen = 100; 992 if (nDashLen == 0 && aLineDash.Dashes > 0) 993 nDashLen = 100; 994 bIsConverted = true; 995 if (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300) 996 { 997 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dot"); 998 } 999 else if (nDotLen == 400 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300) 1000 { 1001 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash"); 1002 } 1003 else if (nDotLen == 400 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300) 1004 { 1005 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dashDot"); 1006 } 1007 else if (nDotLen == 800 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300) 1008 { 1009 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDash"); 1010 } 1011 else if (nDotLen == 800 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300) 1012 { 1013 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDot"); 1014 } 1015 else if (nDotLen == 800 && aLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 300) 1016 { 1017 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDotDot"); 1018 } 1019 else if (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100) 1020 { 1021 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDot"); 1022 } 1023 else if (nDotLen == 300 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100) 1024 { 1025 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDash"); 1026 } 1027 else if (nDotLen == 300 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 100) 1028 { 1029 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDot"); 1030 } 1031 else if (nDotLen == 300 && aLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 100) 1032 { 1033 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDotDot"); 1034 } 1035 else 1036 bIsConverted = false; 1037 } 1038 // Do not map our own line styles to OOXML prstDash values, because custDash gives better results. 1039 if (!bIsConverted) 1040 { 1041 mpFS->startElementNS(XML_a, XML_custDash); 1042 // In case of hairline we would need the current pixel size. Instead use a reasonable 1043 // ersatz for it. The value is the same as SMALLEST_DASH_WIDTH in xattr.cxx. 1044 // (And it makes sure fLineWidth is not zero in below division.) 1045 double fLineWidth = nLineWidth > 0 ? nLineWidth : 26.95; 1046 int i; 1047 double fSp = bIsRelative ? aLineDash.Distance : aLineDash.Distance * 100.0 / fLineWidth; 1048 // LO uses line width, in case Distance is zero. MS Office would use a space of zero length. 1049 // So set 100% explicitly. 1050 if (aLineDash.Distance <= 0) 1051 fSp = 100.0; 1052 // In case of custDash, round caps are included in dash length in MS Office. Square caps are added 1053 // to dash length, same as in ODF. Change the length values accordingly. 1054 if (aLineCap == LineCap_ROUND && fSp > 99.0) 1055 fSp -= 99.0; 1056 1057 if (aLineDash.Dots > 0) 1058 { 1059 double fD = bIsRelative ? aLineDash.DotLen : aLineDash.DotLen * 100.0 / fLineWidth; 1060 // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended. 1061 if (aLineDash.DotLen == 0) 1062 fD = 100.0; 1063 // Tweak dash length, see above. 1064 if (aLineCap == LineCap_ROUND && fSp > 99.0) 1065 fD += 99.0; 1066 1067 for( i = 0; i < aLineDash.Dots; i ++ ) 1068 { 1069 mpFS->singleElementNS( XML_a, XML_ds, 1070 XML_d , write1000thOfAPercent(fD), 1071 XML_sp, write1000thOfAPercent(fSp) ); 1072 } 1073 } 1074 if ( aLineDash.Dashes > 0 ) 1075 { 1076 double fD = bIsRelative ? aLineDash.DashLen : aLineDash.DashLen * 100.0 / fLineWidth; 1077 // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended. 1078 if (aLineDash.DashLen == 0) 1079 fD = 100.0; 1080 // Tweak dash length, see above. 1081 if (aLineCap == LineCap_ROUND && fSp > 99.0) 1082 fD += 99.0; 1083 1084 for( i = 0; i < aLineDash.Dashes; i ++ ) 1085 { 1086 mpFS->singleElementNS( XML_a , XML_ds, 1087 XML_d , write1000thOfAPercent(fD), 1088 XML_sp, write1000thOfAPercent(fSp) ); 1089 } 1090 } 1091 1092 SAL_WARN_IF(nLineWidth <= 0, 1093 "oox.shape", "while writing outline - custom dash - line width was < 0 : " << nLineWidth); 1094 SAL_WARN_IF(aLineDash.Dashes < 0, 1095 "oox.shape", "while writing outline - custom dash - number of dashes was < 0 : " << aLineDash.Dashes); 1096 SAL_WARN_IF(aLineDash.Dashes > 0 && aLineDash.DashLen <= 0, 1097 "oox.shape", "while writing outline - custom dash - dash length was < 0 : " << aLineDash.DashLen); 1098 SAL_WARN_IF(aLineDash.Dots < 0, 1099 "oox.shape", "while writing outline - custom dash - number of dots was < 0 : " << aLineDash.Dots); 1100 SAL_WARN_IF(aLineDash.Dots > 0 && aLineDash.DotLen <= 0, 1101 "oox.shape", "while writing outline - custom dash - dot length was < 0 : " << aLineDash.DotLen); 1102 SAL_WARN_IF(aLineDash.Distance <= 0, 1103 "oox.shape", "while writing outline - custom dash - distance was < 0 : " << aLineDash.Distance); 1104 1105 mpFS->endElementNS( XML_a, XML_custDash ); 1106 } 1107 } 1108 1109 if (!bNoFill && nLineWidth > 1 && GetProperty(rXPropSet, "LineJoint")) 1110 { 1111 LineJoint eLineJoint = mAny.get<LineJoint>(); 1112 1113 if( aStyleLineJoint == LineJoint_NONE || aStyleLineJoint != eLineJoint ) 1114 { 1115 // style-defined line joint does not exist, or is different from the shape's joint 1116 switch( eLineJoint ) 1117 { 1118 case LineJoint_NONE: 1119 case LineJoint_BEVEL: 1120 mpFS->singleElementNS(XML_a, XML_bevel); 1121 break; 1122 default: 1123 case LineJoint_MIDDLE: 1124 case LineJoint_MITER: 1125 mpFS->singleElementNS(XML_a, XML_miter); 1126 break; 1127 case LineJoint_ROUND: 1128 mpFS->singleElementNS(XML_a, XML_round); 1129 break; 1130 } 1131 } 1132 } 1133 1134 if( !bNoFill ) 1135 { 1136 WriteLineArrow( rXPropSet, true ); 1137 WriteLineArrow( rXPropSet, false ); 1138 } 1139 else 1140 { 1141 mpFS->singleElementNS(XML_a, XML_noFill); 1142 } 1143 1144 mpFS->endElementNS( XML_a, XML_ln ); 1145 } 1146 1147 const char* DrawingML::GetComponentDir() const 1148 { 1149 switch ( meDocumentType ) 1150 { 1151 case DOCUMENT_DOCX: return "word"; 1152 case DOCUMENT_PPTX: return "ppt"; 1153 case DOCUMENT_XLSX: return "xl"; 1154 } 1155 1156 return "unknown"; 1157 } 1158 1159 const char* DrawingML::GetRelationCompPrefix() const 1160 { 1161 switch ( meDocumentType ) 1162 { 1163 case DOCUMENT_DOCX: return ""; 1164 case DOCUMENT_PPTX: 1165 case DOCUMENT_XLSX: return "../"; 1166 } 1167 1168 return "unknown"; 1169 } 1170 1171 OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia, OUString* pFileName ) 1172 { 1173 GfxLink aLink = rGraphic.GetGfxLink (); 1174 OUString sMediaType; 1175 const char* pExtension = ""; 1176 OUString sRelId; 1177 1178 SvMemoryStream aStream; 1179 const void* aData = aLink.GetData(); 1180 std::size_t nDataSize = aLink.GetDataSize(); 1181 1182 switch ( aLink.GetType() ) 1183 { 1184 case GfxLinkType::NativeGif: 1185 sMediaType = "image/gif"; 1186 pExtension = ".gif"; 1187 break; 1188 1189 // #i15508# added BMP type for better exports 1190 // export not yet active, so adding for reference (not checked) 1191 case GfxLinkType::NativeBmp: 1192 sMediaType = "image/bmp"; 1193 pExtension = ".bmp"; 1194 break; 1195 1196 case GfxLinkType::NativeJpg: 1197 sMediaType = "image/jpeg"; 1198 pExtension = ".jpeg"; 1199 break; 1200 case GfxLinkType::NativePng: 1201 sMediaType = "image/png"; 1202 pExtension = ".png"; 1203 break; 1204 case GfxLinkType::NativeTif: 1205 sMediaType = "image/tiff"; 1206 pExtension = ".tif"; 1207 break; 1208 case GfxLinkType::NativeWmf: 1209 sMediaType = "image/x-wmf"; 1210 pExtension = ".wmf"; 1211 break; 1212 case GfxLinkType::NativeMet: 1213 sMediaType = "image/x-met"; 1214 pExtension = ".met"; 1215 break; 1216 case GfxLinkType::NativePct: 1217 sMediaType = "image/x-pict"; 1218 pExtension = ".pct"; 1219 break; 1220 case GfxLinkType::NativeMov: 1221 sMediaType = "application/movie"; 1222 pExtension = ".MOV"; 1223 break; 1224 default: 1225 { 1226 GraphicType aType = rGraphic.GetType(); 1227 if ( aType == GraphicType::Bitmap || aType == GraphicType::GdiMetafile) 1228 { 1229 if ( aType == GraphicType::Bitmap ) 1230 { 1231 (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::PNG ); 1232 sMediaType = "image/png"; 1233 pExtension = ".png"; 1234 } 1235 else 1236 { 1237 (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::EMF ); 1238 sMediaType = "image/x-emf"; 1239 pExtension = ".emf"; 1240 } 1241 } 1242 else 1243 { 1244 SAL_WARN("oox.shape", "unhandled graphic type " << static_cast<int>(aType) ); 1245 /*Earlier, even in case of unhandled graphic types we were 1246 proceeding to write the image, which would eventually 1247 write an empty image with a zero size, and return a valid 1248 relationID, which is incorrect. 1249 */ 1250 return sRelId; 1251 } 1252 1253 aData = aStream.GetData(); 1254 nDataSize = aStream.GetEndOfData(); 1255 break; 1256 } 1257 } 1258 1259 Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer() 1260 .appendAscii( GetComponentDir() ) 1261 .append( "/media/image" + 1262 OUString::number(mnImageCounter) ) 1263 .appendAscii( pExtension ) 1264 .makeStringAndClear(), 1265 sMediaType ); 1266 xOutStream->writeBytes( Sequence< sal_Int8 >( static_cast<const sal_Int8*>(aData), nDataSize ) ); 1267 xOutStream->closeOutput(); 1268 1269 const OString sRelPathToMedia = "media/image"; 1270 OString sRelationCompPrefix; 1271 if ( bRelPathToMedia ) 1272 sRelationCompPrefix = "../"; 1273 else 1274 sRelationCompPrefix = GetRelationCompPrefix(); 1275 OUString sPath = OUStringBuffer() 1276 .appendAscii( sRelationCompPrefix.getStr() ) 1277 .appendAscii( sRelPathToMedia.getStr() ) 1278 .append( static_cast<sal_Int32>(mnImageCounter ++) ) 1279 .appendAscii( pExtension ) 1280 .makeStringAndClear(); 1281 sRelId = mpFB->addRelation( mpFS->getOutputStream(), 1282 oox::getRelationship(Relationship::IMAGE), 1283 sPath ); 1284 1285 if (pFileName) 1286 *pFileName = sPath; 1287 return sRelId; 1288 } 1289 1290 void DrawingML::WriteMediaNonVisualProperties(const css::uno::Reference<css::drawing::XShape>& xShape) 1291 { 1292 SdrMediaObj* pMediaObj = dynamic_cast<SdrMediaObj*>(GetSdrObjectFromXShape(xShape)); 1293 if (!pMediaObj) 1294 return; 1295 1296 // extension 1297 OUString aExtension; 1298 const OUString& rURL(pMediaObj->getURL()); 1299 int nLastDot = rURL.lastIndexOf('.'); 1300 if (nLastDot >= 0) 1301 aExtension = rURL.copy(nLastDot); 1302 1303 bool bEmbed = rURL.startsWith("vnd.sun.star.Package:"); 1304 Relationship eMediaType = Relationship::VIDEO; 1305 1306 // mime type 1307 #if HAVE_FEATURE_AVMEDIA 1308 OUString aMimeType(pMediaObj->getMediaProperties().getMimeType()); 1309 #else 1310 OUString aMimeType("none"); 1311 #endif 1312 if (aMimeType == "application/vnd.sun.star.media") 1313 { 1314 // try to set something better 1315 // TODO fix the importer to actually set the mimetype on import 1316 if (aExtension.equalsIgnoreAsciiCase(".avi")) 1317 aMimeType = "video/x-msvideo"; 1318 else if (aExtension.equalsIgnoreAsciiCase(".flv")) 1319 aMimeType = "video/x-flv"; 1320 else if (aExtension.equalsIgnoreAsciiCase(".mp4")) 1321 aMimeType = "video/mp4"; 1322 else if (aExtension.equalsIgnoreAsciiCase(".mov")) 1323 aMimeType = "video/quicktime"; 1324 else if (aExtension.equalsIgnoreAsciiCase(".ogv")) 1325 aMimeType = "video/ogg"; 1326 else if (aExtension.equalsIgnoreAsciiCase(".wmv")) 1327 aMimeType = "video/x-ms-wmv"; 1328 else if (aExtension.equalsIgnoreAsciiCase(".wav")) 1329 { 1330 aMimeType = "audio/x-wav"; 1331 eMediaType = Relationship::AUDIO; 1332 } 1333 else if (aExtension.equalsIgnoreAsciiCase(".m4a")) 1334 { 1335 aMimeType = "audio/mp4"; 1336 eMediaType = Relationship::AUDIO; 1337 } 1338 } 1339 1340 OUString aVideoFileRelId; 1341 OUString aMediaRelId; 1342 1343 if (bEmbed) 1344 { 1345 // copy the video stream 1346 Reference<XOutputStream> xOutStream = mpFB->openFragmentStream(OUStringBuffer() 1347 .appendAscii(GetComponentDir()) 1348 .append("/media/media" + 1349 OUString::number(mnImageCounter) + 1350 aExtension) 1351 .makeStringAndClear(), 1352 aMimeType); 1353 1354 uno::Reference<io::XInputStream> xInputStream(pMediaObj->GetInputStream()); 1355 comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xOutStream); 1356 1357 xOutStream->closeOutput(); 1358 1359 // create the relation 1360 OUString aPath = OUStringBuffer().appendAscii(GetRelationCompPrefix()) 1361 .append("media/media" + OUString::number(mnImageCounter++) + aExtension) 1362 .makeStringAndClear(); 1363 aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), aPath); 1364 aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), aPath); 1365 } 1366 else 1367 { 1368 aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), rURL); 1369 aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), rURL); 1370 } 1371 1372 GetFS()->startElementNS(XML_p, XML_nvPr); 1373 1374 GetFS()->singleElementNS(XML_a, eMediaType == Relationship::VIDEO ? XML_videoFile : XML_audioFile, 1375 FSNS(XML_r, XML_link), aVideoFileRelId); 1376 1377 GetFS()->startElementNS(XML_p, XML_extLst); 1378 // media extensions; google this ID for details 1379 GetFS()->startElementNS(XML_p, XML_ext, XML_uri, "{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}"); 1380 1381 GetFS()->singleElementNS(XML_p14, XML_media, 1382 bEmbed? FSNS(XML_r, XML_embed): FSNS(XML_r, XML_link), aMediaRelId); 1383 1384 GetFS()->endElementNS(XML_p, XML_ext); 1385 GetFS()->endElementNS(XML_p, XML_extLst); 1386 1387 GetFS()->endElementNS(XML_p, XML_nvPr); 1388 } 1389 1390 void DrawingML::WriteImageBrightnessContrastTransparence(uno::Reference<beans::XPropertySet> const & rXPropSet) 1391 { 1392 sal_Int16 nBright = 0; 1393 sal_Int32 nContrast = 0; 1394 sal_Int32 nTransparence = 0; 1395 1396 if (GetProperty(rXPropSet, "AdjustLuminance")) 1397 nBright = mAny.get<sal_Int16>(); 1398 if (GetProperty(rXPropSet, "AdjustContrast")) 1399 nContrast = mAny.get<sal_Int32>(); 1400 // Used for shapes with picture fill 1401 if (GetProperty(rXPropSet, "FillTransparence")) 1402 nTransparence = mAny.get<sal_Int32>(); 1403 // Used for pictures 1404 if (nTransparence == 0 && GetProperty(rXPropSet, "Transparency")) 1405 nTransparence = static_cast<sal_Int32>(mAny.get<sal_Int16>()); 1406 1407 if (GetProperty(rXPropSet, "GraphicColorMode")) 1408 { 1409 drawing::ColorMode aColorMode; 1410 mAny >>= aColorMode; 1411 if (aColorMode == drawing::ColorMode_GREYS) 1412 mpFS->singleElementNS(XML_a, XML_grayscl); 1413 else if (aColorMode == drawing::ColorMode_MONO) 1414 //black/white has a 0,5 threshold in LibreOffice 1415 mpFS->singleElementNS(XML_a, XML_biLevel, XML_thresh, OString::number(50000)); 1416 else if (aColorMode == drawing::ColorMode_WATERMARK) 1417 { 1418 //map watermark with mso washout 1419 nBright = 70; 1420 nContrast = -70; 1421 } 1422 } 1423 1424 1425 if (nBright || nContrast) 1426 { 1427 mpFS->singleElementNS(XML_a, XML_lum, 1428 XML_bright, sax_fastparser::UseIf(OString::number(nBright * 1000), nBright != 0), 1429 XML_contrast, sax_fastparser::UseIf(OString::number(nContrast * 1000), nContrast != 0)); 1430 } 1431 1432 if (nTransparence) 1433 { 1434 sal_Int32 nAlphaMod = (100 - nTransparence ) * PER_PERCENT; 1435 mpFS->singleElementNS(XML_a, XML_alphaModFix, XML_amt, OString::number(nAlphaMod)); 1436 } 1437 } 1438 1439 OUString DrawingML::WriteXGraphicBlip(uno::Reference<beans::XPropertySet> const & rXPropSet, 1440 uno::Reference<graphic::XGraphic> const & rxGraphic, 1441 bool bRelPathToMedia) 1442 { 1443 OUString sRelId; 1444 OUString sFileName; 1445 1446 if (!rxGraphic.is()) 1447 return sRelId; 1448 1449 Graphic aGraphic(rxGraphic); 1450 if (mpTextExport) 1451 { 1452 BitmapChecksum nChecksum = aGraphic.GetChecksum(); 1453 sRelId = mpTextExport->FindRelId(nChecksum); 1454 sFileName = mpTextExport->FindFileName(nChecksum); 1455 } 1456 if (sRelId.isEmpty()) 1457 { 1458 sRelId = WriteImage(aGraphic, bRelPathToMedia, &sFileName); 1459 if (mpTextExport) 1460 { 1461 BitmapChecksum nChecksum = aGraphic.GetChecksum(); 1462 mpTextExport->CacheRelId(nChecksum, sRelId, sFileName); 1463 } 1464 } 1465 else 1466 { 1467 // Include the same relation again. This makes it possible to 1468 // reuse an image across different headers. 1469 sRelId = mpFB->addRelation( mpFS->getOutputStream(), 1470 oox::getRelationship(Relationship::IMAGE), 1471 sFileName ); 1472 } 1473 1474 mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId); 1475 1476 WriteImageBrightnessContrastTransparence(rXPropSet); 1477 1478 WriteArtisticEffect(rXPropSet); 1479 1480 mpFS->endElementNS(XML_a, XML_blip); 1481 1482 return sRelId; 1483 } 1484 1485 void DrawingML::WriteXGraphicBlipMode(uno::Reference<beans::XPropertySet> const & rXPropSet, 1486 uno::Reference<graphic::XGraphic> const & rxGraphic) 1487 { 1488 BitmapMode eBitmapMode(BitmapMode_NO_REPEAT); 1489 if (GetProperty(rXPropSet, "FillBitmapMode")) 1490 mAny >>= eBitmapMode; 1491 1492 SAL_INFO("oox.shape", "fill bitmap mode: " << int(eBitmapMode)); 1493 1494 switch (eBitmapMode) 1495 { 1496 case BitmapMode_REPEAT: 1497 mpFS->singleElementNS(XML_a, XML_tile); 1498 break; 1499 case BitmapMode_STRETCH: 1500 WriteXGraphicStretch(rXPropSet, rxGraphic); 1501 break; 1502 default: 1503 break; 1504 } 1505 } 1506 1507 void DrawingML::WriteBlipOrNormalFill( const Reference< XPropertySet >& xPropSet, const OUString& rURLPropName ) 1508 { 1509 // check for blip and otherwise fall back to normal fill 1510 // we always store normal fill properties but OOXML 1511 // uses a choice between our fill props and BlipFill 1512 if (GetProperty ( xPropSet, rURLPropName )) 1513 WriteBlipFill( xPropSet, rURLPropName ); 1514 else 1515 WriteFill(xPropSet); 1516 } 1517 1518 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName ) 1519 { 1520 WriteBlipFill( rXPropSet, sURLPropName, XML_a ); 1521 } 1522 1523 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName, sal_Int32 nXmlNamespace ) 1524 { 1525 if ( !GetProperty( rXPropSet, sURLPropName ) ) 1526 return; 1527 1528 uno::Reference<graphic::XGraphic> xGraphic; 1529 if (mAny.has<uno::Reference<awt::XBitmap>>()) 1530 { 1531 uno::Reference<awt::XBitmap> xBitmap = mAny.get<uno::Reference<awt::XBitmap>>(); 1532 if (xBitmap.is()) 1533 xGraphic.set(xBitmap, uno::UNO_QUERY); 1534 } 1535 else if (mAny.has<uno::Reference<graphic::XGraphic>>()) 1536 { 1537 xGraphic = mAny.get<uno::Reference<graphic::XGraphic>>(); 1538 } 1539 1540 if (xGraphic.is()) 1541 { 1542 bool bWriteMode = false; 1543 if (sURLPropName == "FillBitmap" || sURLPropName == "BackGraphic") 1544 bWriteMode = true; 1545 WriteXGraphicBlipFill(rXPropSet, xGraphic, nXmlNamespace, bWriteMode); 1546 } 1547 } 1548 1549 void DrawingML::WriteXGraphicBlipFill(uno::Reference<beans::XPropertySet> const & rXPropSet, 1550 uno::Reference<graphic::XGraphic> const & rxGraphic, 1551 sal_Int32 nXmlNamespace, bool bWriteMode, bool bRelPathToMedia) 1552 { 1553 if (!rxGraphic.is() ) 1554 return; 1555 1556 mpFS->startElementNS(nXmlNamespace , XML_blipFill, XML_rotWithShape, "0"); 1557 1558 WriteXGraphicBlip(rXPropSet, rxGraphic, bRelPathToMedia); 1559 1560 if (GetDocumentType() != DOCUMENT_DOCX) 1561 { 1562 // Write the crop rectangle of Impress as a source rectangle. 1563 WriteSrcRectXGraphic(rXPropSet, rxGraphic); 1564 } 1565 1566 if (bWriteMode) 1567 { 1568 WriteXGraphicBlipMode(rXPropSet, rxGraphic); 1569 } 1570 else if(GetProperty(rXPropSet, "FillBitmapStretch")) 1571 { 1572 bool bStretch = mAny.get<bool>(); 1573 1574 if (bStretch) 1575 { 1576 WriteXGraphicStretch(rXPropSet, rxGraphic); 1577 } 1578 } 1579 mpFS->endElementNS(nXmlNamespace, XML_blipFill); 1580 } 1581 1582 void DrawingML::WritePattFill( const Reference< XPropertySet >& rXPropSet ) 1583 { 1584 if ( GetProperty( rXPropSet, "FillHatch" ) ) 1585 { 1586 drawing::Hatch aHatch; 1587 mAny >>= aHatch; 1588 WritePattFill(rXPropSet, aHatch); 1589 } 1590 } 1591 1592 void DrawingML::WritePattFill(const Reference<XPropertySet>& rXPropSet, const css::drawing::Hatch& rHatch) 1593 { 1594 mpFS->startElementNS(XML_a, XML_pattFill, XML_prst, GetHatchPattern(rHatch)); 1595 1596 mpFS->startElementNS(XML_a, XML_fgClr); 1597 WriteColor(::Color(ColorTransparency, rHatch.Color)); 1598 mpFS->endElementNS( XML_a , XML_fgClr ); 1599 1600 ::Color nColor = COL_WHITE; 1601 sal_Int32 nAlpha = 0; 1602 1603 if ( GetProperty( rXPropSet, "FillBackground" ) ) 1604 { 1605 bool isBackgroundFilled = false; 1606 mAny >>= isBackgroundFilled; 1607 if( isBackgroundFilled ) 1608 { 1609 nAlpha = MAX_PERCENT; 1610 1611 if( GetProperty( rXPropSet, "FillColor" ) ) 1612 { 1613 mAny >>= nColor; 1614 } 1615 } 1616 } 1617 1618 mpFS->startElementNS(XML_a, XML_bgClr); 1619 WriteColor(nColor, nAlpha); 1620 mpFS->endElementNS( XML_a , XML_bgClr ); 1621 1622 mpFS->endElementNS( XML_a , XML_pattFill ); 1623 } 1624 1625 void DrawingML::WriteGraphicCropProperties(uno::Reference<beans::XPropertySet> const & rXPropSet, 1626 Size const & rOriginalSize, 1627 MapMode const & rMapMode) 1628 { 1629 if (!GetProperty(rXPropSet, "GraphicCrop")) 1630 return; 1631 1632 css::text::GraphicCrop aGraphicCropStruct; 1633 mAny >>= aGraphicCropStruct; 1634 1635 if(GetProperty(rXPropSet, "CustomShapeGeometry")) 1636 { 1637 // tdf#134210 GraphicCrop property is handled in import filter because of LibreOffice has not core 1638 // feature. We cropped the bitmap physically and MSO shouldn't crop bitmap one more time. When we 1639 // have core feature for graphic cropping in custom shapes, we should uncomment the code anymore. 1640 1641 mpFS->singleElementNS( XML_a, XML_srcRect); 1642 } 1643 else 1644 { 1645 Size aOriginalSize(rOriginalSize); 1646 1647 // GraphicCrop is in mm100, so in case the original size is in pixels, convert it over. 1648 if (rMapMode.GetMapUnit() == MapUnit::MapPixel) 1649 aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, MapMode(MapUnit::Map100thMM)); 1650 1651 if ( (0 != aGraphicCropStruct.Left) || (0 != aGraphicCropStruct.Top) || (0 != aGraphicCropStruct.Right) || (0 != aGraphicCropStruct.Bottom) ) 1652 { 1653 mpFS->singleElementNS( XML_a, XML_srcRect, 1654 XML_l, OString::number(rtl::math::round(aGraphicCropStruct.Left * 100000.0 / aOriginalSize.Width())), 1655 XML_t, OString::number(rtl::math::round(aGraphicCropStruct.Top * 100000.0 / aOriginalSize.Height())), 1656 XML_r, OString::number(rtl::math::round(aGraphicCropStruct.Right * 100000.0 / aOriginalSize.Width())), 1657 XML_b, OString::number(rtl::math::round(aGraphicCropStruct.Bottom * 100000.0 / aOriginalSize.Height())) ); 1658 } 1659 } 1660 } 1661 1662 void DrawingML::WriteSrcRectXGraphic(uno::Reference<beans::XPropertySet> const & rxPropertySet, 1663 uno::Reference<graphic::XGraphic> const & rxGraphic) 1664 { 1665 Graphic aGraphic(rxGraphic); 1666 Size aOriginalSize = aGraphic.GetPrefSize(); 1667 const MapMode& rMapMode = aGraphic.GetPrefMapMode(); 1668 WriteGraphicCropProperties(rxPropertySet, aOriginalSize, rMapMode); 1669 } 1670 1671 void DrawingML::WriteXGraphicStretch(uno::Reference<beans::XPropertySet> const & rXPropSet, 1672 uno::Reference<graphic::XGraphic> const & rxGraphic) 1673 { 1674 if (GetDocumentType() != DOCUMENT_DOCX) 1675 { 1676 // Limiting the area used for stretching is not supported in Impress. 1677 mpFS->singleElementNS(XML_a, XML_stretch); 1678 return; 1679 } 1680 1681 mpFS->startElementNS(XML_a, XML_stretch); 1682 1683 bool bCrop = false; 1684 if (GetProperty(rXPropSet, "GraphicCrop")) 1685 { 1686 css::text::GraphicCrop aGraphicCropStruct; 1687 mAny >>= aGraphicCropStruct; 1688 1689 if ((0 != aGraphicCropStruct.Left) 1690 || (0 != aGraphicCropStruct.Top) 1691 || (0 != aGraphicCropStruct.Right) 1692 || (0 != aGraphicCropStruct.Bottom)) 1693 { 1694 Graphic aGraphic(rxGraphic); 1695 Size aOriginalSize(aGraphic.GetPrefSize()); 1696 mpFS->singleElementNS(XML_a, XML_fillRect, 1697 XML_l, OString::number(((aGraphicCropStruct.Left) * 100000) / aOriginalSize.Width()), 1698 XML_t, OString::number(((aGraphicCropStruct.Top) * 100000) / aOriginalSize.Height()), 1699 XML_r, OString::number(((aGraphicCropStruct.Right) * 100000) / aOriginalSize.Width()), 1700 XML_b, OString::number(((aGraphicCropStruct.Bottom) * 100000) / aOriginalSize.Height())); 1701 bCrop = true; 1702 } 1703 } 1704 1705 if (!bCrop) 1706 { 1707 mpFS->singleElementNS(XML_a, XML_fillRect); 1708 } 1709 1710 mpFS->endElementNS(XML_a, XML_stretch); 1711 } 1712 1713 namespace 1714 { 1715 bool IsTopGroupObj(const uno::Reference<drawing::XShape>& xShape) 1716 { 1717 SdrObject* pObject = GetSdrObjectFromXShape(xShape); 1718 if (!pObject) 1719 return false; 1720 1721 if (pObject->getParentSdrObjectFromSdrObject()) 1722 return false; 1723 1724 return pObject->IsGroupObject(); 1725 } 1726 } 1727 1728 void DrawingML::WriteTransformation(const Reference< XShape >& xShape, const tools::Rectangle& rRect, 1729 sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, sal_Int32 nRotation, bool bIsGroupShape) 1730 { 1731 1732 mpFS->startElementNS( nXmlNamespace, XML_xfrm, 1733 XML_flipH, sax_fastparser::UseIf("1", bFlipH), 1734 XML_flipV, sax_fastparser::UseIf("1", bFlipV), 1735 XML_rot, sax_fastparser::UseIf(OString::number(nRotation), nRotation % 21600000 != 0)); 1736 1737 sal_Int32 nLeft = rRect.Left(); 1738 sal_Int32 nChildLeft = nLeft; 1739 sal_Int32 nTop = rRect.Top(); 1740 sal_Int32 nChildTop = nTop; 1741 if (GetDocumentType() == DOCUMENT_DOCX && !m_xParent.is()) 1742 { 1743 nLeft = 0; 1744 nTop = 0; 1745 } 1746 1747 mpFS->singleElementNS(XML_a, XML_off, 1748 XML_x, OString::number(oox::drawingml::convertHmmToEmu(nLeft)), 1749 XML_y, OString::number(oox::drawingml::convertHmmToEmu(nTop))); 1750 mpFS->singleElementNS(XML_a, XML_ext, 1751 XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())), 1752 XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight()))); 1753 1754 if (bIsGroupShape && (GetDocumentType() != DOCUMENT_DOCX || IsTopGroupObj(xShape))) 1755 { 1756 mpFS->singleElementNS(XML_a, XML_chOff, 1757 XML_x, OString::number(oox::drawingml::convertHmmToEmu(nChildLeft)), 1758 XML_y, OString::number(oox::drawingml::convertHmmToEmu(nChildTop))); 1759 mpFS->singleElementNS(XML_a, XML_chExt, 1760 XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())), 1761 XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight()))); 1762 } 1763 1764 mpFS->endElementNS( nXmlNamespace, XML_xfrm ); 1765 } 1766 1767 void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, bool bSuppressRotation, bool bSuppressFlipping, bool bFlippedBeforeRotation ) 1768 { 1769 SAL_INFO("oox.shape", "write shape transformation"); 1770 1771 Degree100 nRotation; 1772 Degree100 nCameraRotation; 1773 awt::Point aPos = rXShape->getPosition(); 1774 awt::Size aSize = rXShape->getSize(); 1775 1776 bool bFlipHWrite = bFlipH && !bSuppressFlipping; 1777 bool bFlipVWrite = bFlipV && !bSuppressFlipping; 1778 bFlipH = bFlipH && !bFlippedBeforeRotation; 1779 bFlipV = bFlipV && !bFlippedBeforeRotation; 1780 1781 if (GetDocumentType() == DOCUMENT_DOCX && m_xParent.is()) 1782 { 1783 awt::Point aParentPos = m_xParent->getPosition(); 1784 aPos.X -= aParentPos.X; 1785 aPos.Y -= aParentPos.Y; 1786 } 1787 1788 if ( aSize.Width < 0 ) 1789 aSize.Width = 1000; 1790 if ( aSize.Height < 0 ) 1791 aSize.Height = 1000; 1792 if (!bSuppressRotation) 1793 { 1794 SdrObject* pShape = GetSdrObjectFromXShape( rXShape ); 1795 nRotation = pShape ? pShape->GetRotateAngle() : 0_deg100; 1796 if ( GetDocumentType() != DOCUMENT_DOCX ) 1797 { 1798 int faccos=bFlipV ? -1 : 1; 1799 int facsin=bFlipH ? -1 : 1; 1800 aPos.X-=(1-faccos*cos(nRotation.get()*F_PI18000))*aSize.Width/2-facsin*sin(nRotation.get()*F_PI18000)*aSize.Height/2; 1801 aPos.Y-=(1-faccos*cos(nRotation.get()*F_PI18000))*aSize.Height/2+facsin*sin(nRotation.get()*F_PI18000)*aSize.Width/2; 1802 } 1803 1804 // The RotateAngle property's value is independent from any flipping, and that's exactly what we need here. 1805 uno::Reference<beans::XPropertySet> xPropertySet(rXShape, uno::UNO_QUERY); 1806 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo(); 1807 if (xPropertySetInfo->hasPropertyByName("RotateAngle")) 1808 { 1809 sal_Int32 nTmp; 1810 if (xPropertySet->getPropertyValue("RotateAngle") >>= nTmp) 1811 nRotation = Degree100(nTmp); 1812 } 1813 // tdf#133037: restore original rotate angle before output 1814 if (nRotation && xPropertySetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG)) 1815 { 1816 uno::Sequence<beans::PropertyValue> aGrabBagProps; 1817 xPropertySet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= aGrabBagProps; 1818 auto p3DEffectProps = std::find_if(std::cbegin(aGrabBagProps), std::cend(aGrabBagProps), 1819 [](const PropertyValue& rProp) { return rProp.Name == "3DEffectProperties"; }); 1820 if (p3DEffectProps != std::cend(aGrabBagProps)) 1821 { 1822 uno::Sequence<beans::PropertyValue> a3DEffectProps; 1823 p3DEffectProps->Value >>= a3DEffectProps; 1824 auto pCameraProps = std::find_if(std::cbegin(a3DEffectProps), std::cend(a3DEffectProps), 1825 [](const PropertyValue& rProp) { return rProp.Name == "Camera"; }); 1826 if (pCameraProps != std::cend(a3DEffectProps)) 1827 { 1828 uno::Sequence<beans::PropertyValue> aCameraProps; 1829 pCameraProps->Value >>= aCameraProps; 1830 auto pZRotationProp = std::find_if(std::cbegin(aCameraProps), std::cend(aCameraProps), 1831 [](const PropertyValue& rProp) { return rProp.Name == "rotRev"; }); 1832 if (pZRotationProp != std::cend(aCameraProps)) 1833 { 1834 sal_Int32 nTmp = 0; 1835 pZRotationProp->Value >>= nTmp; 1836 nCameraRotation = NormAngle36000(Degree100(nTmp / -600)); 1837 } 1838 } 1839 } 1840 } 1841 } 1842 1843 // OOXML flips shapes before rotating them. 1844 if(bFlipH != bFlipV) 1845 nRotation = Degree100(nRotation.get() * -1 + 36000); 1846 1847 WriteTransformation(rXShape, tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)), nXmlNamespace, 1848 bFlipHWrite, bFlipVWrite, ExportRotateClockwisify(nRotation + nCameraRotation), IsGroupShape( rXShape )); 1849 } 1850 1851 static OUString lcl_GetTarget(const css::uno::Reference<css::frame::XModel>& xModel, OUString& rURL) 1852 { 1853 Reference<drawing::XDrawPagesSupplier> xDPS(xModel, uno::UNO_QUERY_THROW); 1854 Reference<drawing::XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW); 1855 sal_uInt32 nPageCount = xDrawPages->getCount(); 1856 OUString sTarget; 1857 1858 for (sal_uInt32 i = 0; i < nPageCount; ++i) 1859 { 1860 Reference<XDrawPage> xDrawPage; 1861 xDrawPages->getByIndex(i) >>= xDrawPage; 1862 Reference<container::XNamed> xNamed(xDrawPage, UNO_QUERY); 1863 if (!xNamed) 1864 continue; 1865 OUString sSlideName = "#" + xNamed->getName(); 1866 if (rURL == sSlideName) 1867 { 1868 sTarget = "slide" + OUString::number(i + 1) + ".xml"; 1869 break; 1870 } 1871 } 1872 1873 return sTarget; 1874 } 1875 1876 void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bIsField, sal_Int32 nElement, 1877 bool bCheckDirect,bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, 1878 sal_Int16 nScriptType, const Reference< XPropertySet >& rXShapePropSet) 1879 { 1880 Reference< XPropertySet > rXPropSet = rRun; 1881 Reference< XPropertyState > rXPropState( rRun, UNO_QUERY ); 1882 OUString usLanguage; 1883 PropertyState eState; 1884 bool bComplex = ( nScriptType == css::i18n::ScriptType::COMPLEX ); 1885 const char* bold = "0"; 1886 const char* italic = nullptr; 1887 const char* underline = nullptr; 1888 const char* strikeout = nullptr; 1889 const char* cap = nullptr; 1890 sal_Int32 nSize = 1800; 1891 sal_Int32 nCharEscapement = 0; 1892 sal_Int32 nCharKerning = 0; 1893 1894 if ( nElement == XML_endParaRPr && rbOverridingCharHeight ) 1895 { 1896 nSize = rnCharHeight; 1897 } 1898 else if (GetProperty(rXPropSet, "CharHeight")) 1899 { 1900 nSize = static_cast<sal_Int32>(100*(*o3tl::doAccess<float>(mAny))); 1901 if ( nElement == XML_rPr ) 1902 { 1903 rbOverridingCharHeight = true; 1904 rnCharHeight = nSize; 1905 } 1906 } 1907 1908 if (GetProperty(rXPropSet, "CharKerning")) 1909 nCharKerning = static_cast<sal_Int32>(*o3tl::doAccess<sal_Int16>(mAny)); 1910 /** While setting values in propertymap, 1911 * CharKerning converted using GetTextSpacingPoint 1912 * i.e set @ https://opengrok.libreoffice.org/xref/core/oox/source/drawingml/textcharacterproperties.cxx#129 1913 * therefore to get original value CharKerning need to be convert. 1914 * https://opengrok.libreoffice.org/xref/core/oox/source/drawingml/drawingmltypes.cxx#95 1915 **/ 1916 nCharKerning = ((nCharKerning * 720)-360) / 254; 1917 1918 if ((bComplex && GetProperty(rXPropSet, "CharWeightComplex")) 1919 || GetProperty(rXPropSet, "CharWeight")) 1920 { 1921 if ( *o3tl::doAccess<float>(mAny) >= awt::FontWeight::SEMIBOLD ) 1922 bold = "1"; 1923 } 1924 1925 if ((bComplex && GetProperty(rXPropSet, "CharPostureComplex")) 1926 || GetProperty(rXPropSet, "CharPosture")) 1927 switch ( *o3tl::doAccess<awt::FontSlant>(mAny) ) 1928 { 1929 case awt::FontSlant_OBLIQUE : 1930 case awt::FontSlant_ITALIC : 1931 italic = "1"; 1932 break; 1933 default: 1934 break; 1935 } 1936 1937 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderline", eState) 1938 && eState == beans::PropertyState_DIRECT_VALUE) 1939 || GetProperty(rXPropSet, "CharUnderline")) 1940 { 1941 switch ( *o3tl::doAccess<sal_Int16>(mAny) ) 1942 { 1943 case awt::FontUnderline::SINGLE : 1944 underline = "sng"; 1945 break; 1946 case awt::FontUnderline::DOUBLE : 1947 underline = "dbl"; 1948 break; 1949 case awt::FontUnderline::DOTTED : 1950 underline = "dotted"; 1951 break; 1952 case awt::FontUnderline::DASH : 1953 underline = "dash"; 1954 break; 1955 case awt::FontUnderline::LONGDASH : 1956 underline = "dashLong"; 1957 break; 1958 case awt::FontUnderline::DASHDOT : 1959 underline = "dotDash"; 1960 break; 1961 case awt::FontUnderline::DASHDOTDOT : 1962 underline = "dotDotDash"; 1963 break; 1964 case awt::FontUnderline::WAVE : 1965 underline = "wavy"; 1966 break; 1967 case awt::FontUnderline::DOUBLEWAVE : 1968 underline = "wavyDbl"; 1969 break; 1970 case awt::FontUnderline::BOLD : 1971 underline = "heavy"; 1972 break; 1973 case awt::FontUnderline::BOLDDOTTED : 1974 underline = "dottedHeavy"; 1975 break; 1976 case awt::FontUnderline::BOLDDASH : 1977 underline = "dashHeavy"; 1978 break; 1979 case awt::FontUnderline::BOLDLONGDASH : 1980 underline = "dashLongHeavy"; 1981 break; 1982 case awt::FontUnderline::BOLDDASHDOT : 1983 underline = "dotDashHeavy"; 1984 break; 1985 case awt::FontUnderline::BOLDDASHDOTDOT : 1986 underline = "dotDotDashHeavy"; 1987 break; 1988 case awt::FontUnderline::BOLDWAVE : 1989 underline = "wavyHeavy"; 1990 break; 1991 } 1992 } 1993 1994 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharStrikeout", eState) 1995 && eState == beans::PropertyState_DIRECT_VALUE) 1996 || GetProperty(rXPropSet, "CharStrikeout")) 1997 { 1998 switch ( *o3tl::doAccess<sal_Int16>(mAny) ) 1999 { 2000 case awt::FontStrikeout::NONE : 2001 strikeout = "noStrike"; 2002 break; 2003 case awt::FontStrikeout::SINGLE : 2004 // LibO supports further values of character 2005 // strikeout, OOXML standard (20.1.10.78, 2006 // ST_TextStrikeType) however specifies only 2007 // 3 - single, double and none. Approximate 2008 // the remaining ones by single strike (better 2009 // some strike than none at all). 2010 // TODO: figure out how to do this better 2011 case awt::FontStrikeout::BOLD : 2012 case awt::FontStrikeout::SLASH : 2013 case awt::FontStrikeout::X : 2014 strikeout = "sngStrike"; 2015 break; 2016 case awt::FontStrikeout::DOUBLE : 2017 strikeout = "dblStrike"; 2018 break; 2019 } 2020 } 2021 2022 bool bLang = false; 2023 switch(nScriptType) 2024 { 2025 case css::i18n::ScriptType::ASIAN: 2026 bLang = GetProperty(rXPropSet, "CharLocaleAsian"); break; 2027 case css::i18n::ScriptType::COMPLEX: 2028 bLang = GetProperty(rXPropSet, "CharLocaleComplex"); break; 2029 default: 2030 bLang = GetProperty(rXPropSet, "CharLocale"); break; 2031 } 2032 2033 if (bLang) 2034 { 2035 css::lang::Locale aLocale; 2036 mAny >>= aLocale; 2037 LanguageTag aLanguageTag( aLocale); 2038 if (!aLanguageTag.isSystemLocale()) 2039 usLanguage = aLanguageTag.getBcp47MS(); 2040 } 2041 2042 if (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapement", eState) 2043 && eState == beans::PropertyState_DIRECT_VALUE) 2044 mAny >>= nCharEscapement; 2045 2046 if (nCharEscapement 2047 && (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapementHeight", eState) 2048 && eState == beans::PropertyState_DIRECT_VALUE)) 2049 { 2050 sal_uInt32 nCharEscapementHeight = 0; 2051 mAny >>= nCharEscapementHeight; 2052 nSize = (nSize * nCharEscapementHeight) / 100; 2053 // MSO uses default ~58% size 2054 nSize = (nSize / 0.58); 2055 } 2056 2057 if (GetProperty(rXPropSet, "CharCaseMap")) 2058 { 2059 switch ( *o3tl::doAccess<sal_Int16>(mAny) ) 2060 { 2061 case CaseMap::UPPERCASE : 2062 cap = "all"; 2063 break; 2064 case CaseMap::SMALLCAPS : 2065 cap = "small"; 2066 break; 2067 } 2068 } 2069 2070 mpFS->startElementNS( XML_a, nElement, 2071 XML_b, bold, 2072 XML_i, italic, 2073 XML_lang, sax_fastparser::UseIf(usLanguage, !usLanguage.isEmpty()), 2074 XML_sz, OString::number(nSize), 2075 // For Condensed character spacing spc value is negative. 2076 XML_spc, sax_fastparser::UseIf(OString::number(nCharKerning), nCharKerning != 0), 2077 XML_strike, strikeout, 2078 XML_u, underline, 2079 XML_baseline, sax_fastparser::UseIf(OString::number(nCharEscapement*1000), nCharEscapement != 0), 2080 XML_cap, cap ); 2081 2082 // Fontwork-shapes in LO have text outline and fill from shape stroke and shape fill 2083 // PowerPoint has this as run properties 2084 if (IsFontworkShape(rXShapePropSet)) 2085 { 2086 WriteOutline(rXShapePropSet); 2087 WriteBlipOrNormalFill(rXShapePropSet, "Graphic"); 2088 WriteShapeEffects(rXShapePropSet); 2089 } 2090 else 2091 { 2092 // mso doesn't like text color to be placed after typeface 2093 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharColor", eState) 2094 && eState == beans::PropertyState_DIRECT_VALUE) 2095 || GetProperty(rXPropSet, "CharColor")) 2096 { 2097 ::Color color( ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny) ); 2098 SAL_INFO("oox.shape", "run color: " << sal_uInt32(color) << " auto: " << sal_uInt32(COL_AUTO)); 2099 2100 // WriteSolidFill() handles MAX_PERCENT as "no transparency". 2101 sal_Int32 nTransparency = MAX_PERCENT; 2102 if (rXPropSet->getPropertySetInfo()->hasPropertyByName("CharTransparence")) 2103 { 2104 rXPropSet->getPropertyValue("CharTransparence") >>= nTransparency; 2105 // UNO scale is 0..100, OOXML scale is 0..100000; also UNO tracks transparency, OOXML 2106 // tracks opacity. 2107 nTransparency = MAX_PERCENT - (nTransparency * PER_PERCENT); 2108 } 2109 2110 // tdf#104219 In LibreOffice and MS Office, there are two types of colors: 2111 // Automatic and Fixed. OOXML is setting automatic color, by not providing color. 2112 if( color != COL_AUTO ) 2113 { 2114 color.SetAlpha(255); 2115 // TODO: special handle embossed/engraved 2116 WriteSolidFill(color, nTransparency); 2117 } 2118 } 2119 } 2120 2121 // tdf#128096, exporting XML_highlight to docx already works fine, 2122 // so make sure this code is only run when exporting to pptx, just in case 2123 if (GetDocumentType() == DOCUMENT_PPTX) 2124 { 2125 if (GetProperty(rXPropSet, "CharBackColor")) 2126 { 2127 ::Color color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny)); 2128 if( color != COL_AUTO ) 2129 { 2130 mpFS->startElementNS(XML_a, XML_highlight); 2131 WriteColor( color ); 2132 mpFS->endElementNS( XML_a, XML_highlight ); 2133 } 2134 } 2135 } 2136 2137 if (underline 2138 && ((bCheckDirect 2139 && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderlineColor", eState) 2140 && eState == beans::PropertyState_DIRECT_VALUE) 2141 || GetProperty(rXPropSet, "CharUnderlineColor"))) 2142 { 2143 ::Color color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny)); 2144 // if color is automatic, then we shouldn't write information about color but to take color from character 2145 if( color != COL_AUTO ) 2146 { 2147 mpFS->startElementNS(XML_a, XML_uFill); 2148 WriteSolidFill( color ); 2149 mpFS->endElementNS( XML_a, XML_uFill ); 2150 } 2151 else 2152 { 2153 mpFS->singleElementNS(XML_a, XML_uFillTx); 2154 } 2155 } 2156 2157 if (GetProperty(rXPropSet, "CharFontName")) 2158 { 2159 const char* const pitch = nullptr; 2160 const char* const charset = nullptr; 2161 OUString usTypeface; 2162 2163 mAny >>= usTypeface; 2164 OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) ); 2165 if (!aSubstName.isEmpty()) 2166 usTypeface = aSubstName; 2167 2168 mpFS->singleElementNS( XML_a, XML_latin, 2169 XML_typeface, usTypeface, 2170 XML_pitchFamily, pitch, 2171 XML_charset, charset ); 2172 } 2173 2174 if ((bComplex 2175 && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameComplex", eState) 2176 && eState == beans::PropertyState_DIRECT_VALUE)) 2177 || (!bComplex 2178 && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameAsian", eState) 2179 && eState == beans::PropertyState_DIRECT_VALUE))) 2180 { 2181 const char* const pitch = nullptr; 2182 const char* const charset = nullptr; 2183 OUString usTypeface; 2184 2185 mAny >>= usTypeface; 2186 OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) ); 2187 if (!aSubstName.isEmpty()) 2188 usTypeface = aSubstName; 2189 2190 mpFS->singleElementNS( XML_a, bComplex ? XML_cs : XML_ea, 2191 XML_typeface, usTypeface, 2192 XML_pitchFamily, pitch, 2193 XML_charset, charset ); 2194 } 2195 2196 if( bIsField ) 2197 { 2198 Reference< XTextField > rXTextField; 2199 if (GetProperty(rXPropSet, "TextField")) 2200 mAny >>= rXTextField; 2201 if( rXTextField.is() ) 2202 rXPropSet.set( rXTextField, UNO_QUERY ); 2203 } 2204 2205 // field properties starts here 2206 if (GetProperty(rXPropSet, "URL")) 2207 { 2208 OUString sURL; 2209 2210 mAny >>= sURL; 2211 if (!sURL.isEmpty()) 2212 { 2213 if (!sURL.match("#action?jump=")) 2214 { 2215 bool bExtURL = URLTransformer().isExternalURL(sURL); 2216 sURL = bExtURL ? sURL : lcl_GetTarget(GetFB()->getModel(), sURL); 2217 2218 OUString sRelId 2219 = mpFB->addRelation(mpFS->getOutputStream(), 2220 bExtURL ? oox::getRelationship(Relationship::HYPERLINK) 2221 : oox::getRelationship(Relationship::SLIDE), 2222 sURL, bExtURL); 2223 2224 if (bExtURL) 2225 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId); 2226 else 2227 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId, 2228 XML_action, "ppaction://hlinksldjump"); 2229 } 2230 else 2231 { 2232 sal_Int32 nIndex = sURL.indexOf('='); 2233 OUString aDestination(sURL.copy(nIndex + 1)); 2234 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), "", XML_action, 2235 "ppaction://hlinkshowjump?jump=" + aDestination); 2236 } 2237 } 2238 } 2239 mpFS->endElementNS( XML_a, nElement ); 2240 } 2241 2242 OUString DrawingML::GetFieldValue( const css::uno::Reference< css::text::XTextRange >& rRun, bool& bIsURLField ) 2243 { 2244 Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY ); 2245 OUString aFieldType, aFieldValue; 2246 2247 if (GetProperty(rXPropSet, "TextPortionType")) 2248 { 2249 aFieldType = *o3tl::doAccess<OUString>(mAny); 2250 SAL_INFO("oox.shape", "field type: " << aFieldType); 2251 } 2252 2253 if( aFieldType == "TextField" ) 2254 { 2255 Reference< XTextField > rXTextField; 2256 if (GetProperty(rXPropSet, "TextField")) 2257 mAny >>= rXTextField; 2258 if( rXTextField.is() ) 2259 { 2260 rXPropSet.set( rXTextField, UNO_QUERY ); 2261 if( rXPropSet.is() ) 2262 { 2263 OUString aFieldKind( rXTextField->getPresentation( true ) ); 2264 SAL_INFO("oox.shape", "field kind: " << aFieldKind); 2265 if( aFieldKind == "Page" ) 2266 { 2267 aFieldValue = "slidenum"; 2268 } 2269 else if( aFieldKind == "Pages" ) 2270 { 2271 aFieldValue = "slidecount"; 2272 } 2273 else if( aFieldKind == "PageName" ) 2274 { 2275 aFieldValue = "slidename"; 2276 } 2277 else if( aFieldKind == "URL" ) 2278 { 2279 bIsURLField = true; 2280 if (GetProperty(rXPropSet, "Representation")) 2281 mAny >>= aFieldValue; 2282 2283 } 2284 else if(aFieldKind == "Date") 2285 { 2286 sal_Int32 nNumFmt = -1; 2287 rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt; 2288 switch(static_cast<SvxDateFormat>(nNumFmt)) 2289 { 2290 case SvxDateFormat::StdSmall: 2291 case SvxDateFormat::A: aFieldValue = "datetime"; // 13/02/96 2292 break; 2293 case SvxDateFormat::B: aFieldValue = "datetime1"; // 13/02/1996 2294 break; 2295 case SvxDateFormat::StdBig: 2296 case SvxDateFormat::D: aFieldValue = "datetime3"; // 13 February 1996 2297 break; 2298 default: break; 2299 } 2300 } 2301 else if(aFieldKind == "ExtTime") 2302 { 2303 sal_Int32 nNumFmt = -1; 2304 rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt; 2305 switch(static_cast<SvxTimeFormat>(nNumFmt)) 2306 { 2307 case SvxTimeFormat::Standard: 2308 case SvxTimeFormat::HH24_MM_SS: 2309 aFieldValue = "datetime11"; // 13:49:38 2310 break; 2311 case SvxTimeFormat::HH24_MM: 2312 aFieldValue = "datetime10"; // 13:49 2313 break; 2314 case SvxTimeFormat::HH12_MM: 2315 aFieldValue = "datetime12"; // 01:49 PM 2316 break; 2317 case SvxTimeFormat::HH12_MM_SS: 2318 aFieldValue = "datetime13"; // 01:49:38 PM 2319 break; 2320 default: break; 2321 } 2322 } 2323 else if(aFieldKind == "ExtFile") 2324 { 2325 sal_Int32 nNumFmt = -1; 2326 rXPropSet->getPropertyValue(UNO_TC_PROP_FILE_FORMAT) >>= nNumFmt; 2327 switch(nNumFmt) 2328 { 2329 case 0: aFieldValue = "file"; // Path/File name 2330 break; 2331 case 1: aFieldValue = "file1"; // Path 2332 break; 2333 case 2: aFieldValue = "file2"; // File name without extension 2334 break; 2335 case 3: aFieldValue = "file3"; // File name with extension 2336 } 2337 } 2338 else if(aFieldKind == "Author") 2339 { 2340 aFieldValue = "author"; 2341 } 2342 } 2343 } 2344 } 2345 return aFieldValue; 2346 } 2347 2348 void DrawingML::WriteRun( const Reference< XTextRange >& rRun, 2349 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, 2350 const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet) 2351 { 2352 Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY ); 2353 sal_Int16 nLevel = -1; 2354 if (GetProperty(rXPropSet, "NumberingLevel")) 2355 mAny >>= nLevel; 2356 2357 bool bNumberingIsNumber = true; 2358 if (GetProperty(rXPropSet, "NumberingIsNumber")) 2359 mAny >>= bNumberingIsNumber; 2360 2361 bool bIsURLField = false; 2362 OUString sFieldValue = GetFieldValue( rRun, bIsURLField ); 2363 bool bWriteField = !( sFieldValue.isEmpty() || bIsURLField ); 2364 2365 OUString sText = rRun->getString(); 2366 2367 //if there is no text following the bullet, add a space after the bullet 2368 if (nLevel !=-1 && bNumberingIsNumber && sText.isEmpty() ) 2369 sText=" "; 2370 2371 if ( bIsURLField ) 2372 sText = sFieldValue; 2373 2374 if( sText.isEmpty()) 2375 { 2376 Reference< XPropertySet > xPropSet( rRun, UNO_QUERY ); 2377 2378 try 2379 { 2380 if( !xPropSet.is() || !( xPropSet->getPropertyValue( "PlaceholderText" ) >>= sText ) ) 2381 return; 2382 if( sText.isEmpty() ) 2383 return; 2384 } 2385 catch (const Exception &) 2386 { 2387 return; 2388 } 2389 } 2390 2391 if (sText == "\n") 2392 { 2393 mpFS->singleElementNS(XML_a, XML_br); 2394 } 2395 else 2396 { 2397 if( bWriteField ) 2398 { 2399 OString sUUID(comphelper::xml::generateGUIDString()); 2400 mpFS->startElementNS( XML_a, XML_fld, 2401 XML_id, sUUID.getStr(), 2402 XML_type, sFieldValue ); 2403 } 2404 else 2405 { 2406 mpFS->startElementNS(XML_a, XML_r); 2407 } 2408 2409 Reference< XPropertySet > xPropSet( rRun, uno::UNO_QUERY ); 2410 2411 WriteRunProperties( xPropSet, bIsURLField, XML_rPr, true, rbOverridingCharHeight, rnCharHeight, GetScriptType(sText), rXShapePropSet); 2412 mpFS->startElementNS(XML_a, XML_t); 2413 mpFS->writeEscaped( sText ); 2414 mpFS->endElementNS( XML_a, XML_t ); 2415 2416 if( bWriteField ) 2417 mpFS->endElementNS( XML_a, XML_fld ); 2418 else 2419 mpFS->endElementNS( XML_a, XML_r ); 2420 } 2421 } 2422 2423 static OUString GetAutoNumType(SvxNumType nNumberingType, bool bSDot, bool bPBehind, bool bPBoth) 2424 { 2425 OUString sPrefixSuffix; 2426 2427 if (bPBoth) 2428 sPrefixSuffix = "ParenBoth"; 2429 else if (bPBehind) 2430 sPrefixSuffix = "ParenR"; 2431 else if (bSDot) 2432 sPrefixSuffix = "Period"; 2433 2434 switch( nNumberingType ) 2435 { 2436 case SVX_NUM_CHARS_UPPER_LETTER_N : 2437 case SVX_NUM_CHARS_UPPER_LETTER : 2438 return "alphaUc" + sPrefixSuffix; 2439 2440 case SVX_NUM_CHARS_LOWER_LETTER_N : 2441 case SVX_NUM_CHARS_LOWER_LETTER : 2442 return "alphaLc" + sPrefixSuffix; 2443 2444 case SVX_NUM_ROMAN_UPPER : 2445 return "romanUc" + sPrefixSuffix; 2446 2447 case SVX_NUM_ROMAN_LOWER : 2448 return "romanLc" + sPrefixSuffix; 2449 2450 case SVX_NUM_ARABIC : 2451 { 2452 if (sPrefixSuffix.isEmpty()) 2453 return "arabicPlain"; 2454 else 2455 return "arabic" + sPrefixSuffix; 2456 } 2457 default: 2458 break; 2459 } 2460 2461 return OUString(); 2462 } 2463 2464 void DrawingML::WriteParagraphNumbering(const Reference< XPropertySet >& rXPropSet, float fFirstCharHeight, sal_Int16 nLevel ) 2465 { 2466 if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules")) 2467 return; 2468 2469 Reference< XIndexAccess > rXIndexAccess; 2470 2471 if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount()) 2472 return; 2473 2474 SAL_INFO("oox.shape", "numbering rules"); 2475 2476 Sequence<PropertyValue> aPropertySequence; 2477 rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence; 2478 2479 if (!aPropertySequence.hasElements()) 2480 return; 2481 2482 SvxNumType nNumberingType = SVX_NUM_NUMBER_NONE; 2483 bool bSDot = false; 2484 bool bPBehind = false; 2485 bool bPBoth = false; 2486 sal_Unicode aBulletChar = 0x2022; // a bullet 2487 awt::FontDescriptor aFontDesc; 2488 bool bHasFontDesc = false; 2489 uno::Reference<graphic::XGraphic> xGraphic; 2490 sal_Int16 nBulletRelSize = 0; 2491 sal_Int16 nStartWith = 1; 2492 ::Color nBulletColor; 2493 bool bHasBulletColor = false; 2494 awt::Size aGraphicSize; 2495 2496 for ( const PropertyValue& rPropValue : std::as_const(aPropertySequence) ) 2497 { 2498 OUString aPropName( rPropValue.Name ); 2499 SAL_INFO("oox.shape", "pro name: " << aPropName); 2500 if ( aPropName == "NumberingType" ) 2501 { 2502 nNumberingType = static_cast<SvxNumType>(*o3tl::doAccess<sal_Int16>(rPropValue.Value)); 2503 } 2504 else if ( aPropName == "Prefix" ) 2505 { 2506 if( *o3tl::doAccess<OUString>(rPropValue.Value) == ")") 2507 bPBoth = true; 2508 } 2509 else if ( aPropName == "Suffix" ) 2510 { 2511 auto s = o3tl::doAccess<OUString>(rPropValue.Value); 2512 if( *s == ".") 2513 bSDot = true; 2514 else if( *s == ")") 2515 bPBehind = true; 2516 } 2517 else if(aPropName == "BulletColor") 2518 { 2519 nBulletColor = ::Color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(rPropValue.Value)); 2520 bHasBulletColor = true; 2521 } 2522 else if ( aPropName == "BulletChar" ) 2523 { 2524 aBulletChar = (*o3tl::doAccess<OUString>(rPropValue.Value))[ 0 ]; 2525 } 2526 else if ( aPropName == "BulletFont" ) 2527 { 2528 aFontDesc = *o3tl::doAccess<awt::FontDescriptor>(rPropValue.Value); 2529 bHasFontDesc = true; 2530 2531 // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font, 2532 // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used. 2533 // Because there might exist a lot of damaged documents I added this two lines 2534 // which fixes the bullet problem for the export. 2535 if ( aFontDesc.Name.equalsIgnoreAsciiCase("StarSymbol") ) 2536 aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252; 2537 2538 } 2539 else if ( aPropName == "BulletRelSize" ) 2540 { 2541 nBulletRelSize = *o3tl::doAccess<sal_Int16>(rPropValue.Value); 2542 } 2543 else if ( aPropName == "StartWith" ) 2544 { 2545 nStartWith = *o3tl::doAccess<sal_Int16>(rPropValue.Value); 2546 } 2547 else if (aPropName == "GraphicBitmap") 2548 { 2549 auto xBitmap = rPropValue.Value.get<uno::Reference<awt::XBitmap>>(); 2550 xGraphic.set(xBitmap, uno::UNO_QUERY); 2551 } 2552 else if ( aPropName == "GraphicSize" ) 2553 { 2554 aGraphicSize = *o3tl::doAccess<awt::Size>(rPropValue.Value); 2555 SAL_INFO("oox.shape", "graphic size: " << aGraphicSize.Width << "x" << aGraphicSize.Height); 2556 } 2557 } 2558 2559 if (nNumberingType == SVX_NUM_NUMBER_NONE) 2560 return; 2561 2562 Graphic aGraphic(xGraphic); 2563 if (xGraphic.is() && aGraphic.GetType() != GraphicType::NONE) 2564 { 2565 tools::Long nFirstCharHeightMm = TransformMetric(fFirstCharHeight * 100.f, FieldUnit::POINT, FieldUnit::MM); 2566 float fBulletSizeRel = aGraphicSize.Height / static_cast<float>(nFirstCharHeightMm) / OOX_BULLET_LIST_SCALE_FACTOR; 2567 2568 OUString sRelationId; 2569 2570 if (fBulletSizeRel < 1.0f) 2571 { 2572 // Add padding to get the bullet point centered in PPT 2573 Size aDestSize(64, 64); 2574 float fBulletSizeRelX = fBulletSizeRel / aGraphicSize.Height * aGraphicSize.Width; 2575 tools::Long nPaddingX = std::max<tools::Long>(0, std::lround((aDestSize.Width() - fBulletSizeRelX * aDestSize.Width()) / 2.f)); 2576 tools::Long nPaddingY = std::lround((aDestSize.Height() - fBulletSizeRel * aDestSize.Height()) / 2.f); 2577 tools::Rectangle aDestRect(nPaddingX, nPaddingY, aDestSize.Width() - nPaddingX, aDestSize.Height() - nPaddingY); 2578 2579 AlphaMask aMask(aDestSize); 2580 aMask.Erase(255); 2581 BitmapEx aSourceBitmap(aGraphic.GetBitmapEx()); 2582 aSourceBitmap.Scale(aDestRect.GetSize()); 2583 tools::Rectangle aSourceRect(Point(0, 0), aDestRect.GetSize()); 2584 BitmapEx aDestBitmap(Bitmap(aDestSize, vcl::PixelFormat::N24_BPP), aMask); 2585 aDestBitmap.CopyPixel(aDestRect, aSourceRect, &aSourceBitmap); 2586 Graphic aDestGraphic(aDestBitmap); 2587 sRelationId = WriteImage(aDestGraphic); 2588 fBulletSizeRel = 1.0f; 2589 } 2590 else 2591 { 2592 sRelationId = WriteImage(aGraphic); 2593 } 2594 2595 mpFS->singleElementNS( XML_a, XML_buSzPct, 2596 XML_val, OString::number(std::min<sal_Int32>(std::lround(100000.f * fBulletSizeRel), 400000))); 2597 mpFS->startElementNS(XML_a, XML_buBlip); 2598 mpFS->singleElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelationId); 2599 mpFS->endElementNS( XML_a, XML_buBlip ); 2600 } 2601 else 2602 { 2603 if(bHasBulletColor) 2604 { 2605 if (nBulletColor == COL_AUTO ) 2606 { 2607 nBulletColor = ::Color(ColorTransparency, mbIsBackgroundDark ? 0xffffff : 0x000000); 2608 } 2609 mpFS->startElementNS(XML_a, XML_buClr); 2610 WriteColor( nBulletColor ); 2611 mpFS->endElementNS( XML_a, XML_buClr ); 2612 } 2613 2614 if( nBulletRelSize && nBulletRelSize != 100 ) 2615 mpFS->singleElementNS( XML_a, XML_buSzPct, 2616 XML_val, OString::number(std::clamp<sal_Int32>(1000*nBulletRelSize, 25000, 400000))); 2617 if( bHasFontDesc ) 2618 { 2619 if ( SVX_NUM_CHAR_SPECIAL == nNumberingType ) 2620 aBulletChar = SubstituteBullet( aBulletChar, aFontDesc ); 2621 mpFS->singleElementNS( XML_a, XML_buFont, 2622 XML_typeface, aFontDesc.Name, 2623 XML_charset, sax_fastparser::UseIf("2", aFontDesc.CharSet == awt::CharSet::SYMBOL)); 2624 } 2625 2626 OUString aAutoNumType = GetAutoNumType( nNumberingType, bSDot, bPBehind, bPBoth ); 2627 2628 if (!aAutoNumType.isEmpty()) 2629 { 2630 mpFS->singleElementNS(XML_a, XML_buAutoNum, 2631 XML_type, aAutoNumType, 2632 XML_startAt, sax_fastparser::UseIf(OString::number(nStartWith), nStartWith > 1)); 2633 } 2634 else 2635 { 2636 mpFS->singleElementNS(XML_a, XML_buChar, XML_char, OUString(aBulletChar)); 2637 } 2638 } 2639 } 2640 2641 void DrawingML::WriteParagraphTabStops(const Reference<XPropertySet>& rXPropSet) 2642 { 2643 css::uno::Sequence<css::style::TabStop> aTabStops; 2644 if (GetProperty(rXPropSet, "ParaTabStops")) 2645 aTabStops = *o3tl::doAccess<css::uno::Sequence<css::style::TabStop>>(mAny); 2646 2647 if (aTabStops.getLength() > 0) 2648 mpFS->startElementNS(XML_a, XML_tabLst); 2649 2650 for (const css::style::TabStop& rTabStop : std::as_const(aTabStops)) 2651 { 2652 OString sPosition = OString::number(GetPointFromCoordinate(rTabStop.Position)); 2653 OString sAlignment; 2654 switch (rTabStop.Alignment) 2655 { 2656 case css::style::TabAlign_DECIMAL: 2657 sAlignment = "dec"; 2658 break; 2659 case css::style::TabAlign_RIGHT: 2660 sAlignment = "r"; 2661 break; 2662 case css::style::TabAlign_CENTER: 2663 sAlignment = "ctr"; 2664 break; 2665 case css::style::TabAlign_LEFT: 2666 default: 2667 sAlignment = "l"; 2668 } 2669 mpFS->singleElementNS(XML_a, XML_tab, XML_algn, sAlignment, XML_pos, sPosition); 2670 } 2671 if (aTabStops.getLength() > 0) 2672 mpFS->endElementNS(XML_a, XML_tabLst); 2673 } 2674 2675 bool DrawingML::IsGroupShape( const Reference< XShape >& rXShape ) 2676 { 2677 bool bRet = false; 2678 if ( rXShape.is() ) 2679 { 2680 uno::Reference<lang::XServiceInfo> xServiceInfo(rXShape, uno::UNO_QUERY_THROW); 2681 bRet = xServiceInfo->supportsService("com.sun.star.drawing.GroupShape"); 2682 } 2683 return bRet; 2684 } 2685 2686 bool DrawingML::IsDiagram(const Reference<XShape>& rXShape) 2687 { 2688 uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY); 2689 if (!xPropSet.is()) 2690 return false; 2691 2692 // if the shape doesn't have the InteropGrabBag property, it's not a diagram 2693 uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo(); 2694 OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; 2695 if (!xPropSetInfo->hasPropertyByName(aName)) 2696 return false; 2697 2698 uno::Sequence<beans::PropertyValue> propList; 2699 xPropSet->getPropertyValue(aName) >>= propList; 2700 return std::any_of(std::cbegin(propList), std::cend(propList), 2701 [](const beans::PropertyValue& rProp) { 2702 // if we find any of the diagram components, it's a diagram 2703 OUString propName = rProp.Name; 2704 return propName == "OOXData" || propName == "OOXLayout" || propName == "OOXStyle" 2705 || propName == "OOXColor" || propName == "OOXDrawing"; 2706 }); 2707 } 2708 2709 sal_Int32 DrawingML::getBulletMarginIndentation (const Reference< XPropertySet >& rXPropSet,sal_Int16 nLevel, std::u16string_view propName) 2710 { 2711 if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules")) 2712 return 0; 2713 2714 Reference< XIndexAccess > rXIndexAccess; 2715 2716 if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount()) 2717 return 0; 2718 2719 SAL_INFO("oox.shape", "numbering rules"); 2720 2721 Sequence<PropertyValue> aPropertySequence; 2722 rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence; 2723 2724 if (!aPropertySequence.hasElements()) 2725 return 0; 2726 2727 for ( const PropertyValue& rPropValue : std::as_const(aPropertySequence) ) 2728 { 2729 OUString aPropName( rPropValue.Name ); 2730 SAL_INFO("oox.shape", "pro name: " << aPropName); 2731 if ( aPropName == propName ) 2732 return *o3tl::doAccess<sal_Int32>(rPropValue.Value); 2733 } 2734 2735 return 0; 2736 } 2737 2738 const char* DrawingML::GetAlignment( style::ParagraphAdjust nAlignment ) 2739 { 2740 const char* sAlignment = nullptr; 2741 2742 switch( nAlignment ) 2743 { 2744 case style::ParagraphAdjust_CENTER: 2745 sAlignment = "ctr"; 2746 break; 2747 case style::ParagraphAdjust_RIGHT: 2748 sAlignment = "r"; 2749 break; 2750 case style::ParagraphAdjust_BLOCK: 2751 sAlignment = "just"; 2752 break; 2753 default: 2754 ; 2755 } 2756 2757 return sAlignment; 2758 } 2759 2760 void DrawingML::WriteLinespacing( const LineSpacing& rSpacing ) 2761 { 2762 if( rSpacing.Mode == LineSpacingMode::PROP ) 2763 { 2764 mpFS->singleElementNS( XML_a, XML_spcPct, 2765 XML_val, OString::number(static_cast<sal_Int32>(rSpacing.Height)*1000)); 2766 } 2767 else 2768 { 2769 mpFS->singleElementNS( XML_a, XML_spcPts, 2770 XML_val, OString::number(std::lround(rSpacing.Height / 25.4 * 72))); 2771 } 2772 } 2773 2774 void DrawingML::WriteParagraphProperties( const Reference< XTextContent >& rParagraph, float fFirstCharHeight) 2775 { 2776 Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY ); 2777 Reference< XPropertyState > rXPropState( rParagraph, UNO_QUERY ); 2778 PropertyState eState; 2779 2780 if( !rXPropSet.is() || !rXPropState.is() ) 2781 return; 2782 2783 sal_Int16 nLevel = -1; 2784 if (GetProperty(rXPropSet, "NumberingLevel")) 2785 mAny >>= nLevel; 2786 2787 sal_Int16 nTmp = sal_Int16(style::ParagraphAdjust_LEFT); 2788 if (GetProperty(rXPropSet, "ParaAdjust")) 2789 mAny >>= nTmp; 2790 style::ParagraphAdjust nAlignment = static_cast<style::ParagraphAdjust>(nTmp); 2791 2792 bool bHasLinespacing = false; 2793 LineSpacing aLineSpacing; 2794 if (GetPropertyAndState(rXPropSet, rXPropState, "ParaLineSpacing", eState) 2795 && eState == beans::PropertyState_DIRECT_VALUE) 2796 bHasLinespacing = ( mAny >>= aLineSpacing ); 2797 2798 bool bRtl = false; 2799 if (GetProperty(rXPropSet, "WritingMode")) 2800 { 2801 sal_Int16 nWritingMode; 2802 if( ( mAny >>= nWritingMode ) && nWritingMode == text::WritingMode2::RL_TB ) 2803 { 2804 bRtl = true; 2805 } 2806 } 2807 2808 sal_Int32 nParaLeftMargin = 0; 2809 sal_Int32 nParaFirstLineIndent = 0; 2810 2811 if (GetProperty(rXPropSet, "ParaLeftMargin")) 2812 mAny >>= nParaLeftMargin; 2813 if (GetProperty(rXPropSet, "ParaFirstLineIndent")) 2814 mAny >>= nParaFirstLineIndent; 2815 2816 sal_Int32 nParaTopMargin = 0; 2817 sal_Int32 nParaBottomMargin = 0; 2818 2819 if (GetProperty(rXPropSet, "ParaTopMargin")) 2820 mAny >>= nParaTopMargin; 2821 if (GetProperty(rXPropSet, "ParaBottomMargin")) 2822 mAny >>= nParaBottomMargin; 2823 2824 sal_Int32 nLeftMargin = getBulletMarginIndentation ( rXPropSet, nLevel,u"LeftMargin"); 2825 sal_Int32 nLineIndentation = getBulletMarginIndentation ( rXPropSet, nLevel,u"FirstLineOffset"); 2826 2827 if( !(nLevel != -1 2828 || nAlignment != style::ParagraphAdjust_LEFT 2829 || bHasLinespacing) ) 2830 return; 2831 2832 if (nParaLeftMargin) // For Paragraph 2833 mpFS->startElementNS( XML_a, XML_pPr, 2834 XML_lvl, sax_fastparser::UseIf(OString::number(nLevel), nLevel > 0), 2835 XML_marL, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nParaLeftMargin)), nParaLeftMargin > 0), 2836 XML_indent, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nParaFirstLineIndent)), nParaFirstLineIndent != 0), 2837 XML_algn, GetAlignment( nAlignment ), 2838 XML_rtl, sax_fastparser::UseIf(ToPsz10(bRtl), bRtl)); 2839 else 2840 mpFS->startElementNS( XML_a, XML_pPr, 2841 XML_lvl, sax_fastparser::UseIf(OString::number(nLevel), nLevel > 0), 2842 XML_marL, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeftMargin)), nLeftMargin > 0), 2843 XML_indent, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLineIndentation)), nLineIndentation != 0), 2844 XML_algn, GetAlignment( nAlignment ), 2845 XML_rtl, sax_fastparser::UseIf(ToPsz10(bRtl), bRtl)); 2846 2847 2848 if( bHasLinespacing ) 2849 { 2850 mpFS->startElementNS(XML_a, XML_lnSpc); 2851 WriteLinespacing( aLineSpacing ); 2852 mpFS->endElementNS( XML_a, XML_lnSpc ); 2853 } 2854 2855 if( nParaTopMargin != 0 ) 2856 { 2857 mpFS->startElementNS(XML_a, XML_spcBef); 2858 { 2859 mpFS->singleElementNS( XML_a, XML_spcPts, 2860 XML_val, OString::number(std::lround(nParaTopMargin / 25.4 * 72))); 2861 } 2862 mpFS->endElementNS( XML_a, XML_spcBef ); 2863 } 2864 2865 if( nParaBottomMargin != 0 ) 2866 { 2867 mpFS->startElementNS(XML_a, XML_spcAft); 2868 { 2869 mpFS->singleElementNS( XML_a, XML_spcPts, 2870 XML_val, OString::number(std::lround(nParaBottomMargin / 25.4 * 72))); 2871 } 2872 mpFS->endElementNS( XML_a, XML_spcAft ); 2873 } 2874 2875 WriteParagraphNumbering( rXPropSet, fFirstCharHeight, nLevel ); 2876 2877 WriteParagraphTabStops( rXPropSet ); 2878 2879 mpFS->endElementNS( XML_a, XML_pPr ); 2880 } 2881 2882 void DrawingML::WriteParagraph( const Reference< XTextContent >& rParagraph, 2883 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, 2884 const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet) 2885 { 2886 Reference< XEnumerationAccess > access( rParagraph, UNO_QUERY ); 2887 if( !access.is() ) 2888 return; 2889 2890 Reference< XEnumeration > enumeration( access->createEnumeration() ); 2891 if( !enumeration.is() ) 2892 return; 2893 2894 mpFS->startElementNS(XML_a, XML_p); 2895 2896 bool bPropertiesWritten = false; 2897 while( enumeration->hasMoreElements() ) 2898 { 2899 Reference< XTextRange > run; 2900 Any any ( enumeration->nextElement() ); 2901 2902 if (any >>= run) 2903 { 2904 if( !bPropertiesWritten ) 2905 { 2906 float fFirstCharHeight = rnCharHeight / 1000.; 2907 Reference< XPropertySet > xFirstRunPropSet (run, UNO_QUERY); 2908 Reference< XPropertySetInfo > xFirstRunPropSetInfo = xFirstRunPropSet->getPropertySetInfo(); 2909 if( xFirstRunPropSetInfo->hasPropertyByName("CharHeight") ) 2910 fFirstCharHeight = xFirstRunPropSet->getPropertyValue("CharHeight").get<float>(); 2911 WriteParagraphProperties( rParagraph, fFirstCharHeight ); 2912 bPropertiesWritten = true; 2913 } 2914 WriteRun( run, rbOverridingCharHeight, rnCharHeight, rXShapePropSet); 2915 } 2916 } 2917 Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY ); 2918 sal_Int16 nDummy = -1; 2919 WriteRunProperties(rXPropSet, false, XML_endParaRPr, false, rbOverridingCharHeight, 2920 rnCharHeight, nDummy, rXShapePropSet); 2921 2922 mpFS->endElementNS( XML_a, XML_p ); 2923 } 2924 2925 bool DrawingML::IsFontworkShape(const css::uno::Reference<css::beans::XPropertySet>& rXShapePropSet) 2926 { 2927 bool bResult(false); 2928 if (rXShapePropSet.is()) 2929 { 2930 Sequence<PropertyValue> aCustomShapeGeometryProps; 2931 if (GetProperty(rXShapePropSet, "CustomShapeGeometry")) 2932 { 2933 mAny >>= aCustomShapeGeometryProps; 2934 uno::Sequence<beans::PropertyValue> aTextPathSeq; 2935 for (const auto& rProp : std::as_const(aCustomShapeGeometryProps)) 2936 { 2937 if (rProp.Name == "TextPath") 2938 { 2939 rProp.Value >>= aTextPathSeq; 2940 for (const auto& rTextPathItem : std::as_const(aTextPathSeq)) 2941 { 2942 if (rTextPathItem.Name == "TextPath") 2943 { 2944 rTextPathItem.Value >>= bResult; 2945 break; 2946 } 2947 } 2948 break; 2949 } 2950 } 2951 } 2952 } 2953 return bResult; 2954 } 2955 2956 void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bool bText, 2957 sal_Int32 nXmlNamespace) 2958 { 2959 // ToDo: Fontwork in DOCX 2960 Reference< XText > xXText( rXIface, UNO_QUERY ); 2961 if( !xXText.is() ) 2962 return; 2963 2964 Reference< XPropertySet > rXPropSet( rXIface, UNO_QUERY ); 2965 2966 sal_Int32 nTextPreRotateAngle = 0; 2967 double nTextRotateAngle = 0; 2968 2969 #define DEFLRINS 254 2970 #define DEFTBINS 127 2971 sal_Int32 nLeft, nRight, nTop, nBottom; 2972 nLeft = nRight = DEFLRINS; 2973 nTop = nBottom = DEFTBINS; 2974 2975 // top inset looks a bit different compared to ppt export 2976 // check if something related doesn't work as expected 2977 if (GetProperty(rXPropSet, "TextLeftDistance")) 2978 mAny >>= nLeft; 2979 if (GetProperty(rXPropSet, "TextRightDistance")) 2980 mAny >>= nRight; 2981 if (GetProperty(rXPropSet, "TextUpperDistance")) 2982 mAny >>= nTop; 2983 if (GetProperty(rXPropSet, "TextLowerDistance")) 2984 mAny >>= nBottom; 2985 2986 TextVerticalAdjust eVerticalAlignment( TextVerticalAdjust_TOP ); 2987 const char* sVerticalAlignment = nullptr; 2988 if (GetProperty(rXPropSet, "TextVerticalAdjust")) 2989 mAny >>= eVerticalAlignment; 2990 if( eVerticalAlignment != TextVerticalAdjust_TOP ) 2991 sVerticalAlignment = GetTextVerticalAdjust(eVerticalAlignment); 2992 2993 const char* sWritingMode = nullptr; 2994 bool bVertical = false; 2995 if (GetProperty(rXPropSet, "TextWritingMode")) 2996 { 2997 WritingMode eMode; 2998 2999 if( ( mAny >>= eMode ) && eMode == WritingMode_TB_RL ) 3000 { 3001 sWritingMode = "eaVert"; 3002 bVertical = true; 3003 } 3004 } 3005 3006 bool bIsFontworkShape(IsFontworkShape(rXPropSet)); 3007 Sequence<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentSeq; 3008 uno::Sequence<beans::PropertyValue> aTextPathSeq; 3009 bool bScaleX(false); 3010 OUString sShapeType("non-primitive"); 3011 // ToDo move to InteropGrabBag 3012 OUString sMSWordPresetTextWarp; 3013 3014 if (GetProperty(rXPropSet, "CustomShapeGeometry")) 3015 { 3016 Sequence< PropertyValue > aProps; 3017 if ( mAny >>= aProps ) 3018 { 3019 for ( const auto& rProp : std::as_const(aProps) ) 3020 { 3021 if ( rProp.Name == "TextPreRotateAngle" && ( rProp.Value >>= nTextPreRotateAngle ) ) 3022 { 3023 if ( nTextPreRotateAngle == -90 ) 3024 { 3025 sWritingMode = "vert"; 3026 bVertical = true; 3027 } 3028 else if ( nTextPreRotateAngle == -270 ) 3029 { 3030 sWritingMode = "vert270"; 3031 bVertical = true; 3032 } 3033 } 3034 else if (rProp.Name == "AdjustmentValues") 3035 rProp.Value >>= aAdjustmentSeq; 3036 else if( rProp.Name == "TextRotateAngle" ) 3037 rProp.Value >>= nTextRotateAngle; 3038 else if (rProp.Name == "Type") 3039 rProp.Value >>= sShapeType; 3040 else if (rProp.Name == "TextPath") 3041 { 3042 rProp.Value >>= aTextPathSeq; 3043 for (const auto& rTextPathItem : std::as_const(aTextPathSeq)) 3044 { 3045 if (rTextPathItem.Name == "ScaleX") 3046 rTextPathItem.Value >>= bScaleX; 3047 } 3048 } 3049 else if (rProp.Name == "PresetTextWarp") 3050 rProp.Value >>= sMSWordPresetTextWarp; 3051 } 3052 } 3053 } 3054 else 3055 { 3056 if (mpTextExport) 3057 { 3058 uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY); 3059 if (xShape) 3060 { 3061 auto xTextFrame = mpTextExport->GetUnoTextFrame(xShape); 3062 if (xTextFrame) 3063 { 3064 uno::Reference<beans::XPropertySet> xPropSet(xTextFrame, uno::UNO_QUERY); 3065 auto aAny = xPropSet->getPropertyValue("WritingMode"); 3066 sal_Int16 nWritingMode; 3067 if (aAny >>= nWritingMode) 3068 { 3069 switch (nWritingMode) 3070 { 3071 case WritingMode2::TB_RL: 3072 sWritingMode = "vert"; 3073 bVertical = true; 3074 break; 3075 case WritingMode2::BT_LR: 3076 sWritingMode = "vert270"; 3077 bVertical = true; 3078 break; 3079 default: 3080 break; 3081 } 3082 } 3083 } 3084 } 3085 } 3086 } 3087 OUString sPresetWarp(PresetGeometryTypeNames::GetMsoName(sShapeType)); 3088 // ODF may have user defined TextPath, use "textPlain" as ersatz. 3089 if (sPresetWarp.isEmpty()) 3090 sPresetWarp = bIsFontworkShape ? std::u16string_view(u"textPlain") : std::u16string_view(u"textNoShape"); 3091 3092 bool bFromWordArt = !bScaleX 3093 && ( sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp" 3094 || sPresetWarp == "textButton" || sPresetWarp == "textCircle"); 3095 3096 TextHorizontalAdjust eHorizontalAlignment( TextHorizontalAdjust_CENTER ); 3097 bool bHorizontalCenter = false; 3098 if (GetProperty(rXPropSet, "TextHorizontalAdjust")) 3099 mAny >>= eHorizontalAlignment; 3100 if( eHorizontalAlignment == TextHorizontalAdjust_CENTER ) 3101 bHorizontalCenter = true; 3102 else if( bVertical && eHorizontalAlignment == TextHorizontalAdjust_LEFT ) 3103 sVerticalAlignment = "b"; 3104 3105 bool bHasWrap = false; 3106 bool bWrap = false; 3107 // Only custom shapes obey the TextWordWrap option, normal text always wraps. 3108 if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextWordWrap")) 3109 { 3110 mAny >>= bWrap; 3111 bHasWrap = true; 3112 } 3113 3114 if (bBodyPr) 3115 { 3116 const char* pWrap = (bHasWrap && !bWrap) || bIsFontworkShape ? "none" : nullptr; 3117 if (GetDocumentType() == DOCUMENT_DOCX) 3118 { 3119 // In case of DOCX, if we want to have the same effect as 3120 // TextShape's automatic word wrapping, then we need to set 3121 // wrapping to square. 3122 uno::Reference<lang::XServiceInfo> xServiceInfo(rXIface, uno::UNO_QUERY); 3123 if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.drawing.TextShape")) 3124 pWrap = "square"; 3125 } 3126 3127 std::optional<OUString> sHorzOverflow; 3128 std::optional<OUString> sVertOverflow; 3129 sal_Int32 nShapeRotateAngle = 0; 3130 if (GetProperty(rXPropSet, "RotateAngle")) 3131 nShapeRotateAngle = rXPropSet->getPropertyValue("RotateAngle").get<sal_Int32>() / 300; 3132 Reference< XPropertySet > xTextSet(xXText, UNO_QUERY); 3133 sal_Int32 nShapeTextRotateAngle = 0; 3134 if (GetProperty(xTextSet, "RotateAngle")) 3135 nShapeTextRotateAngle = rXPropSet->getPropertyValue("RotateAngle").get<sal_Int32>() / 300; 3136 std::optional<OString> isUpright; 3137 if (GetProperty(rXPropSet, "InteropGrabBag")) 3138 { 3139 if (rXPropSet->getPropertySetInfo()->hasPropertyByName("InteropGrabBag")) 3140 { 3141 bool bUpright = false; 3142 sal_Int32 nOldShapeRotation = 0; 3143 sal_Int32 nOldTextRotation = 0; 3144 uno::Sequence<beans::PropertyValue> aGrabBag; 3145 rXPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag; 3146 for (const auto& aProp : std::as_const(aGrabBag)) 3147 { 3148 if (aProp.Name == "Upright") 3149 { 3150 aProp.Value >>= bUpright; 3151 isUpright = OString(bUpright ? "1" : "0"); 3152 } 3153 else if (aProp.Name == "horzOverflow") 3154 { 3155 OUString sValue; 3156 aProp.Value >>= sValue; 3157 sHorzOverflow = sValue; 3158 } 3159 else if (aProp.Name == "vertOverflow") 3160 { 3161 OUString sValue; 3162 aProp.Value >>= sValue; 3163 sVertOverflow = sValue; 3164 } 3165 } 3166 if (bUpright) 3167 { 3168 for (auto& aProp : aGrabBag) 3169 { 3170 if (aProp.Name == "nShapeRotationAtImport") 3171 aProp.Value >>= nOldShapeRotation; 3172 else if (aProp.Name == "nTextRotationAtImport") 3173 aProp.Value >>= nOldTextRotation; 3174 } 3175 // So our shape with the textbox in it was not rotated. 3176 // Keep upright and make the preRotateAngle 0, it is an attribute 3177 // of textBodyPr and must be 0 when upright is true, otherwise 3178 // bad rotation happens in MSO. 3179 if (nShapeRotateAngle == nOldShapeRotation && nShapeTextRotateAngle == nOldTextRotation) 3180 nTextPreRotateAngle = 0; 3181 // So we rotated the shape, in this case lose upright and do 3182 // as LO normally does. 3183 else 3184 isUpright.reset(); 3185 } 3186 } 3187 } 3188 3189 mpFS->startElementNS( (nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr, 3190 XML_wrap, pWrap, 3191 XML_horzOverflow, sHorzOverflow, 3192 XML_vertOverflow, sVertOverflow, 3193 XML_fromWordArt, sax_fastparser::UseIf("1", bFromWordArt), 3194 XML_lIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), nLeft != DEFLRINS), 3195 XML_rIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), nRight != DEFLRINS), 3196 XML_tIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), nTop != DEFTBINS), 3197 XML_bIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)), nBottom != DEFTBINS), 3198 XML_anchor, sVerticalAlignment, 3199 XML_anchorCtr, sax_fastparser::UseIf("1", bHorizontalCenter), 3200 XML_vert, sWritingMode, 3201 XML_upright, isUpright, 3202 XML_rot, sax_fastparser::UseIf(oox::drawingml::calcRotationValue((nTextPreRotateAngle + nTextRotateAngle) * 100), (nTextPreRotateAngle + nTextRotateAngle) != 0)); 3203 if (bIsFontworkShape) 3204 { 3205 if (aAdjustmentSeq.hasElements()) 3206 { 3207 mpFS->startElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp); 3208 mpFS->startElementNS(XML_a, XML_avLst); 3209 bool bHasTwoHandles( 3210 sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour" 3211 || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour" 3212 || sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1" 3213 || sPresetWarp == "textWave2" || sPresetWarp == "textWave4"); 3214 for (sal_Int32 i = 0, nElems = aAdjustmentSeq.getLength(); i < nElems; ++i ) 3215 { 3216 OString sName = "adj" + (bHasTwoHandles ? OString::number(i + 1) : OString()); 3217 double fValue(0.0); 3218 if (aAdjustmentSeq[i].Value.getValueTypeClass() == TypeClass_DOUBLE) 3219 aAdjustmentSeq[i].Value >>= fValue; 3220 else 3221 { 3222 sal_Int32 nNumber(0); 3223 aAdjustmentSeq[i].Value >>= nNumber; 3224 fValue = static_cast<double>(nNumber); 3225 } 3226 // Convert from binary coordinate system with viewBox "0 0 21600 21600" and simple degree 3227 // to DrawingML with coordinate range 0..100000 and angle in 1/60000 degree. 3228 // Reverse to conversion in lcl_createPresetShape in drawingml/shape.cxx on import. 3229 if (sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp" 3230 || sPresetWarp == "textButton" || sPresetWarp == "textCircle" 3231 || ((i == 0) 3232 && (sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour" 3233 || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour"))) 3234 { 3235 fValue *= 60000.0; 3236 if (fValue < 0) 3237 fValue += 21600000; 3238 } 3239 else if ((i == 1) 3240 && (sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1" 3241 || sPresetWarp == "textWave2" || sPresetWarp == "textWave4")) 3242 { 3243 fValue = fValue / 0.216 - 50000.0; 3244 } 3245 else if ((i == 1) 3246 && (sPresetWarp == "textArchDownPour" 3247 || sPresetWarp == "textArchUpPour" 3248 || sPresetWarp == "textButtonPour" 3249 || sPresetWarp == "textCirclePour")) 3250 { 3251 fValue /= 0.108; 3252 } 3253 else 3254 { 3255 fValue /= 0.216; 3256 } 3257 OString sFmla = "val " + OString::number(std::lround(fValue)); 3258 mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla); 3259 // There exists faulty Favorite shapes with one handle but two adjustment values. 3260 if (!bHasTwoHandles) 3261 break; 3262 } 3263 mpFS->endElementNS(XML_a, XML_avLst); 3264 mpFS->endElementNS(XML_a, XML_prstTxWarp); 3265 } 3266 else 3267 { 3268 mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp); 3269 } 3270 } 3271 else if (GetDocumentType() == DOCUMENT_DOCX) 3272 { 3273 // interim solution for fdo#80897, roundtrip DOCX > LO > DOCX 3274 if (!sMSWordPresetTextWarp.isEmpty()) 3275 mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sMSWordPresetTextWarp); 3276 } 3277 3278 if (GetDocumentType() == DOCUMENT_DOCX || GetDocumentType() == DOCUMENT_XLSX) 3279 { 3280 // tdf#112312: only custom shapes obey the TextAutoGrowHeight option 3281 bool bTextAutoGrowHeight = false; 3282 uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY); 3283 auto pSdrObjCustomShape = xShape.is() ? dynamic_cast<SdrObjCustomShape*>(GetSdrObjectFromXShape(xShape)) : nullptr; 3284 if (pSdrObjCustomShape && GetProperty(rXPropSet, "TextAutoGrowHeight")) 3285 { 3286 mAny >>= bTextAutoGrowHeight; 3287 } 3288 mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit)); 3289 } 3290 if (GetDocumentType() == DOCUMENT_PPTX) 3291 { 3292 TextFitToSizeType eFit = TextFitToSizeType_NONE; 3293 if (GetProperty(rXPropSet, "TextFitToSize")) 3294 mAny >>= eFit; 3295 3296 if (eFit == TextFitToSizeType_AUTOFIT) 3297 { 3298 const sal_Int32 MAX_SCALE_VAL = 100000; 3299 sal_Int32 nFontScale = MAX_SCALE_VAL; 3300 SvxShapeText* pTextShape = dynamic_cast<SvxShapeText*>(rXIface.get()); 3301 if (pTextShape) 3302 { 3303 SdrTextObj* pTextObject = dynamic_cast<SdrTextObj*>(pTextShape->GetSdrObject()); 3304 if (pTextObject) 3305 { 3306 double fScaleY = pTextObject->GetFontScaleY(); 3307 nFontScale = static_cast<sal_uInt32>(fScaleY * 100) * 1000; 3308 } 3309 } 3310 3311 mpFS->singleElementNS(XML_a, XML_normAutofit, XML_fontScale, 3312 sax_fastparser::UseIf(OString::number(nFontScale), nFontScale < MAX_SCALE_VAL && nFontScale > 0)); 3313 } 3314 else 3315 { 3316 // tdf#127030: Only custom shapes obey the TextAutoGrowHeight option. 3317 bool bTextAutoGrowHeight = false; 3318 if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextAutoGrowHeight")) 3319 mAny >>= bTextAutoGrowHeight; 3320 mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit)); 3321 } 3322 } 3323 3324 WriteShape3DEffects( rXPropSet ); 3325 3326 mpFS->endElementNS((nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr); 3327 } 3328 3329 Reference< XEnumerationAccess > access( xXText, UNO_QUERY ); 3330 if( !access.is() || !bText ) 3331 return; 3332 3333 Reference< XEnumeration > enumeration( access->createEnumeration() ); 3334 if( !enumeration.is() ) 3335 return; 3336 3337 uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY); 3338 SdrObject* pSdrObject = xShape.is() ? GetSdrObjectFromXShape(xShape) : nullptr; 3339 const SdrTextObj* pTxtObj = dynamic_cast<SdrTextObj*>( pSdrObject ); 3340 if (pTxtObj && mpTextExport) 3341 { 3342 const OutlinerParaObject* pParaObj = nullptr; 3343 bool bOwnParaObj = false; 3344 3345 /* 3346 #i13885# 3347 When the object is actively being edited, that text is not set into 3348 the objects normal text object, but lives in a separate object. 3349 */ 3350 if (pTxtObj->IsTextEditActive()) 3351 { 3352 pParaObj = pTxtObj->CreateEditOutlinerParaObject().release(); 3353 bOwnParaObj = true; 3354 } 3355 else 3356 pParaObj = pTxtObj->GetOutlinerParaObject(); 3357 3358 if (pParaObj) 3359 { 3360 // this is reached only in case some text is attached to the shape 3361 mpTextExport->WriteOutliner(*pParaObj); 3362 if (bOwnParaObj) 3363 delete pParaObj; 3364 } 3365 return; 3366 } 3367 3368 bool bOverridingCharHeight = false; 3369 sal_Int32 nCharHeight = -1; 3370 3371 while( enumeration->hasMoreElements() ) 3372 { 3373 Reference< XTextContent > paragraph; 3374 Any any ( enumeration->nextElement() ); 3375 3376 if( any >>= paragraph) 3377 WriteParagraph( paragraph, bOverridingCharHeight, nCharHeight, rXPropSet ); 3378 } 3379 } 3380 3381 void DrawingML::WritePresetShape( const char* pShape , std::vector< std::pair<sal_Int32,sal_Int32>> & rAvList ) 3382 { 3383 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape); 3384 if ( !rAvList.empty() ) 3385 { 3386 3387 mpFS->startElementNS(XML_a, XML_avLst); 3388 for (auto const& elem : rAvList) 3389 { 3390 OString sName = "adj" + ( ( elem.first > 0 ) ? OString::number(elem.first) : OString() ); 3391 OString sFmla = "val " + OString::number( elem.second ); 3392 3393 mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla); 3394 } 3395 mpFS->endElementNS( XML_a, XML_avLst ); 3396 } 3397 else 3398 mpFS->singleElementNS(XML_a, XML_avLst); 3399 3400 mpFS->endElementNS( XML_a, XML_prstGeom ); 3401 } 3402 3403 void DrawingML::WritePresetShape( const char* pShape ) 3404 { 3405 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape); 3406 mpFS->singleElementNS(XML_a, XML_avLst); 3407 mpFS->endElementNS( XML_a, XML_prstGeom ); 3408 } 3409 3410 static std::map< OString, std::vector<OString> > lcl_getAdjNames() 3411 { 3412 std::map< OString, std::vector<OString> > aRet; 3413 3414 OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/oox-drawingml-adj-names"); 3415 rtl::Bootstrap::expandMacros(aPath); 3416 SvFileStream aStream(aPath, StreamMode::READ); 3417 if (aStream.GetError() != ERRCODE_NONE) 3418 SAL_WARN("oox.shape", "failed to open oox-drawingml-adj-names"); 3419 OString aLine; 3420 bool bNotDone = aStream.ReadLine(aLine); 3421 while (bNotDone) 3422 { 3423 sal_Int32 nIndex = 0; 3424 // Each line is in a "key\tvalue" format: read the key, the rest is the value. 3425 OString aKey = aLine.getToken(0, '\t', nIndex); 3426 OString aValue = aLine.copy(nIndex); 3427 aRet[aKey].push_back(aValue); 3428 bNotDone = aStream.ReadLine(aLine); 3429 } 3430 return aRet; 3431 } 3432 3433 void DrawingML::WritePresetShape( const char* pShape, MSO_SPT eShapeType, bool bPredefinedHandlesUsed, const PropertyValue& rProp ) 3434 { 3435 static std::map< OString, std::vector<OString> > aAdjMap = lcl_getAdjNames(); 3436 // If there are predefined adj names for this shape type, look them up now. 3437 std::vector<OString> aAdjustments; 3438 if (aAdjMap.find(OString(pShape)) != aAdjMap.end()) 3439 aAdjustments = aAdjMap[OString(pShape)]; 3440 3441 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape); 3442 mpFS->startElementNS(XML_a, XML_avLst); 3443 3444 Sequence< drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq; 3445 if ( ( rProp.Value >>= aAdjustmentSeq ) 3446 && eShapeType != mso_sptActionButtonForwardNext // we have adjustments values for these type of shape, but MSO doesn't like them 3447 && eShapeType != mso_sptActionButtonBackPrevious // so they are now disabled 3448 && pShape != std::string_view("rect") //some shape types are commented out in pCustomShapeTypeTranslationTable[] & are being defaulted to rect & rect does not have adjustment values/name. 3449 ) 3450 { 3451 SAL_INFO("oox.shape", "adj seq len: " << aAdjustmentSeq.getLength()); 3452 sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0; 3453 if ( bPredefinedHandlesUsed ) 3454 EscherPropertyContainer::LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted ); 3455 3456 sal_Int32 nValue, nLength = aAdjustmentSeq.getLength(); 3457 // aAdjustments will give info about the number of adj values for a particular geometry. For example for hexagon aAdjustments.size() will be 2 and for circular arrow it will be 5 as per lcl_getAdjNames. 3458 // Sometimes there are more values than needed, so we ignore the excessive ones. 3459 if (aAdjustments.size() <= o3tl::make_unsigned(nLength)) 3460 { 3461 for (sal_Int32 i = 0; i < static_cast<sal_Int32>(aAdjustments.size()); i++) 3462 { 3463 if( EscherPropertyContainer::GetAdjustmentValue( aAdjustmentSeq[ i ], i, nAdjustmentsWhichNeedsToBeConverted, nValue ) ) 3464 { 3465 // If the document model doesn't have an adjustment name (e.g. shape was created from VML), then take it from the predefined list. 3466 OString aAdjName = aAdjustmentSeq[i].Name.isEmpty() 3467 ? aAdjustments[i] 3468 : aAdjustmentSeq[i].Name.toUtf8(); 3469 3470 mpFS->singleElementNS( XML_a, XML_gd, 3471 XML_name, aAdjName, 3472 XML_fmla, "val " + OString::number(nValue)); 3473 } 3474 } 3475 } 3476 } 3477 3478 mpFS->endElementNS( XML_a, XML_avLst ); 3479 mpFS->endElementNS( XML_a, XML_prstGeom ); 3480 } 3481 3482 bool DrawingML::WriteCustomGeometry( 3483 const Reference< XShape >& rXShape, 3484 const SdrObjCustomShape& rSdrObjCustomShape) 3485 { 3486 uno::Reference< beans::XPropertySet > aXPropSet; 3487 uno::Any aAny( rXShape->queryInterface(cppu::UnoType<beans::XPropertySet>::get())); 3488 3489 if ( ! (aAny >>= aXPropSet) ) 3490 return false; 3491 3492 try 3493 { 3494 aAny = aXPropSet->getPropertyValue( "CustomShapeGeometry" ); 3495 if ( !aAny.hasValue() ) 3496 return false; 3497 } 3498 catch( const ::uno::Exception& ) 3499 { 3500 return false; 3501 } 3502 3503 3504 auto pGeometrySeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(aAny); 3505 3506 if ( pGeometrySeq ) 3507 { 3508 for( const beans::PropertyValue& rProp : *pGeometrySeq ) 3509 { 3510 if ( rProp.Name == "Path" ) 3511 { 3512 uno::Sequence<beans::PropertyValue> aPathProp; 3513 rProp.Value >>= aPathProp; 3514 3515 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs; 3516 uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments; 3517 uno::Sequence<awt::Size> aPathSize; 3518 for (const beans::PropertyValue& rPathProp : std::as_const(aPathProp)) 3519 { 3520 if (rPathProp.Name == "Coordinates") 3521 rPathProp.Value >>= aPairs; 3522 else if (rPathProp.Name == "Segments") 3523 rPathProp.Value >>= aSegments; 3524 else if (rPathProp.Name == "SubViewSize") 3525 rPathProp.Value >>= aPathSize; 3526 } 3527 3528 if ( !aPairs.hasElements() ) 3529 return false; 3530 3531 if ( !aSegments.hasElements() ) 3532 { 3533 aSegments = uno::Sequence<drawing::EnhancedCustomShapeSegment>(4); 3534 aSegments[0].Count = 1; 3535 aSegments[0].Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO; 3536 aSegments[1].Count = static_cast<sal_Int16>(std::min( aPairs.getLength() - 1, sal_Int32(32767) )); 3537 aSegments[1].Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO; 3538 aSegments[2].Count = 0; 3539 aSegments[2].Command = drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH; 3540 aSegments[3].Count = 0; 3541 aSegments[3].Command = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH; 3542 } 3543 3544 int nExpectedPairCount = std::accumulate(std::cbegin(aSegments), std::cend(aSegments), 0, 3545 [](const int nSum, const drawing::EnhancedCustomShapeSegment& rSegment) { return nSum + rSegment.Count; }); 3546 3547 if ( nExpectedPairCount > aPairs.getLength() ) 3548 { 3549 SAL_WARN("oox.shape", "Segments need " << nExpectedPairCount << " coordinates, but Coordinates have only " << aPairs.getLength() << " pairs."); 3550 return false; 3551 } 3552 3553 mpFS->startElementNS(XML_a, XML_custGeom); 3554 mpFS->singleElementNS(XML_a, XML_avLst); 3555 mpFS->singleElementNS(XML_a, XML_gdLst); 3556 mpFS->singleElementNS(XML_a, XML_ahLst); 3557 mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t", 3558 XML_r, "r", XML_b, "b"); 3559 mpFS->startElementNS(XML_a, XML_pathLst); 3560 3561 if ( aPathSize.hasElements() ) 3562 { 3563 mpFS->startElementNS( XML_a, XML_path, 3564 XML_w, OString::number(aPathSize[0].Width), 3565 XML_h, OString::number(aPathSize[0].Height) ); 3566 } 3567 else 3568 { 3569 sal_Int32 nXMin(0); 3570 aPairs[0].First.Value >>= nXMin; 3571 sal_Int32 nXMax = nXMin; 3572 sal_Int32 nYMin(0); 3573 aPairs[0].Second.Value >>= nYMin; 3574 sal_Int32 nYMax = nYMin; 3575 3576 for ( const auto& rPair : std::as_const(aPairs) ) 3577 { 3578 sal_Int32 nX = GetCustomGeometryPointValue(rPair.First, rSdrObjCustomShape); 3579 sal_Int32 nY = GetCustomGeometryPointValue(rPair.Second, rSdrObjCustomShape); 3580 if (nX < nXMin) 3581 nXMin = nX; 3582 if (nY < nYMin) 3583 nYMin = nY; 3584 if (nX > nXMax) 3585 nXMax = nX; 3586 if (nY > nYMax) 3587 nYMax = nY; 3588 } 3589 mpFS->startElementNS( XML_a, XML_path, 3590 XML_w, OString::number(nXMax - nXMin), 3591 XML_h, OString::number(nYMax - nYMin) ); 3592 } 3593 3594 3595 int nPairIndex = 0; 3596 bool bOK = true; 3597 for (const auto& rSegment : std::as_const(aSegments)) 3598 { 3599 if ( rSegment.Command == drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH ) 3600 { 3601 mpFS->singleElementNS(XML_a, XML_close); 3602 } 3603 for (int k = 0; k < rSegment.Count && bOK; ++k) 3604 { 3605 switch( rSegment.Command ) 3606 { 3607 case drawing::EnhancedCustomShapeSegmentCommand::MOVETO : 3608 { 3609 if (nPairIndex >= aPairs.getLength()) 3610 bOK = false; 3611 else 3612 { 3613 mpFS->startElementNS(XML_a, XML_moveTo); 3614 WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape); 3615 mpFS->endElementNS( XML_a, XML_moveTo ); 3616 nPairIndex++; 3617 } 3618 break; 3619 } 3620 case drawing::EnhancedCustomShapeSegmentCommand::LINETO : 3621 { 3622 if (nPairIndex >= aPairs.getLength()) 3623 bOK = false; 3624 else 3625 { 3626 mpFS->startElementNS(XML_a, XML_lnTo); 3627 WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape); 3628 mpFS->endElementNS( XML_a, XML_lnTo ); 3629 nPairIndex++; 3630 } 3631 break; 3632 } 3633 case drawing::EnhancedCustomShapeSegmentCommand::CURVETO : 3634 { 3635 if (nPairIndex + 2 >= aPairs.getLength()) 3636 bOK = false; 3637 else 3638 { 3639 mpFS->startElementNS(XML_a, XML_cubicBezTo); 3640 for( sal_uInt8 l = 0; l <= 2; ++l ) 3641 { 3642 WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape); 3643 } 3644 mpFS->endElementNS( XML_a, XML_cubicBezTo ); 3645 nPairIndex += 3; 3646 } 3647 break; 3648 } 3649 case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO : 3650 case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE : 3651 { 3652 nPairIndex += 3; 3653 break; 3654 } 3655 case drawing::EnhancedCustomShapeSegmentCommand::ARCTO : 3656 case drawing::EnhancedCustomShapeSegmentCommand::ARC : 3657 case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO : 3658 case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC : 3659 { 3660 nPairIndex += 4; 3661 break; 3662 } 3663 case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX : 3664 case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY : 3665 { 3666 nPairIndex++; 3667 break; 3668 } 3669 case drawing::EnhancedCustomShapeSegmentCommand::QUADRATICCURVETO : 3670 { 3671 if (nPairIndex + 1 >= aPairs.getLength()) 3672 bOK = false; 3673 else 3674 { 3675 mpFS->startElementNS(XML_a, XML_quadBezTo); 3676 for( sal_uInt8 l = 0; l < 2; ++l ) 3677 { 3678 WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape); 3679 } 3680 mpFS->endElementNS( XML_a, XML_quadBezTo ); 3681 nPairIndex += 2; 3682 } 3683 break; 3684 } 3685 case drawing::EnhancedCustomShapeSegmentCommand::ARCANGLETO : 3686 { 3687 if (nPairIndex + 1 >= aPairs.getLength()) 3688 bOK = false; 3689 else 3690 { 3691 const EnhancedCustomShape2d aCustoShape2d( 3692 const_cast<SdrObjCustomShape&>(rSdrObjCustomShape)); 3693 double fWR = 0.0; 3694 aCustoShape2d.GetParameter(fWR, aPairs[nPairIndex].First, false, 3695 false); 3696 double fHR = 0.0; 3697 aCustoShape2d.GetParameter(fHR, aPairs[nPairIndex].Second, 3698 false, false); 3699 double fStartAngle = 0.0; 3700 aCustoShape2d.GetParameter( 3701 fStartAngle, aPairs[nPairIndex + 1].First, false, false); 3702 sal_Int32 nStartAng(std::lround(fStartAngle * 60000)); 3703 double fSwingAng = 0.0; 3704 aCustoShape2d.GetParameter( 3705 fSwingAng, aPairs[nPairIndex + 1].Second, false, false); 3706 sal_Int32 nSwingAng(std::lround(fSwingAng * 60000)); 3707 mpFS->singleElement(FSNS(XML_a, XML_arcTo), 3708 XML_wR, OString::number(fWR), 3709 XML_hR, OString::number(fHR), 3710 XML_stAng, OString::number(nStartAng), 3711 XML_swAng, OString::number(nSwingAng)); 3712 nPairIndex += 2; 3713 } 3714 break; 3715 } 3716 default: 3717 // do nothing 3718 break; 3719 } 3720 } 3721 if (!bOK) 3722 break; 3723 } 3724 mpFS->endElementNS( XML_a, XML_path ); 3725 mpFS->endElementNS( XML_a, XML_pathLst ); 3726 mpFS->endElementNS( XML_a, XML_custGeom ); 3727 return bOK; 3728 } 3729 } 3730 } 3731 return false; 3732 } 3733 3734 void DrawingML::WriteCustomGeometryPoint( 3735 const drawing::EnhancedCustomShapeParameterPair& rParamPair, 3736 const SdrObjCustomShape& rSdrObjCustomShape) 3737 { 3738 sal_Int32 nX = GetCustomGeometryPointValue(rParamPair.First, rSdrObjCustomShape); 3739 sal_Int32 nY = GetCustomGeometryPointValue(rParamPair.Second, rSdrObjCustomShape); 3740 3741 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(nX), XML_y, OString::number(nY)); 3742 } 3743 3744 sal_Int32 DrawingML::GetCustomGeometryPointValue( 3745 const css::drawing::EnhancedCustomShapeParameter& rParam, 3746 const SdrObjCustomShape& rSdrObjCustomShape) 3747 { 3748 const EnhancedCustomShape2d aCustoShape2d(const_cast< SdrObjCustomShape& >(rSdrObjCustomShape)); 3749 double fValue = 0.0; 3750 aCustoShape2d.GetParameter(fValue, rParam, false, false); 3751 sal_Int32 nValue(std::lround(fValue)); 3752 3753 return nValue; 3754 } 3755 3756 void DrawingML::WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& rXShape, 3757 const tools::PolyPolygon& rPolyPolygon, const bool bClosed) 3758 { 3759 // In case of Writer, the parent element is <wps:spPr>, and there the 3760 // <a:custGeom> element is not optional. 3761 if (rPolyPolygon.Count() < 1 && GetDocumentType() != DOCUMENT_DOCX) 3762 return; 3763 3764 mpFS->startElementNS(XML_a, XML_custGeom); 3765 mpFS->singleElementNS(XML_a, XML_avLst); 3766 mpFS->singleElementNS(XML_a, XML_gdLst); 3767 mpFS->singleElementNS(XML_a, XML_ahLst); 3768 mpFS->singleElementNS(XML_a, XML_rect, XML_l, "0", XML_t, "0", XML_r, "r", XML_b, "b"); 3769 3770 mpFS->startElementNS( XML_a, XML_pathLst ); 3771 3772 const tools::Rectangle aRect( rPolyPolygon.GetBoundRect() ); 3773 3774 // tdf#101122 3775 std::optional<OString> sFill; 3776 if (HasEnhancedCustomShapeSegmentCommand(rXShape, css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL)) 3777 sFill = "none"; // for possible values see ST_PathFillMode in OOXML standard 3778 3779 // Put all polygons of rPolyPolygon in the same path element 3780 // to subtract the overlapped areas. 3781 mpFS->startElementNS( XML_a, XML_path, 3782 XML_fill, sFill, 3783 XML_w, OString::number(aRect.GetWidth()), 3784 XML_h, OString::number(aRect.GetHeight()) ); 3785 3786 for( sal_uInt16 i = 0; i < rPolyPolygon.Count(); i ++ ) 3787 { 3788 3789 const tools::Polygon& rPoly = rPolyPolygon[ i ]; 3790 3791 if( rPoly.GetSize() > 0 ) 3792 { 3793 mpFS->startElementNS(XML_a, XML_moveTo); 3794 3795 mpFS->singleElementNS( XML_a, XML_pt, 3796 XML_x, OString::number(rPoly[0].X() - aRect.Left()), 3797 XML_y, OString::number(rPoly[0].Y() - aRect.Top()) ); 3798 3799 mpFS->endElementNS( XML_a, XML_moveTo ); 3800 } 3801 3802 for( sal_uInt16 j = 1; j < rPoly.GetSize(); j ++ ) 3803 { 3804 PolyFlags flags = rPoly.GetFlags(j); 3805 if( flags == PolyFlags::Control ) 3806 { 3807 // a:cubicBezTo can only contain 3 a:pt elements, so we need to make sure of this 3808 if( j+2 < rPoly.GetSize() && rPoly.GetFlags(j+1) == PolyFlags::Control && rPoly.GetFlags(j+2) != PolyFlags::Control ) 3809 { 3810 3811 mpFS->startElementNS(XML_a, XML_cubicBezTo); 3812 for( sal_uInt8 k = 0; k <= 2; ++k ) 3813 { 3814 mpFS->singleElementNS(XML_a, XML_pt, 3815 XML_x, OString::number(rPoly[j+k].X() - aRect.Left()), 3816 XML_y, OString::number(rPoly[j+k].Y() - aRect.Top())); 3817 3818 } 3819 mpFS->endElementNS( XML_a, XML_cubicBezTo ); 3820 j += 2; 3821 } 3822 } 3823 else if( flags == PolyFlags::Normal ) 3824 { 3825 mpFS->startElementNS(XML_a, XML_lnTo); 3826 mpFS->singleElementNS( XML_a, XML_pt, 3827 XML_x, OString::number(rPoly[j].X() - aRect.Left()), 3828 XML_y, OString::number(rPoly[j].Y() - aRect.Top()) ); 3829 mpFS->endElementNS( XML_a, XML_lnTo ); 3830 } 3831 } 3832 } 3833 if (bClosed) 3834 mpFS->singleElementNS( XML_a, XML_close); 3835 mpFS->endElementNS( XML_a, XML_path ); 3836 3837 mpFS->endElementNS( XML_a, XML_pathLst ); 3838 3839 mpFS->endElementNS( XML_a, XML_custGeom ); 3840 } 3841 3842 void DrawingML::WriteConnectorConnections( EscherConnectorListEntry& rConnectorEntry, sal_Int32 nStartID, sal_Int32 nEndID ) 3843 { 3844 if( nStartID != -1 ) 3845 { 3846 mpFS->singleElementNS( XML_a, XML_stCxn, 3847 XML_id, OString::number(nStartID), 3848 XML_idx, OString::number(rConnectorEntry.GetConnectorRule(true)) ); 3849 } 3850 if( nEndID != -1 ) 3851 { 3852 mpFS->singleElementNS( XML_a, XML_endCxn, 3853 XML_id, OString::number(nEndID), 3854 XML_idx, OString::number(rConnectorEntry.GetConnectorRule(false)) ); 3855 } 3856 } 3857 3858 sal_Unicode DrawingML::SubstituteBullet( sal_Unicode cBulletId, css::awt::FontDescriptor& rFontDesc ) 3859 { 3860 if ( IsStarSymbol(rFontDesc.Name) ) 3861 { 3862 rtl_TextEncoding eCharSet = rFontDesc.CharSet; 3863 cBulletId = msfilter::util::bestFitOpenSymbolToMSFont(cBulletId, eCharSet, rFontDesc.Name); 3864 rFontDesc.CharSet = eCharSet; 3865 } 3866 3867 return cBulletId; 3868 } 3869 3870 sax_fastparser::FSHelperPtr DrawingML::CreateOutputStream ( 3871 const OUString& sFullStream, 3872 std::u16string_view sRelativeStream, 3873 const Reference< XOutputStream >& xParentRelation, 3874 const char* sContentType, 3875 const char* sRelationshipType, 3876 OUString* pRelationshipId ) 3877 { 3878 OUString sRelationshipId; 3879 if (xParentRelation.is()) 3880 sRelationshipId = GetFB()->addRelation( xParentRelation, OUString::createFromAscii( sRelationshipType), sRelativeStream ); 3881 else 3882 sRelationshipId = GetFB()->addRelation( OUString::createFromAscii( sRelationshipType ), sRelativeStream ); 3883 3884 if( pRelationshipId ) 3885 *pRelationshipId = sRelationshipId; 3886 3887 sax_fastparser::FSHelperPtr p = GetFB()->openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) ); 3888 3889 return p; 3890 } 3891 3892 void DrawingML::WriteFill( const Reference< XPropertySet >& xPropSet ) 3893 { 3894 if ( !GetProperty( xPropSet, "FillStyle" ) ) 3895 return; 3896 FillStyle aFillStyle( FillStyle_NONE ); 3897 xPropSet->getPropertyValue( "FillStyle" ) >>= aFillStyle; 3898 3899 // map full transparent background to no fill 3900 if ( aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, "FillTransparence" ) ) 3901 { 3902 sal_Int16 nVal = 0; 3903 xPropSet->getPropertyValue( "FillTransparence" ) >>= nVal; 3904 if ( nVal == 100 ) 3905 aFillStyle = FillStyle_NONE; 3906 } 3907 if (aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, "FillTransparenceGradient")) 3908 { 3909 awt::Gradient aTransparenceGradient; 3910 mAny >>= aTransparenceGradient; 3911 if (aTransparenceGradient.StartColor == 0xffffff && aTransparenceGradient.EndColor == 0xffffff) 3912 aFillStyle = FillStyle_NONE; 3913 } 3914 3915 switch( aFillStyle ) 3916 { 3917 case FillStyle_SOLID : 3918 WriteSolidFill( xPropSet ); 3919 break; 3920 case FillStyle_GRADIENT : 3921 WriteGradientFill( xPropSet ); 3922 break; 3923 case FillStyle_BITMAP : 3924 WriteBlipFill( xPropSet, "FillBitmap" ); 3925 break; 3926 case FillStyle_HATCH : 3927 WritePattFill( xPropSet ); 3928 break; 3929 case FillStyle_NONE: 3930 mpFS->singleElementNS(XML_a, XML_noFill); 3931 break; 3932 default: 3933 ; 3934 } 3935 } 3936 3937 void DrawingML::WriteStyleProperties( sal_Int32 nTokenId, const Sequence< PropertyValue >& aProperties ) 3938 { 3939 if( aProperties.hasElements() ) 3940 { 3941 OUString sSchemeClr; 3942 sal_uInt32 nIdx = 0; 3943 Sequence< PropertyValue > aTransformations; 3944 for( const auto& rProp : aProperties) 3945 { 3946 if( rProp.Name == "SchemeClr" ) 3947 rProp.Value >>= sSchemeClr; 3948 else if( rProp.Name == "Idx" ) 3949 rProp.Value >>= nIdx; 3950 else if( rProp.Name == "Transformations" ) 3951 rProp.Value >>= aTransformations; 3952 } 3953 mpFS->startElementNS(XML_a, nTokenId, XML_idx, OString::number(nIdx)); 3954 WriteColor(sSchemeClr, aTransformations); 3955 mpFS->endElementNS( XML_a, nTokenId ); 3956 } 3957 else 3958 { 3959 // write mock <a:*Ref> tag 3960 mpFS->singleElementNS(XML_a, nTokenId, XML_idx, OString::number(0)); 3961 } 3962 } 3963 3964 void DrawingML::WriteShapeStyle( const Reference< XPropertySet >& xPropSet ) 3965 { 3966 // check existence of the grab bag 3967 if ( !GetProperty( xPropSet, "InteropGrabBag" ) ) 3968 return; 3969 3970 // extract the relevant properties from the grab bag 3971 Sequence< PropertyValue > aGrabBag; 3972 Sequence< PropertyValue > aFillRefProperties, aLnRefProperties, aEffectRefProperties; 3973 mAny >>= aGrabBag; 3974 for( const auto& rProp : std::as_const(aGrabBag)) 3975 { 3976 if( rProp.Name == "StyleFillRef" ) 3977 rProp.Value >>= aFillRefProperties; 3978 else if( rProp.Name == "StyleLnRef" ) 3979 rProp.Value >>= aLnRefProperties; 3980 else if( rProp.Name == "StyleEffectRef" ) 3981 rProp.Value >>= aEffectRefProperties; 3982 } 3983 3984 WriteStyleProperties( XML_lnRef, aLnRefProperties ); 3985 WriteStyleProperties( XML_fillRef, aFillRefProperties ); 3986 WriteStyleProperties( XML_effectRef, aEffectRefProperties ); 3987 3988 // write mock <a:fontRef> 3989 mpFS->singleElementNS(XML_a, XML_fontRef, XML_idx, "minor"); 3990 } 3991 3992 void DrawingML::WriteShapeEffect( std::u16string_view sName, const Sequence< PropertyValue >& aEffectProps ) 3993 { 3994 if( !aEffectProps.hasElements() ) 3995 return; 3996 3997 // assign the proper tag and enable bContainsColor if necessary 3998 sal_Int32 nEffectToken = 0; 3999 bool bContainsColor = false; 4000 if( sName == u"outerShdw" ) 4001 { 4002 nEffectToken = FSNS( XML_a, XML_outerShdw ); 4003 bContainsColor = true; 4004 } 4005 else if( sName == u"innerShdw" ) 4006 { 4007 nEffectToken = FSNS( XML_a, XML_innerShdw ); 4008 bContainsColor = true; 4009 } 4010 else if( sName == u"glow" ) 4011 { 4012 nEffectToken = FSNS( XML_a, XML_glow ); 4013 bContainsColor = true; 4014 } 4015 else if( sName == u"softEdge" ) 4016 nEffectToken = FSNS( XML_a, XML_softEdge ); 4017 else if( sName == u"reflection" ) 4018 nEffectToken = FSNS( XML_a, XML_reflection ); 4019 else if( sName == u"blur" ) 4020 nEffectToken = FSNS( XML_a, XML_blur ); 4021 4022 OUString sSchemeClr; 4023 ::Color nRgbClr; 4024 sal_Int32 nAlpha = MAX_PERCENT; 4025 Sequence< PropertyValue > aTransformations; 4026 rtl::Reference<sax_fastparser::FastAttributeList> aOuterShdwAttrList = FastSerializerHelper::createAttrList(); 4027 for( const auto& rEffectProp : aEffectProps ) 4028 { 4029 if( rEffectProp.Name == "Attribs" ) 4030 { 4031 // read tag attributes 4032 uno::Sequence< beans::PropertyValue > aOuterShdwProps; 4033 rEffectProp.Value >>= aOuterShdwProps; 4034 for( const auto& rOuterShdwProp : std::as_const(aOuterShdwProps) ) 4035 { 4036 if( rOuterShdwProp.Name == "algn" ) 4037 { 4038 OUString sVal; 4039 rOuterShdwProp.Value >>= sVal; 4040 aOuterShdwAttrList->add( XML_algn, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ) ); 4041 } 4042 else if( rOuterShdwProp.Name == "blurRad" ) 4043 { 4044 sal_Int64 nVal = 0; 4045 rOuterShdwProp.Value >>= nVal; 4046 aOuterShdwAttrList->add( XML_blurRad, OString::number( nVal ).getStr() ); 4047 } 4048 else if( rOuterShdwProp.Name == "dir" ) 4049 { 4050 sal_Int32 nVal = 0; 4051 rOuterShdwProp.Value >>= nVal; 4052 aOuterShdwAttrList->add( XML_dir, OString::number( nVal ).getStr() ); 4053 } 4054 else if( rOuterShdwProp.Name == "dist" ) 4055 { 4056 sal_Int32 nVal = 0; 4057 rOuterShdwProp.Value >>= nVal; 4058 aOuterShdwAttrList->add( XML_dist, OString::number( nVal ).getStr() ); 4059 } 4060 else if( rOuterShdwProp.Name == "kx" ) 4061 { 4062 sal_Int32 nVal = 0; 4063 rOuterShdwProp.Value >>= nVal; 4064 aOuterShdwAttrList->add( XML_kx, OString::number( nVal ).getStr() ); 4065 } 4066 else if( rOuterShdwProp.Name == "ky" ) 4067 { 4068 sal_Int32 nVal = 0; 4069 rOuterShdwProp.Value >>= nVal; 4070 aOuterShdwAttrList->add( XML_ky, OString::number( nVal ).getStr() ); 4071 } 4072 else if( rOuterShdwProp.Name == "rotWithShape" ) 4073 { 4074 sal_Int32 nVal = 0; 4075 rOuterShdwProp.Value >>= nVal; 4076 aOuterShdwAttrList->add( XML_rotWithShape, OString::number( nVal ).getStr() ); 4077 } 4078 else if( rOuterShdwProp.Name == "sx" ) 4079 { 4080 sal_Int32 nVal = 0; 4081 rOuterShdwProp.Value >>= nVal; 4082 aOuterShdwAttrList->add( XML_sx, OString::number( nVal ).getStr() ); 4083 } 4084 else if( rOuterShdwProp.Name == "sy" ) 4085 { 4086 sal_Int32 nVal = 0; 4087 rOuterShdwProp.Value >>= nVal; 4088 aOuterShdwAttrList->add( XML_sy, OString::number( nVal ).getStr() ); 4089 } 4090 else if( rOuterShdwProp.Name == "rad" ) 4091 { 4092 sal_Int64 nVal = 0; 4093 rOuterShdwProp.Value >>= nVal; 4094 aOuterShdwAttrList->add( XML_rad, OString::number( nVal ).getStr() ); 4095 } 4096 else if( rOuterShdwProp.Name == "endA" ) 4097 { 4098 sal_Int32 nVal = 0; 4099 rOuterShdwProp.Value >>= nVal; 4100 aOuterShdwAttrList->add( XML_endA, OString::number( nVal ).getStr() ); 4101 } 4102 else if( rOuterShdwProp.Name == "endPos" ) 4103 { 4104 sal_Int32 nVal = 0; 4105 rOuterShdwProp.Value >>= nVal; 4106 aOuterShdwAttrList->add( XML_endPos, OString::number( nVal ).getStr() ); 4107 } 4108 else if( rOuterShdwProp.Name == "fadeDir" ) 4109 { 4110 sal_Int32 nVal = 0; 4111 rOuterShdwProp.Value >>= nVal; 4112 aOuterShdwAttrList->add( XML_fadeDir, OString::number( nVal ).getStr() ); 4113 } 4114 else if( rOuterShdwProp.Name == "stA" ) 4115 { 4116 sal_Int32 nVal = 0; 4117 rOuterShdwProp.Value >>= nVal; 4118 aOuterShdwAttrList->add( XML_stA, OString::number( nVal ).getStr() ); 4119 } 4120 else if( rOuterShdwProp.Name == "stPos" ) 4121 { 4122 sal_Int32 nVal = 0; 4123 rOuterShdwProp.Value >>= nVal; 4124 aOuterShdwAttrList->add( XML_stPos, OString::number( nVal ).getStr() ); 4125 } 4126 else if( rOuterShdwProp.Name == "grow" ) 4127 { 4128 sal_Int32 nVal = 0; 4129 rOuterShdwProp.Value >>= nVal; 4130 aOuterShdwAttrList->add( XML_grow, OString::number( nVal ).getStr() ); 4131 } 4132 } 4133 } 4134 else if(rEffectProp.Name == "RgbClr") 4135 { 4136 rEffectProp.Value >>= nRgbClr; 4137 } 4138 else if(rEffectProp.Name == "RgbClrTransparency") 4139 { 4140 sal_Int32 nTransparency; 4141 if (rEffectProp.Value >>= nTransparency) 4142 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency()) 4143 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency ); 4144 } 4145 else if(rEffectProp.Name == "SchemeClr") 4146 { 4147 rEffectProp.Value >>= sSchemeClr; 4148 } 4149 else if(rEffectProp.Name == "SchemeClrTransformations") 4150 { 4151 rEffectProp.Value >>= aTransformations; 4152 } 4153 } 4154 4155 if( nEffectToken <= 0 ) 4156 return; 4157 4158 mpFS->startElement( nEffectToken, aOuterShdwAttrList ); 4159 4160 if( bContainsColor ) 4161 { 4162 if( sSchemeClr.isEmpty() ) 4163 WriteColor( nRgbClr, nAlpha ); 4164 else 4165 WriteColor( sSchemeClr, aTransformations ); 4166 } 4167 4168 mpFS->endElement( nEffectToken ); 4169 } 4170 4171 static sal_Int32 lcl_CalculateDist(const double dX, const double dY) 4172 { 4173 return static_cast< sal_Int32 >(sqrt(dX*dX + dY*dY) * 360); 4174 } 4175 4176 static sal_Int32 lcl_CalculateDir(const double dX, const double dY) 4177 { 4178 return (static_cast< sal_Int32 >(basegfx::rad2deg(atan2(dY,dX)) * 60000) + 21600000) % 21600000; 4179 } 4180 4181 void DrawingML::WriteShapeEffects( const Reference< XPropertySet >& rXPropSet ) 4182 { 4183 Sequence< PropertyValue > aGrabBag, aEffects, aOuterShdwProps; 4184 bool bHasInteropGrabBag = rXPropSet->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"); 4185 if (bHasInteropGrabBag && GetProperty(rXPropSet, "InteropGrabBag")) 4186 { 4187 mAny >>= aGrabBag; 4188 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag), 4189 [](const PropertyValue& rProp) { return rProp.Name == "EffectProperties"; }); 4190 if (pProp != std::cend(aGrabBag)) 4191 { 4192 pProp->Value >>= aEffects; 4193 auto pEffect = std::find_if(std::cbegin(aEffects), std::cend(aEffects), 4194 [](const PropertyValue& rEffect) { return rEffect.Name == "outerShdw"; }); 4195 if (pEffect != std::cend(aEffects)) 4196 pEffect->Value >>= aOuterShdwProps; 4197 } 4198 } 4199 4200 // tdf#132201: the order of effects is important. Effects order (CT_EffectList in ECMA-376): 4201 // blur -> fillOverlay -> glow -> innerShdw -> outerShdw -> prstShdw -> reflection -> softEdge 4202 4203 if( !aEffects.hasElements() ) 4204 { 4205 bool bHasShadow = false; 4206 if( GetProperty( rXPropSet, "Shadow" ) ) 4207 mAny >>= bHasShadow; 4208 bool bHasEffects = bHasShadow; 4209 if (!bHasEffects && GetProperty(rXPropSet, "GlowEffectRadius")) 4210 { 4211 sal_Int32 rad = 0; 4212 mAny >>= rad; 4213 bHasEffects = rad > 0; 4214 } 4215 if (!bHasEffects && GetProperty(rXPropSet, "SoftEdgeRadius")) 4216 { 4217 sal_Int32 rad = 0; 4218 mAny >>= rad; 4219 bHasEffects = rad > 0; 4220 } 4221 4222 if (bHasEffects) 4223 { 4224 mpFS->startElementNS(XML_a, XML_effectLst); 4225 WriteGlowEffect(rXPropSet); 4226 if( bHasShadow ) 4227 { 4228 Sequence< PropertyValue > aShadowGrabBag( 3 ); 4229 Sequence< PropertyValue > aShadowAttribsGrabBag( 3 ); 4230 4231 double dX = +0.0, dY = +0.0; 4232 sal_Int32 nBlur =0; 4233 rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX; 4234 rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY; 4235 rXPropSet->getPropertyValue( "ShadowBlur" ) >>= nBlur; 4236 4237 aShadowAttribsGrabBag[0].Name = "dist"; 4238 aShadowAttribsGrabBag[0].Value <<= lcl_CalculateDist(dX, dY); 4239 aShadowAttribsGrabBag[1].Name = "dir"; 4240 aShadowAttribsGrabBag[1].Value <<= lcl_CalculateDir(dX, dY); 4241 aShadowAttribsGrabBag[2].Name = "blurRad"; 4242 aShadowAttribsGrabBag[2].Value <<= oox::drawingml::convertHmmToEmu(nBlur); 4243 4244 aShadowGrabBag[0].Name = "Attribs"; 4245 aShadowGrabBag[0].Value <<= aShadowAttribsGrabBag; 4246 aShadowGrabBag[1].Name = "RgbClr"; 4247 aShadowGrabBag[1].Value = rXPropSet->getPropertyValue( "ShadowColor" ); 4248 aShadowGrabBag[2].Name = "RgbClrTransparency"; 4249 aShadowGrabBag[2].Value = rXPropSet->getPropertyValue( "ShadowTransparence" ); 4250 4251 WriteShapeEffect( u"outerShdw", aShadowGrabBag ); 4252 } 4253 WriteSoftEdgeEffect(rXPropSet); 4254 mpFS->endElementNS(XML_a, XML_effectLst); 4255 } 4256 } 4257 else 4258 { 4259 for( auto& rOuterShdwProp : aOuterShdwProps ) 4260 { 4261 if( rOuterShdwProp.Name == "Attribs" ) 4262 { 4263 Sequence< PropertyValue > aAttribsProps; 4264 rOuterShdwProp.Value >>= aAttribsProps; 4265 4266 double dX = +0.0, dY = +0.0; 4267 sal_Int32 nBlur =0; 4268 rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX; 4269 rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY; 4270 rXPropSet->getPropertyValue( "ShadowBlur" ) >>= nBlur; 4271 4272 4273 for( auto& rAttribsProp : aAttribsProps ) 4274 { 4275 if( rAttribsProp.Name == "dist" ) 4276 { 4277 rAttribsProp.Value <<= lcl_CalculateDist(dX, dY); 4278 } 4279 else if( rAttribsProp.Name == "dir" ) 4280 { 4281 rAttribsProp.Value <<= lcl_CalculateDir(dX, dY); 4282 } 4283 else if( rAttribsProp.Name == "blurRad" ) 4284 { 4285 rAttribsProp.Value <<= oox::drawingml::convertHmmToEmu(nBlur); 4286 } 4287 } 4288 4289 rOuterShdwProp.Value <<= aAttribsProps; 4290 } 4291 else if( rOuterShdwProp.Name == "RgbClr" ) 4292 { 4293 rOuterShdwProp.Value = rXPropSet->getPropertyValue( "ShadowColor" ); 4294 } 4295 else if( rOuterShdwProp.Name == "RgbClrTransparency" ) 4296 { 4297 rOuterShdwProp.Value = rXPropSet->getPropertyValue( "ShadowTransparence" ); 4298 } 4299 } 4300 4301 mpFS->startElementNS(XML_a, XML_effectLst); 4302 bool bGlowWritten = false; 4303 for( const auto& rEffect : std::as_const(aEffects) ) 4304 { 4305 if (!bGlowWritten 4306 && (rEffect.Name == "innerShdw" || rEffect.Name == "outerShdw" 4307 || rEffect.Name == "prstShdw" || rEffect.Name == "reflection" 4308 || rEffect.Name == "softEdge")) 4309 { 4310 WriteGlowEffect(rXPropSet); 4311 bGlowWritten = true; 4312 } 4313 4314 if( rEffect.Name == "outerShdw" ) 4315 { 4316 WriteShapeEffect( rEffect.Name, aOuterShdwProps ); 4317 } 4318 else 4319 { 4320 Sequence< PropertyValue > aEffectProps; 4321 rEffect.Value >>= aEffectProps; 4322 WriteShapeEffect( rEffect.Name, aEffectProps ); 4323 } 4324 } 4325 if (!bGlowWritten) 4326 WriteGlowEffect(rXPropSet); 4327 WriteSoftEdgeEffect(rXPropSet); // the last 4328 4329 mpFS->endElementNS(XML_a, XML_effectLst); 4330 } 4331 } 4332 4333 void DrawingML::WriteGlowEffect(const Reference< XPropertySet >& rXPropSet) 4334 { 4335 if (!rXPropSet->getPropertySetInfo()->hasPropertyByName("GlowEffectRadius")) 4336 { 4337 return; 4338 } 4339 4340 sal_Int32 nRad = 0; 4341 rXPropSet->getPropertyValue("GlowEffectRadius") >>= nRad; 4342 if (!nRad) 4343 return; 4344 4345 Sequence< PropertyValue > aGlowAttribs(1); 4346 aGlowAttribs[0].Name = "rad"; 4347 aGlowAttribs[0].Value <<= oox::drawingml::convertHmmToEmu(nRad); 4348 Sequence< PropertyValue > aGlowProps(3); 4349 aGlowProps[0].Name = "Attribs"; 4350 aGlowProps[0].Value <<= aGlowAttribs; 4351 aGlowProps[1].Name = "RgbClr"; 4352 aGlowProps[1].Value = rXPropSet->getPropertyValue("GlowEffectColor"); 4353 aGlowProps[2].Name = "RgbClrTransparency"; 4354 aGlowProps[2].Value = rXPropSet->getPropertyValue("GlowEffectTransparency"); 4355 // TODO other stuff like saturation or luminance 4356 4357 WriteShapeEffect(u"glow", aGlowProps); 4358 } 4359 4360 void DrawingML::WriteSoftEdgeEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet) 4361 { 4362 if (!rXPropSet->getPropertySetInfo()->hasPropertyByName("SoftEdgeRadius")) 4363 { 4364 return; 4365 } 4366 4367 sal_Int32 nRad = 0; 4368 rXPropSet->getPropertyValue("SoftEdgeRadius") >>= nRad; 4369 if (!nRad) 4370 return; 4371 4372 css::uno::Sequence<css::beans::PropertyValue> aAttribs(1); 4373 aAttribs[0].Name = "rad"; 4374 aAttribs[0].Value <<= oox::drawingml::convertHmmToEmu(nRad); 4375 css::uno::Sequence<css::beans::PropertyValue> aProps(1); 4376 aProps[0].Name = "Attribs"; 4377 aProps[0].Value <<= aAttribs; 4378 4379 WriteShapeEffect(u"softEdge", aProps); 4380 } 4381 4382 bool DrawingML::HasEnhancedCustomShapeSegmentCommand( 4383 const css::uno::Reference<css::drawing::XShape>& rXShape, const sal_Int16 nCommand) 4384 { 4385 try 4386 { 4387 uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY_THROW); 4388 if (!GetProperty(xPropSet, "CustomShapeGeometry")) 4389 return false; 4390 Sequence<PropertyValue> aCustomShapeGeometryProps; 4391 mAny >>= aCustomShapeGeometryProps; 4392 for (const beans::PropertyValue& rGeomProp : std::as_const(aCustomShapeGeometryProps)) 4393 { 4394 if (rGeomProp.Name == "Path") 4395 { 4396 uno::Sequence<beans::PropertyValue> aPathProps; 4397 rGeomProp.Value >>= aPathProps; 4398 for (const beans::PropertyValue& rPathProp : std::as_const(aPathProps)) 4399 { 4400 if (rPathProp.Name == "Segments") 4401 { 4402 uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments; 4403 rPathProp.Value >>= aSegments; 4404 for (const auto& rSegment : std::as_const(aSegments)) 4405 { 4406 if (rSegment.Command == nCommand) 4407 return true; 4408 } 4409 } 4410 } 4411 } 4412 } 4413 } 4414 catch (const ::uno::Exception&) 4415 { 4416 } 4417 return false; 4418 } 4419 4420 void DrawingML::WriteShape3DEffects( const Reference< XPropertySet >& xPropSet ) 4421 { 4422 // check existence of the grab bag 4423 if( !GetProperty( xPropSet, "InteropGrabBag" ) ) 4424 return; 4425 4426 // extract the relevant properties from the grab bag 4427 Sequence< PropertyValue > aGrabBag, aEffectProps, aLightRigProps, aShape3DProps; 4428 mAny >>= aGrabBag; 4429 auto pShapeProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag), 4430 [](const PropertyValue& rProp) { return rProp.Name == "3DEffectProperties"; }); 4431 if (pShapeProp != std::cend(aGrabBag)) 4432 { 4433 Sequence< PropertyValue > a3DEffectProps; 4434 pShapeProp->Value >>= a3DEffectProps; 4435 for( const auto& r3DEffectProp : std::as_const(a3DEffectProps) ) 4436 { 4437 if( r3DEffectProp.Name == "Camera" ) 4438 r3DEffectProp.Value >>= aEffectProps; 4439 else if( r3DEffectProp.Name == "LightRig" ) 4440 r3DEffectProp.Value >>= aLightRigProps; 4441 else if( r3DEffectProp.Name == "Shape3D" ) 4442 r3DEffectProp.Value >>= aShape3DProps; 4443 } 4444 } 4445 4446 auto pTextProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag), 4447 [](const PropertyValue& rProp) { return rProp.Name == "Text3DEffectProperties"; }); 4448 4449 if (pTextProp != std::cend(aGrabBag)) 4450 { 4451 Sequence< PropertyValue > a3DEffectProps; 4452 pTextProp->Value >>= a3DEffectProps; 4453 for( const auto& r3DEffectProp : std::as_const(a3DEffectProps) ) 4454 { 4455 if( r3DEffectProp.Name == "Camera" ) 4456 r3DEffectProp.Value >>= aEffectProps; 4457 else if( r3DEffectProp.Name == "LightRig" ) 4458 r3DEffectProp.Value >>= aLightRigProps; 4459 else if( r3DEffectProp.Name == "Shape3D" ) 4460 r3DEffectProp.Value >>= aShape3DProps; 4461 } 4462 } 4463 4464 if( !aEffectProps.hasElements() && !aLightRigProps.hasElements() && !aShape3DProps.hasElements() ) 4465 return; 4466 4467 bool bCameraRotationPresent = false; 4468 rtl::Reference<sax_fastparser::FastAttributeList> aCameraAttrList = FastSerializerHelper::createAttrList(); 4469 rtl::Reference<sax_fastparser::FastAttributeList> aCameraRotationAttrList = FastSerializerHelper::createAttrList(); 4470 for( const auto& rEffectProp : std::as_const(aEffectProps) ) 4471 { 4472 if( rEffectProp.Name == "prst" ) 4473 { 4474 OUString sVal; 4475 rEffectProp.Value >>= sVal; 4476 aCameraAttrList->add(XML_prst, OUStringToOString(sVal, RTL_TEXTENCODING_UTF8)); 4477 } 4478 else if( rEffectProp.Name == "fov" ) 4479 { 4480 float fVal = 0; 4481 rEffectProp.Value >>= fVal; 4482 aCameraAttrList->add( XML_fov, OString::number( fVal * 60000 ).getStr() ); 4483 } 4484 else if( rEffectProp.Name == "zoom" ) 4485 { 4486 float fVal = 1; 4487 rEffectProp.Value >>= fVal; 4488 aCameraAttrList->add( XML_zoom, OString::number( fVal * 100000 ).getStr() ); 4489 } 4490 else if( rEffectProp.Name == "rotLat" || 4491 rEffectProp.Name == "rotLon" || 4492 rEffectProp.Name == "rotRev" ) 4493 { 4494 sal_Int32 nVal = 0, nToken = XML_none; 4495 rEffectProp.Value >>= nVal; 4496 if( rEffectProp.Name == "rotLat" ) 4497 nToken = XML_lat; 4498 else if( rEffectProp.Name == "rotLon" ) 4499 nToken = XML_lon; 4500 else if( rEffectProp.Name == "rotRev" ) 4501 nToken = XML_rev; 4502 aCameraRotationAttrList->add( nToken, OString::number( nVal ).getStr() ); 4503 bCameraRotationPresent = true; 4504 } 4505 } 4506 4507 bool bLightRigRotationPresent = false; 4508 rtl::Reference<sax_fastparser::FastAttributeList> aLightRigAttrList = FastSerializerHelper::createAttrList(); 4509 rtl::Reference<sax_fastparser::FastAttributeList> aLightRigRotationAttrList = FastSerializerHelper::createAttrList(); 4510 for( const auto& rLightRigProp : std::as_const(aLightRigProps) ) 4511 { 4512 if( rLightRigProp.Name == "rig" || rLightRigProp.Name == "dir" ) 4513 { 4514 OUString sVal; 4515 sal_Int32 nToken = XML_none; 4516 rLightRigProp.Value >>= sVal; 4517 if( rLightRigProp.Name == "rig" ) 4518 nToken = XML_rig; 4519 else if( rLightRigProp.Name == "dir" ) 4520 nToken = XML_dir; 4521 aLightRigAttrList->add(nToken, OUStringToOString(sVal, RTL_TEXTENCODING_UTF8)); 4522 } 4523 else if( rLightRigProp.Name == "rotLat" || 4524 rLightRigProp.Name == "rotLon" || 4525 rLightRigProp.Name == "rotRev" ) 4526 { 4527 sal_Int32 nVal = 0, nToken = XML_none; 4528 rLightRigProp.Value >>= nVal; 4529 if( rLightRigProp.Name == "rotLat" ) 4530 nToken = XML_lat; 4531 else if( rLightRigProp.Name == "rotLon" ) 4532 nToken = XML_lon; 4533 else if( rLightRigProp.Name == "rotRev" ) 4534 nToken = XML_rev; 4535 aLightRigRotationAttrList->add( nToken, OString::number( nVal ).getStr() ); 4536 bLightRigRotationPresent = true; 4537 } 4538 } 4539 4540 mpFS->startElementNS(XML_a, XML_scene3d); 4541 4542 if( aEffectProps.hasElements() ) 4543 { 4544 mpFS->startElementNS( XML_a, XML_camera, aCameraAttrList ); 4545 if( bCameraRotationPresent ) 4546 { 4547 mpFS->singleElementNS( XML_a, XML_rot, aCameraRotationAttrList ); 4548 } 4549 mpFS->endElementNS( XML_a, XML_camera ); 4550 } 4551 else 4552 { 4553 // a:camera with Word default values - Word won't open the document if this is not present 4554 mpFS->singleElementNS(XML_a, XML_camera, XML_prst, "orthographicFront"); 4555 } 4556 4557 if( aEffectProps.hasElements() ) 4558 { 4559 mpFS->startElementNS( XML_a, XML_lightRig, aLightRigAttrList ); 4560 if( bLightRigRotationPresent ) 4561 { 4562 mpFS->singleElementNS( XML_a, XML_rot, aLightRigRotationAttrList ); 4563 } 4564 mpFS->endElementNS( XML_a, XML_lightRig ); 4565 } 4566 else 4567 { 4568 // a:lightRig with Word default values - Word won't open the document if this is not present 4569 mpFS->singleElementNS(XML_a, XML_lightRig, XML_rig, "threePt", XML_dir, "t"); 4570 } 4571 4572 mpFS->endElementNS( XML_a, XML_scene3d ); 4573 4574 if( !aShape3DProps.hasElements() ) 4575 return; 4576 4577 bool bBevelTPresent = false, bBevelBPresent = false; 4578 Sequence< PropertyValue > aExtrusionColorProps, aContourColorProps; 4579 rtl::Reference<sax_fastparser::FastAttributeList> aBevelTAttrList = FastSerializerHelper::createAttrList(); 4580 rtl::Reference<sax_fastparser::FastAttributeList> aBevelBAttrList = FastSerializerHelper::createAttrList(); 4581 rtl::Reference<sax_fastparser::FastAttributeList> aShape3DAttrList = FastSerializerHelper::createAttrList(); 4582 for( const auto& rShape3DProp : std::as_const(aShape3DProps) ) 4583 { 4584 if( rShape3DProp.Name == "extrusionH" || rShape3DProp.Name == "contourW" || rShape3DProp.Name == "z" ) 4585 { 4586 sal_Int32 nVal = 0, nToken = XML_none; 4587 rShape3DProp.Value >>= nVal; 4588 if( rShape3DProp.Name == "extrusionH" ) 4589 nToken = XML_extrusionH; 4590 else if( rShape3DProp.Name == "contourW" ) 4591 nToken = XML_contourW; 4592 else if( rShape3DProp.Name == "z" ) 4593 nToken = XML_z; 4594 aShape3DAttrList->add( nToken, OString::number( nVal ).getStr() ); 4595 } 4596 else if( rShape3DProp.Name == "prstMaterial" ) 4597 { 4598 OUString sVal; 4599 rShape3DProp.Value >>= sVal; 4600 aShape3DAttrList->add(XML_prstMaterial, OUStringToOString(sVal, RTL_TEXTENCODING_UTF8)); 4601 } 4602 else if( rShape3DProp.Name == "extrusionClr" ) 4603 { 4604 rShape3DProp.Value >>= aExtrusionColorProps; 4605 } 4606 else if( rShape3DProp.Name == "contourClr" ) 4607 { 4608 rShape3DProp.Value >>= aContourColorProps; 4609 } 4610 else if( rShape3DProp.Name == "bevelT" || rShape3DProp.Name == "bevelB" ) 4611 { 4612 Sequence< PropertyValue > aBevelProps; 4613 rShape3DProp.Value >>= aBevelProps; 4614 if ( !aBevelProps.hasElements() ) 4615 continue; 4616 4617 rtl::Reference<sax_fastparser::FastAttributeList> aBevelAttrList; 4618 if( rShape3DProp.Name == "bevelT" ) 4619 { 4620 bBevelTPresent = true; 4621 aBevelAttrList = aBevelTAttrList; 4622 } 4623 else 4624 { 4625 bBevelBPresent = true; 4626 aBevelAttrList = aBevelBAttrList; 4627 } 4628 for( const auto& rBevelProp : std::as_const(aBevelProps) ) 4629 { 4630 if( rBevelProp.Name == "w" || rBevelProp.Name == "h" ) 4631 { 4632 sal_Int32 nVal = 0, nToken = XML_none; 4633 rBevelProp.Value >>= nVal; 4634 if( rBevelProp.Name == "w" ) 4635 nToken = XML_w; 4636 else if( rBevelProp.Name == "h" ) 4637 nToken = XML_h; 4638 aBevelAttrList->add( nToken, OString::number( nVal ).getStr() ); 4639 } 4640 else if( rBevelProp.Name == "prst" ) 4641 { 4642 OUString sVal; 4643 rBevelProp.Value >>= sVal; 4644 aBevelAttrList->add(XML_prst, OUStringToOString(sVal, RTL_TEXTENCODING_UTF8)); 4645 } 4646 } 4647 4648 } 4649 } 4650 4651 mpFS->startElementNS( XML_a, XML_sp3d, aShape3DAttrList ); 4652 if( bBevelTPresent ) 4653 { 4654 mpFS->singleElementNS( XML_a, XML_bevelT, aBevelTAttrList ); 4655 } 4656 if( bBevelBPresent ) 4657 { 4658 mpFS->singleElementNS( XML_a, XML_bevelB, aBevelBAttrList ); 4659 } 4660 if( aExtrusionColorProps.hasElements() ) 4661 { 4662 OUString sSchemeClr; 4663 ::Color nColor; 4664 sal_Int32 nTransparency(0); 4665 Sequence< PropertyValue > aColorTransformations; 4666 for( const auto& rExtrusionColorProp : std::as_const(aExtrusionColorProps) ) 4667 { 4668 if( rExtrusionColorProp.Name == "schemeClr" ) 4669 rExtrusionColorProp.Value >>= sSchemeClr; 4670 else if( rExtrusionColorProp.Name == "schemeClrTransformations" ) 4671 rExtrusionColorProp.Value >>= aColorTransformations; 4672 else if( rExtrusionColorProp.Name == "rgbClr" ) 4673 rExtrusionColorProp.Value >>= nColor; 4674 else if( rExtrusionColorProp.Name == "rgbClrTransparency" ) 4675 rExtrusionColorProp.Value >>= nTransparency; 4676 } 4677 mpFS->startElementNS(XML_a, XML_extrusionClr); 4678 4679 if( sSchemeClr.isEmpty() ) 4680 WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) ); 4681 else 4682 WriteColor( sSchemeClr, aColorTransformations ); 4683 4684 mpFS->endElementNS( XML_a, XML_extrusionClr ); 4685 } 4686 if( aContourColorProps.hasElements() ) 4687 { 4688 OUString sSchemeClr; 4689 ::Color nColor; 4690 sal_Int32 nTransparency(0); 4691 Sequence< PropertyValue > aColorTransformations; 4692 for( const auto& rContourColorProp : std::as_const(aContourColorProps) ) 4693 { 4694 if( rContourColorProp.Name == "schemeClr" ) 4695 rContourColorProp.Value >>= sSchemeClr; 4696 else if( rContourColorProp.Name == "schemeClrTransformations" ) 4697 rContourColorProp.Value >>= aColorTransformations; 4698 else if( rContourColorProp.Name == "rgbClr" ) 4699 rContourColorProp.Value >>= nColor; 4700 else if( rContourColorProp.Name == "rgbClrTransparency" ) 4701 rContourColorProp.Value >>= nTransparency; 4702 } 4703 mpFS->startElementNS(XML_a, XML_contourClr); 4704 4705 if( sSchemeClr.isEmpty() ) 4706 WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) ); 4707 else 4708 WriteColor( sSchemeClr, aContourColorProps ); 4709 4710 mpFS->endElementNS( XML_a, XML_contourClr ); 4711 } 4712 mpFS->endElementNS( XML_a, XML_sp3d ); 4713 } 4714 4715 void DrawingML::WriteArtisticEffect( const Reference< XPropertySet >& rXPropSet ) 4716 { 4717 if( !GetProperty( rXPropSet, "InteropGrabBag" ) ) 4718 return; 4719 4720 PropertyValue aEffect; 4721 Sequence< PropertyValue > aGrabBag; 4722 mAny >>= aGrabBag; 4723 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag), 4724 [](const PropertyValue& rProp) { return rProp.Name == "ArtisticEffectProperties"; }); 4725 if (pProp != std::cend(aGrabBag)) 4726 pProp->Value >>= aEffect; 4727 sal_Int32 nEffectToken = ArtisticEffectProperties::getEffectToken( aEffect.Name ); 4728 if( nEffectToken == XML_none ) 4729 return; 4730 4731 Sequence< PropertyValue > aAttrs; 4732 aEffect.Value >>= aAttrs; 4733 rtl::Reference<sax_fastparser::FastAttributeList> aAttrList = FastSerializerHelper::createAttrList(); 4734 OString sRelId; 4735 for( const auto& rAttr : std::as_const(aAttrs) ) 4736 { 4737 sal_Int32 nToken = ArtisticEffectProperties::getEffectToken( rAttr.Name ); 4738 if( nToken != XML_none ) 4739 { 4740 sal_Int32 nVal = 0; 4741 rAttr.Value >>= nVal; 4742 aAttrList->add( nToken, OString::number( nVal ).getStr() ); 4743 } 4744 else if( rAttr.Name == "OriginalGraphic" ) 4745 { 4746 Sequence< PropertyValue > aGraphic; 4747 rAttr.Value >>= aGraphic; 4748 Sequence< sal_Int8 > aGraphicData; 4749 OUString sGraphicId; 4750 for( const auto& rProp : std::as_const(aGraphic) ) 4751 { 4752 if( rProp.Name == "Id" ) 4753 rProp.Value >>= sGraphicId; 4754 else if( rProp.Name == "Data" ) 4755 rProp.Value >>= aGraphicData; 4756 } 4757 sRelId = WriteWdpPicture( sGraphicId, aGraphicData ); 4758 } 4759 } 4760 4761 mpFS->startElementNS(XML_a, XML_extLst); 4762 mpFS->startElementNS(XML_a, XML_ext, XML_uri, "{BEBA8EAE-BF5A-486C-A8C5-ECC9F3942E4B}"); 4763 mpFS->startElementNS( XML_a14, XML_imgProps, 4764 FSNS(XML_xmlns, XML_a14), mpFB->getNamespaceURL(OOX_NS(a14)) ); 4765 mpFS->startElementNS(XML_a14, XML_imgLayer, FSNS(XML_r, XML_embed), sRelId); 4766 mpFS->startElementNS(XML_a14, XML_imgEffect); 4767 4768 mpFS->singleElementNS( XML_a14, nEffectToken, aAttrList ); 4769 4770 mpFS->endElementNS( XML_a14, XML_imgEffect ); 4771 mpFS->endElementNS( XML_a14, XML_imgLayer ); 4772 mpFS->endElementNS( XML_a14, XML_imgProps ); 4773 mpFS->endElementNS( XML_a, XML_ext ); 4774 mpFS->endElementNS( XML_a, XML_extLst ); 4775 } 4776 4777 OString DrawingML::WriteWdpPicture( const OUString& rFileId, const Sequence< sal_Int8 >& rPictureData ) 4778 { 4779 std::map<OUString, OUString>::iterator aCachedItem = maWdpCache.find( rFileId ); 4780 if( aCachedItem != maWdpCache.end() ) 4781 return OUStringToOString( aCachedItem->second, RTL_TEXTENCODING_UTF8 ); 4782 4783 OUString sFileName = "media/hdphoto" + OUString::number( mnWdpImageCounter++ ) + ".wdp"; 4784 Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer() 4785 .appendAscii( GetComponentDir() ) 4786 .append( "/" + sFileName ) 4787 .makeStringAndClear(), 4788 "image/vnd.ms-photo" ); 4789 OUString sId; 4790 xOutStream->writeBytes( rPictureData ); 4791 xOutStream->closeOutput(); 4792 4793 sId = mpFB->addRelation( mpFS->getOutputStream(), 4794 oox::getRelationship(Relationship::HDPHOTO), 4795 OUStringBuffer() 4796 .appendAscii( GetRelationCompPrefix() ) 4797 .append( sFileName ) 4798 .makeStringAndClear() ); 4799 4800 maWdpCache[rFileId] = sId; 4801 return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 ); 4802 } 4803 4804 void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId) 4805 { 4806 uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY); 4807 4808 uno::Reference<xml::dom::XDocument> dataDom; 4809 uno::Reference<xml::dom::XDocument> layoutDom; 4810 uno::Reference<xml::dom::XDocument> styleDom; 4811 uno::Reference<xml::dom::XDocument> colorDom; 4812 uno::Reference<xml::dom::XDocument> drawingDom; 4813 uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq; 4814 uno::Sequence<uno::Any> diagramDrawing; 4815 4816 // retrieve the doms from the GrabBag 4817 uno::Sequence<beans::PropertyValue> propList; 4818 xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList; 4819 for (const auto& rProp : std::as_const(propList)) 4820 { 4821 OUString propName = rProp.Name; 4822 if (propName == "OOXData") 4823 rProp.Value >>= dataDom; 4824 else if (propName == "OOXLayout") 4825 rProp.Value >>= layoutDom; 4826 else if (propName == "OOXStyle") 4827 rProp.Value >>= styleDom; 4828 else if (propName == "OOXColor") 4829 rProp.Value >>= colorDom; 4830 else if (propName == "OOXDrawing") 4831 { 4832 rProp.Value >>= diagramDrawing; 4833 diagramDrawing[0] 4834 >>= drawingDom; // if there is OOXDrawing property then set drawingDom here only. 4835 } 4836 else if (propName == "OOXDiagramDataRels") 4837 rProp.Value >>= xDataRelSeq; 4838 } 4839 4840 // check that we have the 4 mandatory XDocuments 4841 // if not, there was an error importing and we won't output anything 4842 if (!dataDom.is() || !layoutDom.is() || !styleDom.is() || !colorDom.is()) 4843 return; 4844 4845 // generate a unique id 4846 rtl::Reference<sax_fastparser::FastAttributeList> pDocPrAttrList 4847 = sax_fastparser::FastSerializerHelper::createAttrList(); 4848 pDocPrAttrList->add(XML_id, OString::number(nDiagramId).getStr()); 4849 OUString sName = "Diagram" + OUString::number(nDiagramId); 4850 pDocPrAttrList->add(XML_name, OUStringToOString(sName, RTL_TEXTENCODING_UTF8)); 4851 4852 if (GetDocumentType() == DOCUMENT_DOCX) 4853 { 4854 mpFS->singleElementNS(XML_wp, XML_docPr, pDocPrAttrList); 4855 mpFS->singleElementNS(XML_wp, XML_cNvGraphicFramePr); 4856 4857 mpFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a), 4858 mpFB->getNamespaceURL(OOX_NS(dml))); 4859 } 4860 else 4861 { 4862 mpFS->startElementNS(XML_p, XML_nvGraphicFramePr); 4863 4864 mpFS->singleElementNS(XML_p, XML_cNvPr, pDocPrAttrList); 4865 mpFS->singleElementNS(XML_p, XML_cNvGraphicFramePr); 4866 4867 mpFS->startElementNS(XML_p, XML_nvPr); 4868 mpFS->startElementNS(XML_p, XML_extLst); 4869 // change tracking extension - required in PPTX 4870 mpFS->startElementNS(XML_p, XML_ext, XML_uri, "{D42A27DB-BD31-4B8C-83A1-F6EECF244321}"); 4871 mpFS->singleElementNS(XML_p14, XML_modId, 4872 FSNS(XML_xmlns, XML_p14), mpFB->getNamespaceURL(OOX_NS(p14)), 4873 XML_val, 4874 OString::number(comphelper::rng::uniform_uint_distribution(1, SAL_MAX_UINT32))); 4875 mpFS->endElementNS(XML_p, XML_ext); 4876 mpFS->endElementNS(XML_p, XML_extLst); 4877 mpFS->endElementNS(XML_p, XML_nvPr); 4878 4879 mpFS->endElementNS(XML_p, XML_nvGraphicFramePr); 4880 4881 // store size and position of background shape instead of group shape 4882 // as some shapes may be outside 4883 css::uno::Reference<css::drawing::XShapes> xShapes(rXShape, uno::UNO_QUERY); 4884 if (xShapes.is() && xShapes->hasElements()) 4885 { 4886 css::uno::Reference<css::drawing::XShape> xShapeBg(xShapes->getByIndex(0), 4887 uno::UNO_QUERY); 4888 awt::Point aPos = xShapeBg->getPosition(); 4889 awt::Size aSize = xShapeBg->getSize(); 4890 WriteTransformation( 4891 xShapeBg, tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)), 4892 XML_p, false, false, 0, false); 4893 } 4894 4895 mpFS->startElementNS(XML_a, XML_graphic); 4896 } 4897 4898 mpFS->startElementNS(XML_a, XML_graphicData, XML_uri, 4899 "http://schemas.openxmlformats.org/drawingml/2006/diagram"); 4900 4901 OUString sRelationCompPrefix = OUString::createFromAscii(GetRelationCompPrefix()); 4902 4903 // add data relation 4904 OUString dataFileName = "diagrams/data" + OUString::number(nDiagramId) + ".xml"; 4905 OString dataRelId = OUStringToOString( 4906 mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDATA), 4907 OUString(sRelationCompPrefix + dataFileName)), 4908 RTL_TEXTENCODING_UTF8); 4909 4910 // add layout relation 4911 OUString layoutFileName = "diagrams/layout" + OUString::number(nDiagramId) + ".xml"; 4912 OString layoutRelId 4913 = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(), 4914 oox::getRelationship(Relationship::DIAGRAMLAYOUT), 4915 OUString(sRelationCompPrefix + layoutFileName)), 4916 RTL_TEXTENCODING_UTF8); 4917 4918 // add style relation 4919 OUString styleFileName = "diagrams/quickStyle" + OUString::number(nDiagramId) + ".xml"; 4920 OString styleRelId 4921 = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(), 4922 oox::getRelationship(Relationship::DIAGRAMQUICKSTYLE), 4923 OUString(sRelationCompPrefix + styleFileName)), 4924 RTL_TEXTENCODING_UTF8); 4925 4926 // add color relation 4927 OUString colorFileName = "diagrams/colors" + OUString::number(nDiagramId) + ".xml"; 4928 OString colorRelId 4929 = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(), 4930 oox::getRelationship(Relationship::DIAGRAMCOLORS), 4931 OUString(sRelationCompPrefix + colorFileName)), 4932 RTL_TEXTENCODING_UTF8); 4933 4934 OUString drawingFileName; 4935 if (drawingDom.is()) 4936 { 4937 // add drawing relation 4938 drawingFileName = "diagrams/drawing" + OUString::number(nDiagramId) + ".xml"; 4939 OUString drawingRelId = mpFB->addRelation( 4940 mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDRAWING), 4941 OUString(sRelationCompPrefix + drawingFileName)); 4942 4943 // the data dom contains a reference to the drawing relation. We need to update it with the new generated 4944 // relation value before writing the dom to a file 4945 4946 // Get the dsp:damaModelExt node from the dom 4947 uno::Reference<xml::dom::XNodeList> nodeList = dataDom->getElementsByTagNameNS( 4948 "http://schemas.microsoft.com/office/drawing/2008/diagram", "dataModelExt"); 4949 4950 // There must be one element only so get it 4951 uno::Reference<xml::dom::XNode> node = nodeList->item(0); 4952 4953 // Get the list of attributes of the node 4954 uno::Reference<xml::dom::XNamedNodeMap> nodeMap = node->getAttributes(); 4955 4956 // Get the node with the relId attribute and set its new value 4957 uno::Reference<xml::dom::XNode> relIdNode = nodeMap->getNamedItem("relId"); 4958 relIdNode->setNodeValue(drawingRelId); 4959 } 4960 4961 mpFS->singleElementNS(XML_dgm, XML_relIds, 4962 FSNS(XML_xmlns, XML_dgm), mpFB->getNamespaceURL(OOX_NS(dmlDiagram)), 4963 FSNS(XML_xmlns, XML_r), mpFB->getNamespaceURL(OOX_NS(officeRel)), 4964 FSNS(XML_r, XML_dm), dataRelId, FSNS(XML_r, XML_lo), layoutRelId, 4965 FSNS(XML_r, XML_qs), styleRelId, FSNS(XML_r, XML_cs), colorRelId); 4966 4967 mpFS->endElementNS(XML_a, XML_graphicData); 4968 mpFS->endElementNS(XML_a, XML_graphic); 4969 4970 uno::Reference<xml::sax::XSAXSerializable> serializer; 4971 uno::Reference<xml::sax::XWriter> writer 4972 = xml::sax::Writer::create(comphelper::getProcessComponentContext()); 4973 4974 OUString sDir = OUString::createFromAscii(GetComponentDir()); 4975 4976 // write data file 4977 serializer.set(dataDom, uno::UNO_QUERY); 4978 uno::Reference<io::XOutputStream> xDataOutputStream = mpFB->openFragmentStream( 4979 sDir + "/" + dataFileName, 4980 "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml"); 4981 writer->setOutputStream(xDataOutputStream); 4982 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), 4983 uno::Sequence<beans::StringPair>()); 4984 4985 // write the associated Images and rels for data file 4986 writeDiagramRels(xDataRelSeq, xDataOutputStream, u"OOXDiagramDataRels", nDiagramId); 4987 4988 // write layout file 4989 serializer.set(layoutDom, uno::UNO_QUERY); 4990 writer->setOutputStream(mpFB->openFragmentStream( 4991 sDir + "/" + layoutFileName, 4992 "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml")); 4993 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), 4994 uno::Sequence<beans::StringPair>()); 4995 4996 // write style file 4997 serializer.set(styleDom, uno::UNO_QUERY); 4998 writer->setOutputStream(mpFB->openFragmentStream( 4999 sDir + "/" + styleFileName, 5000 "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml")); 5001 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), 5002 uno::Sequence<beans::StringPair>()); 5003 5004 // write color file 5005 serializer.set(colorDom, uno::UNO_QUERY); 5006 writer->setOutputStream(mpFB->openFragmentStream( 5007 sDir + "/" + colorFileName, 5008 "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml")); 5009 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), 5010 uno::Sequence<beans::StringPair>()); 5011 5012 // write drawing file 5013 if (!drawingDom.is()) 5014 return; 5015 5016 serializer.set(drawingDom, uno::UNO_QUERY); 5017 uno::Reference<io::XOutputStream> xDrawingOutputStream = mpFB->openFragmentStream( 5018 sDir + "/" + drawingFileName, "application/vnd.ms-office.drawingml.diagramDrawing+xml"); 5019 writer->setOutputStream(xDrawingOutputStream); 5020 serializer->serialize( 5021 uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), 5022 uno::Sequence<beans::StringPair>()); 5023 5024 // write the associated Images and rels for drawing file 5025 uno::Sequence<uno::Sequence<uno::Any>> xDrawingRelSeq; 5026 diagramDrawing[1] >>= xDrawingRelSeq; 5027 writeDiagramRels(xDrawingRelSeq, xDrawingOutputStream, u"OOXDiagramDrawingRels", nDiagramId); 5028 } 5029 5030 void DrawingML::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq, 5031 const uno::Reference<io::XOutputStream>& xOutStream, 5032 std::u16string_view sGrabBagProperyName, int nDiagramId) 5033 { 5034 // add image relationships of OOXData, OOXDiagram 5035 OUString sType(oox::getRelationship(Relationship::IMAGE)); 5036 uno::Reference<xml::sax::XWriter> xWriter 5037 = xml::sax::Writer::create(comphelper::getProcessComponentContext()); 5038 xWriter->setOutputStream(xOutStream); 5039 5040 // retrieve the relationships from Sequence 5041 for (sal_Int32 j = 0; j < xRelSeq.getLength(); j++) 5042 { 5043 // diagramDataRelTuple[0] => RID, 5044 // diagramDataRelTuple[1] => xInputStream 5045 // diagramDataRelTuple[2] => extension 5046 uno::Sequence<uno::Any> diagramDataRelTuple = xRelSeq[j]; 5047 5048 OUString sRelId; 5049 OUString sExtension; 5050 diagramDataRelTuple[0] >>= sRelId; 5051 diagramDataRelTuple[2] >>= sExtension; 5052 OUString sContentType; 5053 if (sExtension.equalsIgnoreAsciiCase(".WMF")) 5054 sContentType = "image/x-wmf"; 5055 else 5056 sContentType = OUString::Concat("image/") + sExtension.subView(1); 5057 sRelId = sRelId.copy(3); 5058 5059 StreamDataSequence dataSeq; 5060 diagramDataRelTuple[1] >>= dataSeq; 5061 uno::Reference<io::XInputStream> dataImagebin( 5062 new ::comphelper::SequenceInputStream(dataSeq)); 5063 5064 //nDiagramId is used to make the name unique irrespective of the number of smart arts. 5065 OUString sFragment = OUString::Concat("media/") + sGrabBagProperyName 5066 + OUString::number(nDiagramId) + "_" 5067 + OUString::number(j) + sExtension; 5068 5069 PropertySet aProps(xOutStream); 5070 aProps.setAnyProperty(PROP_RelId, uno::makeAny(sRelId.toInt32())); 5071 5072 mpFB->addRelation(xOutStream, sType, OUString("../" + sFragment)); 5073 5074 OUString sDir = OUString::createFromAscii(GetComponentDir()); 5075 uno::Reference<io::XOutputStream> xBinOutStream 5076 = mpFB->openFragmentStream(sDir + "/" + sFragment, sContentType); 5077 5078 try 5079 { 5080 comphelper::OStorageHelper::CopyInputToOutput(dataImagebin, xBinOutStream); 5081 } 5082 catch (const uno::Exception&) 5083 { 5084 TOOLS_WARN_EXCEPTION("oox.drawingml", "DrawingML::writeDiagramRels Failed to copy grabbaged Image"); 5085 } 5086 dataImagebin->closeInput(); 5087 } 5088 } 5089 5090 void DrawingML::WriteFromTo(const uno::Reference<css::drawing::XShape>& rXShape, const awt::Size& aPageSize, 5091 const FSHelperPtr& pDrawing) 5092 { 5093 awt::Point aTopLeft = rXShape->getPosition(); 5094 awt::Size aSize = rXShape->getSize(); 5095 5096 SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rXShape); 5097 if (pObj) 5098 { 5099 Degree100 nRotation = pObj->GetRotateAngle(); 5100 if (nRotation) 5101 { 5102 sal_Int16 nHalfWidth = aSize.Width / 2; 5103 sal_Int16 nHalfHeight = aSize.Height / 2; 5104 // aTopLeft needs correction for rotated customshapes 5105 if (pObj->GetObjIdentifier() == OBJ_CUSTOMSHAPE) 5106 { 5107 const tools::Rectangle& aSnapRect(pObj->GetSnapRect()); // bounding box of the rotated shape 5108 aTopLeft.X = aSnapRect.getX() + (aSnapRect.GetWidth() / 2) - nHalfWidth; 5109 aTopLeft.Y = aSnapRect.getY() + (aSnapRect.GetHeight() / 2) - nHalfHeight; 5110 } 5111 5112 // MSO changes the anchor positions at these angles and that does an extra 90 degrees 5113 // rotation on our shapes, so we output it in such position that MSO 5114 // can draw this shape correctly. 5115 if ((nRotation >= 4500_deg100 && nRotation < 13500_deg100) || (nRotation >= 22500_deg100 && nRotation < 31500_deg100)) 5116 { 5117 aTopLeft.X = aTopLeft.X - nHalfHeight + nHalfWidth; 5118 aTopLeft.Y = aTopLeft.Y - nHalfWidth + nHalfHeight; 5119 5120 std::swap(aSize.Width, aSize.Height); 5121 } 5122 } 5123 } 5124 5125 tools::Rectangle aLocation(aTopLeft.X, aTopLeft.Y, aTopLeft.X + aSize.Width, aTopLeft.Y + aSize.Height); 5126 double nXpos = static_cast<double>(aLocation.TopLeft().getX()) / static_cast<double>(aPageSize.Width); 5127 double nYpos = static_cast<double>(aLocation.TopLeft().getY()) / static_cast<double>(aPageSize.Height); 5128 5129 pDrawing->startElement(FSNS(XML_cdr, XML_from)); 5130 pDrawing->startElement(FSNS(XML_cdr, XML_x)); 5131 pDrawing->write(nXpos); 5132 pDrawing->endElement(FSNS(XML_cdr, XML_x)); 5133 pDrawing->startElement(FSNS(XML_cdr, XML_y)); 5134 pDrawing->write(nYpos); 5135 pDrawing->endElement(FSNS(XML_cdr, XML_y)); 5136 pDrawing->endElement(FSNS(XML_cdr, XML_from)); 5137 5138 nXpos = static_cast<double>(aLocation.BottomRight().getX()) / static_cast<double>(aPageSize.Width); 5139 nYpos = static_cast<double>(aLocation.BottomRight().getY()) / static_cast<double>(aPageSize.Height); 5140 5141 pDrawing->startElement(FSNS(XML_cdr, XML_to)); 5142 pDrawing->startElement(FSNS(XML_cdr, XML_x)); 5143 pDrawing->write(nXpos); 5144 pDrawing->endElement(FSNS(XML_cdr, XML_x)); 5145 pDrawing->startElement(FSNS(XML_cdr, XML_y)); 5146 pDrawing->write(nYpos); 5147 pDrawing->endElement(FSNS(XML_cdr, XML_y)); 5148 pDrawing->endElement(FSNS(XML_cdr, XML_to)); 5149 } 5150 5151 } 5152 5153 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 5154
