LibreOffice Module oox (master) 1
drawingml.cxx
Go to the documentation of this file.
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>
27#include <oox/export/utils.hxx>
33#include <oox/token/namespaces.hxx>
34#include <oox/token/properties.hxx>
36#include <oox/token/tokens.hxx>
38#include <svtools/unitconv.hxx>
39#include <sax/fastattribs.hxx>
46
47#include <numeric>
48#include <string_view>
49
50#include <com/sun/star/awt/CharSet.hpp>
51#include <com/sun/star/awt/FontDescriptor.hpp>
52#include <com/sun/star/awt/FontSlant.hpp>
53#include <com/sun/star/awt/FontStrikeout.hpp>
54#include <com/sun/star/awt/FontWeight.hpp>
55#include <com/sun/star/awt/FontUnderline.hpp>
56#include <com/sun/star/awt/Gradient.hpp>
57#include <com/sun/star/awt/Gradient2.hpp>
58#include <com/sun/star/beans/XPropertySet.hpp>
59#include <com/sun/star/beans/XPropertyState.hpp>
60#include <com/sun/star/beans/XPropertySetInfo.hpp>
61#include <com/sun/star/container/XEnumerationAccess.hpp>
62#include <com/sun/star/container/XIndexAccess.hpp>
63#include <com/sun/star/container/XNameAccess.hpp>
64#include <com/sun/star/drawing/BitmapMode.hpp>
65#include <com/sun/star/drawing/ColorMode.hpp>
66#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
67#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
68#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
69#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
70#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
71#include <com/sun/star/drawing/FillStyle.hpp>
72#include <com/sun/star/drawing/Hatch.hpp>
73#include <com/sun/star/drawing/LineDash.hpp>
74#include <com/sun/star/drawing/LineJoint.hpp>
75#include <com/sun/star/drawing/LineStyle.hpp>
76#include <com/sun/star/drawing/TextFitToSizeType.hpp>
77#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
78#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
79#include <com/sun/star/drawing/XShape.hpp>
80#include <com/sun/star/drawing/XShapes.hpp>
81#include <com/sun/star/frame/XModel.hpp>
82#include <com/sun/star/graphic/XGraphic.hpp>
83#include <com/sun/star/i18n/ScriptType.hpp>
84#include <com/sun/star/i18n/BreakIterator.hpp>
85#include <com/sun/star/i18n/XBreakIterator.hpp>
86#include <com/sun/star/io/XOutputStream.hpp>
87#include <com/sun/star/lang/XMultiServiceFactory.hpp>
88#include <com/sun/star/style/LineSpacing.hpp>
89#include <com/sun/star/style/LineSpacingMode.hpp>
90#include <com/sun/star/text/WritingMode.hpp>
91#include <com/sun/star/text/WritingMode2.hpp>
92#include <com/sun/star/text/GraphicCrop.hpp>
93#include <com/sun/star/text/XText.hpp>
94#include <com/sun/star/text/XTextColumns.hpp>
95#include <com/sun/star/text/XTextContent.hpp>
96#include <com/sun/star/text/XTextField.hpp>
97#include <com/sun/star/text/XTextRange.hpp>
98#include <com/sun/star/text/XTextFrame.hpp>
99#include <com/sun/star/style/CaseMap.hpp>
100#include <com/sun/star/xml/dom/XNodeList.hpp>
101#include <com/sun/star/xml/sax/Writer.hpp>
102#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
103#include <com/sun/star/container/XNamed.hpp>
104#include <com/sun/star/drawing/XDrawPages.hpp>
105#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
106#include <com/sun/star/drawing/RectanglePoint.hpp>
107
109#include <comphelper/random.hxx>
113#include <o3tl/any.hxx>
114#include <o3tl/safeint.hxx>
115#include <o3tl/string_view.hxx>
116#include <tools/stream.hxx>
118#include <unotools/fontdefs.hxx>
119#include <vcl/cvtgrf.hxx>
120#include <vcl/svapp.hxx>
121#include <rtl/strbuf.hxx>
124#include <editeng/outlobj.hxx>
125#include <editeng/svxenum.hxx>
126#include <editeng/unonames.hxx>
127#include <editeng/unoprnms.hxx>
128#include <editeng/flditem.hxx>
130#include <editeng/unonrule.hxx>
132#include <svx/svdoashp.hxx>
133#include <svx/svdomedia.hxx>
134#include <svx/svdtrans.hxx>
135#include <svx/unoshape.hxx>
138
139using namespace ::css;
140using namespace ::css::beans;
141using namespace ::css::drawing;
142using namespace ::css::i18n;
143using namespace ::css::style;
144using namespace ::css::text;
145using namespace ::css::uno;
146using namespace ::css::container;
147using namespace ::com::sun::star::drawing::EnhancedCustomShapeSegmentCommand;
148
149using ::css::io::XOutputStream;
150using ::sax_fastparser::FSHelperPtr;
151using ::sax_fastparser::FastSerializerHelper;
152
153namespace
154{
155const char* g_aPredefinedClrNames[] = {
156 "dk1",
157 "lt1",
158 "dk2",
159 "lt2",
160 "accent1",
161 "accent2",
162 "accent3",
163 "accent4",
164 "accent5",
165 "accent6",
166 "hlink",
167 "folHlink",
168};
169}
170
171namespace oox::drawingml {
172
174{
175}
176
177OUString URLTransformer::getTransformedString(const OUString& rString) const
178{
179 return rString;
180}
181
182bool URLTransformer::isExternalURL(const OUString& rURL) const
183{
184 bool bExternal = true;
185 if (rURL.startsWith("#"))
186 bExternal = false;
187 return bExternal;
188}
189
191{
192 static GraphicExportCache staticGraphicExportCache;
193 return staticGraphicExportCache;
194}
195
196static css::uno::Any getLineDash( const css::uno::Reference<css::frame::XModel>& xModel, const OUString& rDashName )
197 {
198 css::uno::Reference<css::lang::XMultiServiceFactory> xFact(xModel, css::uno::UNO_QUERY);
199 css::uno::Reference<css::container::XNameAccess> xNameAccess(
200 xFact->createInstance("com.sun.star.drawing.DashTable"),
201 css::uno::UNO_QUERY );
202 if(xNameAccess.is())
203 {
204 if (!xNameAccess->hasByName(rDashName))
205 return css::uno::Any();
206
207 return xNameAccess->getByName(rDashName);
208 }
209
210 return css::uno::Any();
211 }
212
213namespace
214{
215void WriteGradientPath(const basegfx::BGradient& rBGradient, const FSHelperPtr& pFS, const bool bCircle)
216{
217 pFS->startElementNS(XML_a, XML_path, XML_path, bCircle ? "circle" : "rect");
218
219 // Write the focus rectangle. Work with the focus point, and assume
220 // that it extends 50% in all directions. The below
221 // left/top/right/bottom values are percentages, where 0 means the
222 // edge of the tile rectangle and 100% means the center of it.
225 sal_Int32 nLeftPercent = rBGradient.GetXOffset();
226 pAttributeList->add(XML_l, OString::number(nLeftPercent * PER_PERCENT));
227 sal_Int32 nTopPercent = rBGradient.GetYOffset();
228 pAttributeList->add(XML_t, OString::number(nTopPercent * PER_PERCENT));
229 sal_Int32 nRightPercent = 100 - rBGradient.GetXOffset();
230 pAttributeList->add(XML_r, OString::number(nRightPercent * PER_PERCENT));
231 sal_Int32 nBottomPercent = 100 - rBGradient.GetYOffset();
232 pAttributeList->add(XML_b, OString::number(nBottomPercent * PER_PERCENT));
233 pFS->singleElementNS(XML_a, XML_fillToRect, pAttributeList);
234
235 pFS->endElementNS(XML_a, XML_path);
236}
237}
238
239// not thread safe
240sal_Int32 DrawingML::mnDrawingMLCount = 0;
241sal_Int32 DrawingML::mnVmlCount = 0;
242sal_Int32 DrawingML::mnChartCount = 0;
243
244sal_Int16 DrawingML::GetScriptType(const OUString& rStr)
245{
246 if (rStr.getLength() > 0)
247 {
248 static Reference<css::i18n::XBreakIterator> xBreakIterator =
249 css::i18n::BreakIterator::create(comphelper::getProcessComponentContext());
250
251 sal_Int16 nScriptType = xBreakIterator->getScriptType(rStr, 0);
252
253 if (nScriptType == css::i18n::ScriptType::WEAK)
254 {
255 sal_Int32 nPos = xBreakIterator->nextScript(rStr, 0, nScriptType);
256 if (nPos < rStr.getLength())
257 nScriptType = xBreakIterator->getScriptType(rStr, nPos);
258
259 }
260
261 if (nScriptType != css::i18n::ScriptType::WEAK)
262 return nScriptType;
263 }
264
265 return css::i18n::ScriptType::LATIN;
266}
267
269{
271 mnVmlCount = 0;
272 mnChartCount = 0;
273}
274
275bool DrawingML::GetProperty( const Reference< XPropertySet >& rXPropertySet, const OUString& aName )
276{
277 try
278 {
279 mAny = rXPropertySet->getPropertyValue(aName);
280 if (mAny.hasValue())
281 return true;
282 }
283 catch( const Exception& )
284 {
285 /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
286 }
287 return false;
288}
289
290bool DrawingML::GetPropertyAndState( const Reference< XPropertySet >& rXPropertySet, const Reference< XPropertyState >& rXPropertyState, const OUString& aName, PropertyState& eState )
291{
292 try
293 {
294 mAny = rXPropertySet->getPropertyValue(aName);
295 if (mAny.hasValue())
296 {
297 eState = rXPropertyState->getPropertyState(aName);
298 return true;
299 }
300 }
301 catch( const Exception& )
302 {
303 /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
304 }
305 return false;
306}
307
308namespace
309{
311OString getColorStr(const ::Color nColor)
312{
313 // Transparency is a separate element.
314 OString sColor = OString::number(sal_uInt32(nColor) & 0x00FFFFFF, 16);
315 if (sColor.getLength() < 6)
316 {
317 OStringBuffer sBuf("0");
318 int remains = 5 - sColor.getLength();
319
320 while (remains > 0)
321 {
322 sBuf.append("0");
323 remains--;
324 }
325
326 sBuf.append(sColor);
327
328 sColor = sBuf.toString();
329 }
330 return sColor;
331}
332}
333
334void DrawingML::WriteColor( ::Color nColor, sal_Int32 nAlpha )
335{
336 const auto sColor = getColorStr(nColor);
337 if( nAlpha < MAX_PERCENT )
338 {
339 mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
340 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
341 mpFS->endElementNS( XML_a, XML_srgbClr );
342
343 }
344 else
345 {
346 mpFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
347 }
348}
349
350void DrawingML::WriteColor( const OUString& sColorSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
351{
352 // prevent writing a tag with empty val attribute
353 if( sColorSchemeName.isEmpty() )
354 return;
355
356 if( aTransformations.hasElements() )
357 {
358 mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName);
359 WriteColorTransformations( aTransformations, nAlpha );
360 mpFS->endElementNS( XML_a, XML_schemeClr );
361 }
362 else if(nAlpha < MAX_PERCENT)
363 {
364 mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName);
365 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
366 mpFS->endElementNS( XML_a, XML_schemeClr );
367 }
368 else
369 {
370 mpFS->singleElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName);
371 }
372}
373
374void DrawingML::WriteColor( const ::Color nColor, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
375{
376 const auto sColor = getColorStr(nColor);
377 if( aTransformations.hasElements() )
378 {
379 mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
380 WriteColorTransformations(aTransformations, nAlpha);
381 mpFS->endElementNS(XML_a, XML_srgbClr);
382 }
383 else if(nAlpha < MAX_PERCENT)
384 {
385 mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
386 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
387 mpFS->endElementNS(XML_a, XML_srgbClr);
388 }
389 else
390 {
391 mpFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
392 }
393}
394
395void DrawingML::WriteColorTransformations( const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
396{
397 for( const auto& rTransformation : aTransformations )
398 {
399 sal_Int32 nToken = Color::getColorTransformationToken( rTransformation.Name );
400 if( nToken != XML_TOKEN_INVALID && rTransformation.Value.hasValue() )
401 {
402 if(nToken == XML_alpha && nAlpha < MAX_PERCENT)
403 {
404 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nAlpha));
405 }
406 else
407 {
408 sal_Int32 nValue = rTransformation.Value.get<sal_Int32>();
409 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nValue));
410 }
411 }
412 }
413}
414
415void DrawingML::WriteSolidFill( ::Color nColor, sal_Int32 nAlpha )
416{
417 mpFS->startElementNS(XML_a, XML_solidFill);
418 WriteColor( nColor, nAlpha );
419 mpFS->endElementNS( XML_a, XML_solidFill );
420}
421
422void DrawingML::WriteSolidFill( const OUString& sSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
423{
424 mpFS->startElementNS(XML_a, XML_solidFill);
425 WriteColor( sSchemeName, aTransformations, nAlpha );
426 mpFS->endElementNS( XML_a, XML_solidFill );
427}
428
429void DrawingML::WriteSolidFill( const ::Color nColor, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
430{
431 mpFS->startElementNS(XML_a, XML_solidFill);
432 WriteColor(nColor, aTransformations, nAlpha);
433 mpFS->endElementNS(XML_a, XML_solidFill);
434}
435
436void DrawingML::WriteSolidFill( const Reference< XPropertySet >& rXPropSet )
437{
438 // get fill color
439 if ( !GetProperty( rXPropSet, "FillColor" ) )
440 return;
441 sal_uInt32 nFillColor = mAny.get<sal_uInt32>();
442
443 // get InteropGrabBag and search the relevant attributes
444 OUString sColorFillScheme;
445 sal_uInt32 nOriginalColor = 0;
446 Sequence< PropertyValue > aStyleProperties, aTransformations;
447 if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
448 {
449 Sequence< PropertyValue > aGrabBag;
450 mAny >>= aGrabBag;
451 for( const auto& rProp : std::as_const(aGrabBag) )
452 {
453 if( rProp.Name == "SpPrSolidFillSchemeClr" )
454 rProp.Value >>= sColorFillScheme;
455 else if( rProp.Name == "OriginalSolidFillClr" )
456 rProp.Value >>= nOriginalColor;
457 else if( rProp.Name == "StyleFillRef" )
458 rProp.Value >>= aStyleProperties;
459 else if( rProp.Name == "SpPrSolidFillSchemeClrTransformations" )
460 rProp.Value >>= aTransformations;
461 }
462 }
463
464 sal_Int32 nAlpha = MAX_PERCENT;
465 if( GetProperty( rXPropSet, "FillTransparence" ) )
466 {
467 sal_Int32 nTransparency = 0;
468 mAny >>= nTransparency;
469 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
470 nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
471 }
472
473 // OOXML has no separate transparence gradient but uses transparency in the gradient stops.
474 // So we merge transparency and color and use gradient fill in such case.
475 basegfx::BGradient aTransparenceGradient;
476 OUString sFillTransparenceGradientName;
477 bool bNeedGradientFill(false);
478
479 if (GetProperty(rXPropSet, "FillTransparenceGradientName")
480 && (mAny >>= sFillTransparenceGradientName)
481 && !sFillTransparenceGradientName.isEmpty()
482 && GetProperty(rXPropSet, "FillTransparenceGradient"))
483 {
484 aTransparenceGradient = basegfx::BGradient(mAny);
485 basegfx::BColor aSingleColor;
486 bNeedGradientFill = !aTransparenceGradient.GetColorStops().isSingleColor(aSingleColor);
487
488 // we no longer need to 'guess' if FillTransparenceGradient is used by
489 // comparing it's 1st color to COL_BLACK after having tested that the
490 // FillTransparenceGradientName is set
491 if (!bNeedGradientFill)
492 {
493 // Our alpha is a gray color value.
494 const sal_uInt8 nRed(aSingleColor.getRed() * 255.0);
495
496 // drawingML alpha is a percentage on a 0..100000 scale.
497 nAlpha = (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
498 }
499 }
500
501 // write XML
502 if (bNeedGradientFill)
503 {
504 // no longer create copy/PseudoColorGradient, use new API of
505 // WriteGradientFill to express fix fill color
506 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
507 WriteGradientFill(nullptr, nFillColor, &aTransparenceGradient);
508 mpFS->endElementNS( XML_a, XML_gradFill );
509 }
510 else if ( nFillColor != nOriginalColor )
511 {
512 // the user has set a different color for the shape
513 if (!WriteSchemeColor(u"FillComplexColor", rXPropSet))
514 {
515 WriteSolidFill(::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha);
516 }
517 }
518 // tdf#91332 LO doesn't export the actual theme.xml in XLSX.
519 else if ( !sColorFillScheme.isEmpty() && GetDocumentType() != DOCUMENT_XLSX )
520 {
521 // the shape had a scheme color and the user didn't change it
522 WriteSolidFill( sColorFillScheme, aTransformations, nAlpha );
523 }
524 else
525 {
526 // the shape had a custom color and the user didn't change it
527 // tdf#124013
528 WriteSolidFill( ::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha );
529 }
530}
531
532bool DrawingML::WriteSchemeColor(OUString const& rPropertyName, const uno::Reference<beans::XPropertySet>& xPropertySet)
533{
534 if (!xPropertySet->getPropertySetInfo()->hasPropertyByName(rPropertyName))
535 return false;
536
537 uno::Reference<util::XComplexColor> xComplexColor;
538 xPropertySet->getPropertyValue(rPropertyName) >>= xComplexColor;
539 if (!xComplexColor.is())
540 return false;
541
542 auto aComplexColor = model::color::getFromXComplexColor(xComplexColor);
543 if (aComplexColor.getSchemeType() == model::ThemeColorType::Unknown)
544 return false;
545 const char* pColorName = g_aPredefinedClrNames[sal_Int16(aComplexColor.getSchemeType())];
546 mpFS->startElementNS(XML_a, XML_solidFill);
547 mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, pColorName);
548 for (auto const& rTransform : aComplexColor.getTransformations())
549 {
550 switch (rTransform.meType)
551 {
553 mpFS->singleElementNS(XML_a, XML_lumMod, XML_val, OString::number(rTransform.mnValue * 10));
554 break;
556 mpFS->singleElementNS(XML_a, XML_lumOff, XML_val, OString::number(rTransform.mnValue * 10));
557 break;
559 mpFS->singleElementNS(XML_a, XML_tint, XML_val, OString::number(rTransform.mnValue * 10));
560 break;
562 mpFS->singleElementNS(XML_a, XML_shade, XML_val, OString::number(rTransform.mnValue * 10));
563 break;
564 default:
565 break;
566 }
567 }
568 // Alpha is actually not contained in maTransformations although possible (as of Mar 2023).
569 sal_Int16 nAPITransparency(0);
570 if ((rPropertyName == u"FillComplexColor" && GetProperty(xPropertySet, "FillTransparence"))
571 || (rPropertyName == u"LineComplexColor" && GetProperty(xPropertySet, "LineTransparence"))
572 || (rPropertyName == u"CharComplexColor" && GetProperty(xPropertySet, "CharTransparence")))
573 {
574 mAny >>= nAPITransparency;
575 }
576 if (nAPITransparency != 0)
577 mpFS->singleElementNS(XML_a, XML_alpha, XML_val,
578 OString::number(MAX_PERCENT - (PER_PERCENT * nAPITransparency)));
579
580 mpFS->endElementNS(XML_a, XML_schemeClr);
581 mpFS->endElementNS(XML_a, XML_solidFill);
582
583 return true;
584}
585
586void DrawingML::WriteGradientStop(double fOffset, const basegfx::BColor& rColor, const basegfx::BColor& rAlpha)
587{
588 mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(basegfx::fround(fOffset * 100000)));
590 ::Color(rColor),
592 mpFS->endElementNS( XML_a, XML_gs );
593}
594
595::Color DrawingML::ColorWithIntensity( sal_uInt32 nColor, sal_uInt32 nIntensity )
596{
597 return ::Color(ColorTransparency, ( ( ( nColor & 0xff ) * nIntensity ) / 100 )
598 | ( ( ( ( ( nColor & 0xff00 ) >> 8 ) * nIntensity ) / 100 ) << 8 )
599 | ( ( ( ( ( nColor & 0xff0000 ) >> 8 ) * nIntensity ) / 100 ) << 8 ));
600}
601
603{
604 if (!GetProperty(rXPropSet, "FillGradient"))
605 return;
606
607 // use BGradient constructor directly, it will take care of Gradient/Gradient2
608 basegfx::BGradient aGradient(mAny);
609
610 // get InteropGrabBag and search the relevant attributes
611 basegfx::BGradient aOriginalGradient;
612 Sequence< PropertyValue > aGradientStops;
613 if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
614 {
616 mAny >>= aGrabBag;
617 for( const auto& rProp : std::as_const(aGrabBag) )
618 if( rProp.Name == "GradFillDefinition" )
619 rProp.Value >>= aGradientStops;
620 else if( rProp.Name == "OriginalGradFill" )
621 // use BGradient constructor directly, it will take care of Gradient/Gradient2
622 aOriginalGradient = basegfx::BGradient(rProp.Value);
623 }
624
625 // check if an ooxml gradient had been imported and if the user has modified it
626 // Gradient grab-bag depends on theme grab-bag, which is implemented
627 // only for DOCX.
628 if (aOriginalGradient == aGradient && GetDocumentType() == DOCUMENT_DOCX)
629 {
630 // If we have no gradient stops that means original gradient were defined by a theme.
631 if( aGradientStops.hasElements() )
632 {
633 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
634 WriteGrabBagGradientFill(aGradientStops, aGradient);
635 mpFS->endElementNS( XML_a, XML_gradFill );
636 }
637 }
638 else
639 {
640 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
641
642 basegfx::BGradient aTransparenceGradient;
643 basegfx::BGradient* pTransparenceGradient(nullptr);
644 double fTransparency(0.0);
645 OUString sFillTransparenceGradientName;
646
647 if (GetProperty(rXPropSet, "FillTransparenceGradientName")
648 && (mAny >>= sFillTransparenceGradientName)
649 && !sFillTransparenceGradientName.isEmpty()
650 && GetProperty(rXPropSet, "FillTransparenceGradient"))
651 {
652 // TransparenceGradient is only used when name is not empty
653 aTransparenceGradient = basegfx::BGradient(mAny);
654 pTransparenceGradient = &aTransparenceGradient;
655 }
656 else if (GetProperty(rXPropSet, "FillTransparence"))
657 {
658 // no longer create PseudoTransparencyGradient, use new API of
659 // WriteGradientFill to express fix transparency
660 sal_Int32 nTransparency(0);
661 mAny >>= nTransparency;
662 // nTransparency is [0..100]%
663 fTransparency = nTransparency * 0.01;
664 }
665
666 // tdf#155852 The gradient might wrongly have StepCount==0, as the draw:gradient-step-count
667 // attribute in ODF does not belong to the gradient definition but is an attribute in
668 // the graphic style of the shape.
669 if (GetProperty(rXPropSet, "FillGradientStepCount"))
670 {
671 sal_Int16 nStepCount = 0;
672 mAny >>= nStepCount;
673 aGradient.SetSteps(nStepCount);
674 }
675
676 WriteGradientFill(&aGradient, 0, pTransparenceGradient, fTransparency);
677
678 mpFS->endElementNS(XML_a, XML_gradFill);
679 }
680}
681
683{
684 // write back the original gradient
685 mpFS->startElementNS(XML_a, XML_gsLst);
686
687 // get original stops and write them
688 for( const auto& rGradientStop : aGradientStops )
689 {
690 Sequence< PropertyValue > aGradientStop;
691 rGradientStop.Value >>= aGradientStop;
692
693 // get values
694 OUString sSchemeClr;
695 double nPos = 0;
696 sal_Int16 nTransparency = 0;
697 ::Color nRgbClr;
698 Sequence< PropertyValue > aTransformations;
699 for( const auto& rProp : std::as_const(aGradientStop) )
700 {
701 if( rProp.Name == "SchemeClr" )
702 rProp.Value >>= sSchemeClr;
703 else if( rProp.Name == "RgbClr" )
704 rProp.Value >>= nRgbClr;
705 else if( rProp.Name == "Pos" )
706 rProp.Value >>= nPos;
707 else if( rProp.Name == "Transparency" )
708 rProp.Value >>= nTransparency;
709 else if( rProp.Name == "Transformations" )
710 rProp.Value >>= aTransformations;
711 }
712 // write stop
713 mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nPos * 100000.0));
714 if( sSchemeClr.isEmpty() )
715 {
716 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
717 sal_Int32 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
718 WriteColor( nRgbClr, nAlpha );
719 }
720 else
721 {
722 WriteColor( sSchemeClr, aTransformations );
723 }
724 mpFS->endElementNS( XML_a, XML_gs );
725 }
726 mpFS->endElementNS( XML_a, XML_gsLst );
727
728 switch (rBGradient.GetGradientStyle())
729 {
730 default:
731 {
732 const sal_Int16 nAngle(rBGradient.GetAngle());
733 mpFS->singleElementNS(
734 XML_a, XML_lin, XML_ang,
735 OString::number(((3600 - static_cast<sal_Int32>(nAngle) + 900) * 6000) % 21600000));
736 break;
737 }
738 case awt::GradientStyle_RADIAL:
739 {
740 WriteGradientPath(rBGradient, mpFS, true);
741 break;
742 }
743 }
744}
745
747 const basegfx::BGradient* pColorGradient, sal_Int32 nFixColor,
748 const basegfx::BGradient* pTransparenceGradient, double fFixTransparence)
749{
750 basegfx::BColorStops aColorStops;
751 basegfx::BColorStops aAlphaStops;
752 basegfx::BColor aSingleColor(::Color(ColorTransparency, nFixColor).getBColor());
753 basegfx::BColor aSingleAlpha(fFixTransparence);
754 const basegfx::BGradient* pGradient(pColorGradient);
755
756 if (nullptr != pColorGradient)
757 {
758 // extract and correct/process ColorStops
759 basegfx::utils::prepareColorStops(*pColorGradient, aColorStops, aSingleColor);
760
761 // tdf#155827 Convert 'axial' to 'linear' before synchronize and for each gradient separate.
762 if (aColorStops.size() > 0 && awt::GradientStyle_AXIAL == pColorGradient->GetGradientStyle())
763 aColorStops.doApplyAxial();
764 }
765 if (nullptr != pTransparenceGradient)
766 {
767 // remember basic Gradient definition to use
768 // So we can get the gradient geometry in any case from pGradient.
769 if (nullptr == pGradient)
770 {
771 pGradient = pTransparenceGradient;
772 }
773
774 // extract and correct/process AlphaStops
775 basegfx::utils::prepareColorStops(*pTransparenceGradient, aAlphaStops, aSingleAlpha);
776 if (aAlphaStops.size() > 0
777 && awt::GradientStyle_AXIAL == pTransparenceGradient->GetGradientStyle())
778 {
779 aAlphaStops.doApplyAxial();
780 }
781 }
782
783 if (nullptr == pGradient)
784 {
785 // an error - see comment in header - is to give neither pColorGradient
786 // nor pTransparenceGradient
787 assert(false && "pColorGradient or pTransparenceGradient should be set");
788 return;
789 }
790
791 // Apply steps if used. That increases the number of stops and thus needs to be done before
792 // synchronize.
793 if (pGradient->GetSteps())
794 {
795 aColorStops.doApplySteps(pGradient->GetSteps());
796 // transparency gradients are always automatic, so do not have steps.
797 }
798
799 // synchronize ColorStops and AlphaStops as preparation to export
800 // so also gradients 'coupled' indirectly using the 'FillTransparenceGradient'
801 // method (at import time) will be exported again.
802 basegfx::utils::synchronizeColorStops(aColorStops, aAlphaStops, aSingleColor, aSingleAlpha);
803
804 if (aColorStops.size() != aAlphaStops.size())
805 {
806 // this is an error - synchronizeColorStops above *has* to create that
807 // state, see description there (!)
808 assert(false && "oox::WriteGradientFill: non-synchronized gradients (!)");
809 return;
810 }
811
812 bool bLinearOrAxial(awt::GradientStyle_LINEAR == pGradient->GetGradientStyle()
813 || awt::GradientStyle_AXIAL == pGradient->GetGradientStyle());
814 if (!bLinearOrAxial)
815 {
816 // case awt::GradientStyle_RADIAL:
817 // case awt::GradientStyle_ELLIPTICAL:
818 // case awt::GradientStyle_RECT:
819 // case awt::GradientStyle_SQUARE:
820 // all these types need the gradients to be mirrored
821 aColorStops.reverseColorStops();
822 aAlphaStops.reverseColorStops();
823 }
824
825 // If there were one stop, prepareColorStops() method would have cleared aColorStops, same for
826 // aAlphaStops. In case of empty stops vectors synchronizeColorStops() method creates two stops
827 // for each. So at this point we have at least two stops and can fulfill the requirement of
828 // <gsLst> element to have at least two child elements.
829
830 // export GradientStops (with alpha)
831 mpFS->startElementNS(XML_a, XML_gsLst);
832
833 basegfx::BColorStops::const_iterator aCurrColor(aColorStops.begin());
834 basegfx::BColorStops::const_iterator aCurrAlpha(aAlphaStops.begin());
835
836 while (aCurrColor != aColorStops.end() && aCurrAlpha != aAlphaStops.end())
837 {
839 aCurrColor->getStopOffset(),
840 aCurrColor->getStopColor(),
841 aCurrAlpha->getStopColor());
842 aCurrColor++;
843 aCurrAlpha++;
844 }
845
846 mpFS->endElementNS( XML_a, XML_gsLst );
847
848 if (bLinearOrAxial)
849 {
850 // CT_LinearShadeProperties, cases where gradient rotation has to be exported
851 // 'scaled' does not exist in LO, so only 'ang'.
852 const sal_Int16 nAngle(pGradient->GetAngle());
853 mpFS->singleElementNS(
854 XML_a, XML_lin, XML_ang,
855 OString::number(((3600 - static_cast<sal_Int32>(nAngle) + 900) * 6000) % 21600000));
856 }
857 else
858 {
859 // CT_PathShadeProperties, cases where gradient path has to be exported
860 // Concentric fill is not yet implemented, therefore no type 'shape', only 'circle' or 'rect'
861 const bool bCircle(pGradient->GetGradientStyle() == awt::GradientStyle_RADIAL ||
862 pGradient->GetGradientStyle() == awt::GradientStyle_ELLIPTICAL);
863 WriteGradientPath(*pGradient, mpFS, bCircle);
864 }
865}
866
867void DrawingML::WriteLineArrow( const Reference< XPropertySet >& rXPropSet, bool bLineStart )
868{
869 ESCHER_LineEnd eLineEnd;
870 sal_Int32 nArrowLength;
871 sal_Int32 nArrowWidth;
872
873 if ( !EscherPropertyContainer::GetLineArrow( bLineStart, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) )
874 return;
875
876 const char* len;
877 const char* type;
878 const char* width;
879
880 switch( nArrowLength )
881 {
883 len = "sm";
884 break;
885 default:
887 len = "med";
888 break;
890 len = "lg";
891 break;
892 }
893
894 switch( eLineEnd )
895 {
896 default:
897 case ESCHER_LineNoEnd:
898 type = "none";
899 break;
901 type = "triangle";
902 break;
904 type = "stealth";
905 break;
907 type = "diamond";
908 break;
910 type = "oval";
911 break;
913 type = "arrow";
914 break;
915 }
916
917 switch( nArrowWidth )
918 {
920 width = "sm";
921 break;
922 default:
924 width = "med";
925 break;
927 width = "lg";
928 break;
929 }
930
931 mpFS->singleElementNS( XML_a, bLineStart ? XML_headEnd : XML_tailEnd,
932 XML_len, len,
933 XML_type, type,
934 XML_w, width );
935}
936
938{
939 drawing::LineStyle aLineStyle( drawing::LineStyle_NONE );
940 if (GetProperty(rXPropSet, "LineStyle"))
941 mAny >>= aLineStyle;
942
943 const LineCap aLineCap = GetProperty(rXPropSet, "LineCap") ? mAny.get<drawing::LineCap>() : LineCap_BUTT;
944
945 sal_uInt32 nLineWidth = 0;
946 sal_uInt32 nEmuLineWidth = 0;
947 ::Color nColor;
948 sal_Int32 nColorAlpha = MAX_PERCENT;
949 bool bColorSet = false;
950 const char* cap = nullptr;
951 drawing::LineDash aLineDash;
952 bool bDashSet = false;
953 bool bNoFill = false;
954
955
956 // get InteropGrabBag and search the relevant attributes
957 OUString sColorFillScheme;
958 ::Color aResolvedColorFillScheme;
959
960 ::Color nOriginalColor;
961 ::Color nStyleColor;
962 sal_uInt32 nStyleLineWidth = 0;
963
964 Sequence<PropertyValue> aStyleProperties;
965 Sequence<PropertyValue> aTransformations;
966
967 drawing::LineStyle aStyleLineStyle(drawing::LineStyle_NONE);
968 drawing::LineJoint aStyleLineJoint(drawing::LineJoint_NONE);
969
970 if (GetProperty(rXPropSet, "InteropGrabBag"))
971 {
973 mAny >>= aGrabBag;
974
975 for (const auto& rProp : std::as_const(aGrabBag))
976 {
977 if( rProp.Name == "SpPrLnSolidFillSchemeClr" )
978 rProp.Value >>= sColorFillScheme;
979 if( rProp.Name == "SpPrLnSolidFillResolvedSchemeClr" )
980 rProp.Value >>= aResolvedColorFillScheme;
981 else if( rProp.Name == "OriginalLnSolidFillClr" )
982 rProp.Value >>= nOriginalColor;
983 else if( rProp.Name == "StyleLnRef" )
984 rProp.Value >>= aStyleProperties;
985 else if( rProp.Name == "SpPrLnSolidFillSchemeClrTransformations" )
986 rProp.Value >>= aTransformations;
987 else if( rProp.Name == "EmuLineWidth" )
988 rProp.Value >>= nEmuLineWidth;
989 }
990 for (const auto& rStyleProp : std::as_const(aStyleProperties))
991 {
992 if( rStyleProp.Name == "Color" )
993 rStyleProp.Value >>= nStyleColor;
994 else if( rStyleProp.Name == "LineStyle" )
995 rStyleProp.Value >>= aStyleLineStyle;
996 else if( rStyleProp.Name == "LineJoint" )
997 rStyleProp.Value >>= aStyleLineJoint;
998 else if( rStyleProp.Name == "LineWidth" )
999 rStyleProp.Value >>= nStyleLineWidth;
1000 }
1001 }
1002
1003 if (GetProperty(rXPropSet, "LineWidth"))
1004 mAny >>= nLineWidth;
1005
1006 switch (aLineStyle)
1007 {
1008 case drawing::LineStyle_NONE:
1009 bNoFill = true;
1010 break;
1011 case drawing::LineStyle_DASH:
1012 if (GetProperty(rXPropSet, "LineDash"))
1013 {
1014 aLineDash = mAny.get<drawing::LineDash>();
1015 //this query is good for shapes, but in the case of charts it returns 0 values
1016 if (aLineDash.Dots == 0 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 0) {
1017 OUString aLineDashName;
1018 if (GetProperty(rXPropSet, "LineDashName"))
1019 mAny >>= aLineDashName;
1020 if (!aLineDashName.isEmpty() && xModel) {
1021 css::uno::Any aAny = getLineDash(xModel, aLineDashName);
1022 aAny >>= aLineDash;
1023 }
1024 }
1025 }
1026 else
1027 {
1028 //export the linestyle of chart wall (plot area) and chart page
1029 OUString aLineDashName;
1030 if (GetProperty(rXPropSet, "LineDashName"))
1031 mAny >>= aLineDashName;
1032 if (!aLineDashName.isEmpty() && xModel) {
1033 css::uno::Any aAny = getLineDash(xModel, aLineDashName);
1034 aAny >>= aLineDash;
1035 }
1036 }
1037 bDashSet = true;
1038 if (aLineDash.Style == DashStyle_ROUND || aLineDash.Style == DashStyle_ROUNDRELATIVE)
1039 {
1040 cap = "rnd";
1041 }
1042
1043 SAL_INFO("oox.shape", "dash dots: " << aLineDash.Dots << " dashes: " << aLineDash.Dashes
1044 << " dotlen: " << aLineDash.DotLen << " dashlen: " << aLineDash.DashLen << " distance: " << aLineDash.Distance);
1045
1046 [[fallthrough]];
1047 case drawing::LineStyle_SOLID:
1048 default:
1049 if (GetProperty(rXPropSet, "LineColor"))
1050 {
1051 nColor = ::Color(ColorTransparency, mAny.get<sal_uInt32>() & 0xffffff);
1052 bColorSet = true;
1053 }
1054 if (GetProperty(rXPropSet, "LineTransparence"))
1055 {
1056 nColorAlpha = MAX_PERCENT - (mAny.get<sal_Int16>() * PER_PERCENT);
1057 }
1058 if (aLineCap == LineCap_ROUND)
1059 cap = "rnd";
1060 else if (aLineCap == LineCap_SQUARE)
1061 cap = "sq";
1062 break;
1063 }
1064
1065 // if the line-width was not modified after importing then the original EMU value will be exported to avoid unexpected conversion (rounding) error
1066 if (nEmuLineWidth == 0 || static_cast<sal_uInt32>(oox::drawingml::convertEmuToHmm(nEmuLineWidth)) != nLineWidth)
1068 mpFS->startElementNS( XML_a, XML_ln,
1069 XML_cap, cap,
1070 XML_w, sax_fastparser::UseIf(OString::number(nEmuLineWidth),
1071 nLineWidth == 0 || GetDocumentType() == DOCUMENT_XLSX // tdf#119565 LO doesn't export the actual theme.xml in XLSX.
1072 || (nLineWidth > 1 && nStyleLineWidth != nLineWidth)));
1073
1074 if( bColorSet )
1075 {
1076 if( nColor != nOriginalColor )
1077 {
1078 // the user has set a different color for the line
1079 if (!WriteSchemeColor(u"LineComplexColor", rXPropSet))
1080 WriteSolidFill(nColor, nColorAlpha);
1081 }
1082 else if( !sColorFillScheme.isEmpty() )
1083 {
1084 // the line had a scheme color and the user didn't change it
1085 WriteSolidFill( aResolvedColorFillScheme, aTransformations );
1086 }
1087 else
1088 {
1089 WriteSolidFill( nColor, nColorAlpha );
1090 }
1091 }
1092
1093 if( bDashSet && aStyleLineStyle != drawing::LineStyle_DASH )
1094 {
1095 // Try to detect if it might come from ms preset line style import.
1096 // MS Office styles are always relative, both binary and OOXML.
1097 // "dot" is always the first dash and "dash" the second one. All OOXML presets linestyles
1098 // start with the longer one. Definitions are in OOXML part 1, 20.1.10.49
1099 // The tests are strict, for to not catch styles from standard.sod (as of Aug 2019).
1100 bool bIsConverted = false;
1101
1102 bool bIsRelative(aLineDash.Style == DashStyle_RECTRELATIVE || aLineDash.Style == DashStyle_ROUNDRELATIVE);
1103 if ( bIsRelative && aLineDash.Dots == 1)
1104 { // The length were tweaked on import in case of prstDash. Revert it here.
1105 sal_uInt32 nDotLen = aLineDash.DotLen;
1106 sal_uInt32 nDashLen = aLineDash.DashLen;
1107 sal_uInt32 nDistance = aLineDash.Distance;
1108 if (aLineCap != LineCap_BUTT && nDistance >= 99)
1109 {
1110 nDistance -= 99;
1111 nDotLen += 99;
1112 if (nDashLen > 0)
1113 nDashLen += 99;
1114 }
1115 // LO uses length 0 for 100%, if the attribute is missing in ODF.
1116 // Other applications might write 100%. Make is unique for the conditions.
1117 if (nDotLen == 0)
1118 nDotLen = 100;
1119 if (nDashLen == 0 && aLineDash.Dashes > 0)
1120 nDashLen = 100;
1121 bIsConverted = true;
1122 if (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
1123 {
1124 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dot");
1125 }
1126 else if (nDotLen == 400 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
1127 {
1128 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
1129 }
1130 else if (nDotLen == 400 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300)
1131 {
1132 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dashDot");
1133 }
1134 else if (nDotLen == 800 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
1135 {
1136 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDash");
1137 }
1138 else if (nDotLen == 800 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300)
1139 {
1140 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDot");
1141 }
1142 else if (nDotLen == 800 && aLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 300)
1143 {
1144 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDotDot");
1145 }
1146 else if (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100)
1147 {
1148 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDot");
1149 }
1150 else if (nDotLen == 300 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100)
1151 {
1152 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDash");
1153 }
1154 else if (nDotLen == 300 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 100)
1155 {
1156 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDot");
1157 }
1158 else if (nDotLen == 300 && aLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 100)
1159 {
1160 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDotDot");
1161 }
1162 else
1163 bIsConverted = false;
1164 }
1165 // Do not map our own line styles to OOXML prstDash values, because custDash gives better results.
1166 if (!bIsConverted)
1167 {
1168 mpFS->startElementNS(XML_a, XML_custDash);
1169 // In case of hairline we would need the current pixel size. Instead use a reasonable
1170 // ersatz for it. The value is the same as SMALLEST_DASH_WIDTH in xattr.cxx.
1171 // (And it makes sure fLineWidth is not zero in below division.)
1172 double fLineWidth = nLineWidth > 0 ? nLineWidth : 26.95;
1173 int i;
1174 double fSp = bIsRelative ? aLineDash.Distance : aLineDash.Distance * 100.0 / fLineWidth;
1175 // LO uses line width, in case Distance is zero. MS Office would use a space of zero length.
1176 // So set 100% explicitly.
1177 if (aLineDash.Distance <= 0)
1178 fSp = 100.0;
1179 // In case of custDash, round caps are included in dash length in MS Office. Square caps are added
1180 // to dash length, same as in ODF. Change the length values accordingly.
1181 if (aLineCap == LineCap_ROUND && fSp > 99.0)
1182 fSp -= 99.0;
1183
1184 if (aLineDash.Dots > 0)
1185 {
1186 double fD = bIsRelative ? aLineDash.DotLen : aLineDash.DotLen * 100.0 / fLineWidth;
1187 // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended.
1188 if (aLineDash.DotLen == 0)
1189 fD = 100.0;
1190 // Tweak dash length, see above.
1191 if (aLineCap == LineCap_ROUND && fSp > 99.0)
1192 fD += 99.0;
1193
1194 for( i = 0; i < aLineDash.Dots; i ++ )
1195 {
1196 mpFS->singleElementNS( XML_a, XML_ds,
1197 XML_d , write1000thOfAPercent(fD),
1198 XML_sp, write1000thOfAPercent(fSp) );
1199 }
1200 }
1201 if ( aLineDash.Dashes > 0 )
1202 {
1203 double fD = bIsRelative ? aLineDash.DashLen : aLineDash.DashLen * 100.0 / fLineWidth;
1204 // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended.
1205 if (aLineDash.DashLen == 0)
1206 fD = 100.0;
1207 // Tweak dash length, see above.
1208 if (aLineCap == LineCap_ROUND && fSp > 99.0)
1209 fD += 99.0;
1210
1211 for( i = 0; i < aLineDash.Dashes; i ++ )
1212 {
1213 mpFS->singleElementNS( XML_a , XML_ds,
1214 XML_d , write1000thOfAPercent(fD),
1215 XML_sp, write1000thOfAPercent(fSp) );
1216 }
1217 }
1218
1220 "oox.shape", "while writing outline - custom dash - line width was < 0 : " << nLineWidth);
1221 SAL_WARN_IF(aLineDash.Dashes < 0,
1222 "oox.shape", "while writing outline - custom dash - number of dashes was < 0 : " << aLineDash.Dashes);
1223 SAL_WARN_IF(aLineDash.Dashes > 0 && aLineDash.DashLen <= 0,
1224 "oox.shape", "while writing outline - custom dash - dash length was < 0 : " << aLineDash.DashLen);
1225 SAL_WARN_IF(aLineDash.Dots < 0,
1226 "oox.shape", "while writing outline - custom dash - number of dots was < 0 : " << aLineDash.Dots);
1227 SAL_WARN_IF(aLineDash.Dots > 0 && aLineDash.DotLen <= 0,
1228 "oox.shape", "while writing outline - custom dash - dot length was < 0 : " << aLineDash.DotLen);
1229 SAL_WARN_IF(aLineDash.Distance <= 0,
1230 "oox.shape", "while writing outline - custom dash - distance was < 0 : " << aLineDash.Distance);
1231
1232 mpFS->endElementNS( XML_a, XML_custDash );
1233 }
1234 }
1235
1236 if (!bNoFill && nLineWidth > 1 && GetProperty(rXPropSet, "LineJoint"))
1237 {
1238 LineJoint eLineJoint = mAny.get<LineJoint>();
1239
1240 // tdf#119565 LO doesn't export the actual theme.xml in XLSX.
1241 if (aStyleLineJoint == LineJoint_NONE || GetDocumentType() == DOCUMENT_XLSX
1242 || aStyleLineJoint != eLineJoint)
1243 {
1244 // style-defined line joint does not exist, or is different from the shape's joint
1245 switch( eLineJoint )
1246 {
1247 case LineJoint_NONE:
1248 case LineJoint_BEVEL:
1249 mpFS->singleElementNS(XML_a, XML_bevel);
1250 break;
1251 default:
1252 case LineJoint_MIDDLE:
1253 case LineJoint_MITER:
1254 mpFS->singleElementNS(XML_a, XML_miter);
1255 break;
1256 case LineJoint_ROUND:
1257 mpFS->singleElementNS(XML_a, XML_round);
1258 break;
1259 }
1260 }
1261 }
1262
1263 if( !bNoFill )
1264 {
1265 WriteLineArrow( rXPropSet, true );
1266 WriteLineArrow( rXPropSet, false );
1267 }
1268 else
1269 {
1270 mpFS->singleElementNS(XML_a, XML_noFill);
1271 }
1272
1273 mpFS->endElementNS( XML_a, XML_ln );
1274}
1275
1277{
1279}
1280
1282{
1284}
1285
1286OUString GraphicExport::writeBlip(Graphic const& rGraphic, std::vector<model::BlipEffect> const& rEffects, bool bRelPathToMedia)
1287{
1288 OUString sRelId;
1289
1290 sRelId = writeToStorage(rGraphic, bRelPathToMedia);
1291
1292 mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId);
1293
1294 for (auto const& rEffect : rEffects)
1295 {
1296 switch (rEffect.meType)
1297 {
1299 {
1300 mpFS->singleElementNS(XML_a, XML_alphaBiLevel, XML_thresh, OString::number(rEffect.mnThreshold));
1301 }
1302 break;
1304 {
1305 mpFS->singleElementNS(XML_a, XML_alphaCeiling);
1306 }
1307 break;
1309 {
1310 mpFS->singleElementNS(XML_a, XML_alphaFloor);
1311 }
1312 break;
1314 {
1315 mpFS->singleElementNS(XML_a, XML_alphaInv);
1316 // TODO: export rEffect.maColor1
1317 }
1318 break;
1320 {
1321 mpFS->singleElementNS(XML_a, XML_alphaMod);
1322 // TODO
1323 }
1324 break;
1326 {
1327 mpFS->singleElementNS(XML_a, XML_alphaModFix, XML_amt, OString::number(rEffect.mnAmount));
1328 }
1329 break;
1331 {
1332 mpFS->singleElementNS(XML_a, XML_alphaRepl, XML_a, OString::number(rEffect.mnAlpha));
1333 }
1334 break;
1336 {
1337 mpFS->singleElementNS(XML_a, XML_biLevel, XML_thresh, OString::number(rEffect.mnThreshold));
1338 }
1339 break;
1341 {
1342 mpFS->singleElementNS(XML_a, XML_blur,
1343 XML_rad, OString::number(rEffect.mnRadius),
1344 XML_grow, rEffect.mbGrow ? "1" : "0");
1345 }
1346 break;
1348 {
1349 mpFS->startElementNS(XML_a, XML_clrChange, XML_useA, rEffect.mbUseAlpha ? "1" : "0");
1350 mpFS->endElementNS(XML_a, XML_clrChange);
1351 }
1352 break;
1354 {
1355 mpFS->startElementNS(XML_a, XML_clrRepl);
1356 mpFS->endElementNS(XML_a, XML_clrRepl);
1357 }
1358 break;
1360 {
1361 mpFS->startElementNS(XML_a, XML_duotone);
1362 mpFS->endElementNS(XML_a, XML_duotone);
1363 }
1364 break;
1366 {
1367 mpFS->singleElementNS(XML_a, XML_fillOverlay);
1368 }
1369 break;
1371 {
1372 mpFS->singleElementNS(XML_a, XML_grayscl);
1373 }
1374 break;
1376 {
1377 mpFS->singleElementNS(XML_a, XML_hsl,
1378 XML_hue, OString::number(rEffect.mnHue),
1379 XML_sat, OString::number(rEffect.mnSaturation),
1380 XML_lum, OString::number(rEffect.mnLuminance));
1381 }
1382 break;
1384 {
1385 mpFS->singleElementNS(XML_a, XML_lum,
1386 XML_bright, OString::number(rEffect.mnBrightness),
1387 XML_contrast, OString::number(rEffect.mnContrast));
1388 }
1389 break;
1391 {
1392 mpFS->singleElementNS(XML_a, XML_tint,
1393 XML_hue, OString::number(rEffect.mnHue),
1394 XML_amt, OString::number(rEffect.mnAmount));
1395 }
1396 break;
1397
1398 default:
1399 break;
1400 }
1401 }
1402
1403 mpFS->endElementNS(XML_a, XML_blip);
1404
1405 return sRelId;
1406}
1407
1408OUString GraphicExport::writeToStorage(const Graphic& rGraphic , bool bRelPathToMedia)
1409{
1410 GfxLink aLink = rGraphic.GetGfxLink ();
1411 BitmapChecksum aChecksum = rGraphic.GetChecksum();
1412 OUString sMediaType;
1413 const char* pExtension = "";
1414 OUString sRelId;
1415 OUString sPath;
1416
1417 // tdf#74670 tdf#91286 Save image only once
1418 GraphicExportCache& rGraphicExportCache = GraphicExportCache::get();
1419 sPath = rGraphicExportCache.findExportGraphics(aChecksum);
1420
1421 if (sPath.isEmpty())
1422 {
1423 SvMemoryStream aStream;
1424 const void* aData = aLink.GetData();
1425 std::size_t nDataSize = aLink.GetDataSize();
1426
1427 switch (aLink.GetType())
1428 {
1429 case GfxLinkType::NativeGif:
1430 sMediaType = "image/gif";
1431 pExtension = ".gif";
1432 break;
1433
1434 // #i15508# added BMP type for better exports
1435 // export not yet active, so adding for reference (not checked)
1436 case GfxLinkType::NativeBmp:
1437 sMediaType = "image/bmp";
1438 pExtension = ".bmp";
1439 break;
1440
1441 case GfxLinkType::NativeJpg:
1442 sMediaType = "image/jpeg";
1443 pExtension = ".jpeg";
1444 break;
1445 case GfxLinkType::NativePng:
1446 sMediaType = "image/png";
1447 pExtension = ".png";
1448 break;
1449 case GfxLinkType::NativeTif:
1450 sMediaType = "image/tiff";
1451 pExtension = ".tif";
1452 break;
1453 case GfxLinkType::NativeWmf:
1454 sMediaType = "image/x-wmf";
1455 pExtension = ".wmf";
1456 break;
1457 case GfxLinkType::NativeMet:
1458 sMediaType = "image/x-met";
1459 pExtension = ".met";
1460 break;
1461 case GfxLinkType::NativePct:
1462 sMediaType = "image/x-pict";
1463 pExtension = ".pct";
1464 break;
1465 case GfxLinkType::NativeMov:
1466 sMediaType = "application/movie";
1467 pExtension = ".MOV";
1468 break;
1469 default:
1470 {
1471 GraphicType aType = rGraphic.GetType();
1472 if (aType == GraphicType::Bitmap || aType == GraphicType::GdiMetafile)
1473 {
1474 if (aType == GraphicType::Bitmap)
1475 {
1476 (void)GraphicConverter::Export(aStream, rGraphic, ConvertDataFormat::PNG);
1477 sMediaType = "image/png";
1478 pExtension = ".png";
1479 }
1480 else
1481 {
1482 (void)GraphicConverter::Export(aStream, rGraphic, ConvertDataFormat::EMF);
1483 sMediaType = "image/x-emf";
1484 pExtension = ".emf";
1485 }
1486 }
1487 else
1488 {
1489 SAL_WARN("oox.shape", "unhandled graphic type " << static_cast<int>(aType));
1490 /*Earlier, even in case of unhandled graphic types we were
1491 proceeding to write the image, which would eventually
1492 write an empty image with a zero size, and return a valid
1493 relationID, which is incorrect.
1494 */
1495 return sRelId;
1496 }
1497
1498 aData = aStream.GetData();
1499 nDataSize = aStream.GetEndOfData();
1500 break;
1501 }
1502 }
1503
1504 sal_Int32 nImageCount = rGraphicExportCache.nextImageCount();
1506 OUStringBuffer()
1507 .appendAscii(getComponentDir(meDocumentType))
1508 .append("/media/image" + OUString::number(nImageCount))
1509 .appendAscii(pExtension)
1510 .makeStringAndClear(),
1511 sMediaType);
1512 xOutStream->writeBytes(Sequence<sal_Int8>(static_cast<const sal_Int8*>(aData), nDataSize));
1513 xOutStream->closeOutput();
1514
1515 const char* sRelationCompPrefix;
1516 if (bRelPathToMedia)
1517 sRelationCompPrefix = "../";
1518 else
1519 sRelationCompPrefix = getRelationCompPrefix(meDocumentType);
1520 sPath = OUStringBuffer()
1521 .appendAscii(sRelationCompPrefix)
1522 .append("media/image" + OUString::number(nImageCount))
1523 .appendAscii(pExtension)
1524 .makeStringAndClear();
1525
1526 rGraphicExportCache.addExportGraphics(aChecksum, sPath);
1527 }
1528
1529 sRelId = mpFilterBase->addRelation( mpFS->getOutputStream(),
1531 sPath );
1532
1533 return sRelId;
1534}
1535
1536OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia )
1537{
1539 return exporter.writeToStorage(rGraphic, bRelPathToMedia);
1540}
1541
1542void DrawingML::WriteMediaNonVisualProperties(const css::uno::Reference<css::drawing::XShape>& xShape)
1543{
1544 SdrMediaObj* pMediaObj = dynamic_cast<SdrMediaObj*>(SdrObject::getSdrObjectFromXShape(xShape));
1545 if (!pMediaObj)
1546 return;
1547
1548 // extension
1549 OUString aExtension;
1550 const OUString& rURL(pMediaObj->getURL());
1551 int nLastDot = rURL.lastIndexOf('.');
1552 if (nLastDot >= 0)
1553 aExtension = rURL.copy(nLastDot);
1554
1555 bool bEmbed = rURL.startsWith("vnd.sun.star.Package:");
1556 Relationship eMediaType = Relationship::VIDEO;
1557
1558 // mime type
1559#if HAVE_FEATURE_AVMEDIA
1560 OUString aMimeType(pMediaObj->getMediaProperties().getMimeType());
1561#else
1562 OUString aMimeType("none");
1563#endif
1564 if (aMimeType.startsWith("audio/"))
1565 {
1566 eMediaType = Relationship::AUDIO;
1567 }
1568 else
1569 if (aMimeType == "application/vnd.sun.star.media")
1570 {
1571 // try to set something better
1572 // TODO fix the importer to actually set the mimetype on import
1573 if (aExtension.equalsIgnoreAsciiCase(".avi"))
1574 aMimeType = "video/x-msvideo";
1575 else if (aExtension.equalsIgnoreAsciiCase(".flv"))
1576 aMimeType = "video/x-flv";
1577 else if (aExtension.equalsIgnoreAsciiCase(".mp4"))
1578 aMimeType = "video/mp4";
1579 else if (aExtension.equalsIgnoreAsciiCase(".mov"))
1580 aMimeType = "video/quicktime";
1581 else if (aExtension.equalsIgnoreAsciiCase(".ogv"))
1582 aMimeType = "video/ogg";
1583 else if (aExtension.equalsIgnoreAsciiCase(".wmv"))
1584 aMimeType = "video/x-ms-wmv";
1585 else if (aExtension.equalsIgnoreAsciiCase(".wav"))
1586 {
1587 aMimeType = "audio/x-wav";
1588 eMediaType = Relationship::AUDIO;
1589 }
1590 else if (aExtension.equalsIgnoreAsciiCase(".m4a"))
1591 {
1592 aMimeType = "audio/mp4";
1593 eMediaType = Relationship::AUDIO;
1594 }
1595 else if (aExtension.equalsIgnoreAsciiCase(".mp3"))
1596 {
1597 aMimeType = "audio/mp3";
1598 eMediaType = Relationship::AUDIO;
1599 }
1600 }
1601
1602 OUString aVideoFileRelId;
1603 OUString aMediaRelId;
1604
1605 if (bEmbed)
1606 {
1607 sal_Int32 nImageCount = GraphicExportCache::get().nextImageCount();
1608
1609 OUString sFileName = OUStringBuffer()
1610 .appendAscii(GetComponentDir())
1611 .append("/media/media" + OUString::number(nImageCount) + aExtension)
1612 .makeStringAndClear();
1613
1614 // copy the video stream
1616
1617 uno::Reference<io::XInputStream> xInputStream(pMediaObj->GetInputStream());
1618 comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xOutStream);
1619
1620 xOutStream->closeOutput();
1621
1622 // create the relation
1623 OUString aPath = OUStringBuffer().appendAscii(GetRelationCompPrefix())
1624 .append("media/media" + OUString::number(nImageCount) + aExtension)
1625 .makeStringAndClear();
1626 aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), aPath);
1627 aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), aPath);
1628 }
1629 else
1630 {
1631 aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), rURL, true);
1632 aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), rURL, true);
1633 }
1634
1635 GetFS()->startElementNS(XML_p, XML_nvPr);
1636
1637 GetFS()->singleElementNS(XML_a, eMediaType == Relationship::VIDEO ? XML_videoFile : XML_audioFile,
1638 FSNS(XML_r, XML_link), aVideoFileRelId);
1639
1640 GetFS()->startElementNS(XML_p, XML_extLst);
1641 // media extensions; google this ID for details
1642 GetFS()->startElementNS(XML_p, XML_ext, XML_uri, "{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}");
1643
1644 GetFS()->singleElementNS(XML_p14, XML_media,
1645 bEmbed? FSNS(XML_r, XML_embed): FSNS(XML_r, XML_link), aMediaRelId);
1646
1647 GetFS()->endElementNS(XML_p, XML_ext);
1648 GetFS()->endElementNS(XML_p, XML_extLst);
1649
1650 GetFS()->endElementNS(XML_p, XML_nvPr);
1651}
1652
1653void DrawingML::WriteImageBrightnessContrastTransparence(uno::Reference<beans::XPropertySet> const & rXPropSet)
1654{
1655 sal_Int16 nBright = 0;
1656 sal_Int32 nContrast = 0;
1657 sal_Int32 nTransparence = 0;
1658
1659 if (GetProperty(rXPropSet, "AdjustLuminance"))
1660 nBright = mAny.get<sal_Int16>();
1661 if (GetProperty(rXPropSet, "AdjustContrast"))
1662 nContrast = mAny.get<sal_Int32>();
1663 // Used for shapes with picture fill
1664 if (GetProperty(rXPropSet, "FillTransparence"))
1665 nTransparence = mAny.get<sal_Int32>();
1666 // Used for pictures
1667 if (nTransparence == 0 && GetProperty(rXPropSet, "Transparency"))
1668 nTransparence = static_cast<sal_Int32>(mAny.get<sal_Int16>());
1669
1670 if (GetProperty(rXPropSet, "GraphicColorMode"))
1671 {
1672 drawing::ColorMode aColorMode;
1673 mAny >>= aColorMode;
1674 if (aColorMode == drawing::ColorMode_GREYS)
1675 mpFS->singleElementNS(XML_a, XML_grayscl);
1676 else if (aColorMode == drawing::ColorMode_MONO)
1677 //black/white has a 0,5 threshold in LibreOffice
1678 mpFS->singleElementNS(XML_a, XML_biLevel, XML_thresh, OString::number(50000));
1679 else if (aColorMode == drawing::ColorMode_WATERMARK)
1680 {
1681 //map watermark with mso washout
1682 nBright = 70;
1683 nContrast = -70;
1684 }
1685 }
1686
1687
1688 if (nBright || nContrast)
1689 {
1690 mpFS->singleElementNS(XML_a, XML_lum,
1691 XML_bright, sax_fastparser::UseIf(OString::number(nBright * 1000), nBright != 0),
1692 XML_contrast, sax_fastparser::UseIf(OString::number(nContrast * 1000), nContrast != 0));
1693 }
1694
1695 if (nTransparence)
1696 {
1697 sal_Int32 nAlphaMod = (100 - nTransparence ) * PER_PERCENT;
1698 mpFS->singleElementNS(XML_a, XML_alphaModFix, XML_amt, OString::number(nAlphaMod));
1699 }
1700}
1701
1702OUString DrawingML::WriteXGraphicBlip(uno::Reference<beans::XPropertySet> const & rXPropSet,
1703 uno::Reference<graphic::XGraphic> const & rxGraphic,
1704 bool bRelPathToMedia)
1705{
1706 OUString sRelId;
1707
1708 if (!rxGraphic.is())
1709 return sRelId;
1710
1711 Graphic aGraphic(rxGraphic);
1712 sRelId = WriteImage(aGraphic, bRelPathToMedia);
1713
1714 mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId);
1715
1717
1718 WriteArtisticEffect(rXPropSet);
1719
1720 mpFS->endElementNS(XML_a, XML_blip);
1721
1722 return sRelId;
1723}
1724
1725void DrawingML::WriteXGraphicBlipMode(uno::Reference<beans::XPropertySet> const & rXPropSet,
1726 uno::Reference<graphic::XGraphic> const & rxGraphic,
1727 css::awt::Size const& rSize)
1728{
1729 BitmapMode eBitmapMode(BitmapMode_NO_REPEAT);
1730 if (GetProperty(rXPropSet, "FillBitmapMode"))
1731 mAny >>= eBitmapMode;
1732
1733 SAL_INFO("oox.shape", "fill bitmap mode: " << int(eBitmapMode));
1734
1735 switch (eBitmapMode)
1736 {
1737 case BitmapMode_REPEAT:
1738 WriteXGraphicTile(rXPropSet, rxGraphic, rSize);
1739 break;
1740 case BitmapMode_STRETCH:
1741 WriteXGraphicStretch(rXPropSet, rxGraphic);
1742 break;
1743 case BitmapMode_NO_REPEAT:
1744 WriteXGraphicCustomPosition(rXPropSet, rxGraphic, rSize);
1745 break;
1746 default:
1747 break;
1748 }
1749}
1750
1752 const OUString& rURLPropName, const awt::Size& rSize)
1753{
1754 // check for blip and otherwise fall back to normal fill
1755 // we always store normal fill properties but OOXML
1756 // uses a choice between our fill props and BlipFill
1757 if (GetProperty ( xPropSet, rURLPropName ))
1758 WriteBlipFill( xPropSet, rURLPropName );
1759 else
1760 WriteFill(xPropSet, rSize);
1761}
1762
1764 const OUString& sURLPropName, const awt::Size& rSize)
1765{
1766 WriteBlipFill( rXPropSet, rSize, sURLPropName, XML_a );
1767}
1768
1769void DrawingML::WriteBlipFill(const Reference<XPropertySet>& rXPropSet, const awt::Size& rSize,
1770 const OUString& sURLPropName, sal_Int32 nXmlNamespace)
1771{
1772 if ( !GetProperty( rXPropSet, sURLPropName ) )
1773 return;
1774
1775 uno::Reference<graphic::XGraphic> xGraphic;
1776 if (mAny.has<uno::Reference<awt::XBitmap>>())
1777 {
1778 uno::Reference<awt::XBitmap> xBitmap = mAny.get<uno::Reference<awt::XBitmap>>();
1779 if (xBitmap.is())
1780 xGraphic.set(xBitmap, uno::UNO_QUERY);
1781 }
1782 else if (mAny.has<uno::Reference<graphic::XGraphic>>())
1783 {
1784 xGraphic = mAny.get<uno::Reference<graphic::XGraphic>>();
1785 }
1786
1787 if (xGraphic.is())
1788 {
1789 bool bWriteMode = false;
1790 if (sURLPropName == "FillBitmap" || sURLPropName == "BackGraphic")
1791 bWriteMode = true;
1792 WriteXGraphicBlipFill(rXPropSet, xGraphic, nXmlNamespace, bWriteMode, false, rSize);
1793 }
1794}
1795
1796void DrawingML::WriteXGraphicBlipFill(uno::Reference<beans::XPropertySet> const & rXPropSet,
1797 uno::Reference<graphic::XGraphic> const & rxGraphic,
1798 sal_Int32 nXmlNamespace, bool bWriteMode,
1799 bool bRelPathToMedia, css::awt::Size const& rSize)
1800{
1801 if (!rxGraphic.is() )
1802 return;
1803
1804 mpFS->startElementNS(nXmlNamespace , XML_blipFill, XML_rotWithShape, "0");
1805
1806 WriteXGraphicBlip(rXPropSet, rxGraphic, bRelPathToMedia);
1807
1809 {
1810 // Write the crop rectangle of Impress as a source rectangle.
1811 WriteSrcRectXGraphic(rXPropSet, rxGraphic);
1812 }
1813
1814 if (bWriteMode)
1815 {
1816 WriteXGraphicBlipMode(rXPropSet, rxGraphic, rSize);
1817 }
1818 else if(GetProperty(rXPropSet, "FillBitmapStretch"))
1819 {
1820 bool bStretch = mAny.get<bool>();
1821
1822 if (bStretch)
1823 {
1824 WriteXGraphicStretch(rXPropSet, rxGraphic);
1825 }
1826 }
1827 mpFS->endElementNS(nXmlNamespace, XML_blipFill);
1828}
1829
1831{
1832 if ( GetProperty( rXPropSet, "FillHatch" ) )
1833 {
1834 drawing::Hatch aHatch;
1835 mAny >>= aHatch;
1836 WritePattFill(rXPropSet, aHatch);
1837 }
1838}
1839
1840void DrawingML::WritePattFill(const Reference<XPropertySet>& rXPropSet, const css::drawing::Hatch& rHatch)
1841{
1842 mpFS->startElementNS(XML_a, XML_pattFill, XML_prst, GetHatchPattern(rHatch));
1843
1844 sal_Int32 nAlpha = MAX_PERCENT;
1845 if (GetProperty(rXPropSet, "FillTransparence"))
1846 {
1847 sal_Int32 nTransparency = 0;
1848 mAny >>= nTransparency;
1849 nAlpha = (MAX_PERCENT - (PER_PERCENT * nTransparency));
1850 }
1851
1852 mpFS->startElementNS(XML_a, XML_fgClr);
1853 WriteColor(::Color(ColorTransparency, rHatch.Color), nAlpha);
1854 mpFS->endElementNS( XML_a , XML_fgClr );
1855
1856 ::Color nColor = COL_WHITE;
1857
1858 if ( GetProperty( rXPropSet, "FillBackground" ) )
1859 {
1860 bool isBackgroundFilled = false;
1861 mAny >>= isBackgroundFilled;
1862 if( isBackgroundFilled )
1863 {
1864 if( GetProperty( rXPropSet, "FillColor" ) )
1865 {
1866 mAny >>= nColor;
1867 }
1868 }
1869 else
1870 nAlpha = 0;
1871 }
1872
1873 mpFS->startElementNS(XML_a, XML_bgClr);
1874 WriteColor(nColor, nAlpha);
1875 mpFS->endElementNS( XML_a , XML_bgClr );
1876
1877 mpFS->endElementNS( XML_a , XML_pattFill );
1878}
1879
1880void DrawingML::WriteGraphicCropProperties(uno::Reference<beans::XPropertySet> const & rXPropSet,
1881 Size const & rOriginalSize,
1882 MapMode const & rMapMode)
1883{
1884 if (!GetProperty(rXPropSet, "GraphicCrop"))
1885 return;
1886
1887 css::text::GraphicCrop aGraphicCropStruct;
1888 mAny >>= aGraphicCropStruct;
1889
1890 if(GetProperty(rXPropSet, "CustomShapeGeometry"))
1891 {
1892 // tdf#134210 GraphicCrop property is handled in import filter because of LibreOffice has not core
1893 // feature. We cropped the bitmap physically and MSO shouldn't crop bitmap one more time. When we
1894 // have core feature for graphic cropping in custom shapes, we should uncomment the code anymore.
1895
1896 mpFS->singleElementNS( XML_a, XML_srcRect);
1897 }
1898 else
1899 {
1900 Size aOriginalSize(rOriginalSize);
1901
1902 // GraphicCrop is in mm100, so in case the original size is in pixels, convert it over.
1903 if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
1904 aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, MapMode(MapUnit::Map100thMM));
1905
1906 if ( (0 != aGraphicCropStruct.Left) || (0 != aGraphicCropStruct.Top) || (0 != aGraphicCropStruct.Right) || (0 != aGraphicCropStruct.Bottom) )
1907 {
1908 mpFS->singleElementNS( XML_a, XML_srcRect,
1909 XML_l, OString::number(rtl::math::round(aGraphicCropStruct.Left * 100000.0 / aOriginalSize.Width())),
1910 XML_t, OString::number(rtl::math::round(aGraphicCropStruct.Top * 100000.0 / aOriginalSize.Height())),
1911 XML_r, OString::number(rtl::math::round(aGraphicCropStruct.Right * 100000.0 / aOriginalSize.Width())),
1912 XML_b, OString::number(rtl::math::round(aGraphicCropStruct.Bottom * 100000.0 / aOriginalSize.Height())) );
1913 }
1914 }
1915}
1916
1917void DrawingML::WriteSrcRectXGraphic(uno::Reference<beans::XPropertySet> const & rxPropertySet,
1918 uno::Reference<graphic::XGraphic> const & rxGraphic)
1919{
1920 Graphic aGraphic(rxGraphic);
1921 Size aOriginalSize = aGraphic.GetPrefSize();
1922 const MapMode& rMapMode = aGraphic.GetPrefMapMode();
1923 WriteGraphicCropProperties(rxPropertySet, aOriginalSize, rMapMode);
1924}
1925
1926void DrawingML::WriteXGraphicStretch(uno::Reference<beans::XPropertySet> const & rXPropSet,
1927 uno::Reference<graphic::XGraphic> const & rxGraphic)
1928{
1930 {
1931 // Limiting the area used for stretching is not supported in Impress.
1932 mpFS->singleElementNS(XML_a, XML_stretch);
1933 return;
1934 }
1935
1936 mpFS->startElementNS(XML_a, XML_stretch);
1937
1938 bool bCrop = false;
1939 if (GetProperty(rXPropSet, "GraphicCrop"))
1940 {
1941 css::text::GraphicCrop aGraphicCropStruct;
1942 mAny >>= aGraphicCropStruct;
1943
1944 if ((0 != aGraphicCropStruct.Left)
1945 || (0 != aGraphicCropStruct.Top)
1946 || (0 != aGraphicCropStruct.Right)
1947 || (0 != aGraphicCropStruct.Bottom))
1948 {
1949 Graphic aGraphic(rxGraphic);
1950 Size aOriginalSize(aGraphic.GetPrefSize());
1951 mpFS->singleElementNS(XML_a, XML_fillRect,
1952 XML_l, OString::number(((aGraphicCropStruct.Left) * 100000) / aOriginalSize.Width()),
1953 XML_t, OString::number(((aGraphicCropStruct.Top) * 100000) / aOriginalSize.Height()),
1954 XML_r, OString::number(((aGraphicCropStruct.Right) * 100000) / aOriginalSize.Width()),
1955 XML_b, OString::number(((aGraphicCropStruct.Bottom) * 100000) / aOriginalSize.Height()));
1956 bCrop = true;
1957 }
1958 }
1959
1960 if (!bCrop)
1961 {
1962 mpFS->singleElementNS(XML_a, XML_fillRect);
1963 }
1964
1965 mpFS->endElementNS(XML_a, XML_stretch);
1966}
1967
1968static OUString lclConvertRectanglePointToToken(RectanglePoint eRectanglePoint)
1969{
1970 OUString sAlignment;
1971 switch (eRectanglePoint)
1972 {
1973 case RectanglePoint_LEFT_TOP:
1974 sAlignment = "tl";
1975 break;
1976 case RectanglePoint_MIDDLE_TOP:
1977 sAlignment = "t";
1978 break;
1979 case RectanglePoint_RIGHT_TOP:
1980 sAlignment = "tr";
1981 break;
1982 case RectanglePoint_LEFT_MIDDLE:
1983 sAlignment = "l";
1984 break;
1985 case RectanglePoint_MIDDLE_MIDDLE:
1986 sAlignment = "ctr";
1987 break;
1988 case RectanglePoint_RIGHT_MIDDLE:
1989 sAlignment = "r";
1990 break;
1991 case RectanglePoint_LEFT_BOTTOM:
1992 sAlignment = "bl";
1993 break;
1994 case RectanglePoint_MIDDLE_BOTTOM:
1995 sAlignment = "b";
1996 break;
1997 case RectanglePoint_RIGHT_BOTTOM:
1998 sAlignment = "br";
1999 break;
2000 default:
2001 break;
2002 }
2003 return sAlignment;
2004}
2005
2006void DrawingML::WriteXGraphicTile(uno::Reference<beans::XPropertySet> const& rXPropSet,
2007 uno::Reference<graphic::XGraphic> const& rxGraphic,
2008 css::awt::Size const& rSize)
2009{
2010 Graphic aGraphic(rxGraphic);
2011 Size aOriginalSize(aGraphic.GetPrefSize());
2012 const MapMode& rMapMode = aGraphic.GetPrefMapMode();
2013 // if the original size is in pixel, convert it to mm100
2014 if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
2015 aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize,
2016 MapMode(MapUnit::Map100thMM));
2017 sal_Int32 nSizeX = 0;
2018 sal_Int32 nOffsetX = 0;
2019 if (GetProperty(rXPropSet, "FillBitmapSizeX"))
2020 {
2021 mAny >>= nSizeX;
2022 if (GetProperty(rXPropSet, "FillBitmapPositionOffsetX"))
2023 {
2024 sal_Int32 nX = (nSizeX != 0) ? nSizeX : aOriginalSize.Width();
2025 if (nX < 0 && rSize.Width > 0)
2026 nX = rSize.Width * std::abs(nX) / 100;
2027 nOffsetX = (*o3tl::doAccess<sal_Int32>(mAny)) * nX * 3.6;
2028 }
2029
2030 // convert the X size of bitmap to a percentage
2031 if (nSizeX > 0)
2032 nSizeX = double(nSizeX) / aOriginalSize.Width() * 100000;
2033 else if (nSizeX < 0)
2034 nSizeX *= 1000;
2035 else
2036 nSizeX = 100000;
2037 }
2038
2039 sal_Int32 nSizeY = 0;
2040 sal_Int32 nOffsetY = 0;
2041 if (GetProperty(rXPropSet, "FillBitmapSizeY"))
2042 {
2043 mAny >>= nSizeY;
2044 if (GetProperty(rXPropSet, "FillBitmapPositionOffsetY"))
2045 {
2046 sal_Int32 nY = (nSizeY != 0) ? nSizeY : aOriginalSize.Height();
2047 if (nY < 0 && rSize.Height > 0)
2048 nY = rSize.Height * std::abs(nY) / 100;
2049 nOffsetY = (*o3tl::doAccess<sal_Int32>(mAny)) * nY * 3.6;
2050 }
2051
2052 // convert the Y size of bitmap to a percentage
2053 if (nSizeY > 0)
2054 nSizeY = double(nSizeY) / aOriginalSize.Height() * 100000;
2055 else if (nSizeY < 0)
2056 nSizeY *= 1000;
2057 else
2058 nSizeY = 100000;
2059 }
2060
2061 // if the "Scale" setting is checked in the images settings dialog.
2062 if (nSizeX < 0 && nSizeY < 0)
2063 {
2064 if (rSize.Width != 0 && rSize.Height != 0)
2065 {
2066 nSizeX = rSize.Width / double(aOriginalSize.Width()) * std::abs(nSizeX);
2067 nSizeY = rSize.Height / double(aOriginalSize.Height()) * std::abs(nSizeY);
2068 }
2069 else
2070 {
2071 nSizeX = std::abs(nSizeX);
2072 nSizeY = std::abs(nSizeY);
2073 }
2074 }
2075
2076 OUString sRectanglePoint;
2077 if (GetProperty(rXPropSet, "FillBitmapRectanglePoint"))
2078 sRectanglePoint = lclConvertRectanglePointToToken(*o3tl::doAccess<RectanglePoint>(mAny));
2079
2080 mpFS->singleElementNS(XML_a, XML_tile, XML_tx, OUString::number(nOffsetX), XML_ty,
2081 OUString::number(nOffsetY), XML_sx, OUString::number(nSizeX), XML_sy,
2082 OUString::number(nSizeY), XML_algn, sRectanglePoint);
2083}
2084
2085void DrawingML::WriteXGraphicCustomPosition(uno::Reference<beans::XPropertySet> const& rXPropSet,
2086 uno::Reference<graphic::XGraphic> const& rxGraphic,
2087 css::awt::Size const& rSize)
2088{
2089 Graphic aGraphic(rxGraphic);
2090 Size aOriginalSize(aGraphic.GetPrefSize());
2091 const MapMode& rMapMode = aGraphic.GetPrefMapMode();
2092 // if the original size is in pixel, convert it to mm100
2093 if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
2094 aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize,
2095 MapMode(MapUnit::Map100thMM));
2096 double nSizeX = 0;
2097 if (GetProperty(rXPropSet, "FillBitmapSizeX"))
2098 {
2099 mAny >>= nSizeX;
2100 if (nSizeX <= 0)
2101 {
2102 if (nSizeX == 0)
2103 nSizeX = aOriginalSize.Width();
2104 else
2105 nSizeX /= 100; // percentage
2106 }
2107 }
2108
2109 double nSizeY = 0;
2110 if (GetProperty(rXPropSet, "FillBitmapSizeY"))
2111 {
2112 mAny >>= nSizeY;
2113 if (nSizeY <= 0)
2114 {
2115 if (nSizeY == 0)
2116 nSizeY = aOriginalSize.Height();
2117 else
2118 nSizeY /= 100; // percentage
2119 }
2120 }
2121
2122 if (nSizeX < 0 && nSizeY < 0 && rSize.Width != 0 && rSize.Height != 0)
2123 {
2124 nSizeX = rSize.Width * std::abs(nSizeX);
2125 nSizeY = rSize.Height * std::abs(nSizeY);
2126 }
2127
2128 sal_Int32 nL = 0, nT = 0, nR = 0, nB = 0;
2129 if (GetProperty(rXPropSet, "FillBitmapRectanglePoint") && rSize.Width != 0 && rSize.Height != 0)
2130 {
2131 sal_Int32 nWidth = (1 - (nSizeX / rSize.Width)) * 100000;
2132 sal_Int32 nHeight = (1 - (nSizeY / rSize.Height)) * 100000;
2133
2134 switch (*o3tl::doAccess<RectanglePoint>(mAny))
2135 {
2136 case RectanglePoint_LEFT_TOP: nR = nWidth; nB = nHeight; break;
2137 case RectanglePoint_RIGHT_TOP: nL = nWidth; nB = nHeight; break;
2138 case RectanglePoint_LEFT_BOTTOM: nR = nWidth; nT = nHeight; break;
2139 case RectanglePoint_RIGHT_BOTTOM: nL = nWidth; nT = nHeight; break;
2140 case RectanglePoint_LEFT_MIDDLE: nR = nWidth; nT = nB = nHeight / 2; break;
2141 case RectanglePoint_RIGHT_MIDDLE: nL = nWidth; nT = nB = nHeight / 2; break;
2142 case RectanglePoint_MIDDLE_TOP: nB = nHeight; nL = nR = nWidth / 2; break;
2143 case RectanglePoint_MIDDLE_BOTTOM: nT = nHeight; nL = nR = nWidth / 2; break;
2144 case RectanglePoint_MIDDLE_MIDDLE: nL = nR = nWidth / 2; nT = nB = nHeight / 2; break;
2145 default: break;
2146 }
2147 }
2148
2149 mpFS->startElementNS(XML_a, XML_stretch);
2150
2151 mpFS->singleElementNS(XML_a, XML_fillRect, XML_l,
2152 sax_fastparser::UseIf(OString::number(nL), nL != 0), XML_t,
2153 sax_fastparser::UseIf(OString::number(nT), nT != 0), XML_r,
2154 sax_fastparser::UseIf(OString::number(nR), nR != 0), XML_b,
2155 sax_fastparser::UseIf(OString::number(nB), nB != 0));
2156
2157 mpFS->endElementNS(XML_a, XML_stretch);
2158}
2159
2160namespace
2161{
2162bool IsTopGroupObj(const uno::Reference<drawing::XShape>& xShape)
2163{
2165 if (!pObject)
2166 return false;
2167
2168 if (pObject->getParentSdrObjectFromSdrObject())
2169 return false;
2170
2171 return pObject->IsGroupObject();
2172}
2173}
2174
2176 sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, sal_Int32 nRotation, bool bIsGroupShape)
2177{
2178
2179 mpFS->startElementNS( nXmlNamespace, XML_xfrm,
2180 XML_flipH, sax_fastparser::UseIf("1", bFlipH),
2181 XML_flipV, sax_fastparser::UseIf("1", bFlipV),
2182 XML_rot, sax_fastparser::UseIf(OString::number(nRotation), nRotation % 21600000 != 0));
2183
2184 sal_Int32 nLeft = rRect.Left();
2185 sal_Int32 nTop = rRect.Top();
2186 if (GetDocumentType() == DOCUMENT_DOCX && !m_xParent.is())
2187 {
2188 nLeft = 0;
2189 nTop = 0;
2190 }
2191 sal_Int32 nChildLeft = nLeft;
2192 sal_Int32 nChildTop = nTop;
2193
2194 mpFS->singleElementNS(XML_a, XML_off,
2195 XML_x, OString::number(oox::drawingml::convertHmmToEmu(nLeft)),
2196 XML_y, OString::number(oox::drawingml::convertHmmToEmu(nTop)));
2197 mpFS->singleElementNS(XML_a, XML_ext,
2198 XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
2199 XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
2200
2201 if (bIsGroupShape && (GetDocumentType() != DOCUMENT_DOCX || IsTopGroupObj(xShape)))
2202 {
2203 mpFS->singleElementNS(XML_a, XML_chOff,
2204 XML_x, OString::number(oox::drawingml::convertHmmToEmu(nChildLeft)),
2205 XML_y, OString::number(oox::drawingml::convertHmmToEmu(nChildTop)));
2206 mpFS->singleElementNS(XML_a, XML_chExt,
2207 XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
2208 XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
2209 }
2210
2211 mpFS->endElementNS( nXmlNamespace, XML_xfrm );
2212}
2213
2214void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, bool bSuppressRotation, bool bSuppressFlipping, bool bFlippedBeforeRotation )
2215{
2216 SAL_INFO("oox.shape", "write shape transformation");
2217
2218 Degree100 nRotation;
2219 Degree100 nCameraRotation;
2220 awt::Point aPos = rXShape->getPosition();
2221 awt::Size aSize = rXShape->getSize();
2222
2223 bool bFlipHWrite = bFlipH && !bSuppressFlipping;
2224 bool bFlipVWrite = bFlipV && !bSuppressFlipping;
2225 bFlipH = bFlipH && !bFlippedBeforeRotation;
2226 bFlipV = bFlipV && !bFlippedBeforeRotation;
2227
2228 if (GetDocumentType() == DOCUMENT_DOCX && m_xParent.is())
2229 {
2230 awt::Point aParentPos = m_xParent->getPosition();
2231 aPos.X -= aParentPos.X;
2232 aPos.Y -= aParentPos.Y;
2233 }
2234
2235 if ( aSize.Width < 0 )
2236 aSize.Width = 1000;
2237 if ( aSize.Height < 0 )
2238 aSize.Height = 1000;
2239 if (!bSuppressRotation)
2240 {
2242 nRotation = pShape ? pShape->GetRotateAngle() : 0_deg100;
2243 if ( GetDocumentType() != DOCUMENT_DOCX )
2244 {
2245 int faccos=bFlipV ? -1 : 1;
2246 int facsin=bFlipH ? -1 : 1;
2247 aPos.X-=(1-faccos*cos(toRadians(nRotation)))*aSize.Width/2-facsin*sin(toRadians(nRotation))*aSize.Height/2;
2248 aPos.Y-=(1-faccos*cos(toRadians(nRotation)))*aSize.Height/2+facsin*sin(toRadians(nRotation))*aSize.Width/2;
2249 }
2250 else if (m_xParent.is() && nRotation != 0_deg100)
2251 {
2252 // Position for rotated shapes inside group is not set by DocxSdrExport.
2253 basegfx::B2DRange aRect(-aSize.Width / 2.0, -aSize.Height / 2.0, aSize.Width / 2.0,
2254 aSize.Height / 2.0);
2255 basegfx::B2DHomMatrix aRotateMatrix =
2257 aRect.transform(aRotateMatrix);
2258 aPos.X += -aSize.Width / 2.0 - aRect.getMinX();
2259 aPos.Y += -aSize.Height / 2.0 - aRect.getMinY();
2260 }
2261
2262 // The RotateAngle property's value is independent from any flipping, and that's exactly what we need here.
2263 uno::Reference<beans::XPropertySet> xPropertySet(rXShape, uno::UNO_QUERY);
2264 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
2265 if (xPropertySetInfo->hasPropertyByName("RotateAngle"))
2266 {
2267 sal_Int32 nTmp;
2268 if (xPropertySet->getPropertyValue("RotateAngle") >>= nTmp)
2269 nRotation = Degree100(nTmp);
2270 }
2271 // tdf#133037: restore original rotate angle before output
2272 if (nRotation && xPropertySetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG))
2273 {
2274 uno::Sequence<beans::PropertyValue> aGrabBagProps;
2275 xPropertySet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= aGrabBagProps;
2276 auto p3DEffectProps = std::find_if(std::cbegin(aGrabBagProps), std::cend(aGrabBagProps),
2277 [](const PropertyValue& rProp) { return rProp.Name == "3DEffectProperties"; });
2278 if (p3DEffectProps != std::cend(aGrabBagProps))
2279 {
2280 uno::Sequence<beans::PropertyValue> a3DEffectProps;
2281 p3DEffectProps->Value >>= a3DEffectProps;
2282 auto pCameraProps = std::find_if(std::cbegin(a3DEffectProps), std::cend(a3DEffectProps),
2283 [](const PropertyValue& rProp) { return rProp.Name == "Camera"; });
2284 if (pCameraProps != std::cend(a3DEffectProps))
2285 {
2286 uno::Sequence<beans::PropertyValue> aCameraProps;
2287 pCameraProps->Value >>= aCameraProps;
2288 auto pZRotationProp = std::find_if(std::cbegin(aCameraProps), std::cend(aCameraProps),
2289 [](const PropertyValue& rProp) { return rProp.Name == "rotRev"; });
2290 if (pZRotationProp != std::cend(aCameraProps))
2291 {
2292 sal_Int32 nTmp = 0;
2293 pZRotationProp->Value >>= nTmp;
2294 nCameraRotation = NormAngle36000(Degree100(nTmp / -600));
2295 }
2296 }
2297 }
2298 }
2299 }
2300
2301 // OOXML flips shapes before rotating them.
2302 if(bFlipH != bFlipV)
2303 nRotation = 36000_deg100 - nRotation;
2304
2305 WriteTransformation(rXShape, tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)), nXmlNamespace,
2306 bFlipHWrite, bFlipVWrite, ExportRotateClockwisify(nRotation + nCameraRotation), IsGroupShape( rXShape ));
2307}
2308
2309static OUString lcl_GetTarget(const css::uno::Reference<css::frame::XModel>& xModel, OUString& rURL)
2310{
2311 Reference<drawing::XDrawPagesSupplier> xDPS(xModel, uno::UNO_QUERY_THROW);
2312 Reference<drawing::XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW);
2313 sal_uInt32 nPageCount = xDrawPages->getCount();
2314 OUString sTarget;
2315
2316 for (sal_uInt32 i = 0; i < nPageCount; ++i)
2317 {
2318 Reference<XDrawPage> xDrawPage;
2319 xDrawPages->getByIndex(i) >>= xDrawPage;
2320 Reference<container::XNamed> xNamed(xDrawPage, UNO_QUERY);
2321 if (!xNamed)
2322 continue;
2323 OUString sSlideName = "#" + xNamed->getName();
2324 if (rURL == sSlideName)
2325 {
2326 sTarget = "slide" + OUString::number(i + 1) + ".xml";
2327 break;
2328 }
2329 }
2330 if (sTarget.isEmpty())
2331 {
2332 sal_Int32 nSplit = rURL.lastIndexOf(' ');
2333 if (nSplit > -1)
2334 sTarget = OUString::Concat("slide") + rURL.subView(nSplit + 1) + ".xml";
2335 }
2336
2337 return sTarget;
2338}
2339
2340void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bIsField, sal_Int32 nElement,
2341 bool bCheckDirect,bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
2342 sal_Int16 nScriptType, const Reference< XPropertySet >& rXShapePropSet)
2343{
2344 Reference< XPropertySet > rXPropSet = rRun;
2345 Reference< XPropertyState > rXPropState( rRun, UNO_QUERY );
2346 OUString usLanguage;
2347 PropertyState eState;
2348 bool bComplex = ( nScriptType == css::i18n::ScriptType::COMPLEX );
2349 const char* bold = "0";
2350 const char* italic = nullptr;
2351 const char* underline = nullptr;
2352 const char* strikeout = nullptr;
2353 const char* cap = nullptr;
2354 sal_Int32 nSize = 1800;
2355 sal_Int32 nCharEscapement = 0;
2356 sal_Int32 nCharKerning = 0;
2357 sal_Int32 nCharEscapementHeight = 0;
2358
2359 if ( nElement == XML_endParaRPr && rbOverridingCharHeight )
2360 {
2361 nSize = rnCharHeight;
2362 }
2363 else if (GetProperty(rXPropSet, "CharHeight"))
2364 {
2365 nSize = static_cast<sal_Int32>(100*(*o3tl::doAccess<float>(mAny)));
2366 if ( nElement == XML_rPr || nElement == XML_defRPr )
2367 {
2368 rbOverridingCharHeight = true;
2369 rnCharHeight = nSize;
2370 }
2371 }
2372
2373 if (GetProperty(rXPropSet, "CharKerning"))
2374 nCharKerning = static_cast<sal_Int32>(*o3tl::doAccess<sal_Int16>(mAny));
2381 nCharKerning = ((nCharKerning * 720)-360) / 254;
2382
2383 if ((bComplex && GetProperty(rXPropSet, "CharWeightComplex"))
2384 || GetProperty(rXPropSet, "CharWeight"))
2385 {
2386 if ( *o3tl::doAccess<float>(mAny) >= awt::FontWeight::SEMIBOLD )
2387 bold = "1";
2388 }
2389
2390 if ((bComplex && GetProperty(rXPropSet, "CharPostureComplex"))
2391 || GetProperty(rXPropSet, "CharPosture"))
2392 switch ( *o3tl::doAccess<awt::FontSlant>(mAny) )
2393 {
2394 case awt::FontSlant_OBLIQUE :
2395 case awt::FontSlant_ITALIC :
2396 italic = "1";
2397 break;
2398 default:
2399 break;
2400 }
2401
2402 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderline", eState)
2403 && eState == beans::PropertyState_DIRECT_VALUE)
2404 || GetProperty(rXPropSet, "CharUnderline"))
2405 {
2406 switch ( *o3tl::doAccess<sal_Int16>(mAny) )
2407 {
2408 case awt::FontUnderline::SINGLE :
2409 underline = "sng";
2410 break;
2411 case awt::FontUnderline::DOUBLE :
2412 underline = "dbl";
2413 break;
2414 case awt::FontUnderline::DOTTED :
2415 underline = "dotted";
2416 break;
2417 case awt::FontUnderline::DASH :
2418 underline = "dash";
2419 break;
2420 case awt::FontUnderline::LONGDASH :
2421 underline = "dashLong";
2422 break;
2423 case awt::FontUnderline::DASHDOT :
2424 underline = "dotDash";
2425 break;
2426 case awt::FontUnderline::DASHDOTDOT :
2427 underline = "dotDotDash";
2428 break;
2429 case awt::FontUnderline::WAVE :
2430 underline = "wavy";
2431 break;
2432 case awt::FontUnderline::DOUBLEWAVE :
2433 underline = "wavyDbl";
2434 break;
2435 case awt::FontUnderline::BOLD :
2436 underline = "heavy";
2437 break;
2438 case awt::FontUnderline::BOLDDOTTED :
2439 underline = "dottedHeavy";
2440 break;
2441 case awt::FontUnderline::BOLDDASH :
2442 underline = "dashHeavy";
2443 break;
2444 case awt::FontUnderline::BOLDLONGDASH :
2445 underline = "dashLongHeavy";
2446 break;
2447 case awt::FontUnderline::BOLDDASHDOT :
2448 underline = "dotDashHeavy";
2449 break;
2450 case awt::FontUnderline::BOLDDASHDOTDOT :
2451 underline = "dotDotDashHeavy";
2452 break;
2453 case awt::FontUnderline::BOLDWAVE :
2454 underline = "wavyHeavy";
2455 break;
2456 }
2457 }
2458
2459 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharStrikeout", eState)
2460 && eState == beans::PropertyState_DIRECT_VALUE)
2461 || GetProperty(rXPropSet, "CharStrikeout"))
2462 {
2463 switch ( *o3tl::doAccess<sal_Int16>(mAny) )
2464 {
2465 case awt::FontStrikeout::NONE :
2466 strikeout = "noStrike";
2467 break;
2468 case awt::FontStrikeout::SINGLE :
2469 // LibO supports further values of character
2470 // strikeout, OOXML standard (20.1.10.78,
2471 // ST_TextStrikeType) however specifies only
2472 // 3 - single, double and none. Approximate
2473 // the remaining ones by single strike (better
2474 // some strike than none at all).
2475 // TODO: figure out how to do this better
2476 case awt::FontStrikeout::BOLD :
2477 case awt::FontStrikeout::SLASH :
2478 case awt::FontStrikeout::X :
2479 strikeout = "sngStrike";
2480 break;
2481 case awt::FontStrikeout::DOUBLE :
2482 strikeout = "dblStrike";
2483 break;
2484 }
2485 }
2486
2487 bool bLang = false;
2488 switch(nScriptType)
2489 {
2490 case css::i18n::ScriptType::ASIAN:
2491 bLang = GetProperty(rXPropSet, "CharLocaleAsian"); break;
2492 case css::i18n::ScriptType::COMPLEX:
2493 bLang = GetProperty(rXPropSet, "CharLocaleComplex"); break;
2494 default:
2495 bLang = GetProperty(rXPropSet, "CharLocale"); break;
2496 }
2497
2498 if (bLang)
2499 {
2500 css::lang::Locale aLocale;
2501 mAny >>= aLocale;
2502 LanguageTag aLanguageTag( aLocale);
2503 if (!aLanguageTag.isSystemLocale())
2504 usLanguage = aLanguageTag.getBcp47MS();
2505 }
2506
2507 if (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapement", eState)
2508 && eState == beans::PropertyState_DIRECT_VALUE)
2509 mAny >>= nCharEscapement;
2510
2511 if (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapementHeight", eState)
2512 && eState == beans::PropertyState_DIRECT_VALUE)
2513 mAny >>= nCharEscapementHeight;
2514
2515 if (DFLT_ESC_AUTO_SUPER == nCharEscapement)
2516 {
2517 // Raised by the differences between the ascenders (ascent = baseline to top of highest letter).
2518 // The ascent is generally about 80% of the total font height.
2519 // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER)
2520 nCharEscapement = .8 * (100 - nCharEscapementHeight);
2521 }
2522 else if (DFLT_ESC_AUTO_SUB == nCharEscapement)
2523 {
2524 // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter).
2525 // The descent is generally about 20% of the total font height.
2526 // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB)
2527 nCharEscapement = .2 * -(100 - nCharEscapementHeight);
2528 }
2529
2530 if (nCharEscapement && nCharEscapementHeight)
2531 {
2532 nSize = (nSize * nCharEscapementHeight) / 100;
2533 // MSO uses default ~58% size
2534 nSize = (nSize / 0.58);
2535 }
2536
2537 if (GetProperty(rXPropSet, "CharCaseMap"))
2538 {
2539 switch ( *o3tl::doAccess<sal_Int16>(mAny) )
2540 {
2541 case CaseMap::UPPERCASE :
2542 cap = "all";
2543 break;
2544 case CaseMap::SMALLCAPS :
2545 cap = "small";
2546 break;
2547 }
2548 }
2549
2550 mpFS->startElementNS( XML_a, nElement,
2551 XML_b, bold,
2552 XML_i, italic,
2553 XML_lang, sax_fastparser::UseIf(usLanguage, !usLanguage.isEmpty()),
2554 XML_sz, OString::number(nSize),
2555 // For Condensed character spacing spc value is negative.
2556 XML_spc, sax_fastparser::UseIf(OString::number(nCharKerning), nCharKerning != 0),
2557 XML_strike, strikeout,
2558 XML_u, underline,
2559 XML_baseline, sax_fastparser::UseIf(OString::number(nCharEscapement*1000), nCharEscapement != 0),
2560 XML_cap, cap );
2561
2562 // Fontwork-shapes in LO have text outline and fill from shape stroke and shape fill
2563 // PowerPoint has this as run properties
2564 if (IsFontworkShape(rXShapePropSet))
2565 {
2566 WriteOutline(rXShapePropSet);
2567 WriteBlipOrNormalFill(rXShapePropSet, "Graphic");
2568 WriteShapeEffects(rXShapePropSet);
2569 }
2570 else
2571 {
2572 // mso doesn't like text color to be placed after typeface
2573 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharColor", eState)
2574 && eState == beans::PropertyState_DIRECT_VALUE)
2575 || GetProperty(rXPropSet, "CharColor"))
2576 {
2577 ::Color color( ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny) );
2578 SAL_INFO("oox.shape", "run color: " << sal_uInt32(color) << " auto: " << sal_uInt32(COL_AUTO));
2579
2580 // WriteSolidFill() handles MAX_PERCENT as "no transparency".
2581 sal_Int32 nTransparency = MAX_PERCENT;
2582 if (rXPropSet->getPropertySetInfo()->hasPropertyByName("CharTransparence"))
2583 {
2584 rXPropSet->getPropertyValue("CharTransparence") >>= nTransparency;
2585 // UNO scale is 0..100, OOXML scale is 0..100000; also UNO tracks transparency, OOXML
2586 // tracks opacity.
2587 nTransparency = MAX_PERCENT - (nTransparency * PER_PERCENT);
2588 }
2589
2590 bool bContoured = false;
2591 if (GetProperty(rXPropSet, "CharContoured"))
2592 bContoured = *o3tl::doAccess<bool>(mAny);
2593
2594 // tdf#127696 If the CharContoured is true, then the text color is white and the outline color is the CharColor.
2595 if (bContoured)
2596 {
2597 mpFS->startElementNS(XML_a, XML_ln);
2598 if (color == COL_AUTO)
2599 {
2601 }
2602 else
2603 {
2604 color.SetAlpha(255);
2605 if (!WriteSchemeColor(u"CharComplexColor", rXPropSet))
2606 WriteSolidFill(color, nTransparency);
2607 }
2608 mpFS->endElementNS(XML_a, XML_ln);
2609
2611 }
2612 // tdf#104219 In LibreOffice and MS Office, there are two types of colors:
2613 // Automatic and Fixed. OOXML is setting automatic color, by not providing color.
2614 else if( color != COL_AUTO )
2615 {
2616 color.SetAlpha(255);
2617 // TODO: special handle embossed/engraved
2618 if (!WriteSchemeColor(u"CharComplexColor", rXPropSet))
2619 {
2620 WriteSolidFill(color, nTransparency);
2621 }
2622 }
2623 else if (GetDocumentType() == DOCUMENT_PPTX)
2624 {
2625 // Resolve COL_AUTO for PPTX since MS Powerpoint doesn't have automatic colors.
2626 bool bIsTextBackgroundDark = mbIsBackgroundDark;
2627 if (rXShapePropSet.is() && GetProperty(rXShapePropSet, "FillStyle")
2628 && mAny.get<FillStyle>() != FillStyle_NONE
2629 && GetProperty(rXShapePropSet, "FillColor"))
2630 {
2631 ::Color aShapeFillColor(ColorTransparency, mAny.get<sal_uInt32>());
2632 bIsTextBackgroundDark = aShapeFillColor.IsDark();
2633 }
2634
2635 if (bIsTextBackgroundDark)
2637 else
2639 }
2640 }
2641 }
2642
2643 // tdf#128096, exporting XML_highlight to docx already works fine,
2644 // so make sure this code is only run when exporting to pptx, just in case
2646 {
2647 if (GetProperty(rXPropSet, "CharBackColor"))
2648 {
2649 ::Color color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny));
2650 if( color != COL_AUTO )
2651 {
2652 mpFS->startElementNS(XML_a, XML_highlight);
2653 WriteColor( color );
2654 mpFS->endElementNS( XML_a, XML_highlight );
2655 }
2656 }
2657 }
2658
2659 if (underline
2660 && ((bCheckDirect
2661 && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderlineColor", eState)
2662 && eState == beans::PropertyState_DIRECT_VALUE)
2663 || GetProperty(rXPropSet, "CharUnderlineColor")))
2664 {
2665 ::Color color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny));
2666 // if color is automatic, then we shouldn't write information about color but to take color from character
2667 if( color != COL_AUTO )
2668 {
2669 mpFS->startElementNS(XML_a, XML_uFill);
2671 mpFS->endElementNS( XML_a, XML_uFill );
2672 }
2673 else
2674 {
2675 mpFS->singleElementNS(XML_a, XML_uFillTx);
2676 }
2677 }
2678
2679 if (GetProperty(rXPropSet, "CharFontName"))
2680 {
2681 const char* const pitch = nullptr;
2682 const char* const charset = nullptr;
2683 OUString usTypeface;
2684
2685 mAny >>= usTypeface;
2686 OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
2687 if (!aSubstName.isEmpty())
2688 usTypeface = aSubstName;
2689
2690 mpFS->singleElementNS( XML_a, XML_latin,
2691 XML_typeface, usTypeface,
2692 XML_pitchFamily, pitch,
2693 XML_charset, charset );
2694 }
2695
2696 if ((bComplex
2697 && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameComplex", eState)
2698 && eState == beans::PropertyState_DIRECT_VALUE))
2699 || (!bComplex
2700 && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameAsian", eState)
2701 && eState == beans::PropertyState_DIRECT_VALUE)))
2702 {
2703 const char* const pitch = nullptr;
2704 const char* const charset = nullptr;
2705 OUString usTypeface;
2706
2707 mAny >>= usTypeface;
2708 OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
2709 if (!aSubstName.isEmpty())
2710 usTypeface = aSubstName;
2711
2712 mpFS->singleElementNS( XML_a, bComplex ? XML_cs : XML_ea,
2713 XML_typeface, usTypeface,
2714 XML_pitchFamily, pitch,
2715 XML_charset, charset );
2716 }
2717
2718 if( bIsField )
2719 {
2720 Reference< XTextField > rXTextField;
2721 if (GetProperty(rXPropSet, "TextField"))
2722 mAny >>= rXTextField;
2723 if( rXTextField.is() )
2724 rXPropSet.set( rXTextField, UNO_QUERY );
2725 }
2726
2727 // field properties starts here
2728 if (GetProperty(rXPropSet, "URL"))
2729 {
2730 OUString sURL;
2731
2732 mAny >>= sURL;
2733 if (!sURL.isEmpty())
2734 {
2735 if (!sURL.match("#action?jump="))
2736 {
2737 bool bExtURL = URLTransformer().isExternalURL(sURL);
2738 sURL = bExtURL ? sURL : lcl_GetTarget(GetFB()->getModel(), sURL);
2739
2740 OUString sRelId
2741 = mpFB->addRelation(mpFS->getOutputStream(),
2744 sURL, bExtURL);
2745
2746 if (bExtURL)
2747 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
2748 else
2749 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
2750 XML_action, "ppaction://hlinksldjump");
2751 }
2752 else
2753 {
2754 sal_Int32 nIndex = sURL.indexOf('=');
2755 std::u16string_view aDestination(sURL.subView(nIndex + 1));
2756 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), "", XML_action,
2757 OUString::Concat("ppaction://hlinkshowjump?jump=") + aDestination);
2758 }
2759 }
2760 }
2761 mpFS->endElementNS( XML_a, nElement );
2762}
2763
2764OUString DrawingML::GetFieldValue( const css::uno::Reference< css::text::XTextRange >& rRun, bool& bIsURLField )
2765{
2766 Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
2767 OUString aFieldType, aFieldValue;
2768
2769 if (GetProperty(rXPropSet, "TextPortionType"))
2770 {
2771 aFieldType = *o3tl::doAccess<OUString>(mAny);
2772 SAL_INFO("oox.shape", "field type: " << aFieldType);
2773 }
2774
2775 if( aFieldType == "TextField" )
2776 {
2777 Reference< XTextField > rXTextField;
2778 if (GetProperty(rXPropSet, "TextField"))
2779 mAny >>= rXTextField;
2780 if( rXTextField.is() )
2781 {
2782 rXPropSet.set( rXTextField, UNO_QUERY );
2783 if( rXPropSet.is() )
2784 {
2785 OUString aFieldKind( rXTextField->getPresentation( true ) );
2786 SAL_INFO("oox.shape", "field kind: " << aFieldKind);
2787 if( aFieldKind == "Page" )
2788 {
2789 aFieldValue = "slidenum";
2790 }
2791 else if( aFieldKind == "Pages" )
2792 {
2793 aFieldValue = "slidecount";
2794 }
2795 else if( aFieldKind == "PageName" )
2796 {
2797 aFieldValue = "slidename";
2798 }
2799 else if( aFieldKind == "URL" )
2800 {
2801 bIsURLField = true;
2802 if (GetProperty(rXPropSet, "Representation"))
2803 mAny >>= aFieldValue;
2804
2805 }
2806 else if(aFieldKind == "Date")
2807 {
2808 sal_Int32 nNumFmt = -1;
2809 rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
2810 aFieldValue = GetDatetimeTypeFromDate(static_cast<SvxDateFormat>(nNumFmt));
2811 }
2812 else if(aFieldKind == "ExtTime")
2813 {
2814 sal_Int32 nNumFmt = -1;
2815 rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
2816 aFieldValue = GetDatetimeTypeFromTime(static_cast<SvxTimeFormat>(nNumFmt));
2817 }
2818 else if(aFieldKind == "ExtFile")
2819 {
2820 sal_Int32 nNumFmt = -1;
2821 rXPropSet->getPropertyValue(UNO_TC_PROP_FILE_FORMAT) >>= nNumFmt;
2822 switch(nNumFmt)
2823 {
2824 case 0: aFieldValue = "file"; // Path/File name
2825 break;
2826 case 1: aFieldValue = "file1"; // Path
2827 break;
2828 case 2: aFieldValue = "file2"; // File name without extension
2829 break;
2830 case 3: aFieldValue = "file3"; // File name with extension
2831 }
2832 }
2833 else if(aFieldKind == "Author")
2834 {
2835 aFieldValue = "author";
2836 }
2837 }
2838 }
2839 }
2840 return aFieldValue;
2841}
2842
2844{
2845 return GetDatetimeTypeFromDateTime(eDate, SvxTimeFormat::AppDefault);
2846}
2847
2849{
2850 return GetDatetimeTypeFromDateTime(SvxDateFormat::AppDefault, eTime);
2851}
2852
2854{
2855 OUString aDateField;
2856 switch (eDate)
2857 {
2858 case SvxDateFormat::StdSmall:
2859 case SvxDateFormat::A:
2860 aDateField = "datetime";
2861 break;
2862 case SvxDateFormat::B:
2863 aDateField = "datetime1"; // 13/02/1996
2864 break;
2865 case SvxDateFormat::C:
2866 aDateField = "datetime5";
2867 break;
2868 case SvxDateFormat::D:
2869 aDateField = "datetime3"; // 13 February 1996
2870 break;
2871 case SvxDateFormat::StdBig:
2872 case SvxDateFormat::E:
2873 case SvxDateFormat::F:
2874 aDateField = "datetime2";
2875 break;
2876 default:
2877 break;
2878 }
2879
2880 OUString aTimeField;
2881 switch (eTime)
2882 {
2883 case SvxTimeFormat::Standard:
2884 case SvxTimeFormat::HH24_MM_SS:
2885 case SvxTimeFormat::HH24_MM_SS_00:
2886 aTimeField = "datetime11"; // 13:49:38
2887 break;
2888 case SvxTimeFormat::HH24_MM:
2889 aTimeField = "datetime10"; // 13:49
2890 break;
2891 case SvxTimeFormat::HH12_MM:
2892 case SvxTimeFormat::HH12_MM_AMPM:
2893 aTimeField = "datetime12"; // 01:49 PM
2894 break;
2895 case SvxTimeFormat::HH12_MM_SS:
2896 case SvxTimeFormat::HH12_MM_SS_AMPM:
2897 case SvxTimeFormat::HH12_MM_SS_00:
2898 case SvxTimeFormat::HH12_MM_SS_00_AMPM:
2899 aTimeField = "datetime13"; // 01:49:38 PM
2900 break;
2901 default:
2902 break;
2903 }
2904
2905 if (!aDateField.isEmpty() && aTimeField.isEmpty())
2906 return aDateField;
2907 else if (!aTimeField.isEmpty() && aDateField.isEmpty())
2908 return aTimeField;
2909 else if (!aDateField.isEmpty() && !aTimeField.isEmpty())
2910 {
2911 if (aTimeField == "datetime11" || aTimeField == "datetime13")
2912 // only datetime format that has Date and HH:MM:SS
2913 return "datetime9"; // dd/mm/yyyy H:MM:SS
2914 else
2915 // only datetime format that has Date and HH:MM
2916 return "datetime8"; // dd/mm/yyyy H:MM
2917 }
2918 else
2919 return "";
2920}
2921
2923 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
2924 const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet)
2925{
2926 Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
2927 sal_Int16 nLevel = -1;
2928 if (GetProperty(rXPropSet, "NumberingLevel"))
2929 mAny >>= nLevel;
2930
2931 bool bNumberingIsNumber = true;
2932 if (GetProperty(rXPropSet, "NumberingIsNumber"))
2933 mAny >>= bNumberingIsNumber;
2934
2935 float nFontSize = -1;
2936 if (GetProperty(rXPropSet, "CharHeight"))
2937 mAny >>= nFontSize;
2938
2939 bool bIsURLField = false;
2940 OUString sFieldValue = GetFieldValue( rRun, bIsURLField );
2941 bool bWriteField = !( sFieldValue.isEmpty() || bIsURLField );
2942
2943 OUString sText = rRun->getString();
2944
2945 //if there is no text following the bullet, add a space after the bullet
2946 if (nLevel !=-1 && bNumberingIsNumber && sText.isEmpty() )
2947 sText=" ";
2948
2949 if ( bIsURLField )
2950 sText = sFieldValue;
2951
2952 if( sText.isEmpty())
2953 {
2954 Reference< XPropertySet > xPropSet( rRun, UNO_QUERY );
2955
2956 try
2957 {
2958 if( !xPropSet.is() || !( xPropSet->getPropertyValue( "PlaceholderText" ) >>= sText ) )
2959 return;
2960 if( sText.isEmpty() )
2961 return;
2962 }
2963 catch (const Exception &)
2964 {
2965 return;
2966 }
2967 }
2968
2969 if (sText == "\n")
2970 {
2971 // Empty run? Do not forget to write the font size in case of pptx:
2972 if ((GetDocumentType() == DOCUMENT_PPTX) && (nFontSize != -1))
2973 {
2974 mpFS->startElementNS(XML_a, XML_br);
2975 mpFS->singleElementNS(XML_a, XML_rPr, XML_sz,
2976 OString::number(nFontSize * 100));
2977 mpFS->endElementNS(XML_a, XML_br);
2978 }
2979 else
2980 mpFS->singleElementNS(XML_a, XML_br);
2981 }
2982 else
2983 {
2984 if( bWriteField )
2985 {
2986 OString sUUID(comphelper::xml::generateGUIDString());
2987 mpFS->startElementNS( XML_a, XML_fld,
2988 XML_id, sUUID.getStr(),
2989 XML_type, sFieldValue );
2990 }
2991 else
2992 {
2993 mpFS->startElementNS(XML_a, XML_r);
2994 }
2995
2996 Reference< XPropertySet > xPropSet( rRun, uno::UNO_QUERY );
2997
2998 WriteRunProperties( xPropSet, bIsURLField, XML_rPr, true, rbOverridingCharHeight, rnCharHeight, GetScriptType(sText), rXShapePropSet);
2999 mpFS->startElementNS(XML_a, XML_t);
3000 mpFS->writeEscaped( sText );
3001 mpFS->endElementNS( XML_a, XML_t );
3002
3003 if( bWriteField )
3004 mpFS->endElementNS( XML_a, XML_fld );
3005 else
3006 mpFS->endElementNS( XML_a, XML_r );
3007 }
3008}
3009
3010static OUString GetAutoNumType(SvxNumType nNumberingType, bool bSDot, bool bPBehind, bool bPBoth)
3011{
3012 OUString sPrefixSuffix;
3013
3014 if (bPBoth)
3015 sPrefixSuffix = "ParenBoth";
3016 else if (bPBehind)
3017 sPrefixSuffix = "ParenR";
3018 else if (bSDot)
3019 sPrefixSuffix = "Period";
3020
3021 switch( nNumberingType )
3022 {
3025 return "alphaUc" + sPrefixSuffix;
3026
3029 return "alphaLc" + sPrefixSuffix;
3030
3031 case SVX_NUM_ROMAN_UPPER :
3032 return "romanUc" + sPrefixSuffix;
3033
3034 case SVX_NUM_ROMAN_LOWER :
3035 return "romanLc" + sPrefixSuffix;
3036
3037 case SVX_NUM_ARABIC :
3038 {
3039 if (sPrefixSuffix.isEmpty())
3040 return "arabicPlain";
3041 else
3042 return "arabic" + sPrefixSuffix;
3043 }
3044 default:
3045 break;
3046 }
3047
3048 return OUString();
3049}
3050
3051void DrawingML::WriteParagraphNumbering(const Reference< XPropertySet >& rXPropSet, float fFirstCharHeight, sal_Int16 nLevel )
3052{
3053 if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
3054 return;
3055
3056 Reference< XIndexAccess > rXIndexAccess;
3057
3058 if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
3059 return;
3060
3061 SAL_INFO("oox.shape", "numbering rules");
3062
3063 Sequence<PropertyValue> aPropertySequence;
3064 rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
3065
3066 if (!aPropertySequence.hasElements())
3067 return;
3068
3069 SvxNumType nNumberingType = SVX_NUM_NUMBER_NONE;
3070 bool bSDot = false;
3071 bool bPBehind = false;
3072 bool bPBoth = false;
3073 sal_Unicode aBulletChar = 0x2022; // a bullet
3074 awt::FontDescriptor aFontDesc;
3075 bool bHasFontDesc = false;
3076 uno::Reference<graphic::XGraphic> xGraphic;
3077 sal_Int16 nBulletRelSize = 0;
3078 sal_Int16 nStartWith = 1;
3079 ::Color nBulletColor;
3080 bool bHasBulletColor = false;
3081 awt::Size aGraphicSize;
3082
3083 for ( const PropertyValue& rPropValue : std::as_const(aPropertySequence) )
3084 {
3085 OUString aPropName( rPropValue.Name );
3086 SAL_INFO("oox.shape", "pro name: " << aPropName);
3087 if ( aPropName == "NumberingType" )
3088 {
3089 nNumberingType = static_cast<SvxNumType>(*o3tl::doAccess<sal_Int16>(rPropValue.Value));
3090 }
3091 else if ( aPropName == "Prefix" )
3092 {
3093 if( *o3tl::doAccess<OUString>(rPropValue.Value) == ")")
3094 bPBoth = true;
3095 }
3096 else if ( aPropName == "Suffix" )
3097 {
3098 auto s = o3tl::doAccess<OUString>(rPropValue.Value);
3099 if( *s == ".")
3100 bSDot = true;
3101 else if( *s == ")")
3102 bPBehind = true;
3103 }
3104 else if(aPropName == "BulletColor")
3105 {
3106 nBulletColor = ::Color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(rPropValue.Value));
3107 bHasBulletColor = true;
3108 }
3109 else if ( aPropName == "BulletChar" )
3110 {
3111 aBulletChar = (*o3tl::doAccess<OUString>(rPropValue.Value))[ 0 ];
3112 }
3113 else if ( aPropName == "BulletFont" )
3114 {
3115 aFontDesc = *o3tl::doAccess<awt::FontDescriptor>(rPropValue.Value);
3116 bHasFontDesc = true;
3117
3118 // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font,
3119 // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used.
3120 // Because there might exist a lot of damaged documents I added this two lines
3121 // which fixes the bullet problem for the export.
3122 if ( aFontDesc.Name.equalsIgnoreAsciiCase("StarSymbol") )
3123 aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252;
3124
3125 }
3126 else if ( aPropName == "BulletRelSize" )
3127 {
3128 nBulletRelSize = *o3tl::doAccess<sal_Int16>(rPropValue.Value);
3129 }
3130 else if ( aPropName == "StartWith" )
3131 {
3132 nStartWith = *o3tl::doAccess<sal_Int16>(rPropValue.Value);
3133 }
3134 else if (aPropName == "GraphicBitmap")
3135 {
3136 auto xBitmap = rPropValue.Value.get<uno::Reference<awt::XBitmap>>();
3137 xGraphic.set(xBitmap, uno::UNO_QUERY);
3138 }
3139 else if ( aPropName == "GraphicSize" )
3140 {
3141 aGraphicSize = *o3tl::doAccess<awt::Size>(rPropValue.Value);
3142 SAL_INFO("oox.shape", "graphic size: " << aGraphicSize.Width << "x" << aGraphicSize.Height);
3143 }
3144 }
3145
3146 if (nNumberingType == SVX_NUM_NUMBER_NONE)
3147 return;
3148
3149 Graphic aGraphic(xGraphic);
3150 if (xGraphic.is() && aGraphic.GetType() != GraphicType::NONE)
3151 {
3152 tools::Long nFirstCharHeightMm = TransformMetric(fFirstCharHeight * 100.f, FieldUnit::POINT, FieldUnit::MM);
3153 float fBulletSizeRel = aGraphicSize.Height / static_cast<float>(nFirstCharHeightMm) / OOX_BULLET_LIST_SCALE_FACTOR;
3154
3155 OUString sRelationId;
3156
3157 if (fBulletSizeRel < 1.0f)
3158 {
3159 // Add padding to get the bullet point centered in PPT
3160 Size aDestSize(64, 64);
3161 float fBulletSizeRelX = fBulletSizeRel / aGraphicSize.Height * aGraphicSize.Width;
3162 tools::Long nPaddingX = std::max<tools::Long>(0, std::lround((aDestSize.Width() - fBulletSizeRelX * aDestSize.Width()) / 2.f));
3163 tools::Long nPaddingY = std::lround((aDestSize.Height() - fBulletSizeRel * aDestSize.Height()) / 2.f);
3164 tools::Rectangle aDestRect(nPaddingX, nPaddingY, aDestSize.Width() - nPaddingX, aDestSize.Height() - nPaddingY);
3165
3166 AlphaMask aMask(aDestSize);
3167 aMask.Erase(255);
3168 BitmapEx aSourceBitmap(aGraphic.GetBitmapEx());
3169 aSourceBitmap.Scale(aDestRect.GetSize());
3170 tools::Rectangle aSourceRect(Point(0, 0), aDestRect.GetSize());
3171 BitmapEx aDestBitmap(Bitmap(aDestSize, vcl::PixelFormat::N24_BPP), aMask);
3172 aDestBitmap.CopyPixel(aDestRect, aSourceRect, &aSourceBitmap);
3173 Graphic aDestGraphic(aDestBitmap);
3174 sRelationId = WriteImage(aDestGraphic);
3175 fBulletSizeRel = 1.0f;
3176 }
3177 else
3178 {
3179 sRelationId = WriteImage(aGraphic);
3180 }
3181
3182 mpFS->singleElementNS( XML_a, XML_buSzPct,
3183 XML_val, OString::number(std::min<sal_Int32>(std::lround(100000.f * fBulletSizeRel), 400000)));
3184 mpFS->startElementNS(XML_a, XML_buBlip);
3185 mpFS->singleElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelationId);
3186 mpFS->endElementNS( XML_a, XML_buBlip );
3187 }
3188 else
3189 {
3190 if(bHasBulletColor)
3191 {
3192 if (nBulletColor == COL_AUTO )
3193 {
3194 nBulletColor = ::Color(ColorTransparency, mbIsBackgroundDark ? 0xffffff : 0x000000);
3195 }
3196 mpFS->startElementNS(XML_a, XML_buClr);
3197 WriteColor( nBulletColor );
3198 mpFS->endElementNS( XML_a, XML_buClr );
3199 }
3200
3201 if( nBulletRelSize && nBulletRelSize != 100 )
3202 mpFS->singleElementNS( XML_a, XML_buSzPct,
3203 XML_val, OString::number(std::clamp<sal_Int32>(1000*nBulletRelSize, 25000, 400000)));
3204 if( bHasFontDesc )
3205 {
3206 if ( SVX_NUM_CHAR_SPECIAL == nNumberingType )
3207 aBulletChar = SubstituteBullet( aBulletChar, aFontDesc );
3208 mpFS->singleElementNS( XML_a, XML_buFont,
3209 XML_typeface, aFontDesc.Name,
3210 XML_charset, sax_fastparser::UseIf("2", aFontDesc.CharSet == awt::CharSet::SYMBOL));
3211 }
3212
3213 OUString aAutoNumType = GetAutoNumType( nNumberingType, bSDot, bPBehind, bPBoth );
3214
3215 if (!aAutoNumType.isEmpty())
3216 {
3217 mpFS->singleElementNS(XML_a, XML_buAutoNum,
3218 XML_type, aAutoNumType,
3219 XML_startAt, sax_fastparser::UseIf(OString::number(nStartWith), nStartWith > 1));
3220 }
3221 else
3222 {
3223 mpFS->singleElementNS(XML_a, XML_buChar, XML_char, OUString(aBulletChar));
3224 }
3225 }
3226}
3227
3229{
3230 css::uno::Sequence<css::style::TabStop> aTabStops;
3231 if (GetProperty(rXPropSet, "ParaTabStops"))
3232 aTabStops = *o3tl::doAccess<css::uno::Sequence<css::style::TabStop>>(mAny);
3233
3234 if (aTabStops.getLength() > 0)
3235 mpFS->startElementNS(XML_a, XML_tabLst);
3236
3237 for (const css::style::TabStop& rTabStop : std::as_const(aTabStops))
3238 {
3239 OString sPosition = OString::number(GetPointFromCoordinate(rTabStop.Position));
3240 OString sAlignment;
3241 switch (rTabStop.Alignment)
3242 {
3243 case css::style::TabAlign_DECIMAL:
3244 sAlignment = "dec";
3245 break;
3246 case css::style::TabAlign_RIGHT:
3247 sAlignment = "r";
3248 break;
3249 case css::style::TabAlign_CENTER:
3250 sAlignment = "ctr";
3251 break;
3252 case css::style::TabAlign_LEFT:
3253 default:
3254 sAlignment = "l";
3255 }
3256 mpFS->singleElementNS(XML_a, XML_tab, XML_algn, sAlignment, XML_pos, sPosition);
3257 }
3258 if (aTabStops.getLength() > 0)
3259 mpFS->endElementNS(XML_a, XML_tabLst);
3260}
3261
3263{
3264 bool bRet = false;
3265 if ( rXShape.is() )
3266 {
3267 uno::Reference<lang::XServiceInfo> xServiceInfo(rXShape, uno::UNO_QUERY_THROW);
3268 bRet = xServiceInfo->supportsService("com.sun.star.drawing.GroupShape");
3269 }
3270 return bRet;
3271}
3272
3273sal_Int32 DrawingML::getBulletMarginIndentation (const Reference< XPropertySet >& rXPropSet,sal_Int16 nLevel, std::u16string_view propName)
3274{
3275 if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
3276 return 0;
3277
3278 Reference< XIndexAccess > rXIndexAccess;
3279
3280 if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
3281 return 0;
3282
3283 SAL_INFO("oox.shape", "numbering rules");
3284
3285 Sequence<PropertyValue> aPropertySequence;
3286 rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
3287
3288 if (!aPropertySequence.hasElements())
3289 return 0;
3290
3291 for ( const PropertyValue& rPropValue : std::as_const(aPropertySequence) )
3292 {
3293 OUString aPropName( rPropValue.Name );
3294 SAL_INFO("oox.shape", "pro name: " << aPropName);
3295 if ( aPropName == propName )
3296 return *o3tl::doAccess<sal_Int32>(rPropValue.Value);
3297 }
3298
3299 return 0;
3300}
3301
3302const char* DrawingML::GetAlignment( style::ParagraphAdjust nAlignment )
3303{
3304 const char* sAlignment = nullptr;
3305
3306 switch( nAlignment )
3307 {
3308 case style::ParagraphAdjust_CENTER:
3309 sAlignment = "ctr";
3310 break;
3311 case style::ParagraphAdjust_RIGHT:
3312 sAlignment = "r";
3313 break;
3314 case style::ParagraphAdjust_BLOCK:
3315 sAlignment = "just";
3316 break;
3317 default:
3318 ;
3319 }
3320
3321 return sAlignment;
3322}
3323
3324void DrawingML::WriteLinespacing(const LineSpacing& rSpacing, float fFirstCharHeight)
3325{
3326 if( rSpacing.Mode == LineSpacingMode::PROP )
3327 {
3328 mpFS->singleElementNS( XML_a, XML_spcPct,
3329 XML_val, OString::number(static_cast<sal_Int32>(rSpacing.Height)*1000));
3330 }
3331 else if (rSpacing.Mode == LineSpacingMode::MINIMUM
3332 && fFirstCharHeight > static_cast<float>(rSpacing.Height) * 0.001 * 72.0 / 2.54)
3333 {
3334 // 100% proportional line spacing = single line spacing
3335 mpFS->singleElementNS(XML_a, XML_spcPct, XML_val,
3336 OString::number(static_cast<sal_Int32>(100000)));
3337 }
3338 else
3339 {
3340 mpFS->singleElementNS( XML_a, XML_spcPts,
3341 XML_val, OString::number(std::lround(rSpacing.Height / 25.4 * 72)));
3342 }
3343}
3344
3345bool DrawingML::WriteParagraphProperties(const Reference<XTextContent>& rParagraph, float fFirstCharHeight, sal_Int32 nElement)
3346{
3347 Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
3348 Reference< XPropertyState > rXPropState( rParagraph, UNO_QUERY );
3349 PropertyState eState;
3350
3351 if( !rXPropSet.is() || !rXPropState.is() )
3352 return false;
3353
3354 sal_Int16 nLevel = -1;
3355 if (GetProperty(rXPropSet, "NumberingLevel"))
3356 mAny >>= nLevel;
3357
3358 bool bWriteNumbering = true;
3359 bool bForceZeroIndent = false;
3360 if (mbPlaceholder)
3361 {
3362 Reference< text::XTextRange > xParaText(rParagraph, UNO_QUERY);
3363 if (xParaText)
3364 {
3365 bool bNumberingOnThisLevel = false;
3366 if (nLevel > -1)
3367 {
3368 Reference< XIndexAccess > xNumberingRules(rXPropSet->getPropertyValue("NumberingRules"), UNO_QUERY);
3369 const PropertyValues& rNumRuleOfLevel = xNumberingRules->getByIndex(nLevel).get<PropertyValues>();
3370 for (const PropertyValue& rRule : rNumRuleOfLevel)
3371 if (rRule.Name == "NumberingType" && rRule.Value.hasValue())
3372 bNumberingOnThisLevel = rRule.Value.get<sal_uInt16>() != style::NumberingType::NUMBER_NONE;
3373 }
3374
3375 const bool bIsNumberingVisible = rXPropSet->getPropertyValue("NumberingIsNumber").get<bool>();
3376 const bool bIsLineEmpty = !xParaText->getString().getLength();
3377
3378 bWriteNumbering = !bIsLineEmpty && bIsNumberingVisible && (nLevel != -1);
3379 bForceZeroIndent = (!bIsNumberingVisible || bIsLineEmpty || !bNumberingOnThisLevel);
3380 }
3381
3382 }
3383
3384 sal_Int16 nTmp = sal_Int16(style::ParagraphAdjust_LEFT);
3385 if (GetProperty(rXPropSet, "ParaAdjust"))
3386 mAny >>= nTmp;
3387 style::ParagraphAdjust nAlignment = static_cast<style::ParagraphAdjust>(nTmp);
3388
3389 bool bHasLinespacing = false;
3390 LineSpacing aLineSpacing;
3391 if (GetPropertyAndState(rXPropSet, rXPropState, "ParaLineSpacing", eState)
3392 && (mAny >>= aLineSpacing)
3393 && (eState == beans::PropertyState_DIRECT_VALUE ||
3394 // only export if it differs from the default 100% line spacing
3395 aLineSpacing.Mode != LineSpacingMode::PROP || aLineSpacing.Height != 100))
3396 bHasLinespacing = true;
3397
3398 bool bRtl = false;
3399 if (GetProperty(rXPropSet, "WritingMode"))
3400 {
3401 sal_Int16 nWritingMode;
3402 if( ( mAny >>= nWritingMode ) && nWritingMode == text::WritingMode2::RL_TB )
3403 {
3404 bRtl = true;
3405 }
3406 }
3407
3408 sal_Int32 nParaLeftMargin = 0;
3409 sal_Int32 nParaFirstLineIndent = 0;
3410
3411 if (GetProperty(rXPropSet, "ParaLeftMargin"))
3412 mAny >>= nParaLeftMargin;
3413 if (GetProperty(rXPropSet, "ParaFirstLineIndent"))
3414 mAny >>= nParaFirstLineIndent;
3415
3416 sal_Int32 nParaTopMargin = 0;
3417 sal_Int32 nParaBottomMargin = 0;
3418
3419 if (GetProperty(rXPropSet, "ParaTopMargin"))
3420 mAny >>= nParaTopMargin;
3421 if (GetProperty(rXPropSet, "ParaBottomMargin"))
3422 mAny >>= nParaBottomMargin;
3423
3424 sal_Int32 nLeftMargin = getBulletMarginIndentation ( rXPropSet, nLevel,u"LeftMargin");
3425 sal_Int32 nLineIndentation = getBulletMarginIndentation ( rXPropSet, nLevel,u"FirstLineOffset");
3426
3427 if (bWriteNumbering && !bForceZeroIndent)
3428 {
3429 if (!(nLevel != -1
3430 || nAlignment != style::ParagraphAdjust_LEFT
3431 || bHasLinespacing))
3432 return false;
3433 }
3434
3435 sal_Int32 nParaDefaultTabSize = 0;
3436 if (GetProperty(rXPropSet, "ParaTabStopDefaultDistance"))
3437 mAny >>= nParaDefaultTabSize;
3438
3439 if (nParaLeftMargin) // For Paragraph
3440 mpFS->startElementNS( XML_a, nElement,
3441 XML_lvl, sax_fastparser::UseIf(OString::number(nLevel), nLevel > 0),
3442 XML_marL, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nParaLeftMargin)), nParaLeftMargin > 0),
3443 XML_indent, sax_fastparser::UseIf(OString::number(!bForceZeroIndent ? oox::drawingml::convertHmmToEmu(nParaFirstLineIndent) : 0), (bForceZeroIndent || (nParaFirstLineIndent != 0))),
3444 XML_algn, GetAlignment( nAlignment ),
3445 XML_defTabSz, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nParaDefaultTabSize)), nParaDefaultTabSize > 0),
3446 XML_rtl, sax_fastparser::UseIf(ToPsz10(bRtl), bRtl));
3447 else
3448 mpFS->startElementNS( XML_a, nElement,
3449 XML_lvl, sax_fastparser::UseIf(OString::number(nLevel), nLevel > 0),
3451 XML_indent, sax_fastparser::UseIf(OString::number(!bForceZeroIndent ? oox::drawingml::convertHmmToEmu(nLineIndentation) : 0), (bForceZeroIndent || ( nLineIndentation != 0))),
3452 XML_algn, GetAlignment( nAlignment ),
3453 XML_defTabSz, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nParaDefaultTabSize)), nParaDefaultTabSize > 0),
3454 XML_rtl, sax_fastparser::UseIf(ToPsz10(bRtl), bRtl));
3455
3456
3457 if( bHasLinespacing )
3458 {
3459 mpFS->startElementNS(XML_a, XML_lnSpc);
3460 WriteLinespacing(aLineSpacing, fFirstCharHeight);
3461 mpFS->endElementNS( XML_a, XML_lnSpc );
3462 }
3463
3464 if( nParaTopMargin != 0 )
3465 {
3466 mpFS->startElementNS(XML_a, XML_spcBef);
3467 {
3468 mpFS->singleElementNS( XML_a, XML_spcPts,
3469 XML_val, OString::number(std::lround(nParaTopMargin / 25.4 * 72)));
3470 }
3471 mpFS->endElementNS( XML_a, XML_spcBef );
3472 }
3473
3474 if( nParaBottomMargin != 0 )
3475 {
3476 mpFS->startElementNS(XML_a, XML_spcAft);
3477 {
3478 mpFS->singleElementNS( XML_a, XML_spcPts,
3479 XML_val, OString::number(std::lround(nParaBottomMargin / 25.4 * 72)));
3480 }
3481 mpFS->endElementNS( XML_a, XML_spcAft );
3482 }
3483
3484 if (!bWriteNumbering)
3485 mpFS->singleElementNS(XML_a, XML_buNone);
3486 else
3487 WriteParagraphNumbering( rXPropSet, fFirstCharHeight, nLevel );
3488
3489 WriteParagraphTabStops( rXPropSet );
3490
3491 // do not end element for lstStyles since, defRPr should be stacked inside it
3492 if( nElement != XML_lvl1pPr )
3493 mpFS->endElementNS( XML_a, nElement );
3494
3495 return true;
3496}
3497
3498void DrawingML::WriteLstStyles(const css::uno::Reference<css::text::XTextContent>& rParagraph,
3499 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
3500 const css::uno::Reference<css::beans::XPropertySet>& rXShapePropSet)
3501{
3502 Reference<XEnumerationAccess> xAccess(rParagraph, UNO_QUERY);
3503 if (!xAccess.is())
3504 return;
3505
3506 Reference<XEnumeration> xEnumeration(xAccess->createEnumeration());
3507 if (!xEnumeration.is())
3508 return;
3509
3510
3512
3513 if (!xEnumeration->hasMoreElements())
3514 return;
3515
3516 Any aAny(xEnumeration->nextElement());
3517 if (aAny >>= rRun)
3518 {
3519 float fFirstCharHeight = rnCharHeight / 1000.;
3520 Reference<XPropertySet> xFirstRunPropSet(rRun, UNO_QUERY);
3521 Reference<XPropertySetInfo> xFirstRunPropSetInfo
3522 = xFirstRunPropSet->getPropertySetInfo();
3523
3524 if (xFirstRunPropSetInfo->hasPropertyByName("CharHeight"))
3525 fFirstCharHeight = xFirstRunPropSet->getPropertyValue("CharHeight").get<float>();
3526
3527 mpFS->startElementNS(XML_a, XML_lstStyle);
3528 if( !WriteParagraphProperties(rParagraph, fFirstCharHeight, XML_lvl1pPr) )
3529 mpFS->startElementNS(XML_a, XML_lvl1pPr);
3530 WriteRunProperties(xFirstRunPropSet, false, XML_defRPr, true, rbOverridingCharHeight,
3531 rnCharHeight, GetScriptType(rRun->getString()), rXShapePropSet);
3532 mpFS->endElementNS(XML_a, XML_lvl1pPr);
3533 mpFS->endElementNS(XML_a, XML_lstStyle);
3534 }
3535}
3536
3538 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
3539 const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet)
3540{
3541 Reference< XEnumerationAccess > access( rParagraph, UNO_QUERY );
3542 if( !access.is() )
3543 return;
3544
3545 Reference< XEnumeration > enumeration( access->createEnumeration() );
3546 if( !enumeration.is() )
3547 return;
3548
3549 mpFS->startElementNS(XML_a, XML_p);
3550
3551 bool bPropertiesWritten = false;
3552 while( enumeration->hasMoreElements() )
3553 {
3555 Any any ( enumeration->nextElement() );
3556
3557 if (any >>= run)
3558 {
3559 if( !bPropertiesWritten )
3560 {
3561 float fFirstCharHeight = rnCharHeight / 1000.;
3562 Reference< XPropertySet > xFirstRunPropSet (run, UNO_QUERY);
3563 Reference< XPropertySetInfo > xFirstRunPropSetInfo = xFirstRunPropSet->getPropertySetInfo();
3564 if( xFirstRunPropSetInfo->hasPropertyByName("CharHeight") )
3565 {
3566 fFirstCharHeight = xFirstRunPropSet->getPropertyValue("CharHeight").get<float>();
3567 rnCharHeight = 100 * fFirstCharHeight;
3568 rbOverridingCharHeight = true;
3569 }
3570 WriteParagraphProperties(rParagraph, fFirstCharHeight, XML_pPr);
3571 bPropertiesWritten = true;
3572 }
3573 WriteRun( run, rbOverridingCharHeight, rnCharHeight, rXShapePropSet);
3574 }
3575 }
3576 Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
3577 sal_Int16 nDummy = -1;
3578 WriteRunProperties(rXPropSet, false, XML_endParaRPr, false, rbOverridingCharHeight,
3579 rnCharHeight, nDummy, rXShapePropSet);
3580
3581 mpFS->endElementNS( XML_a, XML_p );
3582}
3583
3584bool DrawingML::IsFontworkShape(const css::uno::Reference<css::beans::XPropertySet>& rXShapePropSet)
3585{
3586 bool bResult(false);
3587 if (rXShapePropSet.is())
3588 {
3589 Sequence<PropertyValue> aCustomShapeGeometryProps;
3590 if (GetProperty(rXShapePropSet, "CustomShapeGeometry"))
3591 {
3592 mAny >>= aCustomShapeGeometryProps;
3593 uno::Sequence<beans::PropertyValue> aTextPathSeq;
3594 for (const auto& rProp : std::as_const(aCustomShapeGeometryProps))
3595 {
3596 if (rProp.Name == "TextPath")
3597 {
3598 rProp.Value >>= aTextPathSeq;
3599 for (const auto& rTextPathItem : std::as_const(aTextPathSeq))
3600 {
3601 if (rTextPathItem.Name == "TextPath")
3602 {
3603 rTextPathItem.Value >>= bResult;
3604 break;
3605 }
3606 }
3607 break;
3608 }
3609 }
3610 }
3611 }
3612 return bResult;
3613}
3614
3615void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bool bText,
3616 sal_Int32 nXmlNamespace, bool bWritePropertiesAsLstStyles)
3617{
3618 // ToDo: Fontwork in DOCX
3619 uno::Reference<XText> xXText(rXIface, UNO_QUERY);
3620 if( !xXText.is() )
3621 return;
3622
3623 uno::Reference<drawing::XShape> xShape(rXIface, UNO_QUERY);
3624 uno::Reference<XPropertySet> rXPropSet(rXIface, UNO_QUERY);
3625
3626 constexpr const sal_Int32 constDefaultLeftRightInset = 254;
3627 constexpr const sal_Int32 constDefaultTopBottomInset = 127;
3628 sal_Int32 nLeft = constDefaultLeftRightInset;
3629 sal_Int32 nRight = constDefaultLeftRightInset;
3630 sal_Int32 nTop = constDefaultTopBottomInset;
3631 sal_Int32 nBottom = constDefaultTopBottomInset;
3632
3633 // top inset looks a bit different compared to ppt export
3634 // check if something related doesn't work as expected
3635 if (GetProperty(rXPropSet, "TextLeftDistance"))
3636 mAny >>= nLeft;
3637 if (GetProperty(rXPropSet, "TextRightDistance"))
3638 mAny >>= nRight;
3639 if (GetProperty(rXPropSet, "TextUpperDistance"))
3640 mAny >>= nTop;
3641 if (GetProperty(rXPropSet, "TextLowerDistance"))
3642 mAny >>= nBottom;
3643
3644 // Transform the text distance values so they are compatible with OOXML insets
3645 if (xShape.is())
3646 {
3647 sal_Int32 nTextHeight = xShape->getSize().Height; // Hmm, default
3648
3649 // CustomShape can have text area different from shape rectangle
3650 auto* pCustomShape
3651 = dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape));
3652 if (pCustomShape)
3653 {
3654 const EnhancedCustomShape2d aCustomShape2d(*pCustomShape);
3655 nTextHeight = aCustomShape2d.GetTextRect().getOpenHeight();
3657 nTextHeight = convertTwipToMm100(nTextHeight);
3658 }
3659
3660 if (nTop + nBottom >= nTextHeight)
3661 {
3662 // Effective bottom would be above effective top of text area. LO normalizes the
3663 // effective text area in such case implicitly for rendering. MS needs indents so that
3664 // the result is the normalized effective text area.
3665 std::swap(nTop, nBottom);
3666 nTop = nTextHeight - nTop;
3667 nBottom = nTextHeight - nBottom;
3668 }
3669 }
3670
3671 std::optional<OString> sWritingMode;
3672 if (GetProperty(rXPropSet, "TextWritingMode"))
3673 {
3675 if( ( mAny >>= eMode ) && eMode == WritingMode_TB_RL )
3676 sWritingMode = "eaVert";
3677 }
3678 if (GetProperty(rXPropSet, "WritingMode"))
3679 {
3680 sal_Int16 nWritingMode;
3681 if (mAny >>= nWritingMode)
3682 {
3683 if (nWritingMode == text::WritingMode2::TB_RL)
3684 sWritingMode = "eaVert";
3685 else if (nWritingMode == text::WritingMode2::BT_LR)
3686 sWritingMode = "vert270";
3687 else if (nWritingMode == text::WritingMode2::TB_RL90)
3688 sWritingMode = "vert";
3689 else if (nWritingMode == text::WritingMode2::TB_LR)
3690 sWritingMode = "mongolianVert";
3691 }
3692 }
3693
3694 // read values from CustomShapeGeometry
3696 uno::Sequence<beans::PropertyValue> aTextPathSeq;
3697 bool bScaleX(false);
3698 OUString sShapeType("non-primitive");
3699 OUString sMSWordPresetTextWarp;
3700 sal_Int32 nTextPreRotateAngle = 0; // degree
3701 std::optional<Degree100> nTextRotateAngleDeg100; // text area rotation
3702
3703 if (GetProperty(rXPropSet, "CustomShapeGeometry"))
3704 {
3706 if ( mAny >>= aProps )
3707 {
3708 for ( const auto& rProp : std::as_const(aProps) )
3709 {
3710 if (rProp.Name == "TextPreRotateAngle")
3711 rProp.Value >>= nTextPreRotateAngle;
3712 else if (rProp.Name == "AdjustmentValues")
3713 rProp.Value >>= aAdjustmentSeq;
3714 else if (rProp.Name == "TextRotateAngle")
3715 {
3716 double fTextRotateAngle = 0; // degree
3717 rProp.Value >>= fTextRotateAngle;
3718 nTextRotateAngleDeg100 = Degree100(std::lround(fTextRotateAngle * 100.0));
3719 }
3720 else if (rProp.Name == "Type")
3721 rProp.Value >>= sShapeType;
3722 else if (rProp.Name == "TextPath")
3723 {
3724 rProp.Value >>= aTextPathSeq;
3725 for (const auto& rTextPathItem : std::as_const(aTextPathSeq))
3726 {
3727 if (rTextPathItem.Name == "ScaleX")
3728 rTextPathItem.Value >>= bScaleX;
3729 }
3730 }
3731 else if (rProp.Name == "PresetTextWarp")
3732 rProp.Value >>= sMSWordPresetTextWarp;
3733 }
3734 }
3735 }
3736 else
3737 {
3738 if (mpTextExport)
3739 {
3740 if (xShape)
3741 {
3742 auto xTextFrame = mpTextExport->GetUnoTextFrame(xShape);
3743 if (xTextFrame)
3744 {
3745 uno::Reference<beans::XPropertySet> xPropSet(xTextFrame, uno::UNO_QUERY);
3746 auto aAny = xPropSet->getPropertyValue("WritingMode");
3747 sal_Int16 nWritingMode;
3748 if (aAny >>= nWritingMode)
3749 {
3750 switch (nWritingMode)
3751 {
3752 case WritingMode2::TB_RL:
3753 sWritingMode = "eaVert";
3754 break;
3755 case WritingMode2::BT_LR:
3756 sWritingMode = "vert270";
3757 break;
3758 case WritingMode2::TB_RL90:
3759 sWritingMode = "vert";
3760 break;
3761 case WritingMode2::TB_LR:
3762 sWritingMode = "mongolianVert";
3763 break;
3764 default:
3765 break;
3766 }
3767 }
3768 }
3769 }
3770 }
3771 }
3772
3773 // read InteropGrabBag if any
3774 std::optional<OUString> sHorzOverflow;
3775 std::optional<OUString> sVertOverflow;
3776 bool bUpright = false;
3777 std::optional<OString> isUpright;
3778 if (rXPropSet->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
3779 {
3780 uno::Sequence<beans::PropertyValue> aGrabBag;
3781 rXPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
3782 for (const auto& aProp : std::as_const(aGrabBag))
3783 {
3784 if (aProp.Name == "Upright")
3785 {
3786 aProp.Value >>= bUpright;
3787 isUpright = OString(bUpright ? "1" : "0");
3788 }
3789 else if (aProp.Name == "horzOverflow")
3790 {
3791 OUString sValue;
3792 aProp.Value >>= sValue;
3793 sHorzOverflow = sValue;
3794 }
3795 else if (aProp.Name == "vertOverflow")
3796 {
3797 OUString sValue;
3798 aProp.Value >>= sValue;
3799 sVertOverflow = sValue;
3800 }
3801 }
3802 }
3803
3804 bool bIsFontworkShape(IsFontworkShape(rXPropSet));
3805 OUString sPresetWarp(PresetGeometryTypeNames::GetMsoName(sShapeType));
3806 // ODF may have user defined TextPath, use "textPlain" as ersatz.
3807 if (sPresetWarp.isEmpty())
3808 sPresetWarp = bIsFontworkShape ? std::u16string_view(u"textPlain") : std::u16string_view(u"textNoShape");
3809
3810 bool bFromWordArt = !bScaleX
3811 && ( sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp"
3812 || sPresetWarp == "textButton" || sPresetWarp == "textCircle");
3813
3814 // Fontwork shapes in LO ignore insets in rendering, Word interprets them.
3815 if (GetDocumentType() == DOCUMENT_DOCX && bIsFontworkShape)
3816 {
3817 nLeft = 0;
3818 nRight = 0;
3819 nTop = 0;
3820 nBottom = 0;
3821 }
3822
3823 if (bUpright)
3824 {
3825 Degree100 nShapeRotateAngleDeg100(0_deg100);
3826 if (GetProperty(rXPropSet, "RotateAngle"))
3827 nShapeRotateAngleDeg100 = Degree100(mAny.get<sal_Int32>());
3828 // Depending on shape rotation, the import has made 90deg changes to properties
3829 // "TextPreRotateAngle" and "TextRotateAngle". Revert it.
3830 bool bWasAngleChanged
3831 = (nShapeRotateAngleDeg100 > 4500_deg100 && nShapeRotateAngleDeg100 <= 13500_deg100)
3832 || (nShapeRotateAngleDeg100 > 22500_deg100
3833 && nShapeRotateAngleDeg100 <= 31500_deg100);
3834 if (bWasAngleChanged)
3835 {
3836 nTextRotateAngleDeg100 = nTextRotateAngleDeg100.value_or(0_deg100) + 9000_deg100;
3837 nTextPreRotateAngle -= 90;
3838 }
3839 // If text is no longer upright, user has changed something. Do not write 'upright' then.
3840 // This try to detect the case assumes, that the text area rotation was 0 in the original
3841 // MS Office document. That is likely because MS Office has no UI to set it and the
3842 // predefined SmartArt shapes, which use it, do not use 'upright'.
3843 Degree100 nAngleSum = nShapeRotateAngleDeg100 + nTextRotateAngleDeg100.value_or(0_deg100);
3844 if (abs(NormAngle18000(nAngleSum)) < 100_deg100) // consider inaccuracy from rounding
3845 {
3846 nTextRotateAngleDeg100.reset(); // 'upright' does not overrule text area rotation.
3847 }
3848 else
3849 {
3850 // User changes. Keep current angles.
3851 isUpright.reset();
3852 if (bWasAngleChanged)
3853 {
3854 nTextPreRotateAngle += 90;
3855 nTextRotateAngleDeg100 = nTextRotateAngleDeg100.value_or(0_deg100) - 9000_deg100;
3856 }
3857 }
3858 }
3859
3860 // ToDo: Unsure about this. Need to investigate shapes from diagram import, especially diagrams
3861 // with vertical text directions.
3862 if (nTextPreRotateAngle != 0 && !sWritingMode)
3863 {
3864 if (nTextPreRotateAngle == -90 || nTextPreRotateAngle == 270)
3865 sWritingMode = "vert";
3866 else if (nTextPreRotateAngle == -270 || nTextPreRotateAngle == 90)
3867 sWritingMode = "vert270";
3868 else if (nTextPreRotateAngle == -180 || nTextPreRotateAngle == 180)
3869 {
3870#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12
3871#pragma GCC diagnostic push
3872#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
3873#endif
3874 nTextRotateAngleDeg100
3875 = NormAngle18000(nTextRotateAngleDeg100.value_or(0_deg100) + 18000_deg100);
3876#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12
3877#pragma GCC diagnostic pop
3878#endif
3879 // ToDo: Examine insets. They might need rotation too. Check diagrams (SmartArt).
3880 }
3881 else
3882 SAL_WARN("oox", "unsuitable value for TextPreRotateAngle:" << nTextPreRotateAngle);
3883 }
3884 else if (nTextPreRotateAngle != 0 && sWritingMode && sWritingMode.value() == "eaVert")
3885 {
3886 // ToDo: eaVert plus 270deg clockwise rotation has to be written with vert="horz"
3887 // plus attribute 'normalEastAsianFlow="1"' on the <wps:wsp> element.
3888 }
3889 // else nothing to do
3890
3891 // Our WritingMode introduces text pre rotation which includes padding, MSO vert does not include
3892 // padding. Therefore set padding so, that is looks the same in MSO as in LO.
3893 if (sWritingMode)
3894 {
3895 if (sWritingMode.value() == "vert" || sWritingMode.value() == "eaVert")
3896 {
3897 sal_Int32 nHelp = nLeft;
3898 nLeft = nBottom;
3899 nBottom = nRight;
3900 nRight = nTop;
3901 nTop = nHelp;
3902 }
3903 else if (sWritingMode.value() == "vert270")
3904 {
3905 sal_Int32 nHelp = nLeft;
3906 nLeft = nTop;
3907 nTop = nRight;
3908 nRight = nBottom;
3909 nBottom = nHelp;
3910 }
3911 else if (sWritingMode.value() == "mongolianVert")
3912 {
3913 // ToDo: Examine padding
3914 }
3915 }
3916
3917
3918 std::optional<OString> sTextRotateAngleMSUnit;
3919 if (nTextRotateAngleDeg100.has_value())
3920#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12
3921#pragma GCC diagnostic push
3922#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
3923#endif
3924 sTextRotateAngleMSUnit
3925 = oox::drawingml::calcRotationValue(nTextRotateAngleDeg100.value().get());
3926#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12
3927#pragma GCC diagnostic pop
3928#endif
3929
3930 // Prepare attributes 'anchor' and 'anchorCtr'
3931 // LibreOffice has 12 value sets, MS Office only 6. We map them so, that it reverses the
3932 // 6 mappings from import, and we assign the others approximately.
3933 TextVerticalAdjust eVerticalAlignment(TextVerticalAdjust_TOP);
3934 if (GetProperty(rXPropSet, "TextVerticalAdjust"))
3935 mAny >>= eVerticalAlignment;
3936 TextHorizontalAdjust eHorizontalAlignment(TextHorizontalAdjust_CENTER);
3937 if (GetProperty(rXPropSet, "TextHorizontalAdjust"))
3938 mAny >>= eHorizontalAlignment;
3939
3940 const char* sAnchor = nullptr;
3941 bool bAnchorCtr = false;
3942 if (sWritingMode.has_value()
3943 && (sWritingMode.value() == "eaVert" || sWritingMode.value() == "mongolianVert"))
3944 {
3945 bAnchorCtr = eVerticalAlignment == TextVerticalAdjust_CENTER
3946 || eVerticalAlignment == TextVerticalAdjust_BOTTOM
3947 || eVerticalAlignment == TextVerticalAdjust_BLOCK;
3948 switch (eHorizontalAlignment)
3949 {
3950 case TextHorizontalAdjust_CENTER:
3951 sAnchor = "ctr";
3952 break;
3953 case TextHorizontalAdjust_LEFT:
3954 sAnchor = sWritingMode.value() == "eaVert" ? "b" : "t";
3955 break;
3956 case TextHorizontalAdjust_RIGHT:
3957 default: // TextHorizontalAdjust_BLOCK, should not happen
3958 sAnchor = sWritingMode.value() == "eaVert" ? "t" : "b";
3959 break;
3960 }
3961 }
3962 else
3963 {
3964 bAnchorCtr = eHorizontalAlignment == TextHorizontalAdjust_CENTER
3965 || eHorizontalAlignment == TextHorizontalAdjust_RIGHT;
3966 sAnchor = GetTextVerticalAdjust(eVerticalAlignment);
3967 }
3968
3969 bool bHasWrap = false;
3970 bool bWrap = false;
3971 // Only custom shapes obey the TextWordWrap option, normal text always wraps.
3972 if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextWordWrap"))
3973 {
3974 mAny >>= bWrap;
3975 bHasWrap = true;
3976 }
3977
3978 if (bBodyPr)
3979 {
3980 const char* pWrap = (bHasWrap && !bWrap) || bIsFontworkShape ? "none" : nullptr;
3982 {
3983 // In case of DOCX, if we want to have the same effect as
3984 // TextShape's automatic word wrapping, then we need to set
3985 // wrapping to square.
3986 uno::Reference<lang::XServiceInfo> xServiceInfo(rXIface, uno::UNO_QUERY);
3987 if ((xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.drawing.TextShape"))
3988 || bIsFontworkShape)
3989 pWrap = "square";
3990 }
3991
3992 sal_Int16 nCols = 0;
3993 sal_Int32 nColSpacing = -1;
3994 if (GetProperty(rXPropSet, "TextColumns"))
3995 {
3996 if (css::uno::Reference<css::text::XTextColumns> xCols{ mAny, css::uno::UNO_QUERY })
3997 {
3998 nCols = xCols->getColumnCount();
3999 if (css::uno::Reference<css::beans::XPropertySet> xProps{ mAny,
4000 css::uno::UNO_QUERY })
4001 {
4002 if (GetProperty(xProps, "AutomaticDistance"))
4003 mAny >>= nColSpacing;
4004 }
4005 }
4006 }
4007
4008 if (!sVertOverflow && GetProperty(rXPropSet, "TextClipVerticalOverflow") && mAny.get<bool>())
4009 {
4010 sVertOverflow = "clip";
4011 }
4012
4013 mpFS->startElementNS( (nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr,
4014 XML_numCol, sax_fastparser::UseIf(OString::number(nCols), nCols > 0),
4015 XML_spcCol, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nColSpacing)), nCols > 0 && nColSpacing >= 0),
4016 XML_wrap, pWrap,
4017 XML_horzOverflow, sHorzOverflow,
4018 XML_vertOverflow, sVertOverflow,
4019 XML_fromWordArt, sax_fastparser::UseIf("1", bFromWordArt),
4020 XML_lIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), nLeft != constDefaultLeftRightInset),
4021 XML_rIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), nRight != constDefaultLeftRightInset),
4022 XML_tIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), nTop != constDefaultTopBottomInset),
4023 XML_bIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)), nBottom != constDefaultTopBottomInset),
4024 XML_anchor, sAnchor,
4025 XML_anchorCtr, sax_fastparser::UseIf("1", bAnchorCtr),
4026 XML_vert, sWritingMode,
4027 XML_upright, isUpright,
4028 XML_rot, sTextRotateAngleMSUnit);
4029
4030 if (bIsFontworkShape)
4031 {
4032 if (aAdjustmentSeq.hasElements())
4033 {
4034 mpFS->startElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp);
4035 mpFS->startElementNS(XML_a, XML_avLst);
4036 bool bHasTwoHandles(
4037 sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour"
4038 || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour"
4039 || sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1"
4040 || sPresetWarp == "textWave2" || sPresetWarp == "textWave4");
4041 for (sal_Int32 i = 0, nElems = aAdjustmentSeq.getLength(); i < nElems; ++i )
4042 {
4043 OString sName = "adj" + (bHasTwoHandles ? OString::number(i + 1) : OString());
4044 double fValue(0.0);
4045 if (aAdjustmentSeq[i].Value.getValueTypeClass() == TypeClass_DOUBLE)
4046 aAdjustmentSeq[i].Value >>= fValue;
4047 else
4048 {
4049 sal_Int32 nNumber(0);
4050 aAdjustmentSeq[i].Value >>= nNumber;
4051 fValue = static_cast<double>(nNumber);
4052 }
4053 // Convert from binary coordinate system with viewBox "0 0 21600 21600" and simple degree
4054 // to DrawingML with coordinate range 0..100000 and angle in 1/60000 degree.
4055 // Reverse to conversion in lcl_createPresetShape in drawingml/shape.cxx on import.
4056 if (sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp"
4057 || sPresetWarp == "textButton" || sPresetWarp == "textCircle"
4058 || ((i == 0)
4059 && (sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour"
4060 || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour")))
4061 {
4062 fValue *= 60000.0;
4063 if (fValue < 0)
4064 fValue += 21600000;
4065 }
4066 else if ((i == 1)
4067 && (sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1"
4068 || sPresetWarp == "textWave2" || sPresetWarp == "textWave4"))
4069 {
4070 fValue = fValue / 0.216 - 50000.0;
4071 }
4072 else if ((i == 1)
4073 && (sPresetWarp == "textArchDownPour"
4074 || sPresetWarp == "textArchUpPour"
4075 || sPresetWarp == "textButtonPour"
4076 || sPresetWarp == "textCirclePour"))
4077 {
4078 fValue /= 0.108;
4079 }
4080 else
4081 {
4082 fValue /= 0.216;
4083 }
4084 OString sFmla = "val " + OString::number(std::lround(fValue));
4085 mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
4086 // There exists faulty Favorite shapes with one handle but two adjustment values.
4087 if (!bHasTwoHandles)
4088 break;
4089 }
4090 mpFS->endElementNS(XML_a, XML_avLst);
4091 mpFS->endElementNS(XML_a, XML_prstTxWarp);
4092 }
4093 else
4094 {
4095 mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp);
4096 }
4097 }
4098 else if (GetDocumentType() == DOCUMENT_DOCX)
4099 {
4100 // interim solution for fdo#80897, roundtrip DOCX > LO > DOCX
4101 if (!sMSWordPresetTextWarp.isEmpty())
4102 mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sMSWordPresetTextWarp);
4103 }
4104
4106 {
4107 // tdf#112312: only custom shapes obey the TextAutoGrowHeight option
4108 bool bTextAutoGrowHeight = false;
4109 auto pSdrObjCustomShape = xShape.is() ? dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape)) : nullptr;
4110 if (pSdrObjCustomShape && GetProperty(rXPropSet, "TextAutoGrowHeight"))
4111 {
4112 mAny >>= bTextAutoGrowHeight;
4113 }
4114 mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
4115 }
4117 {
4118 TextFitToSizeType eFit = TextFitToSizeType_NONE;
4119 if (GetProperty(rXPropSet, "TextFitToSize"))
4120 mAny >>= eFit;
4121
4122 if (eFit == TextFitToSizeType_AUTOFIT)
4123 {
4124 const sal_Int32 MAX_SCALE_VAL = 100000;
4125 sal_Int32 nFontScale = MAX_SCALE_VAL;
4126 sal_Int32 nSpacingReduction = 0;
4127 SvxShapeText* pTextShape = dynamic_cast<SvxShapeText*>(rXIface.get());
4128 if (pTextShape)
4129 {
4130 SdrTextObj* pTextObject = DynCastSdrTextObj(pTextShape->GetSdrObject());
4131 if (pTextObject)
4132 {
4133 nFontScale = sal_Int32(pTextObject->GetFontScale() * 1000.0);
4134 nSpacingReduction = sal_Int32((100.0 - pTextObject->GetSpacingScale()) * 1000.0);
4135 }
4136 }
4137
4138 bool bExportFontScale = false;
4139 if (nFontScale < MAX_SCALE_VAL && nFontScale > 0)
4140 bExportFontScale = true;
4141
4142 bool bExportSpaceReduction = false;
4143 if (nSpacingReduction < MAX_SCALE_VAL && nSpacingReduction > 0)
4144 bExportSpaceReduction = true;
4145
4146 mpFS->singleElementNS(XML_a, XML_normAutofit,
4147 XML_fontScale, sax_fastparser::UseIf(OString::number(nFontScale), bExportFontScale),
4148 XML_lnSpcReduction, sax_fastparser::UseIf(OString::number(nSpacingReduction), bExportSpaceReduction));
4149 }
4150 else
4151 {
4152 // tdf#127030: Only custom shapes obey the TextAutoGrowHeight option.
4153 bool bTextAutoGrowHeight = false;
4154 if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextAutoGrowHeight"))
4155 mAny >>= bTextAutoGrowHeight;
4156 mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
4157 }
4158 }
4159
4160 Write3DEffects( rXPropSet, /*bIsText=*/true );
4161
4162 mpFS->endElementNS((nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr);
4163 }
4164
4165 Reference< XEnumerationAccess > access( xXText, UNO_QUERY );
4166 if( !access.is() || !bText )
4167 return;
4168
4169 Reference< XEnumeration > enumeration( access->createEnumeration() );
4170 if( !enumeration.is() )
4171 return;
4172
4173 SdrObject* pSdrObject = xShape.is() ? SdrObject::getSdrObjectFromXShape(xShape) : nullptr;
4174 const SdrTextObj* pTxtObj = DynCastSdrTextObj( pSdrObject );
4175 if (pTxtObj && mpTextExport)
4176 {
4177 std::vector<beans::PropertyValue> aOldCharFillPropVec;
4178 if (bIsFontworkShape)
4179 {
4180 // Users may have set the character fill properties for more convenient editing.
4181 // Save the properties before changing them for Fontwork export.
4182 FontworkHelpers::collectCharColorProps(xXText, aOldCharFillPropVec);
4183 // Word has properties for abc-transform in the run properties of the text of the shape.
4184 // Writer has the Fontwork properties as shape properties. Create the character fill
4185 // properties needed for export from the shape fill properties
4186 // and apply them to all runs.
4187 std::vector<beans::PropertyValue> aExportCharFillPropVec;
4188 FontworkHelpers::createCharFillPropsFromShape(rXPropSet, aExportCharFillPropVec);
4189 FontworkHelpers::applyPropsToRuns(aExportCharFillPropVec, xXText);
4190 // Import has converted some items from CharInteropGrabBag to fill and line
4191 // properties of the shape. For export we convert them back because users might have
4192 // changed them. And we create them in case we come from an odt document.
4193 std::vector<beans::PropertyValue> aUpdatePropVec;
4196 }
4197
4199
4200 /*
4201 #i13885#
4202 When the object is actively being edited, that text is not set into
4203 the objects normal text object, but lives in a separate object.
4204 */
4205 if (pTxtObj->IsTextEditActive())
4206 {
4207 pParaObj = pTxtObj->CreateEditOutlinerParaObject();
4208 }
4209 else if (pTxtObj->GetOutlinerParaObject())
4210 pParaObj = *pTxtObj->GetOutlinerParaObject();
4211
4212 if (pParaObj)
4213 {
4214 // this is reached only in case some text is attached to the shape
4215 mpTextExport->WriteOutliner(*pParaObj);
4216 }
4217
4218 if (bIsFontworkShape)
4219 FontworkHelpers::applyPropsToRuns(aOldCharFillPropVec, xXText);
4220 return;
4221 }
4222
4223 bool bOverridingCharHeight = false;
4224 sal_Int32 nCharHeight = -1;
4225 bool bFirstParagraph = true;
4226
4227 // tdf#144092 For shapes without text: Export run properties (into
4228 // endParaRPr) from the shape's propset instead of the paragraph's.
4229 if(xXText->getString().isEmpty() && enumeration->hasMoreElements())
4230 {
4231 Any aAny (enumeration->nextElement());
4232 Reference<XTextContent> xParagraph;
4233 if( aAny >>= xParagraph )
4234 {
4235 mpFS->startElementNS(XML_a, XML_p);
4236 WriteParagraphProperties(xParagraph, nCharHeight, XML_pPr);
4237 sal_Int16 nDummy = -1;
4238 WriteRunProperties(rXPropSet, false, XML_endParaRPr, false,
4239 bOverridingCharHeight, nCharHeight, nDummy, rXPropSet);
4240 mpFS->endElementNS(XML_a, XML_p);
4241 }
4242 return;
4243 }
4244
4245 while( enumeration->hasMoreElements() )
4246 {
4248 Any any ( enumeration->nextElement() );
4249
4250 if( any >>= paragraph)
4251 {
4252 if (bFirstParagraph && bWritePropertiesAsLstStyles)
4253 WriteLstStyles(paragraph, bOverridingCharHeight, nCharHeight, rXPropSet);
4254
4255 WriteParagraph(paragraph, bOverridingCharHeight, nCharHeight, rXPropSet);
4256 bFirstParagraph = false;
4257 }
4258 }
4259}
4261void DrawingML::WritePresetShape( const OString& pShape , std::vector< std::pair<sal_Int32,sal_Int32>> & rAvList )
4262{
4263 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
4264 if ( !rAvList.empty() )
4265 {
4266
4267 mpFS->startElementNS(XML_a, XML_avLst);
4268 for (auto const& elem : rAvList)
4269 {
4270 OString sName = "adj" + ( ( elem.first > 0 ) ? OString::number(elem.first) : OString() );
4271 OString sFmla = "val " + OString::number( elem.second );
4272
4273 mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
4274 }
4275 mpFS->endElementNS( XML_a, XML_avLst );
4276 }
4277 else
4278 mpFS->singleElementNS(XML_a, XML_avLst);
4279
4280 mpFS->endElementNS( XML_a, XML_prstGeom );
4281}
4283void DrawingML::WritePresetShape( const OString& pShape )
4284{
4285 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
4286 mpFS->singleElementNS(XML_a, XML_avLst);
4287 mpFS->endElementNS( XML_a, XML_prstGeom );
4288}
4290static std::map< OString, std::vector<OString> > lcl_getAdjNames()
4291{
4292 std::map< OString, std::vector<OString> > aRet;
4293
4294 OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/oox-drawingml-adj-names");
4295 rtl::Bootstrap::expandMacros(aPath);
4296 SvFileStream aStream(aPath, StreamMode::READ);
4297 if (aStream.GetError() != ERRCODE_NONE)
4298 SAL_WARN("oox.shape", "failed to open oox-drawingml-adj-names");
4299 OStringBuffer aLine;
4300 bool bNotDone = aStream.ReadLine(aLine);
4301 while (bNotDone)
4302 {
4303 sal_Int32 nIndex = 0;
4304 // Each line is in a "key\tvalue" format: read the key, the rest is the value.
4305 OString aKey( o3tl::getToken(aLine, 0, '\t', nIndex) );
4306 OString aValue( std::string_view(aLine).substr(nIndex) );
4307 aRet[aKey].push_back(aValue);
4308 bNotDone = aStream.ReadLine(aLine);
4309 }
4310 return aRet;
4311}
4312
4313void DrawingML::WritePresetShape( const OString& pShape, MSO_SPT eShapeType, bool bPredefinedHandlesUsed, const PropertyValue& rProp )
4314{
4315 static std::map< OString, std::vector<OString> > aAdjMap = lcl_getAdjNames();
4316 // If there are predefined adj names for this shape type, look them up now.
4317 std::vector<OString> aAdjustments;
4318 if (aAdjMap.find(pShape) != aAdjMap.end())
4319 aAdjustments = aAdjMap[pShape];
4320
4321 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
4322 mpFS->startElementNS(XML_a, XML_avLst);
4323
4324 Sequence< drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
4325 if ( ( rProp.Value >>= aAdjustmentSeq )
4326 && eShapeType != mso_sptActionButtonForwardNext // we have adjustments values for these type of shape, but MSO doesn't like them
4327 && eShapeType != mso_sptActionButtonBackPrevious // so they are now disabled
4328 && pShape != "rect" //some shape types are commented out in pCustomShapeTypeTranslationTable[] & are being defaulted to rect & rect does not have adjustment values/name.
4329 )
4330 {
4331 SAL_INFO("oox.shape", "adj seq len: " << aAdjustmentSeq.getLength());
4332 sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0;
4333 if ( bPredefinedHandlesUsed )
4334 EscherPropertyContainer::LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted );
4335
4336 sal_Int32 nValue, nLength = aAdjustmentSeq.getLength();
4337 // 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.
4338 // Sometimes there are more values than needed, so we ignore the excessive ones.
4339 if (aAdjustments.size() <= o3tl::make_unsigned(nLength))
4340 {
4341 for (sal_Int32 i = 0; i < static_cast<sal_Int32>(aAdjustments.size()); i++)
4342 {
4343 if( EscherPropertyContainer::GetAdjustmentValue( aAdjustmentSeq[ i ], i, nAdjustmentsWhichNeedsToBeConverted, nValue ) )
4344 {
4345 // If the document model doesn't have an adjustment name (e.g. shape was created from VML), then take it from the predefined list.
4346 OString aAdjName = aAdjustmentSeq[i].Name.isEmpty()
4347 ? aAdjustments[i]
4348 : aAdjustmentSeq[i].Name.toUtf8();
4349
4350 mpFS->singleElementNS( XML_a, XML_gd,
4351 XML_name, aAdjName,
4352 XML_fmla, "val " + OString::number(nValue));
4353 }
4354 }
4355 }
4356 }
4357
4358 mpFS->endElementNS( XML_a, XML_avLst );
4359 mpFS->endElementNS( XML_a, XML_prstGeom );
4360}
4361
4362namespace // helpers for DrawingML::WriteCustomGeometry
4363{
4364sal_Int32
4365FindNextCommandEndSubpath(const sal_Int32 nStart,
4366 const uno::Sequence<drawing::EnhancedCustomShapeSegment>& rSegments)
4367{
4368 sal_Int32 i = nStart < 0 ? 0 : nStart;
4369 while (i < rSegments.getLength() && rSegments[i].Command != ENDSUBPATH)
4370 i++;
4371 return i;
4372}
4373
4374bool HasCommandInSubPath(const sal_Int16 nCommand, const sal_Int32 nFirst, const sal_Int32 nLast,
4375 const uno::Sequence<drawing::EnhancedCustomShapeSegment>& rSegments)
4376{
4377 for (sal_Int32 i = nFirst < 0 ? 0 : nFirst; i <= nLast && i < rSegments.getLength(); i++)
4378 {
4379 if (rSegments[i].Command == nCommand)
4380 return true;
4381 }
4382 return false;
4383}
4384
4385// Ellipse is given by radii fwR and fhR and center (fCx|fCy). The ray from center through point RayP
4386// intersects the ellipse in point S and this point S has angle fAngleDeg in degrees.
4387void getEllipsePointAndAngleFromRayPoint(double& rfAngleDeg, double& rfSx, double& rfSy,
4388 const double fWR, const double fHR, const double fCx,
4389 const double fCy, const double fRayPx, const double fRayPy)
4390{
4392 {
4393 rfSx = fCx; // needed for getting new 'current point'
4394 rfSy = fCy;
4395 }
4396 else
4397 {
4398 // center ellipse at origin, stretch in y-direction to circle, flip to Math orientation
4399 // and get angle
4400 double fCircleMathAngle = atan2(-fWR / fHR * (fRayPy - fCy), fRayPx - fCx);
4401 // use angle for intersection point on circle and stretch back to ellipse
4402 double fPointMathEllipse_x = fWR * cos(fCircleMathAngle);
4403 double fPointMathEllipse_y = fHR * sin(fCircleMathAngle);
4404 // get angle of intersection point on ellipse
4405 double fEllipseMathAngle = atan2(fPointMathEllipse_y, fPointMathEllipse_x);
4406 // convert from Math to View orientation and shift ellipse back from origin
4407 rfAngleDeg = -basegfx::rad2deg(fEllipseMathAngle);
4408 rfSx = fPointMathEllipse_x + fCx;
4409 rfSy = -fPointMathEllipse_y + fCy;
4410 }
4411}
4412
4413void getEllipsePointFromViewAngle(double& rfSx, double& rfSy, const double fWR, const double fHR,
4414 const double fCx, const double fCy, const double fViewAngleDeg)
4415{
4417 {
4418 rfSx = fCx; // needed for getting new 'current point'
4419 rfSy = fCy;
4420 }
4421 else
4422 {
4423 double fX = cos(basegfx::deg2rad(fViewAngleDeg)) / fWR;
4424 double fY = sin(basegfx::deg2rad(fViewAngleDeg)) / fHR;
4425 double fRadius = 1.0 / std::hypot(fX, fY);
4426 rfSx = fCx + fRadius * cos(basegfx::deg2rad(fViewAngleDeg));
4427 rfSy = fCy + fRadius * sin(basegfx::deg2rad(fViewAngleDeg));
4428 }
4429}
4430
4431sal_Int32 GetCustomGeometryPointValue(const css::drawing::EnhancedCustomShapeParameter& rParam,
4432 const EnhancedCustomShape2d& rCustomShape2d,
4433 const bool bReplaceGeoWidth, const bool bReplaceGeoHeight)
4434{
4435 double fValue = 0.0;
4436 rCustomShape2d.GetParameter(fValue, rParam, bReplaceGeoWidth, bReplaceGeoHeight);
4437 sal_Int32 nValue(std::lround(fValue));
4438
4439 return nValue;
4440}
4441
4442struct TextAreaRect
4444 OString left;
4445 OString top;
4446 OString right;
4447 OString bottom;
4448};
4449
4450struct Guide
4452 OString sName;
4453 OString sFormula;
4454};
4455
4456void prepareTextArea(const EnhancedCustomShape2d& rEnhancedCustomShape2d,
4457 std::vector<Guide>& rGuideList, TextAreaRect& rTextAreaRect)
4458{
4459 tools::Rectangle aTextAreaLO(rEnhancedCustomShape2d.GetTextRect());
4460 tools::Rectangle aLogicRectLO(rEnhancedCustomShape2d.GetLogicRect());
4461 if (aTextAreaLO == aLogicRectLO)
4462 {
4463 rTextAreaRect.left = "l";
4464 rTextAreaRect.top = "t";
4465 rTextAreaRect.right = "r";
4466 rTextAreaRect.bottom = "b";
4467 return;
4468 }
4469 // Flip aTextAreaLO if shape is flipped
4470 if (rEnhancedCustomShape2d.IsFlipHorz())
4471 aTextAreaLO.Move((aLogicRectLO.Center().X() - aTextAreaLO.Center().X()) * 2, 0);
4472 if (rEnhancedCustomShape2d.IsFlipVert())
4473 aTextAreaLO.Move(0, (aLogicRectLO.Center().Y() - aTextAreaLO.Center().Y()) * 2);
4474
4475 Guide aGuide;
4476 // horizontal
4477 const sal_Int32 nWidth = aLogicRectLO.Right() - aLogicRectLO.Left();
4478 const OString sWidth = OString::number(oox::drawingml::convertHmmToEmu(nWidth));
4479
4480 // left
4481 aGuide.sName = "textAreaLeft";
4482 sal_Int32 nHelp = aTextAreaLO.Left() - aLogicRectLO.Left();
4483 const OString sLeft = OString::number(oox::drawingml::convertHmmToEmu(nHelp));
4484 aGuide.sFormula = "*/ " + sLeft + " w " + sWidth;
4485 rTextAreaRect.left = aGuide.sName;
4486 rGuideList.push_back(aGuide);
4487
4488 // right
4489 aGuide.sName = "textAreaRight";
4490 nHelp = aTextAreaLO.Right() - aLogicRectLO.Left();
4491 const OString sRight = OString::number(oox::drawingml::convertHmmToEmu(nHelp));
4492 aGuide.sFormula = "*/ " + sRight + " w " + sWidth;
4493 rTextAreaRect.right = aGuide.sName;
4494 rGuideList.push_back(aGuide);
4495
4496 // vertical
4497 const sal_Int32 nHeight = aLogicRectLO.Bottom() - aLogicRectLO.Top();
4498 const OString sHeight = OString::number(oox::drawingml::convertHmmToEmu(nHeight));
4499
4500 // top
4501 aGuide.sName = "textAreaTop";
4502 nHelp = aTextAreaLO.Top() - aLogicRectLO.Top();
4503 const OString sTop = OString::number(oox::drawingml::convertHmmToEmu(nHelp));
4504 aGuide.sFormula = "*/ " + sTop + " h " + sHeight;
4505 rTextAreaRect.top = aGuide.sName;
4506 rGuideList.push_back(aGuide);
4507
4508 // bottom
4509 aGuide.sName = "textAreaBottom";
4510 nHelp = aTextAreaLO.Bottom() - aLogicRectLO.Top();
4511 const OString sBottom = OString::number(oox::drawingml::convertHmmToEmu(nHelp));
4512 aGuide.sFormula = "*/ " + sBottom + " h " + sHeight;
4513 rTextAreaRect.bottom = aGuide.sName;
4514 rGuideList.push_back(aGuide);
4515
4516 return;
4517}
4518}
4521 const Reference< XShape >& rXShape,
4522 const SdrObjCustomShape& rSdrObjCustomShape)
4523{
4524 uno::Reference< beans::XPropertySet > aXPropSet;
4525 uno::Any aAny( rXShape->queryInterface(cppu::UnoType<beans::XPropertySet>::get()));
4526
4527 if ( ! (aAny >>= aXPropSet) )
4528 return false;
4529
4530 try
4531 {
4532 aAny = aXPropSet->getPropertyValue( "CustomShapeGeometry" );
4533 if ( !aAny.hasValue() )
4534 return false;
4535 }
4536 catch( const ::uno::Exception& )
4537 {
4538 return false;
4539 }
4540
4541 auto pGeometrySeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(aAny);
4542 if (!pGeometrySeq)
4543 return false;
4544
4545 auto pPathProp = std::find_if(std::cbegin(*pGeometrySeq), std::cend(*pGeometrySeq),
4546 [](const PropertyValue& rProp) { return rProp.Name == "Path"; });
4547 if (pPathProp == std::cend(*pGeometrySeq))
4548 return false;
4549
4550 uno::Sequence<beans::PropertyValue> aPathProp;
4551 pPathProp->Value >>= aPathProp;
4552
4553 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs;
4554 uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
4555 uno::Sequence<awt::Size> aPathSize;
4556 bool bReplaceGeoWidth = false;
4557 bool bReplaceGeoHeight = false;
4558 for (const beans::PropertyValue& rPathProp : std::as_const(aPathProp))
4559 {
4560 if (rPathProp.Name == "Coordinates")
4561 rPathProp.Value >>= aPairs;
4562 else if (rPathProp.Name == "Segments")
4563 rPathProp.Value >>= aSegments;
4564 else if (rPathProp.Name == "SubViewSize")
4565 rPathProp.Value >>= aPathSize;
4566 else if (rPathProp.Name == "StretchX")
4567 bReplaceGeoWidth = true;
4568 else if (rPathProp.Name == "StretchY")
4569 bReplaceGeoHeight = true;
4570 }
4571
4572 if ( !aPairs.hasElements() )
4573 return false;
4574
4575 if ( !aSegments.hasElements() )
4576 {
4577 aSegments = uno::Sequence<drawing::EnhancedCustomShapeSegment>
4578 {
4579 { MOVETO, 1 },
4580 { LINETO,
4581 static_cast<sal_Int16>(std::min( aPairs.getLength() - 1, sal_Int32(32767) )) },
4582 { CLOSESUBPATH, 0 },
4583 { ENDSUBPATH, 0 }
4584 };
4585 };
4586
4587 int nExpectedPairCount = std::accumulate(std::cbegin(aSegments), std::cend(aSegments), 0,
4588 [](const int nSum, const drawing::EnhancedCustomShapeSegment& rSegment) { return nSum + rSegment.Count; });
4589
4590 if ( nExpectedPairCount > aPairs.getLength() )
4591 {
4592 SAL_WARN("oox.shape", "Segments need " << nExpectedPairCount << " coordinates, but Coordinates have only " << aPairs.getLength() << " pairs.");
4593 return false;
4594 }
4595
4596 // A EnhancedCustomShape2d caches the equation results. Therefore we use only one of it for the
4597 // entire method.
4598 const EnhancedCustomShape2d aCustomShape2d(const_cast<SdrObjCustomShape&>(rSdrObjCustomShape));
4599
4600 TextAreaRect aTextAreaRect;
4601 std::vector<Guide> aGuideList; // for now only for <a:rect>
4602 prepareTextArea(aCustomShape2d, aGuideList, aTextAreaRect);
4603 mpFS->startElementNS(XML_a, XML_custGeom);
4604 mpFS->singleElementNS(XML_a, XML_avLst);
4605 if (aGuideList.empty())
4606 {
4607 mpFS->singleElementNS(XML_a, XML_gdLst);
4608 }
4609 else
4610 {
4611 mpFS->startElementNS(XML_a, XML_gdLst);
4612 for (auto const& elem : aGuideList)
4613 {
4614 mpFS->singleElementNS(XML_a, XML_gd, XML_name, elem.sName, XML_fmla, elem.sFormula);
4615 }
4616 mpFS->endElementNS(XML_a, XML_gdLst);
4617 }
4618 mpFS->singleElementNS(XML_a, XML_ahLst);
4619 mpFS->singleElementNS(XML_a, XML_rect, XML_l, aTextAreaRect.left, XML_t, aTextAreaRect.top,
4620 XML_r, aTextAreaRect.right, XML_b, aTextAreaRect.bottom);
4621 mpFS->startElementNS(XML_a, XML_pathLst);
4622
4623 // Prepare width and height for <a:path>
4624 bool bUseGlobalViewBox(false);
4625
4626 // nViewBoxWidth must be integer otherwise ReplaceGeoWidth in aCustomShape2d.GetParameter() is not
4627 // triggered; same for height.
4628 sal_Int32 nViewBoxWidth(0);
4629 sal_Int32 nViewBoxHeight(0);
4630 if (!aPathSize.hasElements())
4631 {
4632 bUseGlobalViewBox = true;
4633 // If draw:viewBox is missing in draw:enhancedGeometry, then import sets
4634 // viewBox="0 0 21600 21600". Missing ViewBox can only occur, if user has manipulated
4635 // current file via macro. Author of macro has to fix it.
4636 auto pProp = std::find_if(
4637 std::cbegin(*pGeometrySeq), std::cend(*pGeometrySeq),
4638 [](const beans::PropertyValue& rGeomProp) { return rGeomProp.Name == "ViewBox"; });
4639 if (pProp != std::cend(*pGeometrySeq))
4640 {
4641 css::awt::Rectangle aViewBox;
4642 if (pProp->Value >>= aViewBox)
4643 {
4644 nViewBoxWidth = aViewBox.Width;
4645 nViewBoxHeight = aViewBox.Height;
4646 css::drawing::EnhancedCustomShapeParameter aECSP;
4647 aECSP.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
4648 aECSP.Value <<= nViewBoxWidth;
4649 double fRetValue;
4650 aCustomShape2d.GetParameter(fRetValue, aECSP, true, false);
4651 nViewBoxWidth = basegfx::fround(fRetValue);
4652 aECSP.Value <<= nViewBoxHeight;
4653 aCustomShape2d.GetParameter(fRetValue, aECSP, false, true);
4654 nViewBoxHeight = basegfx::fround(fRetValue);
4655 }
4656 }
4657 // Import from oox or documents, which are imported from oox and saved to strict ODF, might
4658 // have no subViewSize but viewBox="0 0 0 0". We need to generate width and height in those
4659 // cases. Even if that is fixed, we need the substitute for old documents.
4660 if ((nViewBoxWidth == 0 && nViewBoxHeight == 0) || pProp == std::cend(*pGeometrySeq))
4661 {
4662 // Generate a substitute based on point coordinates
4663 sal_Int32 nXMin(0);
4664 aPairs[0].First.Value >>= nXMin;
4665 sal_Int32 nXMax = nXMin;
4666 sal_Int32 nYMin(0);
4667 aPairs[0].Second.Value >>= nYMin;
4668 sal_Int32 nYMax = nYMin;
4669
4670 for (const auto& rPair : std::as_const(aPairs))
4671 {
4672 sal_Int32 nX = GetCustomGeometryPointValue(rPair.First, aCustomShape2d,
4673 bReplaceGeoWidth, false);
4674 sal_Int32 nY = GetCustomGeometryPointValue(rPair.Second, aCustomShape2d, false,
4675 bReplaceGeoHeight);
4676 if (nX < nXMin)
4677 nXMin = nX;
4678 if (nY < nYMin)
4679 nYMin = nY;
4680 if (nX > nXMax)
4681 nXMax = nX;
4682 if (nY > nYMax)
4683 nYMax = nY;
4684 }
4685 nViewBoxWidth = std::max(nXMax, nXMax - nXMin);
4686 nViewBoxHeight = std::max(nYMax, nYMax - nYMin);
4687 }
4688 // ToDo: Other values of left,top than 0,0 are not considered yet. Such would require a
4689 // shift of the resulting path coordinates.
4690 }
4691
4692 // Iterate over subpaths
4693 sal_Int32 nPairIndex = 0; // index over "Coordinates"
4694 sal_Int32 nPathSizeIndex = 0; // index over "SubViewSize"
4695 sal_Int32 nSubpathStartIndex(0); // index over "Segments"
4696 sal_Int32 nSubPathIndex(0); // serial number of current subpath
4697 do
4698 {
4699 bool bOK(true); // catch faulty paths were commands do not correspond to points
4700 // get index of next command ENDSUBPATH; if such doesn't exist use index behind last segment
4701 sal_Int32 nNextNcommandIndex = FindNextCommandEndSubpath(nSubpathStartIndex, aSegments);
4702
4703 // Prepare attributes for a:path start element
4704 // NOFILL or one of the LIGHTEN commands
4705 std::optional<OString> sFill;
4706 if (HasCommandInSubPath(NOFILL, nSubpathStartIndex, nNextNcommandIndex - 1, aSegments))
4707 sFill = "none";
4708 else if (HasCommandInSubPath(DARKEN, nSubpathStartIndex, nNextNcommandIndex - 1, aSegments))
4709 sFill = "darken";
4710 else if (HasCommandInSubPath(DARKENLESS, nSubpathStartIndex, nNextNcommandIndex - 1,
4711 aSegments))
4712 sFill = "darkenLess";
4713 else if (HasCommandInSubPath(LIGHTEN, nSubpathStartIndex, nNextNcommandIndex - 1,
4714 aSegments))
4715 sFill = "lighten";
4716 else if (HasCommandInSubPath(LIGHTENLESS, nSubpathStartIndex, nNextNcommandIndex - 1,
4717 aSegments))
4718 sFill = "lightenLess";
4719 else
4720 {
4721 // shading info might be in object type, e.g. "Octagon Bevel".
4722 sal_Int32 nLuminanceChange(aCustomShape2d.GetLuminanceChange(nSubPathIndex));
4723 if (nLuminanceChange <= -40)
4724 sFill = "darken";
4725 else if (nLuminanceChange <= -10)
4726 sFill = "darkenLess";
4727 else if (nLuminanceChange >= 40)
4728 sFill = "lighten";
4729 else if (nLuminanceChange >= 10)
4730 sFill = "lightenLess";
4731 }
4732 // NOSTROKE
4733 std::optional<OString> sStroke;
4734 if (HasCommandInSubPath(NOSTROKE, nSubpathStartIndex, nNextNcommandIndex - 1, aSegments))
4735 sStroke = "0";
4736
4737 // Write a:path start element
4738 mpFS->startElementNS(
4739 XML_a, XML_path, XML_fill, sFill, XML_stroke, sStroke, XML_w,
4740 OString::number(bUseGlobalViewBox ? nViewBoxWidth : aPathSize[nPathSizeIndex].Width),
4741 XML_h,
4742 OString::number(bUseGlobalViewBox ? nViewBoxHeight : aPathSize[nPathSizeIndex].Height));
4743
4744 // Arcs drawn by commands ELLIPTICALQUADRANTX and ELLIPTICALQUADRANTY depend on the position
4745 // of the target point in regard to the current point. Therefore we need to track the
4746 // current point. A current point is not defined in the beginning.
4747 double fCurrentX(0.0);
4748 double fCurrentY(0.0);
4749 bool bCurrentValid(false);
4750 // Actually write the subpath
4751 for (sal_Int32 nSegmentIndex = nSubpathStartIndex; nSegmentIndex < nNextNcommandIndex;
4752 ++nSegmentIndex)
4753 {
4754 const auto& rSegment(aSegments[nSegmentIndex]);
4755 if (rSegment.Command == CLOSESUBPATH)
4756 {
4757 mpFS->singleElementNS(XML_a, XML_close); // command Z has no parameter
4758 // ODF 1.4 specifies, that the start of the subpath becomes the current point.
4759 // But that is not implemented yet. Currently LO keeps the last current point.
4760 }
4761 for (sal_Int32 k = 0; k < rSegment.Count && bOK; ++k)
4762 {
4763 bOK = WriteCustomGeometrySegment(rSegment.Command, k, aPairs, nPairIndex, fCurrentX,
4764 fCurrentY, bCurrentValid, aCustomShape2d,
4765 bReplaceGeoWidth, bReplaceGeoHeight);
4766 }
4767 } // end loop over all commands of subpath
4768 // finish this subpath in any case
4769 mpFS->endElementNS(XML_a, XML_path);
4770
4771 if (!bOK)
4772 break; // exit loop if not enough values in aPairs
4773
4774 // step forward to next subpath
4775 nSubpathStartIndex = nNextNcommandIndex + 1;
4776 nPathSizeIndex++;
4777 nSubPathIndex++;
4778 } while (nSubpathStartIndex < aSegments.getLength());
4779
4780 mpFS->endElementNS(XML_a, XML_pathLst);
4781 mpFS->endElementNS(XML_a, XML_custGeom);
4782 return true; // We have written custGeom even if path is poorly structured.
4783}
4786 const sal_Int16 eCommand, const sal_Int32 nCount,
4787 const uno::Sequence<css::drawing::EnhancedCustomShapeParameterPair>& rPairs,
4788 sal_Int32& rnPairIndex, double& rfCurrentX, double& rfCurrentY, bool& rbCurrentValid,
4789 const EnhancedCustomShape2d& rCustomShape2d, const bool bReplaceGeoWidth,
4790 const bool bReplaceGeoHeight)
4791{
4792 switch (eCommand)
4793 {
4794 case MOVETO:
4795 {
4796 if (rnPairIndex >= rPairs.getLength())
4797 return false;
4798
4799 mpFS->startElementNS(XML_a, XML_moveTo);
4800 WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d, bReplaceGeoWidth,
4801 bReplaceGeoHeight);
4802 mpFS->endElementNS(XML_a, XML_moveTo);
4803 rCustomShape2d.GetParameter(rfCurrentX, rPairs[rnPairIndex].First, bReplaceGeoWidth,
4804 false);
4805 rCustomShape2d.GetParameter(rfCurrentY, rPairs[rnPairIndex].Second, false,
4806 bReplaceGeoHeight);
4807 rbCurrentValid = true;
4808 rnPairIndex++;
4809 break;
4810 }
4811 case LINETO:
4812 {
4813 if (rnPairIndex >= rPairs.getLength())
4814 return false;
4815 // LINETO without valid current point is a faulty path. LO is tolerant and makes a
4816 // moveTo instead. Do the same on export. MS OFFICE requires a current point for lnTo,
4817 // otherwise it shows nothing of the shape.
4818 if (rbCurrentValid)
4819 {
4820 mpFS->startElementNS(XML_a, XML_lnTo);
4821 WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d, bReplaceGeoWidth,
4822 bReplaceGeoHeight);
4823 mpFS->endElementNS(XML_a, XML_lnTo);
4824 }
4825 else
4826 {
4827 mpFS->startElementNS(XML_a, XML_moveTo);
4828 WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d, bReplaceGeoWidth,
4829 bReplaceGeoHeight);
4830 mpFS->endElementNS(XML_a, XML_moveTo);
4831 }
4832 rCustomShape2d.GetParameter(rfCurrentX, rPairs[rnPairIndex].First, bReplaceGeoWidth,
4833 false);
4834 rCustomShape2d.GetParameter(rfCurrentY, rPairs[rnPairIndex].Second, false,
4835 bReplaceGeoHeight);
4836 rbCurrentValid = true;
4837 rnPairIndex++;
4838 break;
4839 }
4840 case CURVETO:
4841 {
4842 if (rnPairIndex + 2 >= rPairs.getLength())
4843 return false;
4844
4845 mpFS->startElementNS(XML_a, XML_cubicBezTo);
4846 for (sal_uInt8 i = 0; i <= 2; ++i)
4847 {
4848 WriteCustomGeometryPoint(rPairs[rnPairIndex + i], rCustomShape2d, bReplaceGeoWidth,
4849 bReplaceGeoHeight);
4850 }
4851 mpFS->endElementNS(XML_a, XML_cubicBezTo);
4852 rCustomShape2d.GetParameter(rfCurrentX, rPairs[rnPairIndex + 2].First, bReplaceGeoWidth,
4853 false);
4854 rCustomShape2d.GetParameter(rfCurrentY, rPairs[rnPairIndex + 2].Second, false,
4855 bReplaceGeoHeight);
4856 rbCurrentValid = true;
4857 rnPairIndex += 3;
4858 break;
4859 }
4860 case ANGLEELLIPSETO:
4861 case ANGLEELLIPSE:
4862 {
4863 if (rnPairIndex + 2 >= rPairs.getLength())
4864 return false;
4865
4866 // Read parameters
4867 double fCx = 0.0;
4868 rCustomShape2d.GetParameter(fCx, rPairs[rnPairIndex].First, bReplaceGeoWidth, false);
4869 double fCy = 0.0;
4870 rCustomShape2d.GetParameter(fCy, rPairs[rnPairIndex].Second, false, bReplaceGeoHeight);
4871 double fWR = 0.0;
4872 rCustomShape2d.GetParameter(fWR, rPairs[rnPairIndex + 1].First, false, false);
4873 double fHR = 0.0;
4874 rCustomShape2d.GetParameter(fHR, rPairs[rnPairIndex + 1].Second, false, false);
4875 double fStartAngle = 0.0;
4876 rCustomShape2d.GetParameter(fStartAngle, rPairs[rnPairIndex + 2].First, false, false);
4877 double fEndAngle = 0.0;
4878 rCustomShape2d.GetParameter(fEndAngle, rPairs[rnPairIndex + 2].Second, false, false);
4879
4880 // Prepare start and swing angle
4881 sal_Int32 nStartAng(std::lround(fStartAngle * 60000));
4882 sal_Int32 nSwingAng = 0;
4883 if (basegfx::fTools::equalZero(fStartAngle)
4884 && basegfx::fTools::equalZero(fEndAngle - 360.0))
4885 nSwingAng = 360 * 60000; // special case full circle
4886 else
4887 {
4888 nSwingAng = std::lround((fEndAngle - fStartAngle) * 60000);
4889 if (nSwingAng < 0)
4890 nSwingAng += 360 * 60000;
4891 }
4892
4893 // calculate start point on ellipse
4894 double fSx = 0.0;
4895 double fSy = 0.0;
4896 getEllipsePointFromViewAngle(fSx, fSy, fWR, fHR, fCx, fCy, fStartAngle);
4897
4898 // write markup for going to start point
4899 // lnTo requires a valid current point
4900 if (eCommand == ANGLEELLIPSETO && rbCurrentValid)
4901 {
4902 mpFS->startElementNS(XML_a, XML_lnTo);
4903 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(std::lround(fSx)),
4904 XML_y, OString::number(std::lround(fSy)));
4905 mpFS->endElementNS(XML_a, XML_lnTo);
4906 }
4907 else
4908 {
4909 mpFS->startElementNS(XML_a, XML_moveTo);
4910 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(std::lround(fSx)),
4911 XML_y, OString::number(std::lround(fSy)));
4912 mpFS->endElementNS(XML_a, XML_moveTo);
4913 }
4914 // write markup for arcTo
4916 mpFS->singleElement(
4917 FSNS(XML_a, XML_arcTo), XML_wR, OString::number(std::lround(fWR)), XML_hR,
4918 OString::number(std::lround(fHR)), XML_stAng, OString::number(nStartAng),
4919 XML_swAng, OString::number(nSwingAng));
4920
4921 getEllipsePointFromViewAngle(rfCurrentX, rfCurrentY, fWR, fHR, fCx, fCy, fEndAngle);
4922 rbCurrentValid = true;
4923 rnPairIndex += 3;
4924 break;
4925 }
4926 case ARCTO:
4927 case ARC:
4928 case CLOCKWISEARCTO:
4929 case CLOCKWISEARC:
4930 {
4931 if (rnPairIndex + 3 >= rPairs.getLength())
4932 return false;
4933
4934 // read parameters
4935 double fX1 = 0.0;
4936 rCustomShape2d.GetParameter(fX1, rPairs[rnPairIndex].First, bReplaceGeoWidth, false);
4937 double fY1 = 0.0;
4938 rCustomShape2d.GetParameter(fY1, rPairs[rnPairIndex].Second, false, bReplaceGeoHeight);
4939 double fX2 = 0.0;
4940 rCustomShape2d.GetParameter(fX2, rPairs[rnPairIndex + 1].First, bReplaceGeoWidth,
4941 false);
4942 double fY2 = 0.0;
4943 rCustomShape2d.GetParameter(fY2, rPairs[rnPairIndex + 1].Second, false,
4944 bReplaceGeoHeight);
4945 double fX3 = 0.0;
4946 rCustomShape2d.GetParameter(fX3, rPairs[rnPairIndex + 2].First, bReplaceGeoWidth,
4947 false);
4948 double fY3 = 0.0;
4949 rCustomShape2d.GetParameter(fY3, rPairs[rnPairIndex + 2].Second, false,
4950 bReplaceGeoHeight);
4951 double fX4 = 0.0;
4952 rCustomShape2d.GetParameter(fX4, rPairs[rnPairIndex + 3].First, bReplaceGeoWidth,
4953 false);
4954 double fY4 = 0.0;
4955 rCustomShape2d.GetParameter(fY4, rPairs[rnPairIndex + 3].Second, false,
4956 bReplaceGeoHeight);
4957 // calculate ellipse parameter
4958 const double fWR = (fX2 - fX1) / 2.0;
4959 const double fHR = (fY2 - fY1) / 2.0;
4960 const double fCx = (fX1 + fX2) / 2.0;
4961 const double fCy = (fY1 + fY2) / 2.0;
4962 // calculate start angle
4963 double fStartAngle = 0.0;
4964 double fPx = 0.0;
4965 double fPy = 0.0;
4966 getEllipsePointAndAngleFromRayPoint(fStartAngle, fPx, fPy, fWR, fHR, fCx, fCy, fX3,
4967 fY3);
4968 // markup for going to start point
4969 // lnTo requires a valid current point.
4970 if ((eCommand == ARCTO || eCommand == CLOCKWISEARCTO) && rbCurrentValid)
4971 {
4972 mpFS->startElementNS(XML_a, XML_lnTo);
4973 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(std::lround(fPx)),
4974 XML_y, OString::number(std::lround(fPy)));
4975 mpFS->endElementNS(XML_a, XML_lnTo);
4976 }
4977 else
4978 {
4979 mpFS->startElementNS(XML_a, XML_moveTo);
4980 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(std::lround(fPx)),
4981 XML_y, OString::number(std::lround(fPy)));
4982 mpFS->endElementNS(XML_a, XML_moveTo);
4983 }
4984 // calculate swing angle
4985 double fEndAngle = 0.0;
4986 getEllipsePointAndAngleFromRayPoint(fEndAngle, fPx, fPy, fWR, fHR, fCx, fCy, fX4, fY4);
4987 double fSwingAngle(fEndAngle - fStartAngle);
4988 const bool bIsClockwise(eCommand == CLOCKWISEARCTO || eCommand == CLOCKWISEARC);
4989 if (bIsClockwise && fSwingAngle < 0)
4990 fSwingAngle += 360.0;
4991 else if (!bIsClockwise && fSwingAngle > 0)
4992 fSwingAngle -= 360.0;
4993 // markup for arcTo
4994 // ToDo: write markup for case zero width or height of ellipse
4995 const sal_Int32 nStartAng(std::lround(fStartAngle * 60000));
4996 const sal_Int32 nSwingAng(std::lround(fSwingAngle * 60000));
4997 mpFS->singleElement(FSNS(XML_a, XML_arcTo), XML_wR, OString::number(std::lround(fWR)),
4998 XML_hR, OString::number(std::lround(fHR)), XML_stAng,
4999 OString::number(nStartAng), XML_swAng, OString::number(nSwingAng));
5000 rfCurrentX = fPx;
5001 rfCurrentY = fPy;
5002 rbCurrentValid = true;
5003 rnPairIndex += 4;
5004 break;
5005 }
5006 case ELLIPTICALQUADRANTX:
5007 case ELLIPTICALQUADRANTY:
5008 {
5009 if (rnPairIndex >= rPairs.getLength())
5010 return false;
5011
5012 // read parameters
5013 double fX = 0.0;
5014 rCustomShape2d.GetParameter(fX, rPairs[rnPairIndex].First, bReplaceGeoWidth, false);
5015 double fY = 0.0;
5016 rCustomShape2d.GetParameter(fY, rPairs[rnPairIndex].Second, false, bReplaceGeoHeight);
5017
5018 // Prepare parameters for arcTo
5019 if (rbCurrentValid)
5020 {
5021 double fWR = std::abs(rfCurrentX - fX);
5022 double fHR = std::abs(rfCurrentY - fY);
5023 double fStartAngle(0.0);
5024 double fSwingAngle(0.0);
5025 // The starting direction of the arc toggles between X and Y
5026 if ((eCommand == ELLIPTICALQUADRANTX && !(nCount % 2))
5027 || (eCommand == ELLIPTICALQUADRANTY && (nCount % 2)))
5028 {
5029 // arc starts horizontal
5030 fStartAngle = fY < rfCurrentY ? 90.0 : 270.0;
5031 const bool bClockwise = (fX < rfCurrentX && fY < rfCurrentY)
5032 || (fX > rfCurrentX && fY > rfCurrentY);
5033 fSwingAngle = bClockwise ? 90.0 : -90.0;
5034 }
5035 else
5036 {
5037 // arc starts vertical
5038 fStartAngle = fX < rfCurrentX ? 0.0 : 180.0;
5039 const bool bClockwise = (fX < rfCurrentX && fY > rfCurrentY)
5040 || (fX > rfCurrentX && fY < rfCurrentY);
5041 fSwingAngle = bClockwise ? 90.0 : -90.0;
5042 }
5043 sal_Int32 nStartAng(std::lround(fStartAngle * 60000));
5044 sal_Int32 nSwingAng(std::lround(fSwingAngle * 60000));
5045 mpFS->singleElement(
5046 FSNS(XML_a, XML_arcTo), XML_wR, OString::number(std::lround(fWR)), XML_hR,
5047 OString::number(std::lround(fHR)), XML_stAng, OString::number(nStartAng),
5048 XML_swAng, OString::number(nSwingAng));
5049 }
5050 else
5051 {
5052 // faulty path, but we continue with the target point
5053 mpFS->startElementNS(XML_a, XML_moveTo);
5054 WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d, bReplaceGeoWidth,
5055 bReplaceGeoHeight);
5056 mpFS->endElementNS(XML_a, XML_moveTo);
5057 }
5058 rfCurrentX = fX;
5059 rfCurrentY = fY;
5060 rbCurrentValid = true;
5061 rnPairIndex++;
5062 break;
5063 }
5064 case QUADRATICCURVETO:
5065 {
5066 if (rnPairIndex + 1 >= rPairs.getLength())
5067 return false;
5068
5069 mpFS->startElementNS(XML_a, XML_quadBezTo);
5070 for (sal_uInt8 i = 0; i < 2; ++i)
5071 {
5072 WriteCustomGeometryPoint(rPairs[rnPairIndex + i], rCustomShape2d, bReplaceGeoWidth,
5073 bReplaceGeoHeight);
5074 }
5075 mpFS->endElementNS(XML_a, XML_quadBezTo);
5076 rCustomShape2d.GetParameter(rfCurrentX, rPairs[rnPairIndex + 1].First, bReplaceGeoWidth,
5077 false);
5078 rCustomShape2d.GetParameter(rfCurrentY, rPairs[rnPairIndex + 1].Second, false,
5079 bReplaceGeoHeight);
5080 rbCurrentValid = true;
5081 rnPairIndex += 2;
5082 break;
5083 }
5084 case ARCANGLETO:
5085 {
5086 if (rnPairIndex + 1 >= rPairs.getLength())
5087 return false;
5088
5089 double fWR = 0.0;
5090 rCustomShape2d.GetParameter(fWR, rPairs[rnPairIndex].First, false, false);
5091 double fHR = 0.0;
5092 rCustomShape2d.GetParameter(fHR, rPairs[rnPairIndex].Second, false, false);
5093 double fStartAngle = 0.0;
5094 rCustomShape2d.GetParameter(fStartAngle, rPairs[rnPairIndex + 1].First, false, false);
5095 sal_Int32 nStartAng(std::lround(fStartAngle * 60000));
5096 double fSwingAng = 0.0;
5097 rCustomShape2d.GetParameter(fSwingAng, rPairs[rnPairIndex + 1].Second, false, false);
5098 sal_Int32 nSwingAng(std::lround(fSwingAng * 60000));
5099 mpFS->singleElement(FSNS(XML_a, XML_arcTo), XML_wR, OString::number(fWR), XML_hR,
5100 OString::number(fHR), XML_stAng, OString::number(nStartAng),
5101 XML_swAng, OString::number(nSwingAng));
5102 double fPx = 0.0;
5103 double fPy = 0.0;
5104 getEllipsePointFromViewAngle(fPx, fPy, fWR, fHR, 0.0, 0.0, fStartAngle);
5105 double fCx = rfCurrentX - fPx;
5106 double fCy = rfCurrentY - fPy;
5107 getEllipsePointFromViewAngle(rfCurrentX, rfCurrentY, fWR, fHR, fCx, fCy,
5108 fStartAngle + fSwingAng);
5109 rbCurrentValid = true;
5110 rnPairIndex += 2;
5111 break;
5112 }
5113 default:
5114 // do nothing
5115 break;
5116 }
5117 return true;
5118}
5121 const drawing::EnhancedCustomShapeParameterPair& rParamPair,
5122 const EnhancedCustomShape2d& rCustomShape2d, const bool bReplaceGeoWidth,
5123 const bool bReplaceGeoHeight)
5124{
5125 sal_Int32 nX
5126 = GetCustomGeometryPointValue(rParamPair.First, rCustomShape2d, bReplaceGeoWidth, false);
5127 sal_Int32 nY
5128 = GetCustomGeometryPointValue(rParamPair.Second, rCustomShape2d, false, bReplaceGeoHeight);
5129
5130 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(nX), XML_y, OString::number(nY));
5131}
5134{
5135 // This method is used for export to docx in case WriteCustomGeometry fails.
5136 mpFS->startElementNS(XML_a, XML_custGeom);
5137 mpFS->singleElementNS(XML_a, XML_avLst);
5138 mpFS->singleElementNS(XML_a, XML_gdLst);
5139 mpFS->singleElementNS(XML_a, XML_ahLst);
5140 mpFS->singleElementNS(XML_a, XML_rect, XML_l, "0", XML_t, "0", XML_r, "r", XML_b, "b");
5141 mpFS->singleElementNS(XML_a, XML_pathLst);
5142 mpFS->endElementNS(XML_a, XML_custGeom);
5143}
5144
5145// version for SdrPathObj
5146void DrawingML::WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& rXShape,
5147 const bool bClosed)
5148{
5150 // In case of Writer, the parent element is <wps:spPr>, and there the
5151 // <a:custGeom> element is not optional.
5152 if (aPolyPolygon.Count() < 1 && GetDocumentType() != DOCUMENT_DOCX)
5153 return;
5154
5155 mpFS->startElementNS(XML_a, XML_custGeom);
5156 mpFS->singleElementNS(XML_a, XML_avLst);
5157 mpFS->singleElementNS(XML_a, XML_gdLst);
5158 mpFS->singleElementNS(XML_a, XML_ahLst);
5159 mpFS->singleElementNS(XML_a, XML_rect, XML_l, "0", XML_t, "0", XML_r, "r", XML_b, "b");
5160
5161 mpFS->startElementNS(XML_a, XML_pathLst);
5162
5163 awt::Size aSize = rXShape->getSize();
5164 awt::Point aPos = rXShape->getPosition();
5165 Reference<XPropertySet> xPropertySet(rXShape, UNO_QUERY);
5166 uno::Reference<XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
5167 if (xPropertySetInfo->hasPropertyByName("AnchorPosition"))
5168 {
5169 awt::Point aAnchorPosition;
5170 xPropertySet->getPropertyValue("AnchorPosition") >>= aAnchorPosition;
5171 aPos.X += aAnchorPosition.X;
5172 aPos.Y += aAnchorPosition.Y;
5173 }
5174
5175 // Only closed SdrPathObj can be filled
5176 std::optional<OString> sFill;
5177 if (!bClosed)
5178 sFill = "none"; // for possible values see ST_PathFillMode in OOXML standard
5179
5180 // Put all polygons of rPolyPolygon in the same path element
5181 // to subtract the overlapped areas.
5182 mpFS->startElementNS(XML_a, XML_path, XML_fill, sFill, XML_w, OString::number(aSize.Width),
5183 XML_h, OString::number(aSize.Height));
5184
5185 for (sal_uInt16 i = 0; i < aPolyPolygon.Count(); i++)
5186 {
5187 const tools::Polygon& aPoly = aPolyPolygon[i];
5188
5189 if (aPoly.GetSize() > 0)
5190 {
5191 mpFS->startElementNS(XML_a, XML_moveTo);
5192
5193 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(aPoly[0].X() - aPos.X),
5194 XML_y, OString::number(aPoly[0].Y() - aPos.Y));
5195
5196 mpFS->endElementNS(XML_a, XML_moveTo);
5197 }
5198
5199 for (sal_uInt16 j = 1; j < aPoly.GetSize(); j++)
5200 {
5201 PolyFlags flags = aPoly.GetFlags(j);
5202 if (flags == PolyFlags::Control)
5203 {
5204 // a:cubicBezTo can only contain 3 a:pt elements, so we need to make sure of this
5205 if (j + 2 < aPoly.GetSize() && aPoly.GetFlags(j + 1) == PolyFlags::Control
5206 && aPoly.GetFlags(j + 2) != PolyFlags::Control)
5207 {
5208 mpFS->startElementNS(XML_a, XML_cubicBezTo);
5209 for (sal_uInt8 k = 0; k <= 2; ++k)
5210 {
5211 mpFS->singleElementNS(XML_a, XML_pt, XML_x,
5212 OString::number(aPoly[j + k].X() - aPos.X), XML_y,
5213 OString::number(aPoly[j + k].Y() - aPos.Y));
5214 }
5215 mpFS->endElementNS(XML_a, XML_cubicBezTo);
5216 j += 2;
5217 }
5218 }
5219 else if (flags == PolyFlags::Normal)
5220 {
5221 mpFS->startElementNS(XML_a, XML_lnTo);
5222 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(aPoly[j].X() - aPos.X),
5223 XML_y, OString::number(aPoly[j].Y() - aPos.Y));
5224 mpFS->endElementNS(XML_a, XML_lnTo);
5225 }
5226 }
5227 }
5228 if (bClosed)
5229 mpFS->singleElementNS(XML_a, XML_close);
5230 mpFS->endElementNS(XML_a, XML_path);
5231
5232 mpFS->endElementNS(XML_a, XML_pathLst);
5233
5234 mpFS->endElementNS(XML_a, XML_custGeom);
5235}
5237void DrawingML::WriteConnectorConnections( sal_Int32 nStartGlueId, sal_Int32 nEndGlueId, sal_Int32 nStartID, sal_Int32 nEndID )
5238{
5239 if( nStartID != -1 )
5240 {
5241 mpFS->singleElementNS( XML_a, XML_stCxn,
5242 XML_id, OString::number(nStartID),
5243 XML_idx, OString::number(nStartGlueId) );
5244 }
5245 if( nEndID != -1 )
5246 {
5247 mpFS->singleElementNS( XML_a, XML_endCxn,
5248 XML_id, OString::number(nEndID),
5249 XML_idx, OString::number(nEndGlueId) );
5250 }
5251}
5253sal_Unicode DrawingML::SubstituteBullet( sal_Unicode cBulletId, css::awt::FontDescriptor& rFontDesc )
5254{
5255 if ( IsOpenSymbol(rFontDesc.Name) )
5256 {
5257 rtl_TextEncoding eCharSet = rFontDesc.CharSet;
5258 cBulletId = msfilter::util::bestFitOpenSymbolToMSFont(cBulletId, eCharSet, rFontDesc.Name);
5259 rFontDesc.CharSet = eCharSet;
5260 }
5261
5262 return cBulletId;
5263}
5266 const OUString& sFullStream,
5267 std::u16string_view sRelativeStream,
5268 const Reference< XOutputStream >& xParentRelation,
5269 const OUString& sContentType,
5270 const OUString& sRelationshipType,
5271 OUString* pRelationshipId )
5272{
5273 OUString sRelationshipId;
5274 if (xParentRelation.is())
5275 sRelationshipId = GetFB()->addRelation( xParentRelation, sRelationshipType, sRelativeStream );
5276 else
5277 sRelationshipId = GetFB()->addRelation( sRelationshipType, sRelativeStream );
5278
5279 if( pRelationshipId )
5280 *pRelationshipId = sRelationshipId;
5281
5282 sax_fastparser::FSHelperPtr p = GetFB()->openFragmentStreamWithSerializer( sFullStream, sContentType );
5283
5284 return p;
5285}
5287void DrawingML::WriteFill(const Reference<XPropertySet>& xPropSet, const awt::Size& rSize)
5288{
5289 if ( !GetProperty( xPropSet, "FillStyle" ) )
5290 return;
5291 FillStyle aFillStyle( FillStyle_NONE );
5292 xPropSet->getPropertyValue( "FillStyle" ) >>= aFillStyle;
5293
5294 // map full transparent background to no fill
5295 if (aFillStyle == FillStyle_SOLID)
5296 {
5297 OUString sFillTransparenceGradientName;
5298
5299 if (GetProperty(xPropSet, "FillTransparenceGradientName")
5300 && (mAny >>= sFillTransparenceGradientName)
5301 && !sFillTransparenceGradientName.isEmpty()
5302 && GetProperty(xPropSet, "FillTransparenceGradient"))
5303 {
5304 // check if a fully transparent TransparenceGradient is used
5305 // use BGradient constructor & tooling here now
5306 const basegfx::BGradient aTransparenceGradient(mAny);
5307 basegfx::BColor aSingleColor;
5308 const bool bSingleColor(aTransparenceGradient.GetColorStops().isSingleColor(aSingleColor));
5309 const bool bCompletelyTransparent(bSingleColor && basegfx::fTools::equal(aSingleColor.luminance(), 1.0));
5310
5311 if (bCompletelyTransparent)
5312 {
5313 aFillStyle = FillStyle_NONE;
5314 }
5315 }
5316 else if ( GetProperty( xPropSet, "FillTransparence" ) )
5317 {
5318 // check if a fully transparent FillTransparence is used
5319 sal_Int16 nVal = 0;
5320 xPropSet->getPropertyValue( "FillTransparence" ) >>= nVal;
5321 if ( nVal == 100 )
5322 aFillStyle = FillStyle_NONE;
5323 }
5324 }
5325
5326 bool bUseBackground(false);
5327 if (GetProperty(xPropSet, "FillUseSlideBackground"))
5328 xPropSet->getPropertyValue("FillUseSlideBackground") >>= bUseBackground;
5329
5330 switch( aFillStyle )
5331 {
5332 case FillStyle_SOLID :
5333 WriteSolidFill( xPropSet );
5334 break;
5335 case FillStyle_GRADIENT :
5336 WriteGradientFill( xPropSet );
5337 break;
5338 case FillStyle_BITMAP :
5339 WriteBlipFill( xPropSet, "FillBitmap", rSize );
5340 break;
5341 case FillStyle_HATCH :
5342 WritePattFill( xPropSet );
5343 break;
5344 case FillStyle_NONE:
5345 if (!bUseBackground) // attribute `useBgFill` will be written at parent p:sp shape
5346 mpFS->singleElementNS(XML_a, XML_noFill);
5347 break;
5348 default:
5349 ;
5350 }
5351}
5353void DrawingML::WriteStyleProperties( sal_Int32 nTokenId, const Sequence< PropertyValue >& aProperties )
5354{
5355 if( aProperties.hasElements() )
5356 {
5357 OUString sSchemeClr;
5358 sal_uInt32 nIdx = 0;
5359 Sequence< PropertyValue > aTransformations;
5360 for( const auto& rProp : aProperties)
5361 {
5362 if( rProp.Name == "SchemeClr" )
5363 rProp.Value >>= sSchemeClr;
5364 else if( rProp.Name == "Idx" )
5365 rProp.Value >>= nIdx;
5366 else if( rProp.Name == "Transformations" )
5367 rProp.Value >>= aTransformations;
5368 }
5369 mpFS->startElementNS(XML_a, nTokenId, XML_idx, OString::number(nIdx));
5370 WriteColor(sSchemeClr, aTransformations);
5371 mpFS->endElementNS( XML_a, nTokenId );
5372 }
5373 else
5374 {
5375 // write mock <a:*Ref> tag
5376 mpFS->singleElementNS(XML_a, nTokenId, XML_idx, OString::number(0));
5377 }
5378}
5381{
5382 // check existence of the grab bag
5383 if ( !GetProperty( xPropSet, "InteropGrabBag" ) )
5384 return;
5385
5386 // extract the relevant properties from the grab bag
5388 Sequence< PropertyValue > aFillRefProperties, aLnRefProperties, aEffectRefProperties;
5389 mAny >>= aGrabBag;
5390 for( const auto& rProp : std::as_const(aGrabBag))
5391 {
5392 if( rProp.Name == "StyleFillRef" )
5393 rProp.Value >>= aFillRefProperties;
5394 else if( rProp.Name == "StyleLnRef" )
5395 rProp.Value >>= aLnRefProperties;
5396 else if( rProp.Name == "StyleEffectRef" )
5397 rProp.Value >>= aEffectRefProperties;
5398 }
5399
5400 WriteStyleProperties( XML_lnRef, aLnRefProperties );
5401 WriteStyleProperties( XML_fillRef, aFillRefProperties );
5402 WriteStyleProperties( XML_effectRef, aEffectRefProperties );
5403
5404 // write mock <a:fontRef>
5405 mpFS->singleElementNS(XML_a, XML_fontRef, XML_idx, "minor");
5406}
5408void DrawingML::WriteShapeEffect( std::u16string_view sName, const Sequence< PropertyValue >& aEffectProps )
5409{
5410 if( !aEffectProps.hasElements() )
5411 return;
5412
5413 // assign the proper tag and enable bContainsColor if necessary
5414 sal_Int32 nEffectToken = 0;
5415 bool bContainsColor = false;
5416 if( sName == u"outerShdw" )
5417 {
5418 nEffectToken = FSNS( XML_a, XML_outerShdw );
5419 bContainsColor = true;
5420 }
5421 else if( sName == u"innerShdw" )
5422 {
5423 nEffectToken = FSNS( XML_a, XML_innerShdw );
5424 bContainsColor = true;
5425 }
5426 else if( sName == u"glow" )
5427 {
5428 nEffectToken = FSNS( XML_a, XML_glow );
5429 bContainsColor = true;
5430 }
5431 else if( sName == u"softEdge" )
5432 nEffectToken = FSNS( XML_a, XML_softEdge );
5433 else if( sName == u"reflection" )
5434 nEffectToken = FSNS( XML_a, XML_reflection );
5435 else if( sName == u"blur" )
5436 nEffectToken = FSNS( XML_a, XML_blur );
5437
5438 OUString sSchemeClr;
5439 ::Color nRgbClr;
5440 sal_Int32 nAlpha = MAX_PERCENT;
5441 Sequence< PropertyValue > aTransformations;
5442 rtl::Reference<sax_fastparser::FastAttributeList> aOuterShdwAttrList = FastSerializerHelper::createAttrList();
5443 for( const auto& rEffectProp : aEffectProps )
5444 {
5445 if( rEffectProp.Name == "Attribs" )
5446 {
5447 // read tag attributes
5448 uno::Sequence< beans::PropertyValue > aOuterShdwProps;
5449 rEffectProp.Value >>= aOuterShdwProps;
5450 for( const auto& rOuterShdwProp : std::as_const(aOuterShdwProps) )
5451 {
5452 if( rOuterShdwProp.Name == "algn" )
5453 {
5454 OUString sVal;
5455 rOuterShdwProp.Value >>= sVal;
5456 aOuterShdwAttrList->add( XML_algn, sVal );
5457 }
5458 else if( rOuterShdwProp.Name == "blurRad" )
5459 {
5460 sal_Int64 nVal = 0;
5461 rOuterShdwProp.Value >>= nVal;
5462 aOuterShdwAttrList->add( XML_blurRad, OString::number( nVal ) );
5463 }
5464 else if( rOuterShdwProp.Name == "dir" )
5465 {
5466 sal_Int32 nVal = 0;
5467 rOuterShdwProp.Value >>= nVal;
5468 aOuterShdwAttrList->add( XML_dir, OString::number( nVal ) );
5469 }
5470 else if( rOuterShdwProp.Name == "dist" )
5471 {
5472 sal_Int32 nVal = 0;
5473 rOuterShdwProp.Value >>= nVal;
5474 aOuterShdwAttrList->add( XML_dist, OString::number( nVal ) );
5475 }
5476 else if( rOuterShdwProp.Name == "kx" )
5477 {
5478 sal_Int32 nVal = 0;
5479 rOuterShdwProp.Value >>= nVal;
5480 aOuterShdwAttrList->add( XML_kx, OString::number( nVal ) );
5481 }
5482 else if( rOuterShdwProp.Name == "ky" )
5483 {
5484 sal_Int32 nVal = 0;
5485 rOuterShdwProp.Value >>= nVal;
5486 aOuterShdwAttrList->add( XML_ky, OString::number( nVal ) );
5487 }
5488 else if( rOuterShdwProp.Name == "rotWithShape" )
5489 {
5490 sal_Int32 nVal = 0;
5491 rOuterShdwProp.Value >>= nVal;
5492 aOuterShdwAttrList->add( XML_rotWithShape, OString::number( nVal ) );
5493 }
5494 else if( rOuterShdwProp.Name == "sx" )
5495 {
5496 sal_Int32 nVal = 0;
5497 rOuterShdwProp.Value >>= nVal;
5498 aOuterShdwAttrList->add( XML_sx, OString::number( nVal ) );
5499 }
5500 else if( rOuterShdwProp.Name == "sy" )
5501 {
5502 sal_Int32 nVal = 0;
5503 rOuterShdwProp.Value >>= nVal;
5504 aOuterShdwAttrList->add( XML_sy, OString::number( nVal ) );
5505 }
5506 else if( rOuterShdwProp.Name == "rad" )
5507 {
5508 sal_Int64 nVal = 0;
5509 rOuterShdwProp.Value >>= nVal;
5510 aOuterShdwAttrList->add( XML_rad, OString::number( nVal ) );
5511 }
5512 else if( rOuterShdwProp.Name == "endA" )
5513 {
5514 sal_Int32 nVal = 0;
5515 rOuterShdwProp.Value >>= nVal;
5516 aOuterShdwAttrList->add( XML_endA, OString::number( nVal ) );
5517 }
5518 else if( rOuterShdwProp.Name == "endPos" )
5519 {
5520 sal_Int32 nVal = 0;
5521 rOuterShdwProp.Value >>= nVal;
5522 aOuterShdwAttrList->add( XML_endPos, OString::number( nVal ) );
5523 }
5524 else if( rOuterShdwProp.Name == "fadeDir" )
5525 {
5526 sal_Int32 nVal = 0;
5527 rOuterShdwProp.Value >>= nVal;
5528 aOuterShdwAttrList->add( XML_fadeDir, OString::number( nVal ) );
5529 }
5530 else if( rOuterShdwProp.Name == "stA" )
5531 {
5532 sal_Int32 nVal = 0;
5533 rOuterShdwProp.Value >>= nVal;
5534 aOuterShdwAttrList->add( XML_stA, OString::number( nVal ) );
5535 }
5536 else if( rOuterShdwProp.Name == "stPos" )
5537 {
5538 sal_Int32 nVal = 0;
5539 rOuterShdwProp.Value >>= nVal;
5540 aOuterShdwAttrList->add( XML_stPos, OString::number( nVal ) );
5541 }
5542 else if( rOuterShdwProp.Name == "grow" )
5543 {
5544 sal_Int32 nVal = 0;
5545 rOuterShdwProp.Value >>= nVal;
5546 aOuterShdwAttrList->add( XML_grow, OString::number( nVal ) );
5547 }
5548 }
5549 }
5550 else if(rEffectProp.Name == "RgbClr")
5551 {
5552 rEffectProp.Value >>= nRgbClr;
5553 }
5554 else if(rEffectProp.Name == "RgbClrTransparency")
5555 {
5556 sal_Int32 nTransparency;
5557 if (rEffectProp.Value >>= nTransparency)
5558 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
5559 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
5560 }
5561 else if(rEffectProp.Name == "SchemeClr")
5562 {
5563 rEffectProp.Value >>= sSchemeClr;
5564 }
5565 else if(rEffectProp.Name == "SchemeClrTransformations")
5566 {
5567 rEffectProp.Value >>= aTransformations;
5568 }
5569 }
5570
5571 if( nEffectToken <= 0 )
5572 return;
5573
5574 mpFS->startElement( nEffectToken, aOuterShdwAttrList );
5575
5576 if( bContainsColor )
5577 {
5578 if( sSchemeClr.isEmpty() )
5579 WriteColor( nRgbClr, nAlpha );
5580 else
5581 WriteColor( sSchemeClr, aTransformations );
5582 }
5583
5584 mpFS->endElement( nEffectToken );
5585}
5587static sal_Int32 lcl_CalculateDist(const double dX, const double dY)
5588{
5589 return static_cast< sal_Int32 >(std::hypot(dX, dY) * 360);
5590}
5592static sal_Int32 lcl_CalculateDir(const double dX, const double dY)
5593{
5594 return (static_cast< sal_Int32 >(basegfx::rad2deg<60000>(atan2(dY,dX))) + 21600000) % 21600000;
5595}
5598{
5599 Sequence< PropertyValue > aGrabBag, aEffects, aOuterShdwProps;
5600 bool bHasInteropGrabBag = rXPropSet->getPropertySetInfo()->hasPropertyByName("InteropGrabBag");
5601 if (bHasInteropGrabBag && GetProperty(rXPropSet, "InteropGrabBag"))
5602 {
5603 mAny >>= aGrabBag;
5604 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
5605 [](const PropertyValue& rProp) { return rProp.Name == "EffectProperties"; });
5606 if (pProp != std::cend(aGrabBag))
5607 {
5608 pProp->Value >>= aEffects;
5609 auto pEffect = std::find_if(std::cbegin(aEffects), std::cend(aEffects),
5610 [](const PropertyValue& rEffect) { return rEffect.Name == "outerShdw"; });
5611 if (pEffect != std::cend(aEffects))
5612 pEffect->Value >>= aOuterShdwProps;
5613 }
5614 }
5615
5616 // tdf#132201: the order of effects is important. Effects order (CT_EffectList in ECMA-376):
5617 // blur -> fillOverlay -> glow -> innerShdw -> outerShdw -> prstShdw -> reflection -> softEdge
5618
5619 if( !aEffects.hasElements() )
5620 {
5621 bool bHasShadow = false;
5622 if( GetProperty( rXPropSet, "Shadow" ) )
5623 mAny >>= bHasShadow;
5624 bool bHasEffects = bHasShadow;
5625 if (!bHasEffects && GetProperty(rXPropSet, "GlowEffectRadius"))
5626 {
5627 sal_Int32 rad = 0;
5628 mAny >>= rad;
5629 bHasEffects = rad > 0;
5630 }
5631 if (!bHasEffects && GetProperty(rXPropSet, "SoftEdgeRadius"))
5632 {
5633 sal_Int32 rad = 0;
5634 mAny >>= rad;
5635 bHasEffects = rad > 0;
5636 }
5637
5638 if (bHasEffects)
5639 {
5640 mpFS->startElementNS(XML_a, XML_effectLst);
5641 WriteGlowEffect(rXPropSet);
5642 if( bHasShadow )
5643 {
5644 double dX = +0.0, dY = +0.0;
5645 sal_Int32 nBlur =0;
5646 rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
5647 rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
5648 rXPropSet->getPropertyValue( "ShadowBlur" ) >>= nBlur;
5649
5650 Sequence< PropertyValue > aShadowAttribsGrabBag{
5654 comphelper::makePropertyValue("rotWithShape", false) //ooxml default is 'true', so must write it
5655 };
5656
5657 Sequence< PropertyValue > aShadowGrabBag{
5658 comphelper::makePropertyValue("Attribs", aShadowAttribsGrabBag),
5659 comphelper::makePropertyValue("RgbClr", rXPropSet->getPropertyValue( "ShadowColor" )),
5660 comphelper::makePropertyValue("RgbClrTransparency", rXPropSet->getPropertyValue( "ShadowTransparence" ))
5661 };
5662
5663 WriteShapeEffect( u"outerShdw", aShadowGrabBag );
5664 }
5665 WriteSoftEdgeEffect(rXPropSet);
5666 mpFS->endElementNS(XML_a, XML_effectLst);
5667 }
5668 }
5669 else
5670 {
5671 for( auto& rOuterShdwProp : asNonConstRange(aOuterShdwProps) )
5672 {
5673 if( rOuterShdwProp.Name == "Attribs" )
5674 {
5675 Sequence< PropertyValue > aAttribsProps;
5676 rOuterShdwProp.Value >>= aAttribsProps;
5677
5678 double dX = +0.0, dY = +0.0;
5679 sal_Int32 nBlur =0;
5680 rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
5681 rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
5682 rXPropSet->getPropertyValue( "ShadowBlur" ) >>= nBlur;
5683
5684
5685 for( auto& rAttribsProp : asNonConstRange(aAttribsProps) )
5686 {
5687 if( rAttribsProp.Name == "dist" )
5688 {
5689 rAttribsProp.Value <<= lcl_CalculateDist(dX, dY);
5690 }
5691 else if( rAttribsProp.Name == "dir" )
5692 {
5693 rAttribsProp.Value <<= lcl_CalculateDir(dX, dY);
5694 }
5695 else if( rAttribsProp.Name == "blurRad" )
5696 {
5697 rAttribsProp.Value <<= oox::drawingml::convertHmmToEmu(nBlur);
5698 }
5699 }
5700
5701 rOuterShdwProp.Value <<= aAttribsProps;
5702 }
5703 else if( rOuterShdwProp.Name == "RgbClr" )
5704 {
5705 rOuterShdwProp.Value = rXPropSet->getPropertyValue( "ShadowColor" );
5706 }
5707 else if( rOuterShdwProp.Name == "RgbClrTransparency" )
5708 {
5709 rOuterShdwProp.Value = rXPropSet->getPropertyValue( "ShadowTransparence" );
5710 }
5711 }
5712
5713 mpFS->startElementNS(XML_a, XML_effectLst);
5714 bool bGlowWritten = false;
5715 for( const auto& rEffect : std::as_const(aEffects) )
5716 {
5717 if (!bGlowWritten
5718 && (rEffect.Name == "innerShdw" || rEffect.Name == "outerShdw"
5719 || rEffect.Name == "prstShdw" || rEffect.Name == "reflection"
5720 || rEffect.Name == "softEdge"))
5721 {
5722 WriteGlowEffect(rXPropSet);
5723 bGlowWritten = true;
5724 }
5725
5726 if( rEffect.Name == "outerShdw" )
5727 {
5728 WriteShapeEffect( rEffect.Name, aOuterShdwProps );
5729 }
5730 else
5731 {
5732 Sequence< PropertyValue > aEffectProps;
5733 rEffect.Value >>= aEffectProps;
5734 WriteShapeEffect( rEffect.Name, aEffectProps );
5735 }
5736 }
5737 if (!bGlowWritten)
5738 WriteGlowEffect(rXPropSet);
5739 WriteSoftEdgeEffect(rXPropSet); // the last
5740
5741 mpFS->endElementNS(XML_a, XML_effectLst);
5742 }
5743}
5746{
5747 if (!rXPropSet->getPropertySetInfo()->hasPropertyByName("GlowEffectRadius"))
5748 {
5749 return;
5750 }
5751
5752 sal_Int32 nRad = 0;
5753 rXPropSet->getPropertyValue("GlowEffectRadius") >>= nRad;
5754 if (!nRad)
5755 return;
5756
5758 "rad", oox::drawingml::convertHmmToEmu(nRad)) };
5759 Sequence< PropertyValue > aGlowProps{
5760 comphelper::makePropertyValue("Attribs", aGlowAttribs),
5761 comphelper::makePropertyValue("RgbClr", rXPropSet->getPropertyValue("GlowEffectColor")),
5762 comphelper::makePropertyValue("RgbClrTransparency", rXPropSet->getPropertyValue("GlowEffectTransparency"))
5763 };
5764 // TODO other stuff like saturation or luminance
5765
5766 WriteShapeEffect(u"glow", aGlowProps);
5767}
5769void DrawingML::WriteSoftEdgeEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet)
5770{
5771 if (!rXPropSet->getPropertySetInfo()->hasPropertyByName("SoftEdgeRadius"))
5772 {
5773 return;
5774 }
5775
5776 sal_Int32 nRad = 0;
5777 rXPropSet->getPropertyValue("SoftEdgeRadius") >>= nRad;
5778 if (!nRad)
5779 return;
5780
5781 css::uno::Sequence<css::beans::PropertyValue> aAttribs{ comphelper::makePropertyValue(
5782 "rad", oox::drawingml::convertHmmToEmu(nRad)) };
5783 css::uno::Sequence<css::beans::PropertyValue> aProps{ comphelper::makePropertyValue("Attribs",
5784 aAttribs) };
5785
5786 WriteShapeEffect(u"softEdge", aProps);
5787}
5789void DrawingML::Write3DEffects( const Reference< XPropertySet >& xPropSet, bool bIsText )
5790{
5791 // check existence of the grab bag
5792 if( !GetProperty( xPropSet, "InteropGrabBag" ) )
5793 return;
5794
5795 // extract the relevant properties from the grab bag
5796 Sequence< PropertyValue > aGrabBag, aEffectProps, aLightRigProps, aShape3DProps;
5797 mAny >>= aGrabBag;
5798
5799 auto pShapeProp = std::find_if( std::cbegin(aGrabBag), std::cend(aGrabBag),
5800 [bIsText](const PropertyValue& rProp)
5801 { return rProp.Name == (bIsText ? u"Text3DEffectProperties" : u"3DEffectProperties"); });
5802 if (pShapeProp != std::cend(aGrabBag))
5803 {
5804 Sequence< PropertyValue > a3DEffectProps;
5805 pShapeProp->Value >>= a3DEffectProps;
5806 for( const auto& r3DEffectProp : std::as_const(a3DEffectProps) )
5807 {
5808 if( r3DEffectProp.Name == "Camera" )
5809 r3DEffectProp.Value >>= aEffectProps;
5810 else if( r3DEffectProp.Name == "LightRig" )
5811 r3DEffectProp.Value >>= aLightRigProps;
5812 else if( r3DEffectProp.Name == "Shape3D" )
5813 r3DEffectProp.Value >>= aShape3DProps;
5814 }
5815 }
5816
5817 if( !aEffectProps.hasElements() && !aLightRigProps.hasElements() && !aShape3DProps.hasElements() )
5818 return;
5819
5820 bool bCameraRotationPresent = false;
5821 rtl::Reference<sax_fastparser::FastAttributeList> aCameraAttrList = FastSerializerHelper::createAttrList();
5822 rtl::Reference<sax_fastparser::FastAttributeList> aCameraRotationAttrList = FastSerializerHelper::createAttrList();
5823 for( const auto& rEffectProp : std::as_const(aEffectProps) )
5824 {
5825 if( rEffectProp.Name == "prst" )
5826 {
5827 OUString sVal;
5828 rEffectProp.Value >>= sVal;
5829 aCameraAttrList->add(XML_prst, sVal);
5830 }
5831 else if( rEffectProp.Name == "fov" )
5832 {
5833 float fVal = 0;
5834 rEffectProp.Value >>= fVal;
5835 aCameraAttrList->add( XML_fov, OString::number( fVal * 60000 ) );
5836 }
5837 else if( rEffectProp.Name == "zoom" )
5838 {
5839 float fVal = 1;
5840 rEffectProp.Value >>= fVal;
5841 aCameraAttrList->add( XML_zoom, OString::number( fVal * 100000 ) );
5842 }
5843 else if( rEffectProp.Name == "rotLat" ||
5844 rEffectProp.Name == "rotLon" ||
5845 rEffectProp.Name == "rotRev" )
5846 {
5847 sal_Int32 nVal = 0, nToken = XML_none;
5848 rEffectProp.Value >>= nVal;
5849 if( rEffectProp.Name == "rotLat" )
5850 nToken = XML_lat;
5851 else if( rEffectProp.Name == "rotLon" )
5852 nToken = XML_lon;
5853 else if( rEffectProp.Name == "rotRev" )
5854 nToken = XML_rev;
5855 aCameraRotationAttrList->add( nToken, OString::number( nVal ) );
5856 bCameraRotationPresent = true;
5857 }
5858 }
5859
5860 bool bLightRigRotationPresent = false;
5861 rtl::Reference<sax_fastparser::FastAttributeList> aLightRigAttrList = FastSerializerHelper::createAttrList();
5862 rtl::Reference<sax_fastparser::FastAttributeList> aLightRigRotationAttrList = FastSerializerHelper::createAttrList();
5863 for( const auto& rLightRigProp : std::as_const(aLightRigProps) )
5864 {
5865 if( rLightRigProp.Name == "rig" || rLightRigProp.Name == "dir" )
5866 {
5867 OUString sVal;
5868 sal_Int32 nToken = XML_none;
5869 rLightRigProp.Value >>= sVal;
5870 if( rLightRigProp.Name == "rig" )
5871 nToken = XML_rig;
5872 else if( rLightRigProp.Name == "dir" )
5873 nToken = XML_dir;
5874 aLightRigAttrList->add(nToken, sVal);
5875 }
5876 else if( rLightRigProp.Name == "rotLat" ||
5877 rLightRigProp.Name == "rotLon" ||
5878 rLightRigProp.Name == "rotRev" )
5879 {
5880 sal_Int32 nVal = 0, nToken = XML_none;
5881 rLightRigProp.Value >>= nVal;
5882 if( rLightRigProp.Name == "rotLat" )
5883 nToken = XML_lat;
5884 else if( rLightRigProp.Name == "rotLon" )
5885 nToken = XML_lon;
5886 else if( rLightRigProp.Name == "rotRev" )
5887 nToken = XML_rev;
5888 aLightRigRotationAttrList->add( nToken, OString::number( nVal ) );
5889 bLightRigRotationPresent = true;
5890 }
5891 }
5892
5893 mpFS->startElementNS(XML_a, XML_scene3d);
5894
5895 if( aEffectProps.hasElements() )
5896 {
5897 mpFS->startElementNS( XML_a, XML_camera, aCameraAttrList );
5898 if( bCameraRotationPresent )
5899 {
5900 mpFS->singleElementNS( XML_a, XML_rot, aCameraRotationAttrList );
5901 }
5902 mpFS->endElementNS( XML_a, XML_camera );
5903 }
5904 else
5905 {
5906 // a:camera with Word default values - Word won't open the document if this is not present
5907 mpFS->singleElementNS(XML_a, XML_camera, XML_prst, "orthographicFront");
5908 }
5909
5910 if( aEffectProps.hasElements() )
5911 {
5912 mpFS->startElementNS( XML_a, XML_lightRig, aLightRigAttrList );
5913 if( bLightRigRotationPresent )
5914 {
5915 mpFS->singleElementNS( XML_a, XML_rot, aLightRigRotationAttrList );
5916 }
5917 mpFS->endElementNS( XML_a, XML_lightRig );
5918 }
5919 else
5920 {
5921 // a:lightRig with Word default values - Word won't open the document if this is not present
5922 mpFS->singleElementNS(XML_a, XML_lightRig, XML_rig, "threePt", XML_dir, "t");
5923 }
5924
5925 mpFS->endElementNS( XML_a, XML_scene3d );
5926
5927 if( !aShape3DProps.hasElements() )
5928 return;
5929
5930 bool bBevelTPresent = false, bBevelBPresent = false;
5931 Sequence< PropertyValue > aExtrusionColorProps, aContourColorProps;
5932 rtl::Reference<sax_fastparser::FastAttributeList> aBevelTAttrList = FastSerializerHelper::createAttrList();
5933 rtl::Reference<sax_fastparser::FastAttributeList> aBevelBAttrList = FastSerializerHelper::createAttrList();
5934 rtl::Reference<sax_fastparser::FastAttributeList> aShape3DAttrList = FastSerializerHelper::createAttrList();
5935 for( const auto& rShape3DProp : std::as_const(aShape3DProps) )
5936 {
5937 if( rShape3DProp.Name == "extrusionH" || rShape3DProp.Name == "contourW" || rShape3DProp.Name == "z" )
5938 {
5939 sal_Int32 nVal = 0, nToken = XML_none;
5940 rShape3DProp.Value >>= nVal;
5941 if( rShape3DProp.Name == "extrusionH" )
5942 nToken = XML_extrusionH;
5943 else if( rShape3DProp.Name == "contourW" )
5944 nToken = XML_contourW;
5945 else if( rShape3DProp.Name == "z" )
5946 nToken = XML_z;
5947 aShape3DAttrList->add( nToken, OString::number( nVal ) );
5948 }
5949 else if( rShape3DProp.Name == "prstMaterial" )
5950 {
5951 OUString sVal;
5952 rShape3DProp.Value >>= sVal;
5953 aShape3DAttrList->add(XML_prstMaterial, sVal);
5954 }
5955 else if( rShape3DProp.Name == "extrusionClr" )
5956 {
5957 rShape3DProp.Value >>= aExtrusionColorProps;
5958 }
5959 else if( rShape3DProp.Name == "contourClr" )
5960 {
5961 rShape3DProp.Value >>= aContourColorProps;
5962 }
5963 else if( rShape3DProp.Name == "bevelT" || rShape3DProp.Name == "bevelB" )
5964 {
5965 Sequence< PropertyValue > aBevelProps;
5966 rShape3DProp.Value >>= aBevelProps;
5967 if ( !aBevelProps.hasElements() )
5968 continue;
5969
5971 if( rShape3DProp.Name == "bevelT" )
5972 {
5973 bBevelTPresent = true;
5974 aBevelAttrList = aBevelTAttrList;
5975 }
5976 else
5977 {
5978 bBevelBPresent = true;
5979 aBevelAttrList = aBevelBAttrList;
5980 }
5981 for( const auto& rBevelProp : std::as_const(aBevelProps) )
5982 {
5983 if( rBevelProp.Name == "w" || rBevelProp.Name == "h" )
5984 {
5985 sal_Int32 nVal = 0, nToken = XML_none;
5986 rBevelProp.Value >>= nVal;
5987 if( rBevelProp.Name == "w" )
5988 nToken = XML_w;
5989 else if( rBevelProp.Name == "h" )
5990 nToken = XML_h;
5991 aBevelAttrList->add( nToken, OString::number( nVal ) );
5992 }
5993 else if( rBevelProp.Name == "prst" )
5994 {
5995 OUString sVal;
5996 rBevelProp.Value >>= sVal;
5997 aBevelAttrList->add(XML_prst, sVal);
5998 }
5999 }
6000
6001 }
6002 }
6003
6004 mpFS->startElementNS( XML_a, XML_sp3d, aShape3DAttrList );
6005 if( bBevelTPresent )
6006 {
6007 mpFS->singleElementNS( XML_a, XML_bevelT, aBevelTAttrList );
6008 }
6009 if( bBevelBPresent )
6010 {
6011 mpFS->singleElementNS( XML_a, XML_bevelB, aBevelBAttrList );
6012 }
6013 if( aExtrusionColorProps.hasElements() )
6014 {
6015 OUString sSchemeClr;
6016 ::Color nColor;
6017 sal_Int32 nTransparency(0);
6018 Sequence< PropertyValue > aColorTransformations;
6019 for( const auto& rExtrusionColorProp : std::as_const(aExtrusionColorProps) )
6020 {
6021 if( rExtrusionColorProp.Name == "schemeClr" )
6022 rExtrusionColorProp.Value >>= sSchemeClr;
6023 else if( rExtrusionColorProp.Name == "schemeClrTransformations" )
6024 rExtrusionColorProp.Value >>= aColorTransformations;
6025 else if( rExtrusionColorProp.Name == "rgbClr" )
6026 rExtrusionColorProp.Value >>= nColor;
6027 else if( rExtrusionColorProp.Name == "rgbClrTransparency" )
6028 rExtrusionColorProp.Value >>= nTransparency;
6029 }
6030 mpFS->startElementNS(XML_a, XML_extrusionClr);
6031
6032 if( sSchemeClr.isEmpty() )
6033 WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
6034 else
6035 WriteColor( sSchemeClr, aColorTransformations );
6036
6037 mpFS->endElementNS( XML_a, XML_extrusionClr );
6038 }
6039 if( aContourColorProps.hasElements() )
6040 {
6041 OUString sSchemeClr;
6042 ::Color nColor;
6043 sal_Int32 nTransparency(0);
6044 Sequence< PropertyValue > aColorTransformations;
6045 for( const auto& rContourColorProp : std::as_const(aContourColorProps) )
6046 {
6047 if( rContourColorProp.Name == "schemeClr" )
6048 rContourColorProp.Value >>= sSchemeClr;
6049 else if( rContourColorProp.Name == "schemeClrTransformations" )
6050 rContourColorProp.Value >>= aColorTransformations;
6051 else if( rContourColorProp.Name == "rgbClr" )
6052 rContourColorProp.Value >>= nColor;
6053 else if( rContourColorProp.Name == "rgbClrTransparency" )
6054 rContourColorProp.Value >>= nTransparency;
6055 }
6056 mpFS->startElementNS(XML_a, XML_contourClr);
6057
6058 if( sSchemeClr.isEmpty() )
6059 WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
6060 else
6061 WriteColor( sSchemeClr, aContourColorProps );
6062
6063 mpFS->endElementNS( XML_a, XML_contourClr );
6064 }
6065 mpFS->endElementNS( XML_a, XML_sp3d );
6066}
6069{
6070 if( !GetProperty( rXPropSet, "InteropGrabBag" ) )
6071 return;
6072
6073 PropertyValue aEffect;
6075 mAny >>= aGrabBag;
6076 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
6077 [](const PropertyValue& rProp) { return rProp.Name == "ArtisticEffectProperties"; });
6078 if (pProp != std::cend(aGrabBag))
6079 pProp->Value >>= aEffect;
6080 sal_Int32 nEffectToken = ArtisticEffectProperties::getEffectToken( aEffect.Name );
6081 if( nEffectToken == XML_none )
6082 return;
6083
6085 aEffect.Value >>= aAttrs;
6086 rtl::Reference<sax_fastparser::FastAttributeList> aAttrList = FastSerializerHelper::createAttrList();
6087 OString sRelId;
6088 for( const auto& rAttr : std::as_const(aAttrs) )
6089 {
6090 sal_Int32 nToken = ArtisticEffectProperties::getEffectToken( rAttr.Name );
6091 if( nToken != XML_none )
6092 {
6093 sal_Int32 nVal = 0;
6094 rAttr.Value >>= nVal;
6095 aAttrList->add( nToken, OString::number( nVal ) );
6096 }
6097 else if( rAttr.Name == "OriginalGraphic" )
6098 {
6100 rAttr.Value >>= aGraphic;
6101 Sequence< sal_Int8 > aGraphicData;
6102 OUString sGraphicId;
6103 for( const auto& rProp : std::as_const(aGraphic) )
6104 {
6105 if( rProp.Name == "Id" )
6106 rProp.Value >>= sGraphicId;
6107 else if( rProp.Name == "Data" )
6108 rProp.Value >>= aGraphicData;
6109 }
6110 sRelId = WriteWdpPicture( sGraphicId, aGraphicData );
6111 }
6112 }
6113
6114 mpFS->startElementNS(XML_a, XML_extLst);
6115 mpFS->startElementNS(XML_a, XML_ext, XML_uri, "{BEBA8EAE-BF5A-486C-A8C5-ECC9F3942E4B}");
6116 mpFS->startElementNS( XML_a14, XML_imgProps,
6117 FSNS(XML_xmlns, XML_a14), mpFB->getNamespaceURL(OOX_NS(a14)) );
6118 mpFS->startElementNS(XML_a14, XML_imgLayer, FSNS(XML_r, XML_embed), sRelId);
6119 mpFS->startElementNS(XML_a14, XML_imgEffect);
6120
6121 mpFS->singleElementNS( XML_a14, nEffectToken, aAttrList );
6122
6123 mpFS->endElementNS( XML_a14, XML_imgEffect );
6124 mpFS->endElementNS( XML_a14, XML_imgLayer );
6125 mpFS->endElementNS( XML_a14, XML_imgProps );
6126 mpFS->endElementNS( XML_a, XML_ext );
6127 mpFS->endElementNS( XML_a, XML_extLst );
6128}
6130OString DrawingML::WriteWdpPicture( const OUString& rFileId, const Sequence< sal_Int8 >& rPictureData )
6131{
6132 auto& rGraphicExportCache = GraphicExportCache::get();
6133
6134 OUString aId = rGraphicExportCache.findWdpID(rFileId);
6135 if (!aId.isEmpty())
6136 return OUStringToOString(aId, RTL_TEXTENCODING_UTF8);
6137
6138 sal_Int32 nWdpImageCount = rGraphicExportCache.nextWdpImageCount();
6139 OUString sFileName = "media/hdphoto" + OUString::number(nWdpImageCount) + ".wdp";
6140 OUString sFragment = OUStringBuffer().appendAscii(GetComponentDir()).append( "/" + sFileName).makeStringAndClear();
6141 Reference< XOutputStream > xOutStream = mpFB->openFragmentStream(sFragment, "image/vnd.ms-photo");
6142 xOutStream->writeBytes( rPictureData );
6143 xOutStream->closeOutput();
6144
6145 aId = mpFB->addRelation( mpFS->getOutputStream(),
6147 OUStringBuffer()
6148 .appendAscii( GetRelationCompPrefix() )
6149 .append( sFileName ) );
6150
6151 rGraphicExportCache.addToWdpCache(rFileId, aId);
6152
6153 return OUStringToOString(aId, RTL_TEXTENCODING_UTF8);
6154}
6156void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId)
6157{
6158 uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
6159
6160 uno::Reference<xml::dom::XDocument> dataDom;
6161 uno::Reference<xml::dom::XDocument> layoutDom;
6162 uno::Reference<xml::dom::XDocument> styleDom;
6163 uno::Reference<xml::dom::XDocument> colorDom;
6164 uno::Reference<xml::dom::XDocument> drawingDom;
6165 uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq;
6166 uno::Sequence<uno::Any> diagramDrawing;
6167
6168 // retrieve the doms from the GrabBag
6169 uno::Sequence<beans::PropertyValue> propList;
6170 xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
6171 for (const auto& rProp : std::as_const(propList))
6172 {
6173 OUString propName = rProp.Name;
6174 if (propName == "OOXData")
6175 rProp.Value >>= dataDom;
6176 else if (propName == "OOXLayout")
6177 rProp.Value >>= layoutDom;
6178 else if (propName == "OOXStyle")
6179 rProp.Value >>= styleDom;
6180 else if (propName == "OOXColor")
6181 rProp.Value >>= colorDom;
6182 else if (propName == "OOXDrawing")
6183 {
6184 rProp.Value >>= diagramDrawing;
6185 diagramDrawing[0]
6186 >>= drawingDom; // if there is OOXDrawing property then set drawingDom here only.
6187 }
6188 else if (propName == "OOXDiagramDataRels")
6189 rProp.Value >>= xDataRelSeq;
6190 }
6191
6192 // check that we have the 4 mandatory XDocuments
6193 // if not, there was an error importing and we won't output anything
6194 if (!dataDom.is() || !layoutDom.is() || !styleDom.is() || !colorDom.is())
6195 return;
6196
6197 // generate a unique id
6200 pDocPrAttrList->add(XML_id, OString::number(nDiagramId));
6201 OString sName = "Diagram" + OString::number(nDiagramId);
6202 pDocPrAttrList->add(XML_name, sName);
6203
6205 {
6206 mpFS->singleElementNS(XML_wp, XML_docPr, pDocPrAttrList);
6207 mpFS->singleElementNS(XML_wp, XML_cNvGraphicFramePr);
6208
6209 mpFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
6210 mpFB->getNamespaceURL(OOX_NS(dml)));
6211 }
6212 else
6213 {
6214 mpFS->startElementNS(XML_p, XML_nvGraphicFramePr);
6215
6216 mpFS->singleElementNS(XML_p, XML_cNvPr, pDocPrAttrList);
6217 mpFS->singleElementNS(XML_p, XML_cNvGraphicFramePr);
6218
6219 mpFS->startElementNS(XML_p, XML_nvPr);
6220 mpFS->startElementNS(XML_p, XML_extLst);
6221 // change tracking extension - required in PPTX
6222 mpFS->startElementNS(XML_p, XML_ext, XML_uri, "{D42A27DB-BD31-4B8C-83A1-F6EECF244321}");
6223 mpFS->singleElementNS(XML_p14, XML_modId,
6224 FSNS(XML_xmlns, XML_p14), mpFB->getNamespaceURL(OOX_NS(p14)),
6225 XML_val,
6227 mpFS->endElementNS(XML_p, XML_ext);
6228 mpFS->endElementNS(XML_p, XML_extLst);
6229 mpFS->endElementNS(XML_p, XML_nvPr);
6230
6231 mpFS->endElementNS(XML_p, XML_nvGraphicFramePr);
6232
6233 // store size and position of background shape instead of group shape
6234 // as some shapes may be outside
6235 css::uno::Reference<css::drawing::XShapes> xShapes(rXShape, uno::UNO_QUERY);
6236 if (xShapes.is() && xShapes->hasElements())
6237 {
6238 css::uno::Reference<css::drawing::XShape> xShapeBg(xShapes->getByIndex(0),
6239 uno::UNO_QUERY);
6240 awt::Point aPos = xShapeBg->getPosition();
6241 awt::Size aSize = xShapeBg->getSize();
6243 xShapeBg, tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)),
6244 XML_p, false, false, 0, false);
6245 }
6246
6247 mpFS->startElementNS(XML_a, XML_graphic);
6248 }
6249
6250 mpFS->startElementNS(XML_a, XML_graphicData, XML_uri,
6251 "http://schemas.openxmlformats.org/drawingml/2006/diagram");
6252
6253 OUString sRelationCompPrefix = OUString::createFromAscii(GetRelationCompPrefix());
6254
6255 // add data relation
6256 OUString dataFileName = "diagrams/data" + OUString::number(nDiagramId) + ".xml";
6257 OUString dataRelId =
6259 Concat2View(sRelationCompPrefix + dataFileName));
6260
6261 // add layout relation
6262 OUString layoutFileName = "diagrams/layout" + OUString::number(nDiagramId) + ".xml";
6263 OUString layoutRelId = mpFB->addRelation(mpFS->getOutputStream(),
6265 Concat2View(sRelationCompPrefix + layoutFileName));
6266
6267 // add style relation
6268 OUString styleFileName = "diagrams/quickStyle" + OUString::number(nDiagramId) + ".xml";
6269 OUString styleRelId = mpFB->addRelation(mpFS->getOutputStream(),
6271 Concat2View(sRelationCompPrefix + styleFileName));
6272
6273 // add color relation
6274 OUString colorFileName = "diagrams/colors" + OUString::number(nDiagramId) + ".xml";
6275 OUString colorRelId = mpFB->addRelation(mpFS->getOutputStream(),
6277 Concat2View(sRelationCompPrefix + colorFileName));
6278
6279 OUString drawingFileName;
6280 if (drawingDom.is())
6281 {
6282 // add drawing relation
6283 drawingFileName = "diagrams/drawing" + OUString::number(nDiagramId) + ".xml";
6284 OUString drawingRelId = mpFB->addRelation(
6286 Concat2View(sRelationCompPrefix + drawingFileName));
6287
6288 // the data dom contains a reference to the drawing relation. We need to update it with the new generated
6289 // relation value before writing the dom to a file
6290
6291 // Get the dsp:damaModelExt node from the dom
6292 uno::Reference<xml::dom::XNodeList> nodeList = dataDom->getElementsByTagNameNS(
6293 "http://schemas.microsoft.com/office/drawing/2008/diagram", "dataModelExt");
6294
6295 // There must be one element only so get it
6296 uno::Reference<xml::dom::XNode> node = nodeList->item(0);
6297
6298 // Get the list of attributes of the node
6299 uno::Reference<xml::dom::XNamedNodeMap> nodeMap = node->getAttributes();
6300
6301 // Get the node with the relId attribute and set its new value
6302 uno::Reference<xml::dom::XNode> relIdNode = nodeMap->getNamedItem("relId");
6303 relIdNode->setNodeValue(drawingRelId);
6304 }
6305
6306 mpFS->singleElementNS(XML_dgm, XML_relIds,
6307 FSNS(XML_xmlns, XML_dgm), mpFB->getNamespaceURL(OOX_NS(dmlDiagram)),
6308 FSNS(XML_xmlns, XML_r), mpFB->getNamespaceURL(OOX_NS(officeRel)),
6309 FSNS(XML_r, XML_dm), dataRelId, FSNS(XML_r, XML_lo), layoutRelId,
6310 FSNS(XML_r, XML_qs), styleRelId, FSNS(XML_r, XML_cs), colorRelId);
6311
6312 mpFS->endElementNS(XML_a, XML_graphicData);
6313 mpFS->endElementNS(XML_a, XML_graphic);
6314
6315 uno::Reference<xml::sax::XSAXSerializable> serializer;
6316 uno::Reference<xml::sax::XWriter> writer
6317 = xml::sax::Writer::create(comphelper::getProcessComponentContext());
6318
6319 OUString sDir = OUString::createFromAscii(GetComponentDir());
6320
6321 // write data file
6322 serializer.set(dataDom, uno::UNO_QUERY);
6323 uno::Reference<io::XOutputStream> xDataOutputStream = mpFB->openFragmentStream(
6324 sDir + "/" + dataFileName,
6325 "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml");
6326 writer->setOutputStream(xDataOutputStream);
6327 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
6328 uno::Sequence<beans::StringPair>());
6329
6330 // write the associated Images and rels for data file
6331 writeDiagramRels(xDataRelSeq, xDataOutputStream, u"OOXDiagramDataRels", nDiagramId);
6332
6333 // write layout file
6334 serializer.set(layoutDom, uno::UNO_QUERY);
6335 writer->setOutputStream(mpFB->openFragmentStream(
6336 sDir + "/" + layoutFileName,
6337 "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml"));
6338 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
6339 uno::Sequence<beans::StringPair>());
6340
6341 // write style file
6342 serializer.set(styleDom, uno::UNO_QUERY);
6343 writer->setOutputStream(mpFB->openFragmentStream(
6344 sDir + "/" + styleFileName,
6345 "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml"));
6346 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
6347 uno::Sequence<beans::StringPair>());
6348
6349 // write color file
6350 serializer.set(colorDom, uno::UNO_QUERY);
6351 writer->setOutputStream(mpFB->openFragmentStream(
6352 sDir + "/" + colorFileName,
6353 "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml"));
6354 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
6355 uno::Sequence<beans::StringPair>());
6356
6357 // write drawing file
6358 if (!drawingDom.is())
6359 return;
6360
6361 serializer.set(drawingDom, uno::UNO_QUERY);
6362 uno::Reference<io::XOutputStream> xDrawingOutputStream = mpFB->openFragmentStream(
6363 sDir + "/" + drawingFileName, "application/vnd.ms-office.drawingml.diagramDrawing+xml");
6364 writer->setOutputStream(xDrawingOutputStream);
6365 serializer->serialize(
6366 uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
6367 uno::Sequence<beans::StringPair>());
6368
6369 // write the associated Images and rels for drawing file
6370 uno::Sequence<uno::Sequence<uno::Any>> xDrawingRelSeq;
6371 diagramDrawing[1] >>= xDrawingRelSeq;
6372 writeDiagramRels(xDrawingRelSeq, xDrawingOutputStream, u"OOXDiagramDrawingRels", nDiagramId);
6373}
6375void DrawingML::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq,
6376 const uno::Reference<io::XOutputStream>& xOutStream,
6377 std::u16string_view sGrabBagProperyName, int nDiagramId)
6378{
6379 // add image relationships of OOXData, OOXDiagram
6381 uno::Reference<xml::sax::XWriter> xWriter
6382 = xml::sax::Writer::create(comphelper::getProcessComponentContext());
6383 xWriter->setOutputStream(xOutStream);
6384
6385 // retrieve the relationships from Sequence
6386 for (sal_Int32 j = 0; j < xRelSeq.getLength(); j++)
6387 {
6388 // diagramDataRelTuple[0] => RID,
6389 // diagramDataRelTuple[1] => xInputStream
6390 // diagramDataRelTuple[2] => extension
6391 uno::Sequence<uno::Any> diagramDataRelTuple = xRelSeq[j];
6392
6393 OUString sRelId;
6394 OUString sExtension;
6395 diagramDataRelTuple[0] >>= sRelId;
6396 diagramDataRelTuple[2] >>= sExtension;
6397 OUString sContentType;
6398 if (sExtension.equalsIgnoreAsciiCase(".WMF"))
6399 sContentType = "image/x-wmf";
6400 else
6401 sContentType = OUString::Concat("image/") + sExtension.subView(1);
6402 sRelId = sRelId.copy(3);
6403
6404 StreamDataSequence dataSeq;
6405 diagramDataRelTuple[1] >>= dataSeq;
6406 uno::Reference<io::XInputStream> dataImagebin(
6407 new ::comphelper::SequenceInputStream(dataSeq));
6408
6409 //nDiagramId is used to make the name unique irrespective of the number of smart arts.
6410 OUString sFragment = OUString::Concat("media/") + sGrabBagProperyName
6411 + OUString::number(nDiagramId) + "_"
6412 + OUString::number(j) + sExtension;
6413
6414 PropertySet aProps(xOutStream);
6415 aProps.setAnyProperty(PROP_RelId, uno::Any(sRelId.toInt32()));
6416
6417 mpFB->addRelation(xOutStream, sType, Concat2View("../" + sFragment));
6418
6419 OUString sDir = OUString::createFromAscii(GetComponentDir());
6420 uno::Reference<io::XOutputStream> xBinOutStream
6421 = mpFB->openFragmentStream(sDir + "/" + sFragment, sContentType);
6422
6423 try
6424 {
6425 comphelper::OStorageHelper::CopyInputToOutput(dataImagebin, xBinOutStream);
6426 }
6427 catch (const uno::Exception&)
6428 {
6429 TOOLS_WARN_EXCEPTION("oox.drawingml", "DrawingML::writeDiagramRels Failed to copy grabbaged Image");
6430 }
6431 dataImagebin->closeInput();
6432 }
6433}
6435void DrawingML::WriteFromTo(const uno::Reference<css::drawing::XShape>& rXShape, const awt::Size& aPageSize,
6436 const FSHelperPtr& pDrawing)
6437{
6438 awt::Point aTopLeft = rXShape->getPosition();
6439 awt::Size aSize = rXShape->getSize();
6440
6442 if (pObj)
6443 {
6444 Degree100 nRotation = pObj->GetRotateAngle();
6445 if (nRotation)
6446 {
6447 sal_Int16 nHalfWidth = aSize.Width / 2;
6448 sal_Int16 nHalfHeight = aSize.Height / 2;
6449 // aTopLeft needs correction for rotated customshapes
6450 if (pObj->GetObjIdentifier() == SdrObjKind::CustomShape)
6451 {
6452 // Center of bounding box of the rotated shape
6453 const auto aSnapRectCenter(pObj->GetSnapRect().Center());
6454 aTopLeft.X = aSnapRectCenter.X() - nHalfWidth;
6455 aTopLeft.Y = aSnapRectCenter.Y() - nHalfHeight;
6456 }
6457
6458 // MSO changes the anchor positions at these angles and that does an extra 90 degrees
6459 // rotation on our shapes, so we output it in such position that MSO
6460 // can draw this shape correctly.
6461 if ((nRotation >= 4500_deg100 && nRotation < 13500_deg100) || (nRotation >= 22500_deg100 && nRotation < 31500_deg100))
6462 {
6463 aTopLeft.X = aTopLeft.X - nHalfHeight + nHalfWidth;
6464 aTopLeft.Y = aTopLeft.Y - nHalfWidth + nHalfHeight;
6465
6466 std::swap(aSize.Width, aSize.Height);
6467 }
6468 }
6469 }
6470
6471 tools::Rectangle aLocation(aTopLeft.X, aTopLeft.Y, aTopLeft.X + aSize.Width, aTopLeft.Y + aSize.Height);
6472 double nXpos = static_cast<double>(aLocation.TopLeft().getX()) / static_cast<double>(aPageSize.Width);
6473 double nYpos = static_cast<double>(aLocation.TopLeft().getY()) / static_cast<double>(aPageSize.Height);
6474
6475 pDrawing->startElement(FSNS(XML_cdr, XML_from));
6476 pDrawing->startElement(FSNS(XML_cdr, XML_x));
6477 pDrawing->write(nXpos);
6478 pDrawing->endElement(FSNS(XML_cdr, XML_x));
6479 pDrawing->startElement(FSNS(XML_cdr, XML_y));
6480 pDrawing->write(nYpos);
6481 pDrawing->endElement(FSNS(XML_cdr, XML_y));
6482 pDrawing->endElement(FSNS(XML_cdr, XML_from));
6483
6484 nXpos = static_cast<double>(aLocation.BottomRight().getX()) / static_cast<double>(aPageSize.Width);
6485 nYpos = static_cast<double>(aLocation.BottomRight().getY()) / static_cast<double>(aPageSize.Height);
6486
6487 pDrawing->startElement(FSNS(XML_cdr, XML_to));
6488 pDrawing->startElement(FSNS(XML_cdr, XML_x));
6489 pDrawing->write(nXpos);
6490 pDrawing->endElement(FSNS(XML_cdr, XML_x));
6491 pDrawing->startElement(FSNS(XML_cdr, XML_y));
6492 pDrawing->write(nYpos);
6493 pDrawing->endElement(FSNS(XML_cdr, XML_y));
6494 pDrawing->endElement(FSNS(XML_cdr, XML_to));
6495}
6496
6497}
6498
6499/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OptionalString sType
sal_Int32 nLineWidth
bool bHasShadow
PropertiesInfo aProperties
constexpr auto convertTwipToMm100(N n)
const Any & any
constexpr OUStringLiteral sMediaType
sal_uInt64 BitmapChecksum
void Erase(sal_uInt8 cTransparency)
static OutputDevice * GetDefaultDevice()
bool Scale(const Size &rNewSize, BmpScaleFlag nScaleFlag=BmpScaleFlag::Default)
bool CopyPixel(const tools::Rectangle &rRectDst, const tools::Rectangle &rRectSrc, const BitmapEx *pBmpExSrc)
bool IsDark() const
void GetParameter(double &rParameterReturnValue, const css::drawing::EnhancedCustomShapeParameter &, const bool bReplaceGeoWidth, const bool bReplaceGeoHeight) const
SAL_DLLPRIVATE bool IsFlipHorz() const
const tools::Rectangle & GetLogicRect() const
tools::Rectangle GetTextRect() const
sal_Int32 GetLuminanceChange(sal_uInt32 nIndex) const
SAL_DLLPRIVATE bool IsFlipVert() const
static void LookForPolarHandles(const MSO_SPT eShapeType, sal_Int32 &nAdjustmentsWhichNeedsToBeConverted)
static tools::PolyPolygon GetPolyPolygon(const css::uno::Reference< css::drawing::XShape > &rXShape)
static bool GetAdjustmentValue(const css::drawing::EnhancedCustomShapeAdjustmentValue &rkProp, sal_Int32 nIndex, sal_Int32 nAdjustmentsWhichNeedsToBeConverted, sal_Int32 &nValue)
static bool GetLineArrow(const bool bLineStart, const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, ESCHER_LineEnd &reLineEnd, sal_Int32 &rnArrowLength, sal_Int32 &rnArrowWidth)
static ErrCode Export(SvStream &rOStm, const Graphic &rGraphic, ConvertDataFormat nFormat)
Size GetPrefSize() const
GraphicType GetType() const
GfxLink GetGfxLink() const
BitmapChecksum GetChecksum() const
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
MapMode GetPrefMapMode() const
bool isSystemLocale() const
OUString getBcp47MS() const
MapUnit GetMapUnit() const
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
constexpr tools::Long Y() const
constexpr tools::Long X() const
constexpr tools::Long getX() const
constexpr tools::Long getY() const
css::uno::Reference< css::io::XInputStream > GetInputStream() const
const OUString & getURL() const
const ::avmedia::MediaItem & getMediaProperties() const
static SdrObject * getSdrObjectFromXShape(const css::uno::Reference< css::uno::XInterface > &xInt)
virtual Degree100 GetRotateAngle() const
virtual const tools::Rectangle & GetSnapRect() const
virtual SdrObjKind GetObjIdentifier() const
bool IsTextEditActive() const
double GetFontScale() const
virtual OutlinerParaObject * GetOutlinerParaObject() const override
std::optional< OutlinerParaObject > CreateEditOutlinerParaObject() const
double GetSpacingScale() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
const void * GetData()
std::size_t GetEndOfData() const
bool ReadLine(OStringBuffer &rStr, sal_Int32 nMaxBytesToRead=0xFFFE)
ErrCode GetError() const
SdrObject * GetSdrObject() const
OUString getMimeType() const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
bool isSingleColor(BColor &rSingleColor) const
void doApplySteps(sal_uInt16 nStepCount)
double luminance() const
double getRed() const
sal_uInt16 GetSteps() const
sal_uInt16 GetXOffset() const
const basegfx::BColorStops & GetColorStops() const
Degree10 GetAngle() const
css::awt::GradientStyle GetGradientStyle() const
sal_uInt16 GetYOffset() const
TYPE getMinX() const
TYPE getMinY() const
static void CopyInputToOutput(const css::uno::Reference< css::io::XInputStream > &xInput, const css::uno::Reference< css::io::XOutputStream > &xOutput)
A wrapper for a UNO property set.
Definition: propertyset.hxx:58
bool setAnyProperty(sal_Int32 nPropId, const css::uno::Any &rValue)
Puts the passed any into the property set.
Definition: propertyset.cxx:70
::sax_fastparser::FSHelperPtr openFragmentStreamWithSerializer(const OUString &rStreamName, const OUString &rMediaType)
Opens specified output stream from the base storage with specified media type and returns new fast se...
OUString addRelation(const OUString &rType, std::u16string_view rTarget)
Adds new relation.
css::uno::Reference< css::io::XOutputStream > openFragmentStream(const OUString &rStreamName, const OUString &rMediaType)
Opens and returns the specified output stream from the base storage with specified media type.
OUString getNamespaceURL(sal_Int32 nNSID) const
static sal_Int32 getColorTransformationToken(std::u16string_view sName)
Translates between color transformation token names and the corresponding token.
Definition: color.cxx:495
virtual css::uno::Reference< css::text::XTextFrame > GetUnoTextFrame(css::uno::Reference< css::drawing::XShape > xShape)=0
Get textbox which belongs to the shape.
virtual void WriteOutliner(const OutlinerParaObject &rParaObj)=0
void WriteLinespacing(const css::style::LineSpacing &rLineSpacing, float fFirstCharHeight)
Definition: drawingml.cxx:3324
static const char * GetAlignment(css::style::ParagraphAdjust nAlignment)
Definition: drawingml.cxx:3302
const char * GetComponentDir() const
Definition: drawingml.cxx:1276
::oox::core::XmlFilterBase * mpFB
Definition: drawingml.hxx:287
void WriteXGraphicStretch(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet, css::uno::Reference< css::graphic::XGraphic > const &rxGraphic)
Definition: drawingml.cxx:1926
void WriteColorTransformations(const css::uno::Sequence< css::beans::PropertyValue > &aTransformations, sal_Int32 nAlpha=MAX_PERCENT)
Definition: drawingml.cxx:395
void WriteShapeStyle(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Definition: drawingml.cxx:5379
static sal_Int32 mnDrawingMLCount
Definition: drawingml.hxx:275
void WriteCustomGeometryPoint(const css::drawing::EnhancedCustomShapeParameterPair &rParamPair, const EnhancedCustomShape2d &rCustomShape2d, const bool bReplaceGeoWidth, const bool bReplaceGeoHeight)
Definition: drawingml.cxx:5119
static sal_Int16 GetScriptType(const OUString &rStr)
Definition: drawingml.cxx:244
OString WriteWdpPicture(const OUString &rFileId, const css::uno::Sequence< sal_Int8 > &rPictureData)
Definition: drawingml.cxx:6129
void WriteLineArrow(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, bool bLineStart)
Definition: drawingml.cxx:867
void WriteXGraphicBlipMode(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet, css::uno::Reference< css::graphic::XGraphic > const &rxGraphic, css::awt::Size const &rSize)
Definition: drawingml.cxx:1725
OUString WriteImage(const Graphic &rGraphic, bool bRelPathToMedia=false)
If bRelPathToMedia is true add "../" to image folder path while adding the image relationship.
Definition: drawingml.cxx:1536
bool WriteCustomGeometrySegment(const sal_Int16 eCommand, const sal_Int32 nCount, const css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > &rPairs, sal_Int32 &rnPairIndex, double &rfCurrentX, double &rfCurrentY, bool &rbCurrentValid, const EnhancedCustomShape2d &rCustomShape2d, const bool bReplaceGeoWidth, const bool bReplaceGeoHeight)
Definition: drawingml.cxx:4784
void WriteXGraphicCustomPosition(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet, css::uno::Reference< css::graphic::XGraphic > const &rxGraphic, css::awt::Size const &rSize)
Definition: drawingml.cxx:2085
static sal_Int32 mnChartCount
Definition: drawingml.hxx:291
void WriteGradientFill(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
void WriteRun(const css::uno::Reference< css::text::XTextRange > &rRun, bool &rbOverridingCharHeight, sal_Int32 &rnCharHeight, const css::uno::Reference< css::beans::XPropertySet > &rXShapePropSet)
Definition: drawingml.cxx:2922
OUString GetFieldValue(const css::uno::Reference< css::text::XTextRange > &rRun, bool &bIsURLField)
Definition: drawingml.cxx:2764
static bool IsGroupShape(const css::uno::Reference< css::drawing::XShape > &rXShape)
Definition: drawingml.cxx:3262
void WriteGlowEffect(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Definition: drawingml.cxx:5744
static void WriteFromTo(const css::uno::Reference< css::drawing::XShape > &rXShape, const css::awt::Size &aPageSize, const sax_fastparser::FSHelperPtr &pDrawing)
Definition: drawingml.cxx:6434
void WriteDiagram(const css::uno::Reference< css::drawing::XShape > &rXShape, int nDiagramId)
Definition: drawingml.cxx:6155
void WriteMediaNonVisualProperties(const css::uno::Reference< css::drawing::XShape > &xShape)
Output the media (including copying a video from vnd.sun.star.Package: to the output if necessary).
Definition: drawingml.cxx:1542
sax_fastparser::FSHelperPtr CreateOutputStream(const OUString &sFullStream, std::u16string_view sRelativeStream, const css::uno::Reference< css::io::XOutputStream > &xParentRelation, const OUString &sContentType, const OUString &sRelationshipType, OUString *pRelationshipId)
Definition: drawingml.cxx:5264
void WriteShapeEffects(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Definition: drawingml.cxx:5596
static void ResetMlCounters()
Definition: drawingml.cxx:268
void WriteText(const css::uno::Reference< css::uno::XInterface > &rXIface, bool bBodyPr, bool bText=true, sal_Int32 nXmlNamespace=0, bool bWritePropertiesAsLstStyles=false)
Definition: drawingml.cxx:3615
::sax_fastparser::FSHelperPtr mpFS
Definition: drawingml.hxx:286
bool GetProperty(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, const OUString &aName)
Definition: drawingml.cxx:275
void WriteXGraphicBlipFill(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet, css::uno::Reference< css::graphic::XGraphic > const &rxGraphic, sal_Int32 nXmlNamespace, bool bWriteMode, bool bRelPathToMedia=false, css::awt::Size const &rSize={})
Definition: drawingml.cxx:1796
void WriteParagraphTabStops(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Definition: drawingml.cxx:3228
void WriteStyleProperties(sal_Int32 nTokenId, const css::uno::Sequence< css::beans::PropertyValue > &aProperties)
Definition: drawingml.cxx:5352
static sal_Int32 mnVmlCount
Definition: drawingml.hxx:276
DocumentType meDocumentType
To specify where write eg. the images to (like 'ppt', or 'word' - according to the OPC).
Definition: drawingml.hxx:279
void WriteXGraphicTile(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet, css::uno::Reference< css::graphic::XGraphic > const &rxGraphic, css::awt::Size const &rSize)
Definition: drawingml.cxx:2006
void WriteConnectorConnections(sal_Int32 nStartGlueId, sal_Int32 nEndGlueId, sal_Int32 nStartID, sal_Int32 nEndID)
Definition: drawingml.cxx:5236
void Write3DEffects(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, bool bIsText)
Populates scene3d tag.
Definition: drawingml.cxx:5788
void WriteBlipFill(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, const OUString &sURLPropName, const css::awt::Size &rSize={})
bool WriteCustomGeometry(const css::uno::Reference< css::drawing::XShape > &rXShape, const SdrObjCustomShape &rSdrObjCustomShape)
Definition: drawingml.cxx:4519
void WriteFill(const css::uno::Reference< css::beans::XPropertySet > &xPropSet, const css::awt::Size &rSize={})
Definition: drawingml.cxx:5286
bool IsFontworkShape(const css::uno::Reference< css::beans::XPropertySet > &rXShapePropSet)
Definition: drawingml.cxx:3584
bool WriteParagraphProperties(const css::uno::Reference< css::text::XTextContent > &rParagraph, float fFirstCharHeight, sal_Int32 nElement)
Writes paragraph properties.
Definition: drawingml.cxx:3345
const ::sax_fastparser::FSHelperPtr & GetFS() const
Definition: drawingml.hxx:345
void WriteTransformation(const css::uno::Reference< css::drawing::XShape > &xShape, const tools::Rectangle &rRectangle, sal_Int32 nXmlNamespace, bool bFlipH=false, bool bFlipV=false, sal_Int32 nRotation=0, bool bIsGroupShape=false)
Definition: drawingml.cxx:2175
::oox::core::XmlFilterBase * GetFB()
Definition: drawingml.hxx:346
bool WriteSchemeColor(OUString const &rPropertyName, const css::uno::Reference< css::beans::XPropertySet > &xPropertySet)
Definition: drawingml.cxx:532
void WriteBlipOrNormalFill(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, const OUString &rURLPropName, const css::awt::Size &rSize={})
Definition: drawingml.cxx:1751
sal_Int32 getBulletMarginIndentation(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, sal_Int16 nLevel, std::u16string_view propName)
Definition: drawingml.cxx:3273
void WriteParagraphNumbering(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, float fFirstCharHeight, sal_Int16 nLevel)
Definition: drawingml.cxx:3051
DocumentType GetDocumentType() const
Definition: drawingml.hxx:347
void WriteArtisticEffect(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Definition: drawingml.cxx:6067
void WriteSoftEdgeEffect(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Definition: drawingml.cxx:5768
static OUString GetDatetimeTypeFromTime(SvxTimeFormat eTime)
Gets OOXML datetime field type from LO Time format.
Definition: drawingml.cxx:2848
css::uno::Reference< css::drawing::XShape > m_xParent
If set, this is the parent of the currently handled shape.
Definition: drawingml.hxx:289
const char * GetRelationCompPrefix() const
Definition: drawingml.cxx:1281
void WriteShapeTransformation(const css::uno::Reference< css::drawing::XShape > &rXShape, sal_Int32 nXmlNamespace, bool bFlipH=false, bool bFlipV=false, bool bSuppressRotation=false, bool bSuppressFlipping=false, bool bFlippedBeforeRotation=false)
Definition: drawingml.cxx:2214
void WriteSolidFill(::Color nColor, sal_Int32 nAlpha=MAX_PERCENT)
Definition: drawingml.cxx:415
void WriteGradientStop(double fOffset, const basegfx::BColor &rColor, const basegfx::BColor &rAlpha)
Definition: drawingml.cxx:586
void WriteColor(::Color nColor, sal_Int32 nAlpha=MAX_PERCENT)
Definition: drawingml.cxx:334
void WriteShapeEffect(std::u16string_view sName, const css::uno::Sequence< css::beans::PropertyValue > &aEffectProps)
Definition: drawingml.cxx:5407
void WriteGraphicCropProperties(css::uno::Reference< css::beans::XPropertySet > const &rxPropertySet, Size const &rOriginalSize, MapMode const &rMapMode)
Definition: drawingml.cxx:1880
void WriteImageBrightnessContrastTransparence(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet)
Definition: drawingml.cxx:1653
::Color ColorWithIntensity(sal_uInt32 nColor, sal_uInt32 nIntensity)
Definition: drawingml.cxx:595
void WritePattFill(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
bool GetPropertyAndState(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, const css::uno::Reference< css::beans::XPropertyState > &rXPropState, const OUString &aName, css::beans::PropertyState &eState)
Definition: drawingml.cxx:290
static sal_Unicode SubstituteBullet(sal_Unicode cBulletId, css::awt::FontDescriptor &rFontDesc)
Definition: drawingml.cxx:5252
void WriteSrcRectXGraphic(css::uno::Reference< css::beans::XPropertySet > const &rxPropertySet, css::uno::Reference< css::graphic::XGraphic > const &rxGraphic)
Definition: drawingml.cxx:1917
bool mbPlaceholder
True when exporting presentation placeholder shape.
Definition: drawingml.hxx:294
void WritePresetShape(const OString &pShape, std::vector< std::pair< sal_Int32, sal_Int32 > > &rAvList)
Definition: drawingml.cxx:4260
void WritePolyPolygon(const css::uno::Reference< css::drawing::XShape > &rXShape, const bool bClosed)
Definition: drawingml.cxx:5145
void WriteOutline(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, css::uno::Reference< css::frame::XModel > const &xModel=nullptr)
Definition: drawingml.cxx:937
static OUString GetDatetimeTypeFromDateTime(SvxDateFormat eDate, SvxTimeFormat eTime)
Gets OOXML datetime field type from combination of LO Time and Date formats.
Definition: drawingml.cxx:2853
static OUString GetDatetimeTypeFromDate(SvxDateFormat eDate)
Gets OOXML datetime field type from LO Date format.
Definition: drawingml.cxx:2843
void WriteLstStyles(const css::uno::Reference< css::text::XTextContent > &rParagraph, bool &rbOverridingCharHeight, sal_Int32 &rnCharHeight, const css::uno::Reference< css::beans::XPropertySet > &rXShapePropSet)
Populates the lstStyle with the shape's text run and paragraph properties.
Definition: drawingml.cxx:3498
void writeDiagramRels(const css::uno::Sequence< css::uno::Sequence< css::uno::Any > > &xRelSeq, const css::uno::Reference< css::io::XOutputStream > &xOutStream, std::u16string_view sGrabBagProperyName, int nDiagramId)
Definition: drawingml.cxx:6374
void WriteRunProperties(const css::uno::Reference< css::beans::XPropertySet > &rRun, bool bIsField, sal_Int32 nElement, bool bCheckDirect, bool &rbOverridingCharHeight, sal_Int32 &rnCharHeight, sal_Int16 nScriptType=css::i18n::ScriptType::LATIN, const css::uno::Reference< css::beans::XPropertySet > &rXShapePropSet={})
Definition: drawingml.cxx:2340
void WriteParagraph(const css::uno::Reference< css::text::XTextContent > &rParagraph, bool &rbOverridingCharHeight, sal_Int32 &rnCharHeight, const css::uno::Reference< css::beans::XPropertySet > &rXShapePropSet)
Definition: drawingml.cxx:3537
void WriteGrabBagGradientFill(const css::uno::Sequence< css::beans::PropertyValue > &aGradientStops, const basegfx::BGradient &rGradient)
Definition: drawingml.cxx:682
OUString WriteXGraphicBlip(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet, css::uno::Reference< css::graphic::XGraphic > const &rxGraphic, bool bRelPathToMedia)
Definition: drawingml.cxx:1702
DMLTextExport * mpTextExport
Parent exporter, used for text callback.
Definition: drawingml.hxx:281
OUString findExportGraphics(BitmapChecksum aChecksum)
Definition: drawingml.hxx:205
static GraphicExportCache & get()
Definition: drawingml.cxx:190
void addExportGraphics(BitmapChecksum aChecksum, OUString const &sPath)
Definition: drawingml.hxx:200
OUString writeBlip(Graphic const &rGraphic, std::vector< model::BlipEffect > const &rEffects, bool bRelPathToMedia=false)
Definition: drawingml.cxx:1286
sax_fastparser::FSHelperPtr mpFS
Definition: drawingml.hxx:256
oox::core::XmlFilterBase * mpFilterBase
Definition: drawingml.hxx:257
OUString writeToStorage(Graphic const &rGraphic, bool bRelPathToMedia=false)
Definition: drawingml.cxx:1408
virtual bool isExternalURL(const OUString &rURL) const
Definition: drawingml.cxx:182
virtual OUString getTransformedString(const OUString &rURL) const
Definition: drawingml.cxx:177
static rtl::Reference< FastAttributeList > createAttrList()
sal_uInt16 Count() const
PolyFlags GetFlags(sal_uInt16 nPos) const
sal_uInt16 GetSize() const
constexpr Point Center() const
constexpr tools::Long GetWidth() const
constexpr tools::Long Top() const
constexpr Point TopLeft() const
tools::Long getOpenHeight() const
constexpr Size GetSize() const
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
constexpr tools::Long Right() const
constexpr Point BottomRight() const
constexpr tools::Long GetHeight() const
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
constexpr ::Color COL_WHITE(0xFF, 0xFF, 0xFF)
ColorTransparency
constexpr ::Color COL_AUTO(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
constexpr OUStringLiteral sColor
int nCount
double toRadians(D x)
#define TOOLS_WARN_EXCEPTION(area, stream)
OString sFormula
Definition: drawingml.cxx:4452
OString right
Definition: drawingml.cxx:4445
OString top
Definition: drawingml.cxx:4444
OString bottom
Definition: drawingml.cxx:4446
OString sName
Definition: drawingml.cxx:4451
float u
EmbeddedObjectRef * pObject
#define ERRCODE_NONE
#define DFLT_ESC_AUTO_SUB
#define DFLT_ESC_AUTO_SUPER
ESCHER_LineMediumWidthArrow
ESCHER_LineWideArrow
ESCHER_LineNarrowArrow
ESCHER_LineLongArrow
ESCHER_LineMediumLenArrow
ESCHER_LineShortArrow
ESCHER_LineEnd
ESCHER_LineArrowOvalEnd
ESCHER_LineArrowStealthEnd
ESCHER_LineArrowOpenEnd
ESCHER_LineArrowDiamondEnd
ESCHER_LineNoEnd
ESCHER_LineArrowEnd
SvxTimeFormat
SvxDateFormat
sal_Int16 nValue
UNOTOOLS_DLLPUBLIC bool IsOpenSymbol(std::u16string_view rFontName)
UNOTOOLS_DLLPUBLIC OUString GetSubsFontName(std::u16string_view rName, SubsFontFlags nFlags)
constexpr sal_Int32 FSNS(sal_Int32 namespc, sal_Int32 element)
GraphicType
sal_Int32 nIndex
OUString aName
Mode eMode
void * p
sal_uInt16 nPos
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
MSO_SPT
void applyUpdatesToCharInteropGrabBag(const std::vector< css::beans::PropertyValue > &rUpdatePropVec, css::uno::Reference< css::text::XText > &rXText)
Puts all properties in rUpdatePropVec into the "CharInteropGrabBag" of all non-empty runs in rXText.
void createCharFillPropsFromShape(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, std::vector< css::beans::PropertyValue > &rCharPropVec)
Generates the properties "CharColor", "CharLumMod", "CharLumOff", "CharColorTheme",...
void collectCharColorProps(const css::uno::Reference< css::text::XText > &rXText, std::vector< css::beans::PropertyValue > &rCharPropVec)
Collects the properties "CharColor", "CharLumMod", "CharLumOff", "CharColorTheme",...
void applyPropsToRuns(const std::vector< css::beans::PropertyValue > &rTextPropVec, css::uno::Reference< css::text::XText > &rXText)
Applies all properties in rTextPropVec excluding "CharInteropGrabBag" to all non-empty runs in rXText...
void createCharInteropGrabBagUpdatesFromShapeProps(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, std::vector< css::beans::PropertyValue > &rUpdatePropVec)
Creates the properties "CharTextFillTextEffect", "CharTextOutlineTextEffect", "CharThemeColor",...
def run(arg=None, arg2=-1)
@ paragraph
OUString GetMsoName(std::u16string_view rFontworkType)
bold
tools::Long const nLeftMargin
bool equalZero(const T &rfVal)
bool equal(T const &rfValA, T const &rfValB)
void synchronizeColorStops(BColorStops &rColorStops, BColorStops &rAlphaStops, const BColor &rSingleColor, const BColor &rSingleAlpha)
void prepareColorStops(const basegfx::BGradient &rGradient, BColorStops &rColorStops, BColor &rSingleColor)
B2DHomMatrix createRotateB2DHomMatrix(double fRadiant)
constexpr double rad2deg(double v)
B2IRange fround(const B2DRange &rRange)
constexpr double deg2rad(double v)
@ Exception
enum SAL_DLLPUBLIC_RTTI FillStyle
unsigned int uniform_uint_distribution(unsigned int a, unsigned int b)
OString generateGUIDString()
Reference< XComponentContext > getProcessComponentContext()
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
std::map< sal_Int32, std::shared_ptr< SetItemPropertyStorage > > PropertyValues
Value
OUString aPropName
model::ComplexColor getFromXComplexColor(uno::Reference< util::XComplexColor > const &rxColor)
BitmapMode
sal_Unicode bestFitOpenSymbolToMSFont(sal_Unicode cBullet, rtl_TextEncoding &r_ioChrSet, OUString &r_ioFontName)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
static OUString lclConvertRectanglePointToToken(RectanglePoint eRectanglePoint)
Definition: drawingml.cxx:1968
TextVerticalAdjust GetTextVerticalAdjust(sal_Int32 nToken)
OString calcRotationValue(sal_Int32 nRotation)
nRotation is a 100th of a degree and the return value is in a 60,000th of a degree
sal_Int32 convertEmuToHmm(sal_Int64 nValue)
Converts the passed 64-bit integer value from EMUs to 1/100 mm.
constexpr const char * getComponentDir(DocumentType eDocumentType)
Definition: drawingml.hxx:143
static OUString GetAutoNumType(SvxNumType nNumberingType, bool bSDot, bool bPBehind, bool bPBoth)
Definition: drawingml.cxx:3010
static css::uno::Any getLineDash(const css::uno::Reference< css::frame::XModel > &xModel, const OUString &rDashName)
Definition: drawingml.cxx:196
sal_Int32 GetPointFromCoordinate(sal_Int32 nValue)
converts 1/100mm to EMU
static sal_Int32 lcl_CalculateDist(const double dX, const double dY)
Definition: drawingml.cxx:5586
static OUString lcl_GetTarget(const css::uno::Reference< css::frame::XModel > &xModel, OUString &rURL)
Definition: drawingml.cxx:2309
sal_Int32 ExportRotateClockwisify(Degree100 input)
Definition: drawingml.hxx:123
constexpr const char * getRelationCompPrefix(DocumentType eDocumentType)
Definition: drawingml.hxx:155
const sal_Int32 MAX_PERCENT
static std::map< OString, std::vector< OString > > lcl_getAdjNames()
Definition: drawingml.cxx:4289
const sal_Int32 PER_PERCENT
static sal_Int32 lcl_CalculateDir(const double dX, const double dY)
Definition: drawingml.cxx:5591
const char * GetHatchPattern(const drawing::Hatch &rHatch)
sal_Int64 convertHmmToEmu(sal_Int32 nValue)
Converts the passed 32-bit integer value from 1/100 mm to EMUs.
Relationship
OUString getRelationship(Relationship eRelationship)
css::uno::Sequence< sal_Int8 > StreamDataSequence
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
const char * UseIf(const char *s, bool bUse)
std::shared_ptr< FastSerializerHelper > FSHelperPtr
XML_none
XML_type
long Long
XML_TOKEN_INVALID
SwNodeOffset abs(const SwNodeOffset &a)
GUIDCNamePair aData
Definition: olehelper.cxx:99
PolyFlags
DefTokenId nToken
const sal_Unicode *const aMimeType[]
static sal_Int32 getEffectToken(const OUString &sName)
Translate effect strings to tokens.
bool hasValue()
Object Value
Reference< XModel > xModel
SVXCORE_DLLPUBLIC SdrTextObj * DynCastSdrTextObj(SdrObject *)
SVXCORE_DLLPUBLIC Degree100 NormAngle18000(Degree100 a)
SVXCORE_DLLPUBLIC Degree100 NormAngle36000(Degree100 a)
SvxNumType
SVX_NUM_NUMBER_NONE
SVX_NUM_CHARS_LOWER_LETTER_N
SVX_NUM_CHARS_LOWER_LETTER
SVX_NUM_CHARS_UPPER_LETTER
SVX_NUM_ROMAN_UPPER
SVX_NUM_ROMAN_LOWER
SVX_NUM_ARABIC
SVX_NUM_CHARS_UPPER_LETTER_N
SVX_NUM_CHAR_SPECIAL
#define OOX_BULLET_LIST_SCALE_FACTOR
unsigned char sal_uInt8
sal_uInt16 sal_Unicode
signed char sal_Int8
#define SAL_MAX_UINT32
ResultType type
SVT_DLLPUBLIC tools::Long TransformMetric(tools::Long nVal, FieldUnit aOld, FieldUnit aNew)
constexpr OUStringLiteral UNO_TC_PROP_NUMFORMAT
constexpr OUStringLiteral UNO_TC_PROP_FILE_FORMAT
constexpr OUStringLiteral UNO_NAME_MISC_OBJ_INTEROPGRABBAG
static constexpr const char * ToPsz10(bool b)
Definition: utils.hxx:51
OString write1000thOfAPercent(T number)
Definition: utils.hxx:69
std::vector< ISegmentProgressBarRef > aSegments
sal_uInt64 left
sal_Int32 nLength