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 10 #include "docxsdrexport.hxx" 11 #include <com/sun/star/beans/XPropertySet.hpp> 12 #include <com/sun/star/drawing/PointSequenceSequence.hpp> 13 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp> 14 #include <editeng/lrspitem.hxx> 15 #include <editeng/ulspitem.hxx> 16 #include <editeng/shaditem.hxx> 17 #include <editeng/opaqitem.hxx> 18 #include <editeng/boxitem.hxx> 19 #include <svx/svdogrp.hxx> 20 #include <oox/token/namespaces.hxx> 21 #include <textboxhelper.hxx> 22 #include <fmtanchr.hxx> 23 #include <fmtsrnd.hxx> 24 #include <fmtcntnt.hxx> 25 #include <fmtornt.hxx> 26 #include <fmtfsize.hxx> 27 #include <fmtfollowtextflow.hxx> 28 #include <frmatr.hxx> 29 #include <fmtwrapinfluenceonobjpos.hxx> 30 #include "docxattributeoutput.hxx" 31 #include "docxexportfilter.hxx" 32 #include <comphelper/flagguard.hxx> 33 #include <comphelper/sequence.hxx> 34 #include <comphelper/sequenceashashmap.hxx> 35 #include <sal/log.hxx> 36 #include <frmfmt.hxx> 37 #include <IDocumentDrawModelAccess.hxx> 38 39 #include <tools/diagnose_ex.h> 40 #include <svx/xlnwtit.hxx> 41 42 using namespace com::sun::star; 43 using namespace oox; 44 using namespace sax_fastparser; 45 46 namespace 47 { 48 uno::Sequence<beans::PropertyValue> lclGetProperty(const uno::Reference<drawing::XShape>& rShape, 49 const OUString& rPropName) 50 { 51 uno::Sequence<beans::PropertyValue> aResult; 52 uno::Reference<beans::XPropertySet> xPropertySet(rShape, uno::UNO_QUERY); 53 uno::Reference<beans::XPropertySetInfo> xPropSetInfo; 54 55 if (!xPropertySet.is()) 56 return aResult; 57 58 xPropSetInfo = xPropertySet->getPropertySetInfo(); 59 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(rPropName)) 60 { 61 xPropertySet->getPropertyValue(rPropName) >>= aResult; 62 } 63 return aResult; 64 } 65 66 OUString lclGetAnchorIdFromGrabBag(const SdrObject* pObj) 67 { 68 OUString aResult; 69 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObj)->getUnoShape(), 70 uno::UNO_QUERY); 71 OUString aGrabBagName; 72 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY); 73 if (xServiceInfo->supportsService("com.sun.star.text.TextFrame")) 74 aGrabBagName = "FrameInteropGrabBag"; 75 else 76 aGrabBagName = "InteropGrabBag"; 77 uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, aGrabBagName); 78 auto pProp 79 = std::find_if(propList.begin(), propList.end(), 80 [](const beans::PropertyValue& rProp) { return rProp.Name == "AnchorId"; }); 81 if (pProp != propList.end()) 82 pProp->Value >>= aResult; 83 return aResult; 84 } 85 86 void lclMovePositionWithRotation(awt::Point& aPos, const Size& rSize, Degree100 nRotation100) 87 { 88 // code from ImplEESdrWriter::ImplFlipBoundingBox (filter/source/msfilter/eschesdo.cxx) 89 // TODO: refactor 90 91 if (nRotation100 == 0_deg100) 92 return; 93 sal_Int64 nRotation = nRotation100.get(); 94 if (nRotation < 0) 95 nRotation = (36000 + nRotation) % 36000; 96 if (nRotation % 18000 == 0) 97 nRotation = 0; 98 while (nRotation > 9000) 99 nRotation = (18000 - (nRotation % 18000)); 100 101 double fVal = static_cast<double>(nRotation) * F_PI18000; 102 double fCos = cos(fVal); 103 double fSin = sin(fVal); 104 105 double nWidthHalf = static_cast<double>(rSize.Width()) / 2; 106 double nHeightHalf = static_cast<double>(rSize.Height()) / 2; 107 108 double nXDiff = fSin * nHeightHalf + fCos * nWidthHalf - nWidthHalf; 109 double nYDiff = fSin * nWidthHalf + fCos * nHeightHalf - nHeightHalf; 110 111 aPos.X += nXDiff; 112 aPos.Y += nYDiff; 113 } 114 115 /// Determines if the anchor is inside a paragraph. 116 bool IsAnchorTypeInsideParagraph(const ww8::Frame* pFrame) 117 { 118 const SwFormatAnchor& rAnchor = pFrame->GetFrameFormat().GetAttrSet().GetAnchor(); 119 return rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE; 120 } 121 } 122 123 ExportDataSaveRestore::ExportDataSaveRestore(DocxExport& rExport, sal_uLong nStt, sal_uLong nEnd, 124 ww8::Frame const* pParentFrame) 125 : m_rExport(rExport) 126 { 127 m_rExport.SaveData(nStt, nEnd); 128 m_rExport.m_pParentFrame = pParentFrame; 129 } 130 131 ExportDataSaveRestore::~ExportDataSaveRestore() { m_rExport.RestoreData(); } 132 133 /// Holds data used by DocxSdrExport only. 134 struct DocxSdrExport::Impl 135 { 136 private: 137 DocxExport& m_rExport; 138 sax_fastparser::FSHelperPtr m_pSerializer; 139 oox::drawingml::DrawingML* m_pDrawingML; 140 const Size* m_pFlyFrameSize; 141 bool m_bTextFrameSyntax; 142 bool m_bDMLTextFrameSyntax; 143 rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyAttrList; 144 rtl::Reference<sax_fastparser::FastAttributeList> m_pTextboxAttrList; 145 OStringBuffer m_aTextFrameStyle; 146 bool m_bDrawingOpen; 147 bool m_bParagraphSdtOpen; 148 bool m_bParagraphHasDrawing; ///Flag for checking drawing in a paragraph. 149 rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyFillAttrList; 150 rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyWrapAttrList; 151 rtl::Reference<sax_fastparser::FastAttributeList> m_pBodyPrAttrList; 152 rtl::Reference<sax_fastparser::FastAttributeList> m_pDashLineStyleAttr; 153 bool m_bDMLAndVMLDrawingOpen; 154 /// List of TextBoxes in this document: they are exported as part of their shape, never alone. 155 /// Preserved rotation for TextFrames. 156 Degree100 m_nDMLandVMLTextFrameRotation; 157 158 public: 159 bool m_bFlyFrameGraphic = false; 160 161 Impl(DocxExport& rExport, sax_fastparser::FSHelperPtr pSerializer, 162 oox::drawingml::DrawingML* pDrawingML) 163 : m_rExport(rExport) 164 , m_pSerializer(std::move(pSerializer)) 165 , m_pDrawingML(pDrawingML) 166 , m_pFlyFrameSize(nullptr) 167 , m_bTextFrameSyntax(false) 168 , m_bDMLTextFrameSyntax(false) 169 , m_bDrawingOpen(false) 170 , m_bParagraphSdtOpen(false) 171 , m_bParagraphHasDrawing(false) 172 , m_bDMLAndVMLDrawingOpen(false) 173 { 174 } 175 176 /// Writes wp wrapper code around an SdrObject, which itself is written using drawingML syntax. 177 178 void textFrameShadow(const SwFrameFormat& rFrameFormat); 179 static bool isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape); 180 181 void setSerializer(const sax_fastparser::FSHelperPtr& pSerializer) 182 { 183 m_pSerializer = pSerializer; 184 } 185 186 const sax_fastparser::FSHelperPtr& getSerializer() const { return m_pSerializer; } 187 188 void setFlyFrameSize(const Size* pFlyFrameSize) { m_pFlyFrameSize = pFlyFrameSize; } 189 190 const Size* getFlyFrameSize() const { return m_pFlyFrameSize; } 191 192 void setTextFrameSyntax(bool bTextFrameSyntax) { m_bTextFrameSyntax = bTextFrameSyntax; } 193 194 bool getTextFrameSyntax() const { return m_bTextFrameSyntax; } 195 196 void setDMLTextFrameSyntax(bool bDMLTextFrameSyntax) 197 { 198 m_bDMLTextFrameSyntax = bDMLTextFrameSyntax; 199 } 200 201 bool getDMLTextFrameSyntax() const { return m_bDMLTextFrameSyntax; } 202 203 void setFlyAttrList(const rtl::Reference<sax_fastparser::FastAttributeList>& pFlyAttrList) 204 { 205 m_pFlyAttrList = pFlyAttrList; 206 } 207 208 rtl::Reference<sax_fastparser::FastAttributeList>& getFlyAttrList() { return m_pFlyAttrList; } 209 210 void 211 setTextboxAttrList(const rtl::Reference<sax_fastparser::FastAttributeList>& pTextboxAttrList) 212 { 213 m_pTextboxAttrList = pTextboxAttrList; 214 } 215 216 rtl::Reference<sax_fastparser::FastAttributeList>& getTextboxAttrList() 217 { 218 return m_pTextboxAttrList; 219 } 220 221 OStringBuffer& getTextFrameStyle() { return m_aTextFrameStyle; } 222 223 void setDrawingOpen(bool bDrawingOpen) { m_bDrawingOpen = bDrawingOpen; } 224 225 bool getDrawingOpen() const { return m_bDrawingOpen; } 226 227 void setParagraphSdtOpen(bool bParagraphSdtOpen) { m_bParagraphSdtOpen = bParagraphSdtOpen; } 228 229 bool getParagraphSdtOpen() const { return m_bParagraphSdtOpen; } 230 231 void setDMLAndVMLDrawingOpen(bool bDMLAndVMLDrawingOpen) 232 { 233 m_bDMLAndVMLDrawingOpen = bDMLAndVMLDrawingOpen; 234 } 235 236 bool getDMLAndVMLDrawingOpen() const { return m_bDMLAndVMLDrawingOpen; } 237 238 void setParagraphHasDrawing(bool bParagraphHasDrawing) 239 { 240 m_bParagraphHasDrawing = bParagraphHasDrawing; 241 } 242 243 bool getParagraphHasDrawing() const { return m_bParagraphHasDrawing; } 244 245 rtl::Reference<sax_fastparser::FastAttributeList>& getFlyFillAttrList() 246 { 247 return m_pFlyFillAttrList; 248 } 249 250 void 251 setFlyWrapAttrList(rtl::Reference<sax_fastparser::FastAttributeList> const& pFlyWrapAttrList) 252 { 253 m_pFlyWrapAttrList = pFlyWrapAttrList; 254 } 255 256 sax_fastparser::FastAttributeList* getFlyWrapAttrList() const 257 { 258 return m_pFlyWrapAttrList.get(); 259 } 260 261 void setBodyPrAttrList(sax_fastparser::FastAttributeList* pBodyPrAttrList) 262 { 263 m_pBodyPrAttrList = pBodyPrAttrList; 264 } 265 266 sax_fastparser::FastAttributeList* getBodyPrAttrList() const { return m_pBodyPrAttrList.get(); } 267 268 rtl::Reference<sax_fastparser::FastAttributeList>& getDashLineStyleAttr() 269 { 270 return m_pDashLineStyleAttr; 271 } 272 273 bool getFlyFrameGraphic() const { return m_bFlyFrameGraphic; } 274 275 oox::drawingml::DrawingML* getDrawingML() const { return m_pDrawingML; } 276 277 DocxExport& getExport() const { return m_rExport; } 278 279 void setDMLandVMLTextFrameRotation(Degree100 nDMLandVMLTextFrameRotation) 280 { 281 m_nDMLandVMLTextFrameRotation = nDMLandVMLTextFrameRotation; 282 } 283 284 Degree100& getDMLandVMLTextFrameRotation() { return m_nDMLandVMLTextFrameRotation; } 285 }; 286 287 DocxSdrExport::DocxSdrExport(DocxExport& rExport, const sax_fastparser::FSHelperPtr& pSerializer, 288 oox::drawingml::DrawingML* pDrawingML) 289 : m_pImpl(std::make_unique<Impl>(rExport, pSerializer, pDrawingML)) 290 { 291 } 292 293 DocxSdrExport::~DocxSdrExport() = default; 294 295 void DocxSdrExport::setSerializer(const sax_fastparser::FSHelperPtr& pSerializer) 296 { 297 m_pImpl->setSerializer(pSerializer); 298 } 299 300 const Size* DocxSdrExport::getFlyFrameSize() const { return m_pImpl->getFlyFrameSize(); } 301 302 bool DocxSdrExport::getTextFrameSyntax() const { return m_pImpl->getTextFrameSyntax(); } 303 304 bool DocxSdrExport::getDMLTextFrameSyntax() const { return m_pImpl->getDMLTextFrameSyntax(); } 305 306 rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getFlyAttrList() 307 { 308 return m_pImpl->getFlyAttrList(); 309 } 310 311 rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getTextboxAttrList() 312 { 313 return m_pImpl->getTextboxAttrList(); 314 } 315 316 OStringBuffer& DocxSdrExport::getTextFrameStyle() { return m_pImpl->getTextFrameStyle(); } 317 318 bool DocxSdrExport::IsDrawingOpen() const { return m_pImpl->getDrawingOpen(); } 319 320 void DocxSdrExport::setParagraphSdtOpen(bool bParagraphSdtOpen) 321 { 322 m_pImpl->setParagraphSdtOpen(bParagraphSdtOpen); 323 } 324 325 bool DocxSdrExport::IsDMLAndVMLDrawingOpen() const { return m_pImpl->getDMLAndVMLDrawingOpen(); } 326 327 bool DocxSdrExport::IsParagraphHasDrawing() const { return m_pImpl->getParagraphHasDrawing(); } 328 329 void DocxSdrExport::setParagraphHasDrawing(bool bParagraphHasDrawing) 330 { 331 m_pImpl->setParagraphHasDrawing(bParagraphHasDrawing); 332 } 333 334 rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getFlyFillAttrList() 335 { 336 return m_pImpl->getFlyFillAttrList(); 337 } 338 339 sax_fastparser::FastAttributeList* DocxSdrExport::getBodyPrAttrList() 340 { 341 return m_pImpl->getBodyPrAttrList(); 342 } 343 344 rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getDashLineStyle() 345 { 346 return m_pImpl->getDashLineStyleAttr(); 347 } 348 349 void DocxSdrExport::setFlyWrapAttrList( 350 rtl::Reference<sax_fastparser::FastAttributeList> const& pAttrList) 351 { 352 m_pImpl->setFlyWrapAttrList(pAttrList); 353 } 354 355 void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, const Size& rSize) 356 { 357 m_pImpl->setDrawingOpen(true); 358 m_pImpl->setParagraphHasDrawing(true); 359 m_pImpl->getSerializer()->startElementNS(XML_w, XML_drawing); 360 361 // tdf#135047: It must be allowed to find in parents too, but default value of bInP parameter 362 // for GetLRSpace() and GetULSpace() is true, so no direct setting is required. 363 const SvxLRSpaceItem& aLRSpaceItem = pFrameFormat->GetLRSpace(); 364 const SvxULSpaceItem& aULSpaceItem = pFrameFormat->GetULSpace(); 365 366 bool isAnchor; 367 368 if (m_pImpl->getFlyFrameGraphic()) 369 { 370 isAnchor = false; // make Graphic object inside DMLTextFrame & VMLTextFrame as Inline 371 } 372 else 373 { 374 isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR; 375 } 376 377 // Count effectExtent values, their value is needed before dist{T,B,L,R} is written. 378 SvxShadowItem aShadowItem = pFrameFormat->GetShadow(); 379 sal_Int32 nLeftExt = 0; 380 sal_Int32 nRightExt = 0; 381 sal_Int32 nTopExt = 0; 382 sal_Int32 nBottomExt = 0; 383 if (aShadowItem.GetLocation() != SvxShadowLocation::NONE) 384 { 385 sal_Int32 nShadowWidth(TwipsToEMU(aShadowItem.GetWidth())); 386 switch (aShadowItem.GetLocation()) 387 { 388 case SvxShadowLocation::TopLeft: 389 nTopExt = nLeftExt = nShadowWidth; 390 break; 391 case SvxShadowLocation::TopRight: 392 nTopExt = nRightExt = nShadowWidth; 393 break; 394 case SvxShadowLocation::BottomLeft: 395 nBottomExt = nLeftExt = nShadowWidth; 396 break; 397 case SvxShadowLocation::BottomRight: 398 nBottomExt = nRightExt = nShadowWidth; 399 break; 400 case SvxShadowLocation::NONE: 401 case SvxShadowLocation::End: 402 break; 403 } 404 } 405 else if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject()) 406 { 407 // No shadow, but we have an idea what was the original effectExtent. 408 uno::Any aAny; 409 pObject->GetGrabBagItem(aAny); 410 comphelper::SequenceAsHashMap aGrabBag(aAny); 411 auto it = aGrabBag.find("CT_EffectExtent"); 412 if (it != aGrabBag.end()) 413 { 414 comphelper::SequenceAsHashMap aEffectExtent(it->second); 415 for (const std::pair<const OUString, uno::Any>& rDirection : aEffectExtent) 416 { 417 if (rDirection.first == "l" && rDirection.second.has<sal_Int32>()) 418 nLeftExt = rDirection.second.get<sal_Int32>(); 419 else if (rDirection.first == "t" && rDirection.second.has<sal_Int32>()) 420 nTopExt = rDirection.second.get<sal_Int32>(); 421 else if (rDirection.first == "r" && rDirection.second.has<sal_Int32>()) 422 nRightExt = rDirection.second.get<sal_Int32>(); 423 else if (rDirection.first == "b" && rDirection.second.has<sal_Int32>()) 424 nBottomExt = rDirection.second.get<sal_Int32>(); 425 } 426 } 427 } 428 429 if (isAnchor) 430 { 431 rtl::Reference<sax_fastparser::FastAttributeList> attrList 432 = sax_fastparser::FastSerializerHelper::createAttrList(); 433 bool bOpaque = pFrameFormat->GetOpaque().GetValue(); 434 awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(), 435 pFrameFormat->GetVertOrient().GetPos()); 436 const SdrObject* pObj = pFrameFormat->FindRealSdrObject(); 437 Degree100 nRotation(0); 438 if (pObj != nullptr) 439 { 440 // SdrObjects know their layer, consider that instead of the frame format. 441 bOpaque = pObj->GetLayer() 442 != pFrameFormat->GetDoc()->getIDocumentDrawModelAccess().GetHellId() 443 && pObj->GetLayer() 444 != pFrameFormat->GetDoc() 445 ->getIDocumentDrawModelAccess() 446 .GetInvisibleHellId(); 447 448 // Do not do this with lines. 449 if (pObj->GetObjIdentifier() != OBJ_LINE) 450 { 451 nRotation = pObj->GetRotateAngle(); 452 lclMovePositionWithRotation(aPos, rSize, nRotation); 453 } 454 } 455 attrList->add(XML_behindDoc, bOpaque ? "0" : "1"); 456 sal_Int32 nLineWidth = 0; 457 if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject()) 458 { 459 nLineWidth = pObject->GetMergedItem(XATTR_LINEWIDTH).GetValue(); 460 } 461 462 // Extend distance with the effect extent if the shape is not rotated, which is the opposite 463 // of the mapping done at import time. 464 // The type of dist* attributes is unsigned, so make sure no negative value is written. 465 sal_Int64 nTopExtDist = nRotation ? 0 : nTopExt; 466 nTopExtDist -= TwipsToEMU(nLineWidth / 2); 467 sal_Int64 nDistT = std::max(static_cast<sal_Int64>(0), 468 TwipsToEMU(aULSpaceItem.GetUpper()) - nTopExtDist); 469 attrList->add(XML_distT, OString::number(nDistT).getStr()); 470 sal_Int64 nBottomExtDist = nRotation ? 0 : nBottomExt; 471 nBottomExtDist -= TwipsToEMU(nLineWidth / 2); 472 sal_Int64 nDistB = std::max(static_cast<sal_Int64>(0), 473 TwipsToEMU(aULSpaceItem.GetLower()) - nBottomExtDist); 474 attrList->add(XML_distB, OString::number(nDistB).getStr()); 475 sal_Int64 nLeftExtDist = nRotation ? 0 : nLeftExt; 476 nLeftExtDist -= TwipsToEMU(nLineWidth / 2); 477 sal_Int64 nDistL = std::max(static_cast<sal_Int64>(0), 478 TwipsToEMU(aLRSpaceItem.GetLeft()) - nLeftExtDist); 479 attrList->add(XML_distL, OString::number(nDistL).getStr()); 480 sal_Int64 nRightExtDist = nRotation ? 0 : nRightExt; 481 nRightExtDist -= TwipsToEMU(nLineWidth / 2); 482 sal_Int64 nDistR = std::max(static_cast<sal_Int64>(0), 483 TwipsToEMU(aLRSpaceItem.GetRight()) - nRightExtDist); 484 attrList->add(XML_distR, OString::number(nDistR).getStr()); 485 attrList->add(XML_simplePos, "0"); 486 attrList->add(XML_locked, "0"); 487 bool bLclInTabCell = true; 488 if (pObj) 489 { 490 uno::Reference<drawing::XShape> xShape((const_cast<SdrObject*>(pObj)->getUnoShape()), 491 uno::UNO_QUERY); 492 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); 493 if (xShapeProps.is()) 494 xShapeProps->getPropertyValue("IsFollowingTextFlow") >>= bLclInTabCell; 495 } 496 if (bLclInTabCell) 497 attrList->add(XML_layoutInCell, "1"); 498 else 499 attrList->add(XML_layoutInCell, "0"); 500 bool bAllowOverlap = pFrameFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap(); 501 attrList->add(XML_allowOverlap, bAllowOverlap ? "1" : "0"); 502 if (pObj != nullptr) 503 // It seems 0 and 1 have special meaning: just start counting from 2 to avoid issues with that. 504 attrList->add(XML_relativeHeight, OString::number(pObj->GetOrdNum() + 2)); 505 else 506 // relativeHeight is mandatory attribute, if value is not present, we must write default value 507 attrList->add(XML_relativeHeight, "0"); 508 if (pObj != nullptr) 509 { 510 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj); 511 if (!sAnchorId.isEmpty()) 512 attrList->addNS(XML_wp14, XML_anchorId, 513 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8)); 514 } 515 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_anchor, attrList); 516 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_simplePos, XML_x, "0", XML_y, 517 "0"); // required, unused 518 const char* relativeFromH; 519 const char* relativeFromV; 520 const char* alignH = nullptr; 521 const char* alignV = nullptr; 522 switch (pFrameFormat->GetVertOrient().GetRelationOrient()) 523 { 524 case text::RelOrientation::PAGE_PRINT_AREA: 525 relativeFromV = "margin"; 526 break; 527 case text::RelOrientation::PAGE_PRINT_AREA_TOP: 528 relativeFromV = "topMargin"; 529 break; 530 case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM: 531 relativeFromV = "bottomMargin"; 532 break; 533 case text::RelOrientation::PAGE_FRAME: 534 relativeFromV = "page"; 535 break; 536 case text::RelOrientation::FRAME: 537 relativeFromV = "paragraph"; 538 break; 539 case text::RelOrientation::TEXT_LINE: 540 default: 541 relativeFromV = "line"; 542 break; 543 } 544 switch (pFrameFormat->GetVertOrient().GetVertOrient()) 545 { 546 case text::VertOrientation::TOP: 547 case text::VertOrientation::CHAR_TOP: 548 case text::VertOrientation::LINE_TOP: 549 if (pFrameFormat->GetVertOrient().GetRelationOrient() 550 == text::RelOrientation::TEXT_LINE) 551 alignV = "bottom"; 552 else 553 alignV = "top"; 554 break; 555 case text::VertOrientation::BOTTOM: 556 case text::VertOrientation::CHAR_BOTTOM: 557 case text::VertOrientation::LINE_BOTTOM: 558 if (pFrameFormat->GetVertOrient().GetRelationOrient() 559 == text::RelOrientation::TEXT_LINE) 560 alignV = "top"; 561 else 562 alignV = "bottom"; 563 break; 564 case text::VertOrientation::CENTER: 565 case text::VertOrientation::CHAR_CENTER: 566 case text::VertOrientation::LINE_CENTER: 567 alignV = "center"; 568 break; 569 default: 570 break; 571 } 572 switch (pFrameFormat->GetHoriOrient().GetRelationOrient()) 573 { 574 case text::RelOrientation::PAGE_PRINT_AREA: 575 relativeFromH = "margin"; 576 break; 577 case text::RelOrientation::PAGE_FRAME: 578 relativeFromH = "page"; 579 break; 580 case text::RelOrientation::CHAR: 581 relativeFromH = "character"; 582 break; 583 case text::RelOrientation::PAGE_RIGHT: 584 relativeFromH = "rightMargin"; 585 break; 586 case text::RelOrientation::PAGE_LEFT: 587 relativeFromH = "leftMargin"; 588 break; 589 case text::RelOrientation::FRAME: 590 default: 591 relativeFromH = "column"; 592 break; 593 } 594 switch (pFrameFormat->GetHoriOrient().GetHoriOrient()) 595 { 596 case text::HoriOrientation::LEFT: 597 alignH = "left"; 598 break; 599 case text::HoriOrientation::RIGHT: 600 alignH = "right"; 601 break; 602 case text::HoriOrientation::CENTER: 603 alignH = "center"; 604 break; 605 case text::HoriOrientation::INSIDE: 606 alignH = "inside"; 607 break; 608 case text::HoriOrientation::OUTSIDE: 609 alignH = "outside"; 610 break; 611 default: 612 break; 613 } 614 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionH, XML_relativeFrom, 615 relativeFromH); 616 /** 617 * Sizes of integral types 618 * climits header defines constants with the limits of integral types for the specific system and compiler implementation used. 619 * Use of this might cause platform dependent problem like posOffset exceed the limit. 620 **/ 621 const sal_Int64 MAX_INTEGER_VALUE = SAL_MAX_INT32; 622 const sal_Int64 MIN_INTEGER_VALUE = SAL_MIN_INT32; 623 if (alignH != nullptr) 624 { 625 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align); 626 m_pImpl->getSerializer()->write(alignH); 627 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align); 628 } 629 else 630 { 631 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset); 632 sal_Int64 nPosXEMU = TwipsToEMU(aPos.X); 633 634 /* Absolute Position Offset Value is of type Int. Hence it should not be greater than 635 * Maximum value for Int OR Less than the Minimum value for Int. 636 * - Maximum value for Int = 2147483647 637 * - Minimum value for Int = -2147483648 638 * 639 * As per ECMA Specification : ECMA-376, Second Edition, 640 * Part 1 - Fundamentals And Markup Language Reference[20.4.3.3 ST_PositionOffset (Absolute Position Offset Value)] 641 * 642 * Please refer : http://www.schemacentral.com/sc/xsd/t-xsd_int.html 643 */ 644 645 if (nPosXEMU > MAX_INTEGER_VALUE) 646 { 647 nPosXEMU = MAX_INTEGER_VALUE; 648 } 649 else if (nPosXEMU < MIN_INTEGER_VALUE) 650 { 651 nPosXEMU = MIN_INTEGER_VALUE; 652 } 653 m_pImpl->getSerializer()->write(nPosXEMU); 654 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset); 655 } 656 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionH); 657 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionV, XML_relativeFrom, 658 relativeFromV); 659 660 sal_Int64 nPosYEMU = TwipsToEMU(aPos.Y); 661 662 // tdf#93675, 0 below line/paragraph and/or top line/paragraph with 663 // wrap top+bottom or other wraps is affecting the line directly 664 // above the anchor line, which seems odd, but a tiny adjustment 665 // here to bring the top down convinces msoffice to wrap like us 666 if (nPosYEMU == 0 667 && (strcmp(relativeFromV, "line") == 0 || strcmp(relativeFromV, "paragraph") == 0) 668 && (!alignV || strcmp(alignV, "top") == 0)) 669 { 670 alignV = nullptr; 671 nPosYEMU = TwipsToEMU(1); 672 } 673 674 if (alignV != nullptr) 675 { 676 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align); 677 m_pImpl->getSerializer()->write(alignV); 678 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align); 679 } 680 else 681 { 682 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset); 683 if (nPosYEMU > MAX_INTEGER_VALUE) 684 { 685 nPosYEMU = MAX_INTEGER_VALUE; 686 } 687 else if (nPosYEMU < MIN_INTEGER_VALUE) 688 { 689 nPosYEMU = MIN_INTEGER_VALUE; 690 } 691 m_pImpl->getSerializer()->write(nPosYEMU); 692 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset); 693 } 694 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionV); 695 } 696 else 697 { 698 rtl::Reference<sax_fastparser::FastAttributeList> aAttrList 699 = sax_fastparser::FastSerializerHelper::createAttrList(); 700 aAttrList->add(XML_distT, OString::number(TwipsToEMU(aULSpaceItem.GetUpper())).getStr()); 701 aAttrList->add(XML_distB, OString::number(TwipsToEMU(aULSpaceItem.GetLower())).getStr()); 702 aAttrList->add(XML_distL, OString::number(TwipsToEMU(aLRSpaceItem.GetLeft())).getStr()); 703 aAttrList->add(XML_distR, OString::number(TwipsToEMU(aLRSpaceItem.GetRight())).getStr()); 704 const SdrObject* pObj = pFrameFormat->FindRealSdrObject(); 705 if (pObj != nullptr) 706 { 707 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj); 708 if (!sAnchorId.isEmpty()) 709 aAttrList->addNS(XML_wp14, XML_anchorId, 710 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8)); 711 } 712 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_inline, aAttrList); 713 } 714 715 // now the common parts 716 // extent of the image 717 /** 718 * Extent width is of type long ( i.e cx & cy ) as 719 * 720 * per ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference 721 * [ 20.4.2.7 extent (Drawing Object Size)] 722 * 723 * cy is of type a:ST_PositiveCoordinate. 724 * Minimum inclusive: 0 725 * Maximum inclusive: 27273042316900 726 * 727 * reference : http://www.schemacentral.com/sc/ooxml/e-wp_extent-1.html 728 * 729 * Though ECMA mentions the max value as aforementioned. It appears that MSO does not 730 * handle for the same, in fact it actually can handle a max value of int32 i.e 731 * 2147483647( MAX_INTEGER_VALUE ). 732 * Therefore changing the following accordingly so that LO sync's up with MSO. 733 **/ 734 sal_uInt64 cx 735 = TwipsToEMU(std::clamp(rSize.Width(), tools::Long(0), tools::Long(SAL_MAX_INT32))); 736 OString aWidth(OString::number(std::min(cx, sal_uInt64(SAL_MAX_INT32)))); 737 sal_uInt64 cy 738 = TwipsToEMU(std::clamp(rSize.Height(), tools::Long(0), tools::Long(SAL_MAX_INT32))); 739 OString aHeight(OString::number(std::min(cy, sal_uInt64(SAL_MAX_INT32)))); 740 741 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_extent, XML_cx, aWidth, XML_cy, aHeight); 742 743 // effectExtent, extent including the effect (shadow only for now) 744 m_pImpl->getSerializer()->singleElementNS( 745 XML_wp, XML_effectExtent, XML_l, OString::number(nLeftExt), XML_t, OString::number(nTopExt), 746 XML_r, OString::number(nRightExt), XML_b, OString::number(nBottomExt)); 747 748 // See if we know the exact wrap type from grab-bag. 749 sal_Int32 nWrapToken = 0; 750 if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject()) 751 { 752 uno::Any aAny; 753 pObject->GetGrabBagItem(aAny); 754 comphelper::SequenceAsHashMap aGrabBag(aAny); 755 auto it = aGrabBag.find("EG_WrapType"); 756 if (it != aGrabBag.end()) 757 { 758 auto sType = it->second.get<OUString>(); 759 if (sType == "wrapTight") 760 nWrapToken = XML_wrapTight; 761 else if (sType == "wrapThrough") 762 nWrapToken = XML_wrapThrough; 763 else 764 SAL_WARN("sw.ww8", 765 "DocxSdrExport::startDMLAnchorInline: unexpected EG_WrapType value"); 766 767 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, "bothSides"); 768 769 it = aGrabBag.find("CT_WrapPath"); 770 if (it != aGrabBag.end()) 771 { 772 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0"); 773 auto aSeqSeq = it->second.get<drawing::PointSequenceSequence>(); 774 auto aPoints(comphelper::sequenceToContainer<std::vector<awt::Point>>(aSeqSeq[0])); 775 for (auto i = aPoints.begin(); i != aPoints.end(); ++i) 776 { 777 awt::Point& rPoint = *i; 778 m_pImpl->getSerializer()->singleElementNS( 779 XML_wp, (i == aPoints.begin() ? XML_start : XML_lineTo), XML_x, 780 OString::number(rPoint.X), XML_y, OString::number(rPoint.Y)); 781 } 782 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon); 783 } 784 785 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken); 786 } 787 } 788 789 // Or if we have a contour. 790 if (!nWrapToken && pFrameFormat->GetSurround().IsContour()) 791 { 792 if (const SwNoTextNode* pNd = sw::util::GetNoTextNodeFromSwFrameFormat(*pFrameFormat)) 793 { 794 const tools::PolyPolygon* pPolyPoly = pNd->HasContour(); 795 if (pPolyPoly && pPolyPoly->Count()) 796 { 797 nWrapToken = XML_wrapTight; 798 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, 799 "bothSides"); 800 801 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0"); 802 tools::Polygon aPoly = sw::util::CorrectWordWrapPolygonForExport( 803 *pPolyPoly, pNd, /*bCorrectCrop=*/true); 804 for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i) 805 m_pImpl->getSerializer()->singleElementNS( 806 XML_wp, (i == 0 ? XML_start : XML_lineTo), XML_x, 807 OString::number(aPoly[i].X()), XML_y, OString::number(aPoly[i].Y())); 808 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon); 809 810 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken); 811 } 812 } 813 else if (SdrObject* pSdrObj = const_cast<SdrObject*>(pFrameFormat->FindRealSdrObject())) 814 { 815 // In this case we likely had an odt document to be exported to docx. 816 // There is no grab-bag or something else so for a workaround, 817 // let's export the geometry of the shape... 818 // First get the UNO-shape 819 uno::Reference<drawing::XShape> xShape(pSdrObj->getUnoShape(), uno::UNO_QUERY); 820 821 if (xShape && xShape->getShapeType() == u"com.sun.star.drawing.CustomShape") 822 { 823 try 824 { 825 // Get the properties of the Xshape 826 uno::Reference<beans::XPropertySet> XProps(xShape, uno::UNO_QUERY); 827 // Get the "CustomShapeGeometry" property and from its Any() make a hashMap 828 comphelper::SequenceAsHashMap aCustomShapeGeometry( 829 XProps->getPropertyValue("CustomShapeGeometry")); 830 // Get the "Path" property and from its Any() make a hashMap 831 comphelper::SequenceAsHashMap aPath(aCustomShapeGeometry.getValue("Path")); 832 // From the Any() of the "Coordinates" property get the points 833 uno::Sequence<css::drawing::EnhancedCustomShapeParameterPair> aCoords 834 = aPath.getValue("Coordinates") 835 .get<uno::Sequence<css::drawing::EnhancedCustomShapeParameterPair>>(); 836 837 // Check if only one side wrap allowed 838 OUString sWrapType; 839 switch (pFrameFormat->GetSurround().GetSurround()) 840 { 841 case text::WrapTextMode_DYNAMIC: 842 sWrapType = OUString("largest"); 843 break; 844 case text::WrapTextMode_LEFT: 845 sWrapType = OUString("left"); 846 break; 847 case text::WrapTextMode_RIGHT: 848 sWrapType = OUString("right"); 849 break; 850 case text::WrapTextMode_PARALLEL: 851 default: 852 sWrapType = OUString("bothSides"); 853 break; 854 } 855 856 // And export: 857 nWrapToken = XML_wrapTight; 858 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, 859 sWrapType); 860 861 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, 862 "0"); 863 864 try 865 { 866 // There are the coordinates 867 for (sal_Int32 i = 0; i < aCoords.getLength(); i++) 868 m_pImpl->getSerializer()->singleElementNS( 869 XML_wp, (i == 0 ? XML_start : XML_lineTo), XML_x, 870 OString::number(aCoords[i].First.Value.get<double>()), XML_y, 871 OString::number(aCoords[i].Second.Value.get<double>())); 872 } 873 catch (const uno::Exception& e) 874 { 875 // e.g. on exporting first attachment of tdf#94591 to docx 876 TOOLS_WARN_EXCEPTION( 877 "sw.ww8", 878 "DocxSdrExport::startDMLAnchorInline: bad coordinate: " << e.Message); 879 } 880 881 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon); 882 883 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken); 884 } 885 catch (uno::Exception& e) 886 { 887 TOOLS_WARN_EXCEPTION( 888 "sw.ww8", "DocxSdrExport::startDMLAnchorInline: exception: " << e.Message); 889 } 890 } 891 } 892 } 893 894 // No? Then just approximate based on what we have. 895 if (!isAnchor || nWrapToken) 896 return; 897 898 switch (pFrameFormat->GetSurround().GetValue()) 899 { 900 case css::text::WrapTextMode_NONE: 901 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapTopAndBottom); 902 break; 903 case css::text::WrapTextMode_THROUGH: 904 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapNone); 905 break; 906 case css::text::WrapTextMode_PARALLEL: 907 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText, 908 "bothSides"); 909 break; 910 case css::text::WrapTextMode_DYNAMIC: 911 default: 912 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText, 913 "largest"); 914 break; 915 } 916 } 917 918 void DocxSdrExport::endDMLAnchorInline(const SwFrameFormat* pFrameFormat) 919 { 920 bool isAnchor; 921 if (m_pImpl->getFlyFrameGraphic()) 922 { 923 isAnchor = false; // end Inline Graphic object inside DMLTextFrame 924 } 925 else 926 { 927 isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR; 928 } 929 m_pImpl->getSerializer()->endElementNS(XML_wp, isAnchor ? XML_anchor : XML_inline); 930 931 m_pImpl->getSerializer()->endElementNS(XML_w, XML_drawing); 932 m_pImpl->setDrawingOpen(false); 933 } 934 935 void DocxSdrExport::writeVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat) 936 { 937 m_pImpl->getSerializer()->startElementNS(XML_w, XML_pict); 938 m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer()); 939 // See WinwordAnchoring::SetAnchoring(), these are not part of the SdrObject, have to be passed around manually. 940 941 SwFormatFollowTextFlow const& rFlow(rFrameFormat.GetFollowTextFlow()); 942 const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient(); 943 const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient(); 944 SwFormatSurround const& rSurround(rFrameFormat.GetSurround()); 945 946 rtl::Reference<sax_fastparser::FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround)); 947 m_pImpl->getExport().VMLExporter().AddSdrObject( 948 *sdrObj, rFlow.GetValue(), rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(), 949 rHoriOri.GetRelationOrient(), rVertOri.GetRelationOrient(), pAttrList.get(), true); 950 m_pImpl->getSerializer()->endElementNS(XML_w, XML_pict); 951 } 952 953 static bool lcl_isLockedCanvas(const uno::Reference<drawing::XShape>& xShape) 954 { 955 uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, "InteropGrabBag"); 956 /* 957 * Export as Locked Canvas only if the property 958 * is in the PropertySet 959 */ 960 return std::any_of(propList.begin(), propList.end(), [](const beans::PropertyValue& rProp) { 961 return rProp.Name == "LockedCanvas"; 962 }); 963 } 964 965 void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat, 966 int nAnchorId) 967 { 968 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), 969 uno::UNO_QUERY_THROW); 970 if (!Impl::isSupportedDMLShape(xShape)) 971 return; 972 973 m_pImpl->getExport().DocxAttrOutput().GetSdtEndBefore(pSdrObject); 974 975 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer(); 976 Size aSize(pSdrObject->GetLogicRect().GetWidth(), pSdrObject->GetLogicRect().GetHeight()); 977 startDMLAnchorInline(pFrameFormat, aSize); 978 979 rtl::Reference<sax_fastparser::FastAttributeList> pDocPrAttrList 980 = sax_fastparser::FastSerializerHelper::createAttrList(); 981 pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr()); 982 pDocPrAttrList->add(XML_name, OUStringToOString(pSdrObject->GetName(), RTL_TEXTENCODING_UTF8)); 983 if (!pSdrObject->GetTitle().isEmpty()) 984 pDocPrAttrList->add(XML_title, 985 OUStringToOString(pSdrObject->GetTitle(), RTL_TEXTENCODING_UTF8)); 986 if (!pSdrObject->GetDescription().isEmpty()) 987 pDocPrAttrList->add(XML_descr, 988 OUStringToOString(pSdrObject->GetDescription(), RTL_TEXTENCODING_UTF8)); 989 if (!pSdrObject->IsVisible() 990 && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) 991 992 pDocPrAttrList->add(XML_hidden, OString::number(1).getStr()); 993 pFS->singleElementNS(XML_wp, XML_docPr, pDocPrAttrList); 994 995 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW); 996 const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"; 997 if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape")) 998 pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"; 999 else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape")) 1000 pNamespace = "http://schemas.openxmlformats.org/drawingml/2006/picture"; 1001 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a), 1002 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml))); 1003 pFS->startElementNS(XML_a, XML_graphicData, XML_uri, pNamespace); 1004 1005 bool bLockedCanvas = lcl_isLockedCanvas(xShape); 1006 if (bLockedCanvas) 1007 pFS->startElementNS( 1008 XML_lc, XML_lockedCanvas, FSNS(XML_xmlns, XML_lc), 1009 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dmlLockedCanvas))); 1010 1011 m_pImpl->getExport().OutputDML(xShape); 1012 1013 if (bLockedCanvas) 1014 pFS->endElementNS(XML_lc, XML_lockedCanvas); 1015 pFS->endElementNS(XML_a, XML_graphicData); 1016 pFS->endElementNS(XML_a, XML_graphic); 1017 1018 // Relative size of the drawing. 1019 if (pSdrObject->GetRelativeWidth()) 1020 { 1021 // At the moment drawinglayer objects are always relative from page. 1022 OUString sValue; 1023 switch (pSdrObject->GetRelativeWidthRelation()) 1024 { 1025 case text::RelOrientation::FRAME: 1026 sValue = "margin"; 1027 break; 1028 case text::RelOrientation::PAGE_LEFT: 1029 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror) 1030 sValue = "outsideMargin"; 1031 else 1032 sValue = "leftMargin"; 1033 break; 1034 case text::RelOrientation::PAGE_RIGHT: 1035 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror) 1036 sValue = "insideMargin"; 1037 else 1038 sValue = "rightMargin"; 1039 break; 1040 case text::RelOrientation::PAGE_FRAME: 1041 default: 1042 sValue = "page"; 1043 break; 1044 } 1045 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom, sValue); 1046 pFS->startElementNS(XML_wp14, XML_pctWidth); 1047 pFS->writeEscaped( 1048 OUString::number(*pSdrObject->GetRelativeWidth() * 100 * oox::drawingml::PER_PERCENT)); 1049 pFS->endElementNS(XML_wp14, XML_pctWidth); 1050 pFS->endElementNS(XML_wp14, XML_sizeRelH); 1051 } 1052 if (pSdrObject->GetRelativeHeight()) 1053 { 1054 OUString sValue; 1055 switch (pSdrObject->GetRelativeHeightRelation()) 1056 { 1057 case text::RelOrientation::FRAME: 1058 sValue = "margin"; 1059 break; 1060 case text::RelOrientation::PAGE_PRINT_AREA: 1061 sValue = "topMargin"; 1062 break; 1063 case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM: 1064 sValue = "bottomMargin"; 1065 break; 1066 case text::RelOrientation::PAGE_FRAME: 1067 default: 1068 sValue = "page"; 1069 break; 1070 } 1071 pFS->startElementNS(XML_wp14, XML_sizeRelV, XML_relativeFrom, sValue); 1072 pFS->startElementNS(XML_wp14, XML_pctHeight); 1073 pFS->writeEscaped( 1074 OUString::number(*pSdrObject->GetRelativeHeight() * 100 * oox::drawingml::PER_PERCENT)); 1075 pFS->endElementNS(XML_wp14, XML_pctHeight); 1076 pFS->endElementNS(XML_wp14, XML_sizeRelV); 1077 } 1078 1079 endDMLAnchorInline(pFrameFormat); 1080 } 1081 1082 void DocxSdrExport::Impl::textFrameShadow(const SwFrameFormat& rFrameFormat) 1083 { 1084 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow(); 1085 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE) 1086 return; 1087 1088 OString aShadowWidth(OString::number(double(aShadowItem.GetWidth()) / 20) + "pt"); 1089 OString aOffset; 1090 switch (aShadowItem.GetLocation()) 1091 { 1092 case SvxShadowLocation::TopLeft: 1093 aOffset = "-" + aShadowWidth + ",-" + aShadowWidth; 1094 break; 1095 case SvxShadowLocation::TopRight: 1096 aOffset = aShadowWidth + ",-" + aShadowWidth; 1097 break; 1098 case SvxShadowLocation::BottomLeft: 1099 aOffset = "-" + aShadowWidth + "," + aShadowWidth; 1100 break; 1101 case SvxShadowLocation::BottomRight: 1102 aOffset = aShadowWidth + "," + aShadowWidth; 1103 break; 1104 case SvxShadowLocation::NONE: 1105 case SvxShadowLocation::End: 1106 break; 1107 } 1108 if (aOffset.isEmpty()) 1109 return; 1110 1111 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor()); 1112 m_pSerializer->singleElementNS(XML_v, XML_shadow, XML_on, "t", XML_color, "#" + aShadowColor, 1113 XML_offset, aOffset); 1114 } 1115 1116 bool DocxSdrExport::Impl::isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape) 1117 { 1118 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW); 1119 if (xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonShape") 1120 || xServiceInfo->supportsService("com.sun.star.drawing.PolyLineShape")) 1121 return false; 1122 1123 // For signature line shapes, we don't want DML, just the VML shape. 1124 if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape")) 1125 { 1126 uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY); 1127 bool bIsSignatureLineShape = false; 1128 xShapeProperties->getPropertyValue("IsSignatureLine") >>= bIsSignatureLineShape; 1129 if (bIsSignatureLineShape) 1130 return false; 1131 } 1132 1133 return true; 1134 } 1135 1136 void DocxSdrExport::writeDMLAndVMLDrawing(const SdrObject* sdrObj, 1137 const SwFrameFormat& rFrameFormat, int nAnchorId) 1138 { 1139 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen(); 1140 m_pImpl->setDMLAndVMLDrawingOpen(true); 1141 1142 // Depending on the shape type, we actually don't write the shape as DML. 1143 OUString sShapeType; 1144 ShapeFlag nMirrorFlags = ShapeFlag::NONE; 1145 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObj)->getUnoShape(), 1146 uno::UNO_QUERY_THROW); 1147 1148 MSO_SPT eShapeType 1149 = EscherPropertyContainer::GetCustomShapeType(xShape, nMirrorFlags, sShapeType); 1150 1151 // In case we are already inside a DML block, then write the shape only as VML, turn out that's allowed to do. 1152 // A common service created in util to check for VML shapes which are allowed to have textbox in content 1153 if ((msfilter::util::HasTextBoxContent(eShapeType)) && Impl::isSupportedDMLShape(xShape) 1154 && (!bDMLAndVMLDrawingOpen || lcl_isLockedCanvas(xShape))) // Locked canvas is OK inside DML 1155 { 1156 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_AlternateContent); 1157 1158 auto pObjGroup = dynamic_cast<const SdrObjGroup*>(sdrObj); 1159 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Choice, XML_Requires, 1160 (pObjGroup ? "wpg" : "wps")); 1161 writeDMLDrawing(sdrObj, &rFrameFormat, nAnchorId); 1162 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Choice); 1163 1164 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Fallback); 1165 writeVMLDrawing(sdrObj, rFrameFormat); 1166 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Fallback); 1167 1168 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_AlternateContent); 1169 } 1170 else 1171 writeVMLDrawing(sdrObj, rFrameFormat); 1172 1173 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen); 1174 } 1175 1176 // Converts ARGB transparency (0..255) to drawingml alpha (opposite, and 0..100000) 1177 static OString lcl_TransparencyToDrawingMlAlpha(const Color& rColor) 1178 { 1179 if (rColor.IsTransparent()) 1180 { 1181 sal_Int32 nAlphaPercent = float(rColor.GetAlpha()) / 2.55; 1182 return OString::number(nAlphaPercent * oox::drawingml::PER_PERCENT); 1183 } 1184 1185 return OString(); 1186 } 1187 1188 void DocxSdrExport::writeDMLEffectLst(const SwFrameFormat& rFrameFormat) 1189 { 1190 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow(); 1191 1192 // Output effects 1193 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE) 1194 return; 1195 1196 // Distance is measured diagonally from corner 1197 double nShadowDist 1198 = sqrt(static_cast<double>(aShadowItem.GetWidth()) * aShadowItem.GetWidth() * 2.0); 1199 OString aShadowDist(OString::number(TwipsToEMU(nShadowDist))); 1200 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor()); 1201 OString aShadowAlpha = lcl_TransparencyToDrawingMlAlpha(aShadowItem.GetColor()); 1202 sal_uInt32 nShadowDir = 0; 1203 switch (aShadowItem.GetLocation()) 1204 { 1205 case SvxShadowLocation::TopLeft: 1206 nShadowDir = 13500000; 1207 break; 1208 case SvxShadowLocation::TopRight: 1209 nShadowDir = 18900000; 1210 break; 1211 case SvxShadowLocation::BottomLeft: 1212 nShadowDir = 8100000; 1213 break; 1214 case SvxShadowLocation::BottomRight: 1215 nShadowDir = 2700000; 1216 break; 1217 case SvxShadowLocation::NONE: 1218 case SvxShadowLocation::End: 1219 break; 1220 } 1221 OString aShadowDir(OString::number(nShadowDir)); 1222 1223 m_pImpl->getSerializer()->startElementNS(XML_a, XML_effectLst); 1224 m_pImpl->getSerializer()->startElementNS(XML_a, XML_outerShdw, XML_dist, aShadowDist, XML_dir, 1225 aShadowDir); 1226 if (aShadowAlpha.isEmpty()) 1227 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor); 1228 else 1229 { 1230 m_pImpl->getSerializer()->startElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor); 1231 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_alpha, XML_val, aShadowAlpha); 1232 m_pImpl->getSerializer()->endElementNS(XML_a, XML_srgbClr); 1233 } 1234 m_pImpl->getSerializer()->endElementNS(XML_a, XML_outerShdw); 1235 m_pImpl->getSerializer()->endElementNS(XML_a, XML_effectLst); 1236 } 1237 1238 void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat, 1239 int nDiagramId) 1240 { 1241 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObject)->getUnoShape(), 1242 uno::UNO_QUERY); 1243 1244 // write necessary tags to document.xml 1245 Size aSize(sdrObject->GetSnapRect().GetWidth(), sdrObject->GetSnapRect().GetHeight()); 1246 startDMLAnchorInline(&rFrameFormat, aSize); 1247 1248 m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId); 1249 1250 endDMLAnchorInline(&rFrameFormat); 1251 } 1252 1253 void DocxSdrExport::writeOnlyTextOfFrame(ww8::Frame const* pParentFrame) 1254 { 1255 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat(); 1256 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); 1257 1258 sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0; 1259 sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0; 1260 1261 //Save data here and restore when out of scope 1262 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame); 1263 1264 m_pImpl->setBodyPrAttrList(sax_fastparser::FastSerializerHelper::createAttrList().get()); 1265 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true); 1266 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX); 1267 m_pImpl->getExport().WriteText(); 1268 } 1269 1270 void DocxSdrExport::writeBoxItemLine(const SvxBoxItem& rBox) 1271 { 1272 const editeng::SvxBorderLine* pBorderLine = nullptr; 1273 1274 if (rBox.GetTop()) 1275 { 1276 pBorderLine = rBox.GetTop(); 1277 } 1278 else if (rBox.GetLeft()) 1279 { 1280 pBorderLine = rBox.GetLeft(); 1281 } 1282 else if (rBox.GetBottom()) 1283 { 1284 pBorderLine = rBox.GetBottom(); 1285 } 1286 else if (rBox.GetRight()) 1287 { 1288 pBorderLine = rBox.GetRight(); 1289 } 1290 1291 if (!pBorderLine) 1292 { 1293 return; 1294 } 1295 1296 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer(); 1297 double fConverted(editeng::ConvertBorderWidthToWord(pBorderLine->GetBorderLineStyle(), 1298 pBorderLine->GetWidth())); 1299 OString sWidth(OString::number(TwipsToEMU(fConverted))); 1300 pFS->startElementNS(XML_a, XML_ln, XML_w, sWidth); 1301 1302 pFS->startElementNS(XML_a, XML_solidFill); 1303 OString sColor(msfilter::util::ConvertColor(pBorderLine->GetColor())); 1304 pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor); 1305 pFS->endElementNS(XML_a, XML_solidFill); 1306 1307 if (SvxBorderLineStyle::DASHED == pBorderLine->GetBorderLineStyle()) // Line Style is Dash type 1308 pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash"); 1309 1310 pFS->endElementNS(XML_a, XML_ln); 1311 } 1312 1313 void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId, 1314 bool bTextBoxOnly) 1315 { 1316 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen(); 1317 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame)); 1318 1319 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer(); 1320 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat(); 1321 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); 1322 1323 sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0; 1324 sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0; 1325 1326 //Save data here and restore when out of scope 1327 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame); 1328 1329 // When a frame has some low height, but automatically expanded due 1330 // to lots of contents, this size contains the real size. 1331 const Size aSize = pParentFrame->GetSize(); 1332 1333 uno::Reference<drawing::XShape> xShape; 1334 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject(); 1335 if (pSdrObj) 1336 xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY); 1337 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY); 1338 uno::Reference<beans::XPropertySetInfo> xPropSetInfo; 1339 if (xPropertySet.is()) 1340 xPropSetInfo = xPropertySet->getPropertySetInfo(); 1341 1342 m_pImpl->setBodyPrAttrList(sax_fastparser::FastSerializerHelper::createAttrList().get()); 1343 { 1344 drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP; 1345 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("TextVerticalAdjust")) 1346 xPropertySet->getPropertyValue("TextVerticalAdjust") >>= eAdjust; 1347 m_pImpl->getBodyPrAttrList()->add(XML_anchor, 1348 oox::drawingml::GetTextVerticalAdjust(eAdjust)); 1349 } 1350 1351 if (!bTextBoxOnly) 1352 { 1353 startDMLAnchorInline(&rFrameFormat, aSize); 1354 1355 rtl::Reference<sax_fastparser::FastAttributeList> pDocPrAttrList 1356 = sax_fastparser::FastSerializerHelper::createAttrList(); 1357 pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr()); 1358 pDocPrAttrList->add(XML_name, 1359 OUStringToOString(rFrameFormat.GetName(), RTL_TEXTENCODING_UTF8)); 1360 pFS->singleElementNS(XML_wp, XML_docPr, pDocPrAttrList); 1361 1362 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a), 1363 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml))); 1364 pFS->startElementNS(XML_a, XML_graphicData, XML_uri, 1365 "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"); 1366 pFS->startElementNS(XML_wps, XML_wsp); 1367 pFS->singleElementNS(XML_wps, XML_cNvSpPr, XML_txBox, "1"); 1368 1369 uno::Any aRotation; 1370 m_pImpl->setDMLandVMLTextFrameRotation(0_deg100); 1371 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag")) 1372 { 1373 uno::Sequence<beans::PropertyValue> propList; 1374 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList; 1375 auto pProp = std::find_if(propList.begin(), propList.end(), 1376 [](const beans::PropertyValue& rProp) { 1377 return rProp.Name == "mso-rotation-angle"; 1378 }); 1379 if (pProp != propList.end()) 1380 aRotation = pProp->Value; 1381 } 1382 sal_Int32 nTmp; 1383 if (aRotation >>= nTmp) 1384 m_pImpl->getDMLandVMLTextFrameRotation() = Degree100(nTmp); 1385 OString sRotation(OString::number( 1386 oox::drawingml::ExportRotateClockwisify(m_pImpl->getDMLandVMLTextFrameRotation()))); 1387 // Shape properties 1388 pFS->startElementNS(XML_wps, XML_spPr); 1389 if (m_pImpl->getDMLandVMLTextFrameRotation()) 1390 { 1391 pFS->startElementNS(XML_a, XML_xfrm, XML_rot, sRotation); 1392 } 1393 else 1394 { 1395 pFS->startElementNS(XML_a, XML_xfrm); 1396 } 1397 pFS->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0"); 1398 OString aWidth(OString::number(TwipsToEMU(aSize.Width()))); 1399 OString aHeight(OString::number(TwipsToEMU(aSize.Height()))); 1400 pFS->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight); 1401 pFS->endElementNS(XML_a, XML_xfrm); 1402 OUString shapeType = "rect"; 1403 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag")) 1404 { 1405 uno::Sequence<beans::PropertyValue> propList; 1406 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList; 1407 auto pProp = std::find_if(propList.begin(), propList.end(), 1408 [](const beans::PropertyValue& rProp) { 1409 return rProp.Name == "mso-orig-shape-type"; 1410 }); 1411 if (pProp != propList.end()) 1412 pProp->Value >>= shapeType; 1413 } 1414 //Empty shapeType will lead to corruption so to avoid that shapeType is set to default i.e. "rect" 1415 if (shapeType.isEmpty()) 1416 shapeType = "rect"; 1417 1418 pFS->singleElementNS(XML_a, XML_prstGeom, XML_prst, shapeType); 1419 m_pImpl->setDMLTextFrameSyntax(true); 1420 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true); 1421 m_pImpl->setDMLTextFrameSyntax(false); 1422 writeDMLEffectLst(rFrameFormat); 1423 pFS->endElementNS(XML_wps, XML_spPr); 1424 } 1425 1426 //first, loop through ALL of the chained textboxes to identify a unique ID for each chain, and sequence number for each textbox in that chain. 1427 if (!m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized) 1428 { 1429 sal_Int32 nSeq = 0; 1430 for (auto& rEntry : m_pImpl->getExport().m_aLinkedTextboxesHelper) 1431 { 1432 //find the start of a textbox chain: has no PREVIOUS link, but does have NEXT link 1433 if (rEntry.second.sPrevChain.isEmpty() && !rEntry.second.sNextChain.isEmpty()) 1434 { 1435 //assign this chain a unique ID and start a new sequence 1436 nSeq = 0; 1437 rEntry.second.nId = ++m_pImpl->getExport().m_nLinkedTextboxesChainId; 1438 rEntry.second.nSeq = nSeq; 1439 1440 OUString sCheckForBrokenChains = rEntry.first; 1441 1442 //follow the chain and assign the same id, and incremental sequence numbers. 1443 auto followChainIter 1444 = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(rEntry.second.sNextChain); 1445 while (followChainIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end()) 1446 { 1447 //verify that the NEXT textbox also points to me as the PREVIOUS. 1448 // A broken link indicates a leftover remnant that can be ignored. 1449 if (followChainIter->second.sPrevChain != sCheckForBrokenChains) 1450 break; 1451 1452 followChainIter->second.nId = m_pImpl->getExport().m_nLinkedTextboxesChainId; 1453 followChainIter->second.nSeq = ++nSeq; 1454 1455 //empty next chain indicates the end of the linked chain. 1456 if (followChainIter->second.sNextChain.isEmpty()) 1457 break; 1458 1459 sCheckForBrokenChains = followChainIter->first; 1460 followChainIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find( 1461 followChainIter->second.sNextChain); 1462 } 1463 } 1464 } 1465 m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized = true; 1466 } 1467 1468 m_pImpl->getExport().m_pParentFrame = nullptr; 1469 bool skipTxBxContent = false; 1470 bool isTxbxLinked = false; 1471 1472 OUString sLinkChainName; 1473 if (xPropSetInfo.is()) 1474 { 1475 if (xPropSetInfo->hasPropertyByName("LinkDisplayName")) 1476 xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName; 1477 else if (xPropSetInfo->hasPropertyByName("ChainName")) 1478 xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName; 1479 } 1480 1481 // second, check if THIS textbox is linked and then decide whether to write the tag txbx or linkedTxbx 1482 auto linkedTextboxesIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(sLinkChainName); 1483 if (linkedTextboxesIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end()) 1484 { 1485 if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq != 0)) 1486 { 1487 //not the first in the chain, so write the tag as linkedTxbx 1488 pFS->singleElementNS(XML_wps, XML_linkedTxbx, XML_id, 1489 OString::number(linkedTextboxesIter->second.nId), XML_seq, 1490 OString::number(linkedTextboxesIter->second.nSeq)); 1491 /* no text content should be added to this tag, 1492 since the textbox is linked, the entire content 1493 is written in txbx block 1494 */ 1495 skipTxBxContent = true; 1496 } 1497 else if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq == 0)) 1498 { 1499 /* this is the first textbox in the chaining, we add the text content 1500 to this block*/ 1501 //since the text box is linked, it needs an id. 1502 pFS->startElementNS(XML_wps, XML_txbx, XML_id, 1503 OString::number(linkedTextboxesIter->second.nId)); 1504 isTxbxLinked = true; 1505 } 1506 } 1507 1508 if (!skipTxBxContent) 1509 { 1510 if (!isTxbxLinked) 1511 pFS->startElementNS(XML_wps, XML_txbx); //text box is not linked, therefore no id. 1512 1513 pFS->startElementNS(XML_w, XML_txbxContent); 1514 1515 const SvxFrameDirectionItem& rDirection = rFrameFormat.GetFrameDir(); 1516 if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB) 1517 m_pImpl->getBodyPrAttrList()->add(XML_vert, "eaVert"); 1518 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT) 1519 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert270"); 1520 1521 { 1522 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true); 1523 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX); 1524 m_pImpl->getExport().WriteText(); 1525 if (m_pImpl->getParagraphSdtOpen()) 1526 { 1527 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock(); 1528 m_pImpl->setParagraphSdtOpen(false); 1529 } 1530 } 1531 1532 pFS->endElementNS(XML_w, XML_txbxContent); 1533 pFS->endElementNS(XML_wps, XML_txbx); 1534 } 1535 1536 // We need to init padding to 0, if it's not set. 1537 // In LO the default is 0 and so ins attributes are not set when padding is 0 1538 // but in MSO the default is 254 / 127, so we need to set 0 padding explicitly 1539 if (m_pImpl->getBodyPrAttrList()) 1540 { 1541 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_lIns)) 1542 m_pImpl->getBodyPrAttrList()->add(XML_lIns, OString::number(0)); 1543 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_tIns)) 1544 m_pImpl->getBodyPrAttrList()->add(XML_tIns, OString::number(0)); 1545 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_rIns)) 1546 m_pImpl->getBodyPrAttrList()->add(XML_rIns, OString::number(0)); 1547 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_bIns)) 1548 m_pImpl->getBodyPrAttrList()->add(XML_bIns, OString::number(0)); 1549 } 1550 1551 rtl::Reference<FastAttributeList> xBodyPrAttrList(m_pImpl->getBodyPrAttrList()); 1552 m_pImpl->setBodyPrAttrList(nullptr); 1553 if (!bTextBoxOnly) 1554 { 1555 pFS->startElementNS(XML_wps, XML_bodyPr, xBodyPrAttrList); 1556 // AutoSize of the Text Frame. 1557 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize(); 1558 pFS->singleElementNS( 1559 XML_a, 1560 (rSize.GetHeightSizeType() == SwFrameSize::Variable ? XML_spAutoFit : XML_noAutofit)); 1561 pFS->endElementNS(XML_wps, XML_bodyPr); 1562 1563 pFS->endElementNS(XML_wps, XML_wsp); 1564 pFS->endElementNS(XML_a, XML_graphicData); 1565 pFS->endElementNS(XML_a, XML_graphic); 1566 1567 // Relative size of the Text Frame. 1568 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent(); 1569 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED) 1570 { 1571 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom, 1572 (rSize.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME 1573 ? "page" 1574 : "margin")); 1575 pFS->startElementNS(XML_wp14, XML_pctWidth); 1576 pFS->writeEscaped(OUString::number(nWidthPercent * oox::drawingml::PER_PERCENT)); 1577 pFS->endElementNS(XML_wp14, XML_pctWidth); 1578 pFS->endElementNS(XML_wp14, XML_sizeRelH); 1579 } 1580 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent(); 1581 if (nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED) 1582 { 1583 pFS->startElementNS( 1584 XML_wp14, XML_sizeRelV, XML_relativeFrom, 1585 (rSize.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME ? "page" 1586 : "margin")); 1587 pFS->startElementNS(XML_wp14, XML_pctHeight); 1588 pFS->writeEscaped(OUString::number(nHeightPercent * oox::drawingml::PER_PERCENT)); 1589 pFS->endElementNS(XML_wp14, XML_pctHeight); 1590 pFS->endElementNS(XML_wp14, XML_sizeRelV); 1591 } 1592 1593 endDMLAnchorInline(&rFrameFormat); 1594 } 1595 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen); 1596 } 1597 1598 void DocxSdrExport::writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly) 1599 { 1600 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen(); 1601 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame)); 1602 1603 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer(); 1604 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat(); 1605 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); 1606 1607 sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0; 1608 sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0; 1609 1610 //Save data here and restore when out of scope 1611 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame); 1612 1613 // When a frame has some low height, but automatically expanded due 1614 // to lots of contents, this size contains the real size. 1615 const Size aSize = pParentFrame->GetSize(); 1616 m_pImpl->setFlyFrameSize(&aSize); 1617 1618 m_pImpl->setTextFrameSyntax(true); 1619 m_pImpl->setFlyAttrList(sax_fastparser::FastSerializerHelper::createAttrList()); 1620 m_pImpl->setTextboxAttrList(sax_fastparser::FastSerializerHelper::createAttrList()); 1621 m_pImpl->getTextFrameStyle() = "position:absolute"; 1622 if (!bTextBoxOnly) 1623 { 1624 OString sRotation(OString::number(m_pImpl->getDMLandVMLTextFrameRotation().get() / -100)); 1625 m_pImpl->getExport().SdrExporter().getTextFrameStyle().append(";rotation:" + sRotation); 1626 } 1627 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true); 1628 m_pImpl->getFlyAttrList()->add(XML_style, m_pImpl->getTextFrameStyle().makeStringAndClear()); 1629 1630 const SdrObject* pObject = pParentFrame->GetFrameFormat().FindRealSdrObject(); 1631 if (pObject != nullptr) 1632 { 1633 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObject); 1634 if (!sAnchorId.isEmpty()) 1635 m_pImpl->getFlyAttrList()->addNS(XML_w14, XML_anchorId, 1636 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8)); 1637 } 1638 rtl::Reference<FastAttributeList> xFlyAttrList(m_pImpl->getFlyAttrList()); 1639 m_pImpl->getFlyAttrList().clear(); 1640 rtl::Reference<FastAttributeList> xTextboxAttrList(m_pImpl->getTextboxAttrList()); 1641 m_pImpl->getTextboxAttrList().clear(); 1642 m_pImpl->setTextFrameSyntax(false); 1643 m_pImpl->setFlyFrameSize(nullptr); 1644 m_pImpl->getExport().m_pParentFrame = nullptr; 1645 1646 if (!bTextBoxOnly) 1647 { 1648 pFS->startElementNS(XML_w, XML_pict); 1649 pFS->startElementNS(XML_v, XML_rect, xFlyAttrList); 1650 m_pImpl->textFrameShadow(rFrameFormat); 1651 if (m_pImpl->getFlyFillAttrList().is()) 1652 { 1653 rtl::Reference<FastAttributeList> xFlyFillAttrList(m_pImpl->getFlyFillAttrList()); 1654 m_pImpl->getFlyFillAttrList().clear(); 1655 pFS->singleElementNS(XML_v, XML_fill, xFlyFillAttrList); 1656 } 1657 if (m_pImpl->getDashLineStyleAttr().is()) 1658 { 1659 rtl::Reference<FastAttributeList> xDashLineStyleAttr(m_pImpl->getDashLineStyleAttr()); 1660 m_pImpl->getDashLineStyleAttr().clear(); 1661 pFS->singleElementNS(XML_v, XML_stroke, xDashLineStyleAttr); 1662 } 1663 pFS->startElementNS(XML_v, XML_textbox, xTextboxAttrList); 1664 } 1665 pFS->startElementNS(XML_w, XML_txbxContent); 1666 { 1667 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true); 1668 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX); 1669 m_pImpl->getExport().WriteText(); 1670 if (m_pImpl->getParagraphSdtOpen()) 1671 { 1672 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock(); 1673 m_pImpl->setParagraphSdtOpen(false); 1674 } 1675 } 1676 pFS->endElementNS(XML_w, XML_txbxContent); 1677 if (!bTextBoxOnly) 1678 { 1679 pFS->endElementNS(XML_v, XML_textbox); 1680 1681 if (m_pImpl->getFlyWrapAttrList()) 1682 { 1683 rtl::Reference<FastAttributeList> xFlyWrapAttrList(m_pImpl->getFlyWrapAttrList()); 1684 m_pImpl->setFlyWrapAttrList(nullptr); 1685 pFS->singleElementNS(XML_w10, XML_wrap, xFlyWrapAttrList); 1686 } 1687 1688 pFS->endElementNS(XML_v, XML_rect); 1689 pFS->endElementNS(XML_w, XML_pict); 1690 } 1691 1692 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen); 1693 } 1694 1695 bool DocxSdrExport::isTextBox(const SwFrameFormat& rFrameFormat) 1696 { 1697 return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT); 1698 } 1699 1700 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1701
