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