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 <memory> 21 #include "docxattributeoutput.hxx" 22 #include "docxhelper.hxx" 23 #include "docxsdrexport.hxx" 24 #include "docxexportfilter.hxx" 25 #include "docxfootnotes.hxx" 26 #include "writerwordglue.hxx" 27 #include "ww8par.hxx" 28 #include <fmtcntnt.hxx> 29 #include <fmtftn.hxx> 30 #include <fchrfmt.hxx> 31 #include <tgrditem.hxx> 32 #include <fmtruby.hxx> 33 #include <fmtfollowtextflow.hxx> 34 #include <fmtanchr.hxx> 35 #include <breakit.hxx> 36 #include <redline.hxx> 37 #include <unocoll.hxx> 38 #include <unoframe.hxx> 39 #include <textboxhelper.hxx> 40 #include <rdfhelper.hxx> 41 #include "wrtww8.hxx" 42 43 #include <comphelper/processfactory.hxx> 44 #include <comphelper/random.hxx> 45 #include <comphelper/string.hxx> 46 #include <comphelper/flagguard.hxx> 47 #include <comphelper/sequence.hxx> 48 #include <oox/token/namespaces.hxx> 49 #include <oox/token/tokens.hxx> 50 #include <oox/export/utils.hxx> 51 #include <oox/mathml/export.hxx> 52 #include <oox/drawingml/drawingmltypes.hxx> 53 #include <oox/token/relationship.hxx> 54 #include <oox/export/vmlexport.hxx> 55 #include <oox/ole/olehelper.hxx> 56 57 #include <editeng/autokernitem.hxx> 58 #include <editeng/unoprnms.hxx> 59 #include <editeng/fontitem.hxx> 60 #include <editeng/tstpitem.hxx> 61 #include <editeng/spltitem.hxx> 62 #include <editeng/widwitem.hxx> 63 #include <editeng/shaditem.hxx> 64 #include <editeng/brushitem.hxx> 65 #include <editeng/postitem.hxx> 66 #include <editeng/wghtitem.hxx> 67 #include <editeng/kernitem.hxx> 68 #include <editeng/crossedoutitem.hxx> 69 #include <editeng/cmapitem.hxx> 70 #include <editeng/udlnitem.hxx> 71 #include <editeng/langitem.hxx> 72 #include <editeng/lspcitem.hxx> 73 #include <editeng/escapementitem.hxx> 74 #include <editeng/fhgtitem.hxx> 75 #include <editeng/colritem.hxx> 76 #include <editeng/hyphenzoneitem.hxx> 77 #include <editeng/ulspitem.hxx> 78 #include <editeng/contouritem.hxx> 79 #include <editeng/shdditem.hxx> 80 #include <editeng/emphasismarkitem.hxx> 81 #include <editeng/twolinesitem.hxx> 82 #include <editeng/charscaleitem.hxx> 83 #include <editeng/charrotateitem.hxx> 84 #include <editeng/charreliefitem.hxx> 85 #include <editeng/paravertalignitem.hxx> 86 #include <editeng/pgrditem.hxx> 87 #include <editeng/frmdiritem.hxx> 88 #include <editeng/blinkitem.hxx> 89 #include <editeng/charhiddenitem.hxx> 90 #include <editeng/editobj.hxx> 91 #include <editeng/keepitem.hxx> 92 #include <editeng/borderline.hxx> 93 #include <editeng/prntitem.hxx> 94 #include <sax/tools/converter.hxx> 95 #include <svx/xdef.hxx> 96 #include <svx/xfillit0.hxx> 97 #include <svx/xflclit.hxx> 98 #include <svx/xflgrit.hxx> 99 #include <svx/svdouno.hxx> 100 #include <svx/unobrushitemhelper.hxx> 101 #include <svl/grabbagitem.hxx> 102 #include <sfx2/sfxbasemodel.hxx> 103 #include <tools/date.hxx> 104 #include <tools/datetime.hxx> 105 #include <tools/datetimeutils.hxx> 106 #include <tools/UnitConversion.hxx> 107 #include <svl/whiter.hxx> 108 #include <rtl/tencinfo.h> 109 #include <sal/log.hxx> 110 #include <sot/exchange.hxx> 111 112 #include <docufld.hxx> 113 #include <authfld.hxx> 114 #include <flddropdown.hxx> 115 #include <fmtclds.hxx> 116 #include <fmtinfmt.hxx> 117 #include <fmtrowsplt.hxx> 118 #include <fmtline.hxx> 119 #include <ftninfo.hxx> 120 #include <htmltbl.hxx> 121 #include <lineinfo.hxx> 122 #include <ndgrf.hxx> 123 #include <ndole.hxx> 124 #include <ndtxt.hxx> 125 #include <pagedesc.hxx> 126 #include <paratr.hxx> 127 #include <swmodule.hxx> 128 #include <swtable.hxx> 129 #include <txtftn.hxx> 130 #include <fmtautofmt.hxx> 131 #include <docsh.hxx> 132 #include <docary.hxx> 133 #include <fmtclbl.hxx> 134 #include <IDocumentSettingAccess.hxx> 135 #include <IDocumentRedlineAccess.hxx> 136 #include <grfatr.hxx> 137 #include <frmatr.hxx> 138 #include <txtatr.hxx> 139 #include <frameformats.hxx> 140 141 #include <o3tl/string_view.hxx> 142 #include <o3tl/unit_conversion.hxx> 143 #include <osl/file.hxx> 144 #include <utility> 145 #include <vcl/embeddedfontshelper.hxx> 146 147 #include <com/sun/star/i18n/ScriptType.hpp> 148 #include <com/sun/star/i18n/XBreakIterator.hpp> 149 #include <com/sun/star/chart2/XChartDocument.hpp> 150 #include <com/sun/star/drawing/ShadingPattern.hpp> 151 #include <com/sun/star/text/GraphicCrop.hpp> 152 #include <com/sun/star/embed/EmbedStates.hpp> 153 #include <com/sun/star/embed/Aspects.hpp> 154 155 #include <algorithm> 156 #include <cstddef> 157 #include <stdarg.h> 158 #include <string_view> 159 160 #include <toolkit/helper/vclunohelper.hxx> 161 #include <unicode/regex.h> 162 163 using ::editeng::SvxBorderLine; 164 165 using namespace oox; 166 using namespace docx; 167 using namespace sax_fastparser; 168 using namespace nsSwDocInfoSubType; 169 using namespace sw::util; 170 using namespace ::com::sun::star; 171 using namespace ::com::sun::star::drawing; 172 173 const sal_Int32 Tag_StartParagraph_1 = 1; 174 const sal_Int32 Tag_StartParagraph_2 = 2; 175 const sal_Int32 Tag_WriteSdtBlock = 3; 176 const sal_Int32 Tag_StartParagraphProperties = 4; 177 const sal_Int32 Tag_InitCollectedParagraphProperties = 5; 178 const sal_Int32 Tag_StartRun_1 = 6; 179 const sal_Int32 Tag_StartRun_2 = 7; 180 const sal_Int32 Tag_StartRun_3 = 8; 181 const sal_Int32 Tag_EndRun_1 = 9; 182 const sal_Int32 Tag_EndRun_2 = 10; 183 const sal_Int32 Tag_StartRunProperties = 11; 184 const sal_Int32 Tag_InitCollectedRunProperties = 12; 185 const sal_Int32 Tag_Redline_1 = 13; 186 const sal_Int32 Tag_Redline_2 = 14; 187 const sal_Int32 Tag_TableDefinition = 15; 188 const sal_Int32 Tag_OutputFlyFrame = 16; 189 const sal_Int32 Tag_StartSection = 17; 190 191 namespace { 192 193 class FFDataWriterHelper 194 { 195 ::sax_fastparser::FSHelperPtr m_pSerializer; 196 void writeCommonStart( const OUString& rName, 197 const OUString& rEntryMacro, 198 const OUString& rExitMacro, 199 const OUString& rHelp, 200 const OUString& rHint ) 201 { 202 m_pSerializer->startElementNS(XML_w, XML_ffData); 203 m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName); 204 m_pSerializer->singleElementNS(XML_w, XML_enabled); 205 m_pSerializer->singleElementNS(XML_w, XML_calcOnExit, FSNS(XML_w, XML_val), "0"); 206 207 if ( !rEntryMacro.isEmpty() ) 208 m_pSerializer->singleElementNS( XML_w, XML_entryMacro, 209 FSNS(XML_w, XML_val), rEntryMacro ); 210 211 if ( !rExitMacro.isEmpty() ) 212 m_pSerializer->singleElementNS(XML_w, XML_exitMacro, FSNS(XML_w, XML_val), rExitMacro); 213 214 if ( !rHelp.isEmpty() ) 215 m_pSerializer->singleElementNS( XML_w, XML_helpText, 216 FSNS(XML_w, XML_type), "text", 217 FSNS(XML_w, XML_val), rHelp ); 218 219 if ( !rHint.isEmpty() ) 220 m_pSerializer->singleElementNS( XML_w, XML_statusText, 221 FSNS(XML_w, XML_type), "text", 222 FSNS(XML_w, XML_val), rHint ); 223 224 } 225 void writeFinish() 226 { 227 m_pSerializer->endElementNS( XML_w, XML_ffData ); 228 } 229 public: 230 explicit FFDataWriterHelper( ::sax_fastparser::FSHelperPtr rSerializer ) : m_pSerializer(std::move( rSerializer )){} 231 void WriteFormCheckbox( const OUString& rName, 232 const OUString& rEntryMacro, 233 const OUString& rExitMacro, 234 const OUString& rHelp, 235 const OUString& rHint, 236 bool bChecked ) 237 { 238 writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint ); 239 // Checkbox specific bits 240 m_pSerializer->startElementNS(XML_w, XML_checkBox); 241 // currently hardcoding autosize 242 // #TODO check if this defaulted 243 m_pSerializer->startElementNS(XML_w, XML_sizeAuto); 244 m_pSerializer->endElementNS( XML_w, XML_sizeAuto ); 245 if ( bChecked ) 246 m_pSerializer->singleElementNS(XML_w, XML_checked); 247 m_pSerializer->endElementNS( XML_w, XML_checkBox ); 248 writeFinish(); 249 } 250 251 void WriteFormText( const OUString& rName, 252 const OUString& rEntryMacro, 253 const OUString& rExitMacro, 254 const OUString& rHelp, 255 const OUString& rHint, 256 const OUString& rType, 257 const OUString& rDefaultText, 258 sal_uInt16 nMaxLength, 259 const OUString& rFormat ) 260 { 261 writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint ); 262 263 m_pSerializer->startElementNS(XML_w, XML_textInput); 264 if ( !rType.isEmpty() ) 265 m_pSerializer->singleElementNS(XML_w, XML_type, FSNS(XML_w, XML_val), rType); 266 if ( !rDefaultText.isEmpty() ) 267 m_pSerializer->singleElementNS(XML_w, XML_default, FSNS(XML_w, XML_val), rDefaultText); 268 if ( nMaxLength ) 269 m_pSerializer->singleElementNS( XML_w, XML_maxLength, 270 FSNS(XML_w, XML_val), OString::number(nMaxLength) ); 271 if ( !rFormat.isEmpty() ) 272 m_pSerializer->singleElementNS(XML_w, XML_format, FSNS(XML_w, XML_val), rFormat); 273 m_pSerializer->endElementNS( XML_w, XML_textInput ); 274 275 writeFinish(); 276 } 277 }; 278 279 class FieldMarkParamsHelper 280 { 281 const sw::mark::IFieldmark& mrFieldmark; 282 public: 283 explicit FieldMarkParamsHelper( const sw::mark::IFieldmark& rFieldmark ) : mrFieldmark( rFieldmark ) {} 284 OUString const & getName() const { return mrFieldmark.GetName(); } 285 template < typename T > 286 bool extractParam( const OUString& rKey, T& rResult ) 287 { 288 bool bResult = false; 289 if ( mrFieldmark.GetParameters() ) 290 { 291 sw::mark::IFieldmark::parameter_map_t::const_iterator it = mrFieldmark.GetParameters()->find( rKey ); 292 if ( it != mrFieldmark.GetParameters()->end() ) 293 bResult = ( it->second >>= rResult ); 294 } 295 return bResult; 296 } 297 }; 298 299 // [ISO/IEC29500-1:2016] 17.18.50 ST_LongHexNumber (Eight Digit Hexadecimal Value) 300 OUString NumberToHexBinary(sal_Int32 n) 301 { 302 OUStringBuffer aBuf; 303 sax::Converter::convertNumberToHexBinary(aBuf, n); 304 return aBuf.makeStringAndClear(); 305 } 306 307 } 308 309 void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ ) 310 { 311 if (bIsRTL) 312 m_pSerializer->singleElementNS(XML_w, XML_rtl, FSNS(XML_w, XML_val), "true"); 313 } 314 315 /// Are multiple paragraphs disallowed inside this type of SDT? 316 static bool lcl_isOnelinerSdt(std::u16string_view rName) 317 { 318 return rName == u"Title" || rName == u"Subtitle" || rName == u"Company"; 319 } 320 321 static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ...) 322 { 323 if (!pAttrList.is()) 324 pAttrList = FastSerializerHelper::createAttrList(); 325 326 va_list args; 327 va_start(args, nAttrs); 328 for (sal_Int32 i = 0; i < nAttrs; i++) 329 { 330 sal_Int32 nName = va_arg(args, sal_Int32); 331 const char* pValue = va_arg(args, const char*); 332 if (pValue) 333 pAttrList->add(nName, pValue); 334 } 335 va_end(args); 336 } 337 338 static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const char* sAttrValue) 339 { 340 AddToAttrList(pAttrList, 1, nAttrName, sAttrValue); 341 } 342 343 // write a floating table directly to docx without the surrounding frame 344 void DocxAttributeOutput::WriteFloatingTable(ww8::Frame const* pParentFrame) 345 { 346 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat(); 347 m_aFloatingTablesOfParagraph.insert(&rFrameFormat); 348 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); 349 350 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0); 351 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0); 352 353 //Save data here and restore when out of scope 354 ExportDataSaveRestore aDataGuard(GetExport(), nStt, nEnd, pParentFrame); 355 356 // set a floatingTableFrame AND unset parent frame, 357 // otherwise exporter thinks we are still in a frame 358 m_rExport.SetFloatingTableFrame(pParentFrame); 359 m_rExport.m_pParentFrame = nullptr; 360 361 GetExport().WriteText(); 362 363 m_rExport.SetFloatingTableFrame(nullptr); 364 } 365 366 static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutput) 367 { 368 const auto& rExport = rDocxAttributeOutput.GetExport(); 369 // iterate though all SpzFrameFormats and check whether they are anchored to the current text node 370 for( sal_uInt16 nCnt = rExport.m_rDoc.GetSpzFrameFormats()->size(); nCnt; ) 371 { 372 const SwFrameFormat* pFrameFormat = (*rExport.m_rDoc.GetSpzFrameFormats())[ --nCnt ]; 373 const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor(); 374 const SwPosition* pPosition = rAnchor.GetContentAnchor(); 375 376 if (!pPosition || ! rExport.m_pCurPam->GetNode().GetTextNode()) 377 continue; 378 379 if (pPosition->nNode != rExport.m_pCurPam->GetNode().GetTextNode()->GetIndex()) 380 continue; 381 382 const SwNodeIndex* pStartNode = pFrameFormat->GetContent().GetContentIdx(); 383 if (!pStartNode) 384 continue; 385 386 SwNodeIndex aStartNode = *pStartNode; 387 388 // go to the next node (actual content) 389 ++aStartNode; 390 391 // this has to be a table 392 if (!aStartNode.GetNode().IsTableNode()) 393 continue; 394 395 // go to the end of the table 396 SwNodeOffset aEndIndex = aStartNode.GetNode().EndOfSectionIndex(); 397 // go one deeper 398 aEndIndex++; 399 // this has to be the end of the content 400 if (aEndIndex != pFrameFormat->GetContent().GetContentIdx()->GetNode().EndOfSectionIndex()) 401 continue; 402 403 // check for a grabBag and "TablePosition" attribute -> then we can export the table directly 404 SwTableNode* pTableNode = aStartNode.GetNode().GetTableNode(); 405 SwTable& rTable = pTableNode->GetTable(); 406 SwFrameFormat* pTableFormat = rTable.GetFrameFormat(); 407 const SfxGrabBagItem* pTableGrabBag = pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG); 408 std::map<OUString, css::uno::Any> aTableGrabBag = pTableGrabBag->GetGrabBag(); 409 // no grabbag? 410 if (aTableGrabBag.find("TablePosition") == aTableGrabBag.end()) 411 continue; 412 413 // write table to docx 414 ww8::Frame aFrame(*pFrameFormat,*pPosition); 415 rDocxAttributeOutput.WriteFloatingTable(&aFrame); 416 } 417 } 418 419 sal_Int32 DocxAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, 420 bool bGenerateParaId) 421 { 422 // Paragraphs (in headers/footers/comments/frames etc) can start before another finishes. 423 // So a stack is needed to keep track of each paragraph's status separately. 424 // Complication: Word can't handle nested text boxes, so those need to be collected together. 425 if ( !m_aFramesOfParagraph.size() || !m_nTextFrameLevel ) 426 m_aFramesOfParagraph.push(std::vector<ww8::Frame>()); 427 428 // look ahead for floating tables that were put into a frame during import 429 // floating tables in shapes are not supported: exclude this case 430 if (!pTextNodeInfo && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen()) 431 { 432 checkAndWriteFloatingTables(*this); 433 } 434 435 if ( m_nColBreakStatus == COLBRK_POSTPONE ) 436 m_nColBreakStatus = COLBRK_WRITE; 437 438 // Output table/table row/table cell starts if needed 439 if ( pTextNodeInfo ) 440 { 441 // New cell/row? 442 if ( m_tableReference->m_nTableDepth > 0 && !m_tableReference->m_bTableCellOpen ) 443 { 444 ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner( pTextNodeInfo->getInnerForDepth( m_tableReference->m_nTableDepth ) ); 445 if ( pDeepInner->getCell() == 0 ) 446 StartTableRow( pDeepInner ); 447 448 const sal_uInt32 nCell = pDeepInner->getCell(); 449 const sal_uInt32 nRow = pDeepInner->getRow(); 450 451 SyncNodelessCells(pDeepInner, nCell, nRow); 452 StartTableCell(pDeepInner, nCell, nRow); 453 } 454 455 sal_uInt32 nRow = pTextNodeInfo->getRow(); 456 sal_uInt32 nCell = pTextNodeInfo->getCell(); 457 if (nCell == 0) 458 { 459 // Do we have to start the table? 460 // [If we are at the right depth already, it means that we 461 // continue the table cell] 462 sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth(); 463 464 if ( nCurrentDepth > m_tableReference->m_nTableDepth ) 465 { 466 // Start all the tables that begin here 467 for ( sal_uInt32 nDepth = m_tableReference->m_nTableDepth + 1; nDepth <= nCurrentDepth; ++nDepth ) 468 { 469 ww8::WW8TableNodeInfoInner::Pointer_t pInner( pTextNodeInfo->getInnerForDepth( nDepth ) ); 470 471 StartTable( pInner ); 472 StartTableRow( pInner ); 473 474 StartTableCell(pInner, 0, nDepth == nCurrentDepth ? nRow : 0); 475 } 476 477 m_tableReference->m_nTableDepth = nCurrentDepth; 478 } 479 } 480 } 481 482 // Look up the "sdt end before this paragraph" property early, when it 483 // would normally arrive, it would be too late (would be after the 484 // paragraph start has been written). 485 bool bEndParaSdt = false; 486 if (m_aParagraphSdt.m_bStartedSdt) 487 { 488 SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode(); 489 if (pTextNode && pTextNode->GetpSwAttrSet()) 490 { 491 const SfxItemSet* pSet = pTextNode->GetpSwAttrSet(); 492 if (const SfxPoolItem* pItem = pSet->GetItem(RES_PARATR_GRABBAG)) 493 { 494 const SfxGrabBagItem& rParaGrabBag = static_cast<const SfxGrabBagItem&>(*pItem); 495 const std::map<OUString, css::uno::Any>& rMap = rParaGrabBag.GetGrabBag(); 496 bEndParaSdt = m_aParagraphSdt.m_bStartedSdt && rMap.find("ParaSdtEndBefore") != rMap.end(); 497 } 498 } 499 } 500 // TODO also avoid multiline paragraphs in those SDT types for shape text 501 bool bOneliner = m_aParagraphSdt.m_bStartedSdt && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias); 502 if (bEndParaSdt || (m_aParagraphSdt.m_bStartedSdt && m_bHadSectPr) || bOneliner) 503 { 504 // This is the common case: "close sdt before the current paragraph" was requested by the next paragraph. 505 m_aParagraphSdt.EndSdtBlock(m_pSerializer); 506 m_aStartedParagraphSdtPrAlias.clear(); 507 } 508 m_bHadSectPr = false; 509 510 // this mark is used to be able to enclose the paragraph inside a sdr tag. 511 // We will only know if we have to do that later. 512 m_pSerializer->mark(Tag_StartParagraph_1); 513 514 std::optional<OUString> aParaId; 515 sal_Int32 nParaId = 0; 516 if (bGenerateParaId) 517 { 518 nParaId = m_nNextParaId++; 519 aParaId = NumberToHexBinary(nParaId); 520 } 521 m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId); 522 523 // postpone the output of the run (we get it before the paragraph 524 // properties, but must write it after them) 525 m_pSerializer->mark(Tag_StartParagraph_2); 526 527 // no section break in this paragraph yet; can be set in SectionBreak() 528 m_pSectionInfo.reset(); 529 530 m_bParagraphOpened = true; 531 m_bIsFirstParagraph = false; 532 533 return nParaId; 534 } 535 536 static OString convertToOOXMLVertOrient(sal_Int16 nOrient) 537 { 538 switch( nOrient ) 539 { 540 case text::VertOrientation::CENTER: 541 case text::VertOrientation::LINE_CENTER: 542 return "center"; 543 case text::VertOrientation::BOTTOM: 544 return "bottom"; 545 case text::VertOrientation::LINE_BOTTOM: 546 return "outside"; 547 case text::VertOrientation::TOP: 548 return "top"; 549 case text::VertOrientation::LINE_TOP: 550 return "inside"; 551 default: 552 return OString(); 553 } 554 } 555 556 static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle) 557 { 558 switch( nOrient ) 559 { 560 case text::HoriOrientation::LEFT: 561 return bIsPosToggle ? "inside" : "left"; 562 case text::HoriOrientation::INSIDE: 563 return "inside"; 564 case text::HoriOrientation::RIGHT: 565 return bIsPosToggle ? "outside" : "right"; 566 case text::HoriOrientation::OUTSIDE: 567 return "outside"; 568 case text::HoriOrientation::CENTER: 569 case text::HoriOrientation::FULL: 570 return "center"; 571 default: 572 return OString(); 573 } 574 } 575 576 static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel) 577 { 578 switch (nOrientRel) 579 { 580 case text::RelOrientation::PAGE_PRINT_AREA: 581 return "margin"; 582 case text::RelOrientation::PAGE_FRAME: 583 return "page"; 584 case text::RelOrientation::FRAME: 585 case text::RelOrientation::TEXT_LINE: 586 default: 587 return "text"; 588 } 589 } 590 591 static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel) 592 { 593 switch (nOrientRel) 594 { 595 case text::RelOrientation::PAGE_PRINT_AREA: 596 return "margin"; 597 case text::RelOrientation::PAGE_FRAME: 598 return "page"; 599 case text::RelOrientation::CHAR: 600 case text::RelOrientation::PAGE_RIGHT: 601 case text::RelOrientation::FRAME: 602 default: 603 return "text"; 604 } 605 } 606 607 void SdtBlockHelper::DeleteAndResetTheLists() 608 { 609 if (m_pTokenChildren.is() ) 610 m_pTokenChildren.clear(); 611 if (m_pDataBindingAttrs.is() ) 612 m_pDataBindingAttrs.clear(); 613 if (m_pTextAttrs.is()) 614 m_pTextAttrs.clear(); 615 if (!m_aAlias.isEmpty()) 616 m_aAlias.clear(); 617 if (!m_aPlaceHolderDocPart.isEmpty()) 618 m_aPlaceHolderDocPart.clear(); 619 if (!m_aColor.isEmpty()) 620 m_aColor.clear(); 621 m_bHasId = false; 622 } 623 624 void SdtBlockHelper::WriteSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer, bool bRunTextIsOn, bool bParagraphHasDrawing) 625 { 626 if (m_nSdtPrToken <= 0 && !m_pDataBindingAttrs.is() && !m_bHasId) 627 return; 628 629 // sdt start mark 630 pSerializer->mark(Tag_WriteSdtBlock); 631 632 pSerializer->startElementNS(XML_w, XML_sdt); 633 634 // output sdt properties 635 pSerializer->startElementNS(XML_w, XML_sdtPr); 636 637 if (m_nSdtPrToken > 0 && m_pTokenChildren.is()) 638 { 639 if (!m_pTokenAttributes.is()) 640 pSerializer->startElement(m_nSdtPrToken); 641 else 642 { 643 rtl::Reference<FastAttributeList> xAttrList = std::move(m_pTokenAttributes); 644 pSerializer->startElement(m_nSdtPrToken, xAttrList); 645 } 646 647 if (m_nSdtPrToken == FSNS(XML_w, XML_date) || m_nSdtPrToken == FSNS(XML_w, XML_docPartObj) || m_nSdtPrToken == FSNS(XML_w, XML_docPartList) || m_nSdtPrToken == FSNS(XML_w14, XML_checkbox)) { 648 const uno::Sequence<xml::FastAttribute> aChildren = m_pTokenChildren->getFastAttributes(); 649 for (const auto& rChild : aChildren) 650 pSerializer->singleElement(rChild.Token, FSNS(XML_w, XML_val), rChild.Value); 651 } 652 653 pSerializer->endElement(m_nSdtPrToken); 654 } 655 else if ((m_nSdtPrToken > 0) && m_nSdtPrToken != FSNS(XML_w, XML_id) && !(bRunTextIsOn && bParagraphHasDrawing)) 656 { 657 if (!m_pTokenAttributes.is()) 658 pSerializer->singleElement(m_nSdtPrToken); 659 else 660 { 661 rtl::Reference<FastAttributeList> xAttrList = std::move(m_pTokenAttributes); 662 pSerializer->singleElement(m_nSdtPrToken, xAttrList); 663 } 664 } 665 666 WriteExtraParams(pSerializer); 667 668 pSerializer->endElementNS(XML_w, XML_sdtPr); 669 670 // sdt contents start tag 671 pSerializer->startElementNS(XML_w, XML_sdtContent); 672 673 // prepend the tags since the sdt start mark before the paragraph 674 pSerializer->mergeTopMarks(Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND); 675 676 // write the ending tags after the paragraph 677 m_bStartedSdt = true; 678 679 // clear sdt status 680 m_nSdtPrToken = 0; 681 m_pTokenChildren.clear(); 682 m_pDataBindingAttrs.clear(); 683 m_pTextAttrs.clear(); 684 m_aAlias.clear(); 685 m_bHasId = false; 686 } 687 688 void SdtBlockHelper::WriteExtraParams(::sax_fastparser::FSHelperPtr& pSerializer) 689 { 690 if (m_nSdtPrToken == FSNS(XML_w, XML_id) || m_bHasId) 691 //Word won't open a document with an empty id tag, we fill it with a random number 692 pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val), 693 OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max()))); 694 695 if (m_pDataBindingAttrs.is()) 696 { 697 rtl::Reference<FastAttributeList> xAttrList = std::move(m_pDataBindingAttrs); 698 pSerializer->singleElementNS(XML_w, XML_dataBinding, xAttrList); 699 } 700 701 if (m_pTextAttrs.is()) 702 { 703 rtl::Reference<FastAttributeList> xAttrList = std::move(m_pTextAttrs); 704 pSerializer->singleElementNS(XML_w, XML_text, xAttrList); 705 } 706 707 if (!m_aPlaceHolderDocPart.isEmpty()) 708 { 709 pSerializer->startElementNS(XML_w, XML_placeholder); 710 pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, XML_val), m_aPlaceHolderDocPart); 711 pSerializer->endElementNS(XML_w, XML_placeholder); 712 } 713 if (!m_aColor.isEmpty()) 714 { 715 pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, XML_val), m_aColor); 716 } 717 718 if (!m_aAlias.isEmpty()) 719 pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), m_aAlias); 720 } 721 722 void SdtBlockHelper::EndSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer) 723 { 724 pSerializer->endElementNS(XML_w, XML_sdtContent); 725 pSerializer->endElementNS(XML_w, XML_sdt); 726 m_bStartedSdt = false; 727 } 728 729 void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::PropertyValue>& aGrabBagSdt) 730 { 731 for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt) 732 { 733 if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox") 734 { 735 m_nSdtPrToken = FSNS(XML_w14, XML_checkbox); 736 uno::Sequence<beans::PropertyValue> aGrabBag; 737 aPropertyValue.Value >>= aGrabBag; 738 for (const auto& rProp : std::as_const(aGrabBag)) 739 { 740 OUString sValue = rProp.Value.get<OUString>(); 741 if (rProp.Name == "ooxml:CT_SdtCheckbox_checked") 742 AddToAttrList(m_pTokenChildren, 743 FSNS(XML_w14, XML_checked), 744 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); 745 else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState") 746 AddToAttrList(m_pTokenChildren, 747 FSNS(XML_w14, XML_checkedState), 748 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); 749 else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState") 750 AddToAttrList(m_pTokenChildren, 751 FSNS(XML_w14, XML_uncheckedState), 752 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); 753 } 754 } 755 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pDataBindingAttrs.is()) 756 { 757 uno::Sequence<beans::PropertyValue> aGrabBag; 758 aPropertyValue.Value >>= aGrabBag; 759 for (const auto& rProp : std::as_const(aGrabBag)) 760 { 761 OUString sValue = rProp.Value.get<OUString>(); 762 if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings") 763 AddToAttrList( m_pDataBindingAttrs, 764 FSNS( XML_w, XML_prefixMappings ), 765 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); 766 else if (rProp.Name == "ooxml:CT_DataBinding_xpath") 767 AddToAttrList( m_pDataBindingAttrs, 768 FSNS( XML_w, XML_xpath ), 769 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); 770 else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID") 771 AddToAttrList( m_pDataBindingAttrs, 772 FSNS( XML_w, XML_storeItemID ), 773 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); 774 } 775 } 776 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text") 777 { 778 uno::Sequence<beans::PropertyValue> aGrabBag; 779 aPropertyValue.Value >>= aGrabBag; 780 if (aGrabBag.hasElements()) 781 { 782 for (const auto& rProp : std::as_const(aGrabBag)) 783 { 784 OUString sValue = rProp.Value.get<OUString>(); 785 if (rProp.Name == "ooxml:CT_SdtText_multiLine") 786 AddToAttrList(m_pTextAttrs, 787 FSNS(XML_w, XML_multiLine), 788 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); 789 } 790 } 791 else 792 { 793 // We still have w:text, but no attrs 794 m_nSdtPrToken = FSNS(XML_w, XML_text); 795 } 796 } 797 else if (aPropertyValue.Name == "ooxml:CT_SdtPlaceholder_docPart") 798 { 799 uno::Sequence<beans::PropertyValue> aGrabBag; 800 aPropertyValue.Value >>= aGrabBag; 801 for (const auto& rProp : std::as_const(aGrabBag)) 802 { 803 OUString sValue = rProp.Value.get<OUString>(); 804 if (rProp.Name == "ooxml:CT_SdtPlaceholder_docPart_val") 805 m_aPlaceHolderDocPart = sValue; 806 } 807 } 808 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_color") 809 { 810 uno::Sequence<beans::PropertyValue> aGrabBag; 811 aPropertyValue.Value >>= aGrabBag; 812 for (const auto& rProp : std::as_const(aGrabBag)) 813 { 814 OUString sValue = rProp.Value.get<OUString>(); 815 if (rProp.Name == "ooxml:CT_SdtColor_val") 816 m_aColor = sValue; 817 } 818 } 819 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aAlias.isEmpty()) 820 { 821 if (!(aPropertyValue.Value >>= m_aAlias)) 822 SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt alias value"); 823 } 824 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id") 825 m_bHasId = true; 826 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation") 827 m_nSdtPrToken = FSNS(XML_w, XML_citation); 828 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" || 829 aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList") 830 { 831 if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj") 832 m_nSdtPrToken = FSNS(XML_w, XML_docPartObj); 833 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList") 834 m_nSdtPrToken = FSNS(XML_w, XML_docPartList); 835 836 uno::Sequence<beans::PropertyValue> aGrabBag; 837 aPropertyValue.Value >>= aGrabBag; 838 for (const auto& rProp : std::as_const(aGrabBag)) 839 { 840 OUString sValue = rProp.Value.get<OUString>(); 841 if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery") 842 AddToAttrList(m_pTokenChildren, 843 FSNS(XML_w, XML_docPartGallery), 844 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); 845 else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory") 846 AddToAttrList(m_pTokenChildren, 847 FSNS(XML_w, XML_docPartCategory), 848 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); 849 else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique") 850 { 851 if (sValue.isEmpty()) 852 sValue = "true"; 853 AddToAttrList(m_pTokenChildren, FSNS(XML_w, XML_docPartUnique), 854 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); 855 } 856 } 857 } 858 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation") 859 m_nSdtPrToken = FSNS(XML_w, XML_equation); 860 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture") 861 m_nSdtPrToken = FSNS(XML_w, XML_picture); 862 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group") 863 m_nSdtPrToken = FSNS(XML_w, XML_group); 864 else 865 SAL_WARN("sw.ww8", "GetSdtParamsFromGrabBag unhandled SdtPr grab bag property " << aPropertyValue.Name); 866 } 867 } 868 869 void DocxAttributeOutput::PopulateFrameProperties(const SwFrameFormat* pFrameFormat, const Size& rSize) 870 { 871 rtl::Reference<sax_fastparser::FastAttributeList> attrList = FastSerializerHelper::createAttrList(); 872 873 awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(), pFrameFormat->GetVertOrient().GetPos()); 874 875 attrList->add( FSNS( XML_w, XML_w), OString::number(rSize.Width())); 876 attrList->add( FSNS( XML_w, XML_h), OString::number(rSize.Height())); 877 878 attrList->add( FSNS( XML_w, XML_x), OString::number(aPos.X)); 879 attrList->add( FSNS( XML_w, XML_y), OString::number(aPos.Y)); 880 881 sal_Int16 nLeft = pFrameFormat->GetLRSpace().GetLeft(); 882 sal_Int16 nRight = pFrameFormat->GetLRSpace().GetRight(); 883 sal_Int16 nUpper = pFrameFormat->GetULSpace().GetUpper(); 884 sal_Int16 nLower = pFrameFormat->GetULSpace().GetLower(); 885 886 attrList->add(FSNS(XML_w, XML_hSpace), OString::number((nLeft + nRight) / 2)); 887 attrList->add(FSNS(XML_w, XML_vSpace), OString::number((nUpper + nLower) / 2)); 888 889 OString relativeFromH = convertToOOXMLHoriOrientRel( pFrameFormat->GetHoriOrient().GetRelationOrient() ); 890 OString relativeFromV = convertToOOXMLVertOrientRel( pFrameFormat->GetVertOrient().GetRelationOrient() ); 891 892 switch (pFrameFormat->GetSurround().GetValue()) 893 { 894 case css::text::WrapTextMode_NONE: 895 attrList->add( FSNS( XML_w, XML_wrap), "notBeside"); 896 break; 897 case css::text::WrapTextMode_DYNAMIC: 898 attrList->add(FSNS(XML_w, XML_wrap), "auto"); 899 break; 900 case css::text::WrapTextMode_PARALLEL: 901 default: 902 attrList->add(FSNS(XML_w, XML_wrap), "around"); 903 break; 904 } 905 attrList->add( FSNS( XML_w, XML_vAnchor), relativeFromV ); 906 attrList->add( FSNS( XML_w, XML_hAnchor), relativeFromH ); 907 attrList->add( FSNS( XML_w, XML_hRule), "exact"); 908 909 m_pSerializer->singleElementNS( XML_w, XML_framePr, attrList ); 910 } 911 912 bool DocxAttributeOutput::TextBoxIsFramePr(const SwFrameFormat& rFrameFormat) 913 { 914 uno::Reference< drawing::XShape > xShape; 915 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject(); 916 if (pSdrObj) 917 xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY); 918 uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY); 919 uno::Reference< beans::XPropertySetInfo > xPropSetInfo; 920 if (xPropertySet.is()) 921 xPropSetInfo = xPropertySet->getPropertySetInfo(); 922 uno::Any aFrameProperties ; 923 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag")) 924 { 925 uno::Sequence< beans::PropertyValue > propList; 926 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList; 927 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList), 928 [](const beans::PropertyValue& rProp) { return rProp.Name == "ParaFrameProperties"; }); 929 if (pProp != std::cend(propList)) 930 aFrameProperties = pProp->Value; 931 } 932 bool bFrameProperties = false; 933 aFrameProperties >>= bFrameProperties; 934 return bFrameProperties; 935 } 936 937 void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) 938 { 939 // write the paragraph properties + the run, already in the correct order 940 m_pSerializer->mergeTopMarks(Tag_StartParagraph_2); 941 std::vector< std::shared_ptr <ww8::Frame> > aFramePrTextbox; 942 // Write the anchored frame if any 943 // Word can't handle nested text boxes, so write them on the same level. 944 ++m_nTextFrameLevel; 945 if( m_nTextFrameLevel == 1 && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() ) 946 { 947 comphelper::FlagRestorationGuard aStartedParaSdtGuard(m_aParagraphSdt.m_bStartedSdt, false); 948 949 assert(!m_pPostponedCustomShape); 950 m_pPostponedCustomShape.reset(new std::vector<PostponedDrawing>); 951 952 // The for loop can change the size of m_aFramesOfParagraph, so the max size cannot be set in stone before the loop. 953 size_t nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0; 954 for (size_t nIndex = 0; nIndex < nFrames; ++nIndex) 955 { 956 m_bParagraphFrameOpen = true; 957 ww8::Frame aFrame = m_aFramesOfParagraph.top()[nIndex]; 958 const SwFrameFormat& rFrameFormat = aFrame.GetFrameFormat(); 959 960 if (!TextBoxIsFramePr(rFrameFormat) || m_bWritingHeaderFooter) 961 { 962 if (m_aRunSdt.m_bStartedSdt) 963 { 964 // Run-level SDT still open? Close it before AlternateContent. 965 m_aRunSdt.EndSdtBlock(m_pSerializer); 966 } 967 m_pSerializer->startElementNS(XML_w, XML_r); 968 m_pSerializer->startElementNS(XML_mc, XML_AlternateContent); 969 m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "wps"); 970 /** 971 This is to avoid AlternateContent within another AlternateContent. 972 So when Choice is Open, only write the DML Drawing instead of both DML 973 and VML Drawing in another AlternateContent. 974 **/ 975 SetAlternateContentChoiceOpen( true ); 976 /** Save the table info's before writing the shape 977 as there might be a new table that might get 978 spawned from within the VML & DML block and alter 979 the contents. 980 */ 981 ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_rExport.m_pTableInfo; 982 //Reset the table infos after saving. 983 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>(); 984 985 /** FDO#71834 : 986 Save the table reference attributes before calling WriteDMLTextFrame, 987 otherwise the StartParagraph function will use the previous existing 988 table reference attributes since the variable is being shared. 989 */ 990 { 991 DocxTableExportContext aDMLTableExportContext(*this); 992 m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++); 993 } 994 m_pSerializer->endElementNS(XML_mc, XML_Choice); 995 SetAlternateContentChoiceOpen( false ); 996 997 // Reset table infos, otherwise the depth of the cells will be incorrect, 998 // in case the text frame had table(s) and we try to export the 999 // same table second time. 1000 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>(); 1001 //reset the tableReference. 1002 1003 m_pSerializer->startElementNS(XML_mc, XML_Fallback); 1004 { 1005 DocxTableExportContext aVMLTableExportContext(*this); 1006 m_rExport.SdrExporter().writeVMLTextFrame(&aFrame); 1007 } 1008 m_rExport.m_pTableInfo = pOldTableInfo; 1009 1010 m_pSerializer->endElementNS(XML_mc, XML_Fallback); 1011 m_pSerializer->endElementNS(XML_mc, XML_AlternateContent); 1012 m_pSerializer->endElementNS( XML_w, XML_r ); 1013 m_bParagraphFrameOpen = false; 1014 } 1015 else 1016 { 1017 std::shared_ptr<ww8::Frame> pFramePr = std::make_shared<ww8::Frame>(aFrame); 1018 aFramePrTextbox.push_back(pFramePr); 1019 } 1020 1021 nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0; 1022 } 1023 if (!m_pPostponedCustomShape->empty()) 1024 { 1025 m_pSerializer->startElementNS(XML_w, XML_r); 1026 WritePostponedCustomShape(); 1027 m_pSerializer->endElementNS( XML_w, XML_r ); 1028 } 1029 m_pPostponedCustomShape.reset(); 1030 1031 if ( m_aFramesOfParagraph.size() ) 1032 m_aFramesOfParagraph.top().clear(); 1033 1034 if (!pTextNodeInfoInner) 1035 { 1036 // Ending a non-table paragraph, clear floating tables before paragraph. 1037 m_aFloatingTablesOfParagraph.clear(); 1038 } 1039 } 1040 1041 --m_nTextFrameLevel; 1042 if ( m_aFramesOfParagraph.size() && !m_nTextFrameLevel ) 1043 m_aFramesOfParagraph.pop(); 1044 1045 /* If m_nHyperLinkCount > 0 that means hyperlink tag is not yet closed. 1046 * This is due to nested hyperlink tags. So close it before end of paragraph. 1047 */ 1048 if(m_nHyperLinkCount > 0) 1049 { 1050 for(sal_Int32 nHyperLinkToClose = 0; nHyperLinkToClose < m_nHyperLinkCount; ++nHyperLinkToClose) 1051 m_pSerializer->endElementNS( XML_w, XML_hyperlink ); 1052 m_nHyperLinkCount = 0; 1053 } 1054 1055 if (m_aRunSdt.m_bStartedSdt) 1056 { 1057 // Run-level SDT still open? Close it now. 1058 m_aRunSdt.EndSdtBlock(m_pSerializer); 1059 } 1060 1061 if (m_bPageBreakAfter) 1062 { 1063 // tdf#128889 Trailing page break 1064 SectionBreak(msword::PageBreak, false); 1065 m_bPageBreakAfter = false; 1066 } 1067 1068 m_pSerializer->endElementNS( XML_w, XML_p ); 1069 // on export sdt blocks are never nested ATM 1070 if (!m_bAnchorLinkedToNode && !m_aParagraphSdt.m_bStartedSdt) 1071 { 1072 m_aParagraphSdt.WriteSdtBlock(m_pSerializer, m_bRunTextIsOn, m_rExport.SdrExporter().IsParagraphHasDrawing()); 1073 1074 if (m_aParagraphSdt.m_bStartedSdt) 1075 { 1076 if (m_tableReference->m_bTableCellOpen) 1077 m_tableReference->m_bTableCellParaSdtOpen = true; 1078 if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen()) 1079 m_rExport.SdrExporter().setParagraphSdtOpen(true); 1080 } 1081 } 1082 else 1083 { 1084 //These should be written out to the actual Node and not to the anchor. 1085 //Clear them as they will be repopulated when the node is processed. 1086 m_aParagraphSdt.m_nSdtPrToken = 0; 1087 m_aParagraphSdt.m_bHasId = false; 1088 m_aParagraphSdt.DeleteAndResetTheLists(); 1089 } 1090 1091 m_pSerializer->mark(Tag_StartParagraph_2); 1092 1093 // Write framePr 1094 for ( const auto & pFrame : aFramePrTextbox ) 1095 { 1096 DocxTableExportContext aTableExportContext(*this); 1097 m_pCurrentFrame = pFrame.get(); 1098 m_rExport.SdrExporter().writeOnlyTextOfFrame(pFrame.get()); 1099 m_pCurrentFrame = nullptr; 1100 } 1101 1102 m_pSerializer->mergeTopMarks(Tag_StartParagraph_2, sax_fastparser::MergeMarks::PREPEND); 1103 1104 //sdtcontent is written so Set m_bParagraphHasDrawing to false 1105 m_rExport.SdrExporter().setParagraphHasDrawing(false); 1106 m_bRunTextIsOn = false; 1107 m_pSerializer->mergeTopMarks(Tag_StartParagraph_1); 1108 1109 aFramePrTextbox.clear(); 1110 // Check for end of cell, rows, tables here 1111 FinishTableRowCell( pTextNodeInfoInner ); 1112 1113 if( !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() ) 1114 m_bParagraphOpened = false; 1115 1116 // Clear bookmarks of the current paragraph 1117 m_aBookmarksOfParagraphStart.clear(); 1118 m_aBookmarksOfParagraphEnd.clear(); 1119 } 1120 1121 #define MAX_CELL_IN_WORD 62 1122 1123 void DocxAttributeOutput::SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow) 1124 { 1125 sal_Int32 nOpenCell = lastOpenCell.back(); 1126 if (nOpenCell != -1 && nOpenCell != nCell && nOpenCell < MAX_CELL_IN_WORD) 1127 EndTableCell(nOpenCell); 1128 1129 sal_Int32 nClosedCell = lastClosedCell.back(); 1130 for (sal_Int32 i = nClosedCell+1; i < nCell; ++i) 1131 { 1132 if (i >= MAX_CELL_IN_WORD) 1133 break; 1134 1135 if (i == 0) 1136 StartTableRow(pInner); 1137 1138 StartTableCell(pInner, i, nRow); 1139 m_pSerializer->singleElementNS(XML_w, XML_p); 1140 EndTableCell(i); 1141 } 1142 } 1143 1144 void DocxAttributeOutput::FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, bool bForceEmptyParagraph ) 1145 { 1146 if ( !pInner ) 1147 return; 1148 1149 // Where are we in the table 1150 sal_uInt32 nRow = pInner->getRow(); 1151 sal_Int32 nCell = pInner->getCell(); 1152 1153 InitTableHelper( pInner ); 1154 1155 // HACK 1156 // msoffice seems to have an internal limitation of 63 columns for tables 1157 // and refuses to load .docx with more, even though the spec seems to allow that; 1158 // so simply if there are more columns, don't close the last one msoffice will handle 1159 // and merge the contents of the remaining ones into it (since we don't close the cell 1160 // here, following ones will not be opened) 1161 const bool limitWorkaround = (nCell >= MAX_CELL_IN_WORD && !pInner->isEndOfLine()); 1162 const bool bEndCell = pInner->isEndOfCell() && !limitWorkaround; 1163 const bool bEndRow = pInner->isEndOfLine(); 1164 1165 if (bEndCell) 1166 { 1167 while (pInner->getDepth() < m_tableReference->m_nTableDepth) 1168 { 1169 //we expect that the higher depth row was closed, and 1170 //we are just missing the table close 1171 assert(lastOpenCell.back() == -1 && lastClosedCell.back() == -1); 1172 EndTable(); 1173 } 1174 1175 SyncNodelessCells(pInner, nCell, nRow); 1176 1177 sal_Int32 nClosedCell = lastClosedCell.back(); 1178 if (nCell == nClosedCell) 1179 { 1180 //Start missing trailing cell(s) 1181 ++nCell; 1182 StartTableCell(pInner, nCell, nRow); 1183 1184 //Continue on missing next trailing cell(s) 1185 ww8::RowSpansPtr xRowSpans = pInner->getRowSpansOfRow(); 1186 sal_Int32 nRemainingCells = xRowSpans->size() - nCell; 1187 for (sal_Int32 i = 1; i < nRemainingCells; ++i) 1188 { 1189 if (bForceEmptyParagraph) 1190 { 1191 m_pSerializer->singleElementNS(XML_w, XML_p); 1192 } 1193 1194 EndTableCell(nCell); 1195 1196 StartTableCell(pInner, nCell, nRow); 1197 } 1198 } 1199 1200 if (bForceEmptyParagraph) 1201 { 1202 m_pSerializer->singleElementNS(XML_w, XML_p); 1203 } 1204 1205 EndTableCell(nCell); 1206 } 1207 1208 // This is a line end 1209 if (bEndRow) 1210 EndTableRow(); 1211 1212 // This is the end of the table 1213 if (pInner->isFinalEndOfLine()) 1214 EndTable(); 1215 } 1216 1217 void DocxAttributeOutput::EmptyParagraph() 1218 { 1219 m_pSerializer->singleElementNS(XML_w, XML_p); 1220 } 1221 1222 void DocxAttributeOutput::SectionBreaks(const SwNode& rNode) 1223 { 1224 // output page/section breaks 1225 // Writer can have them at the beginning of a paragraph, or at the end, but 1226 // in docx, we have to output them in the paragraph properties of the last 1227 // paragraph in a section. To get it right, we have to switch to the next 1228 // paragraph, and detect the section breaks there. 1229 SwNodeIndex aNextIndex( rNode, 1 ); 1230 1231 if (rNode.IsTextNode() || rNode.IsSectionNode()) 1232 { 1233 if (aNextIndex.GetNode().IsTextNode()) 1234 { 1235 const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode()); 1236 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen); 1237 } 1238 else if (aNextIndex.GetNode().IsTableNode()) 1239 { 1240 const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode()); 1241 const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat(); 1242 m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode); 1243 } 1244 } 1245 else if (rNode.IsEndNode()) 1246 { 1247 if (aNextIndex.GetNode().IsTextNode()) 1248 { 1249 // Handle section break between a table and a text node following it. 1250 // Also handle section endings 1251 const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode(); 1252 if (rNode.StartOfSectionNode()->IsTableNode() || rNode.StartOfSectionNode()->IsSectionNode()) 1253 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen); 1254 } 1255 else if (aNextIndex.GetNode().IsTableNode()) 1256 { 1257 // Handle section break between tables. 1258 const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode()); 1259 const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat(); 1260 m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode); 1261 } 1262 } 1263 } 1264 1265 void DocxAttributeOutput::StartParagraphProperties() 1266 { 1267 m_pSerializer->mark(Tag_StartParagraphProperties); 1268 1269 m_pSerializer->startElementNS(XML_w, XML_pPr); 1270 1271 // and output the section break now (if it appeared) 1272 if (m_pSectionInfo && m_rExport.m_nTextTyp == TXT_MAINTEXT) 1273 { 1274 m_rExport.SectionProperties( *m_pSectionInfo ); 1275 m_pSectionInfo.reset(); 1276 } 1277 1278 InitCollectedParagraphProperties(); 1279 } 1280 1281 void DocxAttributeOutput::InitCollectedParagraphProperties() 1282 { 1283 m_pParagraphSpacingAttrList.clear(); 1284 1285 // Write the elements in the spec order 1286 static const sal_Int32 aOrder[] = 1287 { 1288 FSNS( XML_w, XML_pStyle ), 1289 FSNS( XML_w, XML_keepNext ), 1290 FSNS( XML_w, XML_keepLines ), 1291 FSNS( XML_w, XML_pageBreakBefore ), 1292 FSNS( XML_w, XML_framePr ), 1293 FSNS( XML_w, XML_widowControl ), 1294 FSNS( XML_w, XML_numPr ), 1295 FSNS( XML_w, XML_suppressLineNumbers ), 1296 FSNS( XML_w, XML_pBdr ), 1297 FSNS( XML_w, XML_shd ), 1298 FSNS( XML_w, XML_tabs ), 1299 FSNS( XML_w, XML_suppressAutoHyphens ), 1300 FSNS( XML_w, XML_kinsoku ), 1301 FSNS( XML_w, XML_wordWrap ), 1302 FSNS( XML_w, XML_overflowPunct ), 1303 FSNS( XML_w, XML_topLinePunct ), 1304 FSNS( XML_w, XML_autoSpaceDE ), 1305 FSNS( XML_w, XML_autoSpaceDN ), 1306 FSNS( XML_w, XML_bidi ), 1307 FSNS( XML_w, XML_adjustRightInd ), 1308 FSNS( XML_w, XML_snapToGrid ), 1309 FSNS( XML_w, XML_spacing ), 1310 FSNS( XML_w, XML_ind ), 1311 FSNS( XML_w, XML_contextualSpacing ), 1312 FSNS( XML_w, XML_mirrorIndents ), 1313 FSNS( XML_w, XML_suppressOverlap ), 1314 FSNS( XML_w, XML_jc ), 1315 FSNS( XML_w, XML_textDirection ), 1316 FSNS( XML_w, XML_textAlignment ), 1317 FSNS( XML_w, XML_textboxTightWrap ), 1318 FSNS( XML_w, XML_outlineLvl ), 1319 FSNS( XML_w, XML_divId ), 1320 FSNS( XML_w, XML_cnfStyle ), 1321 FSNS( XML_w, XML_rPr ), 1322 FSNS( XML_w, XML_sectPr ), 1323 FSNS( XML_w, XML_pPrChange ) 1324 }; 1325 1326 // postpone the output so that we can later [in EndParagraphProperties()] 1327 // prepend the properties before the run 1328 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence 1329 m_pSerializer->mark(Tag_InitCollectedParagraphProperties, comphelper::containerToSequence(aOrder)); 1330 } 1331 1332 void DocxAttributeOutput::WriteCollectedParagraphProperties() 1333 { 1334 if ( m_rExport.SdrExporter().getFlyAttrList().is() ) 1335 { 1336 rtl::Reference<FastAttributeList> xAttrList( m_rExport.SdrExporter().getFlyAttrList() ); 1337 m_rExport.SdrExporter().getFlyAttrList().clear(); 1338 1339 m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList ); 1340 } 1341 1342 if ( m_pParagraphSpacingAttrList.is() ) 1343 { 1344 rtl::Reference<FastAttributeList> xAttrList = std::move( m_pParagraphSpacingAttrList ); 1345 m_pSerializer->singleElementNS( XML_w, XML_spacing, xAttrList ); 1346 } 1347 1348 if ( m_pBackgroundAttrList.is() ) 1349 { 1350 rtl::Reference<FastAttributeList> xAttrList = std::move( m_pBackgroundAttrList ); 1351 m_pSerializer->singleElementNS( XML_w, XML_shd, xAttrList ); 1352 } 1353 } 1354 1355 namespace 1356 { 1357 1358 /// Outputs an item set, that contains the formatting of the paragraph marker. 1359 void lcl_writeParagraphMarkerProperties(DocxAttributeOutput& rAttributeOutput, const SfxItemSet& rParagraphMarkerProperties) 1360 { 1361 const SfxItemSet* pOldI = rAttributeOutput.GetExport().GetCurItemSet(); 1362 rAttributeOutput.GetExport().SetCurItemSet(&rParagraphMarkerProperties); 1363 1364 SfxWhichIter aIter(rParagraphMarkerProperties); 1365 sal_uInt16 nWhichId = aIter.FirstWhich(); 1366 const SfxPoolItem* pItem = nullptr; 1367 // Did we already produce a <w:sz> element? 1368 bool bFontSizeWritten = false; 1369 while (nWhichId) 1370 { 1371 if (rParagraphMarkerProperties.GetItemState(nWhichId, true, &pItem) == SfxItemState::SET) 1372 { 1373 if (isCHRATR(nWhichId) || nWhichId == RES_TXTATR_CHARFMT) 1374 { 1375 // Will this item produce a <w:sz> element? 1376 bool bFontSizeItem = nWhichId == RES_CHRATR_FONTSIZE || nWhichId == RES_CHRATR_CJK_FONTSIZE; 1377 if (!bFontSizeWritten || !bFontSizeItem) 1378 rAttributeOutput.OutputItem(*pItem); 1379 if (bFontSizeItem) 1380 bFontSizeWritten = true; 1381 } 1382 else if (nWhichId == RES_TXTATR_AUTOFMT) 1383 { 1384 const SwFormatAutoFormat* pAutoFormat = static_cast<const SwFormatAutoFormat*>(pItem); 1385 lcl_writeParagraphMarkerProperties(rAttributeOutput, *pAutoFormat->GetStyleHandle()); 1386 } 1387 } 1388 nWhichId = aIter.NextWhich(); 1389 } 1390 rAttributeOutput.GetExport().SetCurItemSet(pOldI); 1391 } 1392 1393 const char *RubyAlignValues[] = 1394 { 1395 "center", 1396 "distributeLetter", 1397 "distributeSpace", 1398 "left", 1399 "right", 1400 "rightVertical" 1401 }; 1402 1403 1404 const char *lclConvertWW8JCToOOXMLRubyAlign(sal_Int32 nJC) 1405 { 1406 const sal_Int32 nElements = SAL_N_ELEMENTS(RubyAlignValues); 1407 if ( nJC >=0 && nJC < nElements ) 1408 return RubyAlignValues[nJC]; 1409 return RubyAlignValues[0]; 1410 } 1411 1412 } 1413 1414 void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted) 1415 { 1416 // Call the 'Redline' function. This will add redline (change-tracking) information that regards to paragraph properties. 1417 // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc. 1418 1419 // If there is RedlineData present, call WriteCollectedParagraphProperties() for writing pPr before calling Redline(). 1420 // As there will be another pPr for redline and LO might mix both. 1421 if(pRedlineData) 1422 WriteCollectedParagraphProperties(); 1423 Redline( pRedlineData ); 1424 1425 WriteCollectedParagraphProperties(); 1426 1427 // Merge the marks for the ordered elements 1428 m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties); 1429 1430 // Write 'Paragraph Mark' properties 1431 m_pSerializer->startElementNS(XML_w, XML_rPr); 1432 // mark() before paragraph mark properties child elements. 1433 InitCollectedRunProperties(); 1434 1435 // The 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' are used to hold information 1436 // that should be collected by different properties in the core, and are all flushed together 1437 // to the DOCX when the function 'WriteCollectedRunProperties' gets called. 1438 // So we need to store the current status of these lists, so that we can revert back to them when 1439 // we are done exporting the redline attributes. 1440 rtl::Reference<sax_fastparser::FastAttributeList> pFontsAttrList_Original(m_pFontsAttrList); 1441 m_pFontsAttrList.clear(); 1442 rtl::Reference<sax_fastparser::FastAttributeList> pEastAsianLayoutAttrList_Original(m_pEastAsianLayoutAttrList); 1443 m_pEastAsianLayoutAttrList.clear(); 1444 rtl::Reference<sax_fastparser::FastAttributeList> pCharLangAttrList_Original(m_pCharLangAttrList); 1445 m_pCharLangAttrList.clear(); 1446 1447 lcl_writeParagraphMarkerProperties(*this, rParagraphMarkerProperties); 1448 1449 // Write the collected run properties that are stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' 1450 WriteCollectedRunProperties(); 1451 1452 // Revert back the original values that were stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' 1453 m_pFontsAttrList = pFontsAttrList_Original.get(); 1454 m_pEastAsianLayoutAttrList = pEastAsianLayoutAttrList_Original.get(); 1455 m_pCharLangAttrList = pCharLangAttrList_Original.get(); 1456 1457 if ( pRedlineParagraphMarkerDeleted ) 1458 { 1459 StartRedline( pRedlineParagraphMarkerDeleted ); 1460 EndRedline( pRedlineParagraphMarkerDeleted ); 1461 } 1462 if ( pRedlineParagraphMarkerInserted ) 1463 { 1464 StartRedline( pRedlineParagraphMarkerInserted ); 1465 EndRedline( pRedlineParagraphMarkerInserted ); 1466 } 1467 1468 // mergeTopMarks() after paragraph mark properties child elements. 1469 m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties); 1470 m_pSerializer->endElementNS( XML_w, XML_rPr ); 1471 1472 if (!m_bWritingHeaderFooter && m_pCurrentFrame) 1473 { 1474 const SwFrameFormat& rFrameFormat = m_pCurrentFrame->GetFrameFormat(); 1475 const SvxBoxItem& rBox = rFrameFormat.GetBox(); 1476 if (TextBoxIsFramePr(rFrameFormat)) 1477 { 1478 const Size aSize = m_pCurrentFrame->GetSize(); 1479 PopulateFrameProperties(&rFrameFormat, aSize); 1480 FormatBox(rBox); 1481 } 1482 } 1483 1484 m_pSerializer->endElementNS( XML_w, XML_pPr ); 1485 1486 // RDF metadata for this text node. 1487 SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode(); 1488 std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", *pTextNode); 1489 if (!aStatements.empty()) 1490 { 1491 m_pSerializer->startElementNS(XML_w, XML_smartTag, 1492 FSNS(XML_w, XML_uri), "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 1493 FSNS(XML_w, XML_element), "RDF"); 1494 m_pSerializer->startElementNS(XML_w, XML_smartTagPr); 1495 for (const auto& rStatement : aStatements) 1496 m_pSerializer->singleElementNS(XML_w, XML_attr, 1497 FSNS(XML_w, XML_name), rStatement.first, 1498 FSNS(XML_w, XML_val), rStatement.second); 1499 m_pSerializer->endElementNS(XML_w, XML_smartTagPr); 1500 m_pSerializer->endElementNS(XML_w, XML_smartTag); 1501 } 1502 1503 if ( m_nColBreakStatus == COLBRK_WRITE || m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE ) 1504 { 1505 m_pSerializer->startElementNS(XML_w, XML_r); 1506 m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "column"); 1507 m_pSerializer->endElementNS( XML_w, XML_r ); 1508 1509 if ( m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE ) 1510 m_nColBreakStatus = COLBRK_POSTPONE; 1511 else 1512 m_nColBreakStatus = COLBRK_NONE; 1513 } 1514 1515 if ( m_bPostponedPageBreak && !m_bWritingHeaderFooter ) 1516 { 1517 m_pSerializer->startElementNS(XML_w, XML_r); 1518 m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page"); 1519 m_pSerializer->endElementNS( XML_w, XML_r ); 1520 1521 m_bPostponedPageBreak = false; 1522 } 1523 1524 // merge the properties _before_ the run (strictly speaking, just 1525 // after the start of the paragraph) 1526 m_pSerializer->mergeTopMarks(Tag_StartParagraphProperties, sax_fastparser::MergeMarks::PREPEND); 1527 } 1528 1529 void DocxAttributeOutput::SetStateOfFlyFrame( FlyProcessingState nStateOfFlyFrame ) 1530 { 1531 m_nStateOfFlyFrame = nStateOfFlyFrame; 1532 } 1533 1534 void DocxAttributeOutput::SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode ) 1535 { 1536 m_bAnchorLinkedToNode = bAnchorLinkedToNode ; 1537 } 1538 1539 void DocxAttributeOutput::ResetFlyProcessingFlag() 1540 { 1541 m_bPostponedProcessingFly = false ; 1542 } 1543 1544 bool DocxAttributeOutput::IsFlyProcessingPostponed() 1545 { 1546 return m_bPostponedProcessingFly; 1547 } 1548 1549 void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/, bool /*bSingleEmptyRun*/ ) 1550 { 1551 // Don't start redline data here, possibly there is a hyperlink later, and 1552 // that has to be started first. 1553 m_pRedlineData = pRedlineData; 1554 1555 // this mark is used to be able to enclose the run inside a sdr tag. 1556 m_pSerializer->mark(Tag_StartRun_1); 1557 1558 // postpone the output of the start of a run (there are elements that need 1559 // to be written before the start of the run, but we learn which they are 1560 // _inside_ of the run) 1561 m_pSerializer->mark(Tag_StartRun_2); // let's call it "postponed run start" 1562 1563 // postpone the output of the text (we get it before the run properties, 1564 // but must write it after them) 1565 m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text" 1566 } 1567 1568 void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool /*bLastRun*/) 1569 { 1570 int nFieldsInPrevHyperlink = m_nFieldsInHyperlink; 1571 // Reset m_nFieldsInHyperlink if a new hyperlink is about to start 1572 if ( m_pHyperlinkAttrList.is() ) 1573 { 1574 m_nFieldsInHyperlink = 0; 1575 } 1576 1577 // Write field starts 1578 for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin() + nFieldsInPrevHyperlink; pIt != m_Fields.end(); ) 1579 { 1580 // Add the fields starts for all but hyperlinks and TOCs 1581 if (pIt->bOpen && pIt->pField && pIt->eType != ww::eFORMDROPDOWN && 1582 // it is not an input field with extra grabbag params (sdt field) 1583 (!(pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements())) 1584 ) 1585 { 1586 StartField_Impl( pNode, nPos, *pIt ); 1587 1588 // Remove the field from the stack if only the start has to be written 1589 // Unknown fields should be removed too 1590 if ( !pIt->bClose || ( pIt->eType == ww::eUNKNOWN ) ) 1591 { 1592 pIt = m_Fields.erase( pIt ); 1593 continue; 1594 } 1595 1596 if (m_startedHyperlink || m_pHyperlinkAttrList.is()) 1597 { 1598 ++m_nFieldsInHyperlink; 1599 } 1600 } 1601 ++pIt; 1602 } 1603 1604 // write the run properties + the text, already in the correct order 1605 m_pSerializer->mergeTopMarks(Tag_StartRun_3); // merges with "postponed text", see above 1606 1607 // level down, to be able to prepend the actual run start attribute (just 1608 // before "postponed run start") 1609 m_pSerializer->mark(Tag_EndRun_1); // let's call it "actual run start" 1610 bool bCloseEarlierSDT = false; 1611 1612 if (m_bEndCharSdt) 1613 { 1614 // This is the common case: "close sdt before the current run" was requested by the next run. 1615 1616 // if another sdt starts in this run, then wait 1617 // as closing the sdt now, might cause nesting of sdts 1618 if (m_aRunSdt.m_nSdtPrToken > 0) 1619 bCloseEarlierSDT = true; 1620 else 1621 m_aRunSdt.EndSdtBlock(m_pSerializer); 1622 m_bEndCharSdt = false; 1623 } 1624 1625 if ( m_closeHyperlinkInPreviousRun ) 1626 { 1627 if ( m_startedHyperlink ) 1628 { 1629 for ( int i = 0; i < nFieldsInPrevHyperlink; i++ ) 1630 { 1631 // If fields begin before hyperlink then 1632 // it should end before hyperlink close 1633 EndField_Impl( pNode, nPos, m_Fields.back( ) ); 1634 m_Fields.pop_back(); 1635 } 1636 m_pSerializer->endElementNS( XML_w, XML_hyperlink ); 1637 m_startedHyperlink = false; 1638 m_endPageRef = false; 1639 m_nHyperLinkCount--; 1640 } 1641 m_closeHyperlinkInPreviousRun = false; 1642 } 1643 1644 // Write the hyperlink and toc fields starts 1645 for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); ) 1646 { 1647 // Add the fields starts for hyperlinks, TOCs and index marks 1648 if (pIt->bOpen && (!pIt->pField || pIt->eType == ww::eFORMDROPDOWN || 1649 // InputField with extra grabbag params - it is sdt field 1650 (pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements()))) 1651 { 1652 StartRedline( m_pRedlineData ); 1653 StartField_Impl( pNode, nPos, *pIt, true ); 1654 EndRedline( m_pRedlineData ); 1655 1656 if (m_startedHyperlink) 1657 ++m_nFieldsInHyperlink; 1658 1659 // Remove the field if no end needs to be written 1660 if (!pIt->bSep) 1661 { 1662 pIt = m_Fields.erase( pIt ); 1663 continue; 1664 } 1665 } 1666 if (pIt->bSep && !pIt->pField) 1667 { 1668 // for TOXMark: 1669 // Word ignores bookmarks in field result that is empty; 1670 // work around this by writing bookmark into field command. 1671 if (!m_sFieldBkm.isEmpty()) 1672 { 1673 DoWriteBookmarkTagStart(m_sFieldBkm); 1674 DoWriteBookmarkTagEnd(m_nNextBookmarkId); 1675 m_nNextBookmarkId++; 1676 m_sFieldBkm.clear(); 1677 } 1678 CmdEndField_Impl(pNode, nPos, true); 1679 // Remove the field if no end needs to be written 1680 if (!pIt->bClose) 1681 { 1682 pIt = m_Fields.erase( pIt ); 1683 continue; 1684 } 1685 } 1686 ++pIt; 1687 } 1688 1689 // Start the hyperlink after the fields separators or we would generate invalid file 1690 bool newStartedHyperlink(false); 1691 if ( m_pHyperlinkAttrList.is() ) 1692 { 1693 // if we are ending a hyperlink and there's another one starting here, 1694 // don't do this, so that the fields are closed further down when 1695 // the end hyperlink is handled, which is more likely to put the end in 1696 // the right place, as far as i can tell (not very far in this muck) 1697 if (!m_closeHyperlinkInThisRun) 1698 { 1699 // end ToX fields that want to end _before_ starting the hyperlink 1700 for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); ) 1701 { 1702 if (it->bClose && !it->pField) 1703 { 1704 EndField_Impl( pNode, nPos, *it ); 1705 it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1)); 1706 } 1707 else 1708 { 1709 ++it; 1710 } 1711 } 1712 } 1713 newStartedHyperlink = true; 1714 1715 rtl::Reference<FastAttributeList> xAttrList = std::move( m_pHyperlinkAttrList ); 1716 1717 m_pSerializer->startElementNS( XML_w, XML_hyperlink, xAttrList ); 1718 m_startedHyperlink = true; 1719 m_nHyperLinkCount++; 1720 } 1721 1722 // if there is some redlining in the document, output it 1723 StartRedline( m_pRedlineData ); 1724 1725 // XML_r node should be surrounded with bookmark-begin and bookmark-end nodes if it has bookmarks. 1726 // The same is applied for permission ranges. 1727 // But due to unit test "testFdo85542" let's output bookmark-begin with bookmark-end. 1728 DoWriteBookmarksStart(m_rBookmarksStart, m_pMoveRedlineData); 1729 DoWriteBookmarksEnd(m_rBookmarksEnd); 1730 DoWritePermissionsStart(); 1731 DoWriteAnnotationMarks(); 1732 1733 if( m_closeHyperlinkInThisRun && m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc")) 1734 { 1735 OUString sToken; 1736 m_pSerializer->startElementNS(XML_w, XML_r); 1737 m_pSerializer->startElementNS(XML_w, XML_rPr); 1738 m_pSerializer->singleElementNS(XML_w, XML_webHidden); 1739 m_pSerializer->endElementNS( XML_w, XML_rPr ); 1740 m_pSerializer->startElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "begin"); 1741 m_pSerializer->endElementNS( XML_w, XML_fldChar ); 1742 m_pSerializer->endElementNS( XML_w, XML_r ); 1743 1744 1745 m_pSerializer->startElementNS(XML_w, XML_r); 1746 m_pSerializer->startElementNS(XML_w, XML_rPr); 1747 m_pSerializer->singleElementNS(XML_w, XML_webHidden); 1748 m_pSerializer->endElementNS( XML_w, XML_rPr ); 1749 sToken = "PAGEREF " + m_hyperLinkAnchor + " \\h"; // '\h' Creates a hyperlink to the bookmarked paragraph. 1750 DoWriteCmd( sToken ); 1751 m_pSerializer->endElementNS( XML_w, XML_r ); 1752 1753 // Write the Field separator 1754 m_pSerializer->startElementNS(XML_w, XML_r); 1755 m_pSerializer->startElementNS(XML_w, XML_rPr); 1756 m_pSerializer->singleElementNS(XML_w, XML_webHidden); 1757 m_pSerializer->endElementNS( XML_w, XML_rPr ); 1758 m_pSerializer->singleElementNS( XML_w, XML_fldChar, 1759 FSNS( XML_w, XML_fldCharType ), "separate" ); 1760 m_pSerializer->endElementNS( XML_w, XML_r ); 1761 // At start of every "PAGEREF" field m_endPageRef value should be true. 1762 m_endPageRef = true; 1763 } 1764 1765 DoWriteBookmarkStartIfExist(nPos); 1766 1767 m_pSerializer->startElementNS(XML_w, XML_r); 1768 if(GetExport().m_bTabInTOC && m_pHyperlinkAttrList.is()) 1769 { 1770 RunText("\t") ; 1771 } 1772 m_pSerializer->mergeTopMarks(Tag_EndRun_1, sax_fastparser::MergeMarks::PREPEND); // merges with "postponed run start", see above 1773 1774 if ( !m_sRawText.isEmpty() ) 1775 { 1776 RunText( m_sRawText ); 1777 m_sRawText.clear(); 1778 } 1779 1780 // write the run start + the run content 1781 m_pSerializer->mergeTopMarks(Tag_StartRun_2); // merges the "actual run start" 1782 // append the actual run end 1783 m_pSerializer->endElementNS( XML_w, XML_r ); 1784 1785 // if there is some redlining in the document, output it 1786 // (except in the case of fields with multiple runs) 1787 EndRedline( m_pRedlineData ); 1788 1789 // enclose in a sdt block, if necessary: if one is already started, then don't do it for now 1790 // (so on export sdt blocks are never nested ATM) 1791 if ( !m_bAnchorLinkedToNode && !m_aRunSdt.m_bStartedSdt) 1792 { 1793 m_aRunSdt.WriteSdtBlock(m_pSerializer, m_bRunTextIsOn, m_rExport.SdrExporter().IsParagraphHasDrawing()); 1794 } 1795 else 1796 { 1797 //These should be written out to the actual Node and not to the anchor. 1798 //Clear them as they will be repopulated when the node is processed. 1799 m_aRunSdt.m_nSdtPrToken = 0; 1800 m_aRunSdt.DeleteAndResetTheLists(); 1801 } 1802 1803 if (bCloseEarlierSDT) 1804 { 1805 m_pSerializer->mark(Tag_EndRun_2); 1806 m_aRunSdt.EndSdtBlock(m_pSerializer); 1807 m_pSerializer->mergeTopMarks(Tag_EndRun_2, sax_fastparser::MergeMarks::PREPEND); 1808 } 1809 1810 m_pSerializer->mergeTopMarks(Tag_StartRun_1); 1811 1812 // XML_r node should be surrounded with permission-begin and permission-end nodes if it has permission. 1813 DoWritePermissionsEnd(); 1814 1815 for (const auto& rpMath : m_aPostponedMaths) 1816 WritePostponedMath(rpMath.pMathObject, rpMath.nMathObjAlignment); 1817 m_aPostponedMaths.clear(); 1818 1819 for (const auto& rpControl : m_aPostponedFormControls) 1820 WritePostponedFormControl(rpControl); 1821 m_aPostponedFormControls.clear(); 1822 1823 WritePostponedActiveXControl(false); 1824 1825 WritePendingPlaceholder(); 1826 1827 if ( !m_bWritingField ) 1828 { 1829 m_pRedlineData = nullptr; 1830 } 1831 1832 if ( m_closeHyperlinkInThisRun ) 1833 { 1834 if ( m_startedHyperlink ) 1835 { 1836 if( m_endPageRef ) 1837 { 1838 // Hyperlink is started and fldchar "end" needs to be written for PAGEREF 1839 m_pSerializer->startElementNS(XML_w, XML_r); 1840 m_pSerializer->startElementNS(XML_w, XML_rPr); 1841 m_pSerializer->singleElementNS(XML_w, XML_webHidden); 1842 m_pSerializer->endElementNS( XML_w, XML_rPr ); 1843 m_pSerializer->singleElementNS( XML_w, XML_fldChar, 1844 FSNS( XML_w, XML_fldCharType ), "end" ); 1845 m_pSerializer->endElementNS( XML_w, XML_r ); 1846 m_endPageRef = false; 1847 m_hyperLinkAnchor.clear(); 1848 } 1849 for ( int i = 0; i < m_nFieldsInHyperlink; i++ ) 1850 { 1851 // If fields begin after hyperlink start then 1852 // it should end before hyperlink close 1853 EndField_Impl( pNode, nPos, m_Fields.back( ) ); 1854 m_Fields.pop_back(); 1855 } 1856 m_nFieldsInHyperlink = 0; 1857 1858 m_pSerializer->endElementNS( XML_w, XML_hyperlink ); 1859 m_startedHyperlink = false; 1860 m_nHyperLinkCount--; 1861 } 1862 m_closeHyperlinkInThisRun = false; 1863 } 1864 1865 if (!newStartedHyperlink) 1866 { 1867 while ( m_Fields.begin() != m_Fields.end() ) 1868 { 1869 EndField_Impl( pNode, nPos, m_Fields.front( ) ); 1870 m_Fields.erase( m_Fields.begin( ) ); 1871 } 1872 m_nFieldsInHyperlink = 0; 1873 } 1874 1875 // end ToX fields 1876 for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); ) 1877 { 1878 if (it->bClose && !it->pField) 1879 { 1880 EndField_Impl( pNode, nPos, *it ); 1881 it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1)); 1882 } 1883 else 1884 { 1885 ++it; 1886 } 1887 } 1888 1889 if ( m_pRedlineData ) 1890 { 1891 EndRedline( m_pRedlineData ); 1892 m_pRedlineData = nullptr; 1893 } 1894 1895 DoWriteBookmarksStart(m_rFinalBookmarksStart); 1896 DoWriteBookmarksEnd(m_rFinalBookmarksEnd); 1897 DoWriteBookmarkEndIfExist(nPos); 1898 } 1899 1900 void DocxAttributeOutput::DoWriteBookmarkTagStart(const OUString & bookmarkName) 1901 { 1902 m_pSerializer->singleElementNS(XML_w, XML_bookmarkStart, 1903 FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId), 1904 FSNS(XML_w, XML_name), BookmarkToWord(bookmarkName)); 1905 } 1906 1907 void DocxAttributeOutput::DoWriteBookmarkTagEnd(sal_Int32 const nId) 1908 { 1909 m_pSerializer->singleElementNS(XML_w, XML_bookmarkEnd, 1910 FSNS(XML_w, XML_id), OString::number(nId)); 1911 } 1912 1913 void DocxAttributeOutput::DoWriteMoveRangeTagStart(const OString & bookmarkName, 1914 bool bFrom, const SwRedlineData* pRedlineData) 1915 { 1916 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) ); 1917 OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) ); 1918 1919 m_pSerializer->singleElementNS(XML_w, bFrom 1920 ? XML_moveFromRangeStart 1921 : XML_moveToRangeStart, 1922 FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId), 1923 FSNS(XML_w, XML_author ), OUStringToOString(rAuthor, RTL_TEXTENCODING_UTF8), 1924 FSNS(XML_w, XML_date ), aDate, 1925 FSNS(XML_w, XML_name), bookmarkName); 1926 } 1927 1928 void DocxAttributeOutput::DoWriteMoveRangeTagEnd(sal_Int32 const nId, bool bFrom) 1929 { 1930 m_pSerializer->singleElementNS(XML_w, bFrom 1931 ? XML_moveFromRangeEnd 1932 : XML_moveToRangeEnd, 1933 FSNS(XML_w, XML_id), OString::number(nId)); 1934 } 1935 1936 void DocxAttributeOutput::DoWriteBookmarkStartIfExist(sal_Int32 nRunPos) 1937 { 1938 auto aRange = m_aBookmarksOfParagraphStart.equal_range(nRunPos); 1939 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) 1940 { 1941 DoWriteBookmarkTagStart(aIter->second); 1942 m_rOpenedBookmarksIds[aIter->second] = m_nNextBookmarkId; 1943 m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(aIter->second), RTL_TEXTENCODING_UTF8); 1944 m_nNextBookmarkId++; 1945 } 1946 } 1947 1948 void DocxAttributeOutput::DoWriteBookmarkEndIfExist(sal_Int32 nRunPos) 1949 { 1950 auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nRunPos); 1951 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) 1952 { 1953 // Get the id of the bookmark 1954 auto pPos = m_rOpenedBookmarksIds.find(aIter->second); 1955 if (pPos != m_rOpenedBookmarksIds.end()) 1956 { 1957 // Output the bookmark 1958 DoWriteBookmarkTagEnd(pPos->second); 1959 m_rOpenedBookmarksIds.erase(aIter->second); 1960 } 1961 } 1962 } 1963 1964 /// Write the start bookmarks 1965 void DocxAttributeOutput::DoWriteBookmarksStart(std::vector<OUString>& rStarts, const SwRedlineData* pRedlineData) 1966 { 1967 for (const OUString & bookmarkName : rStarts) 1968 { 1969 // Output the bookmark (including MoveBookmark of the tracked moving) 1970 bool bMove = false; 1971 bool bFrom = false; 1972 OString sBookmarkName = OUStringToOString( 1973 BookmarkToWord(bookmarkName, &bMove, &bFrom), RTL_TEXTENCODING_UTF8); 1974 if ( bMove ) 1975 { 1976 // TODO: redline data of MoveBookmark is restored from the first redline of the bookmark 1977 // range. But a later deletion within a tracked moving is still imported as plain 1978 // deletion, so check IsMoved() and skip the export of the tracked moving to avoid 1979 // export with bad author or date 1980 if ( pRedlineData && pRedlineData->IsMoved() ) 1981 DoWriteMoveRangeTagStart(sBookmarkName, bFrom, pRedlineData); 1982 } 1983 else 1984 DoWriteBookmarkTagStart(bookmarkName); 1985 1986 m_rOpenedBookmarksIds[bookmarkName] = m_nNextBookmarkId; 1987 m_sLastOpenedBookmark = sBookmarkName; 1988 m_nNextBookmarkId++; 1989 } 1990 rStarts.clear(); 1991 } 1992 1993 /// export the end bookmarks 1994 void DocxAttributeOutput::DoWriteBookmarksEnd(std::vector<OUString>& rEnds) 1995 { 1996 for (const OUString & bookmarkName : rEnds) 1997 { 1998 // Get the id of the bookmark 1999 auto pPos = m_rOpenedBookmarksIds.find(bookmarkName); 2000 2001 if (pPos != m_rOpenedBookmarksIds.end()) 2002 { 2003 bool bMove = false; 2004 bool bFrom = false; 2005 BookmarkToWord(bookmarkName, &bMove, &bFrom); 2006 // Output the bookmark (including MoveBookmark of the tracked moving) 2007 if ( bMove ) 2008 DoWriteMoveRangeTagEnd(pPos->second, bFrom); 2009 else 2010 DoWriteBookmarkTagEnd(pPos->second); 2011 2012 m_rOpenedBookmarksIds.erase(bookmarkName); 2013 } 2014 } 2015 rEnds.clear(); 2016 } 2017 2018 // For construction of the special bookmark name template for permissions: 2019 // see, PermInsertPosition::createBookmarkName() 2020 // 2021 // Syntax: 2022 // - "permission-for-user:<permission-id>:<permission-user-name>" 2023 // - "permission-for-group:<permission-id>:<permission-group-name>" 2024 // 2025 void DocxAttributeOutput::DoWritePermissionTagStart(std::u16string_view permission) 2026 { 2027 std::u16string_view permissionIdAndName; 2028 2029 if (o3tl::starts_with(permission, u"permission-for-group:", &permissionIdAndName)) 2030 { 2031 const std::size_t separatorIndex = permissionIdAndName.find(u':'); 2032 assert(separatorIndex != std::u16string_view::npos); 2033 const std::u16string_view permissionId = permissionIdAndName.substr(0, separatorIndex); 2034 const std::u16string_view permissionName = permissionIdAndName.substr(separatorIndex + 1); 2035 2036 m_pSerializer->singleElementNS(XML_w, XML_permStart, 2037 FSNS(XML_w, XML_id), BookmarkToWord(OUString(permissionId)), 2038 FSNS(XML_w, XML_edGrp), BookmarkToWord(OUString(permissionName))); 2039 } 2040 else 2041 { 2042 auto const ok = o3tl::starts_with( 2043 permission, u"permission-for-user:", &permissionIdAndName); 2044 assert(ok); (void)ok; 2045 const std::size_t separatorIndex = permissionIdAndName.find(u':'); 2046 assert(separatorIndex != std::u16string_view::npos); 2047 const std::u16string_view permissionId = permissionIdAndName.substr(0, separatorIndex); 2048 const std::u16string_view permissionName = permissionIdAndName.substr(separatorIndex + 1); 2049 2050 m_pSerializer->singleElementNS(XML_w, XML_permStart, 2051 FSNS(XML_w, XML_id), BookmarkToWord(OUString(permissionId)), 2052 FSNS(XML_w, XML_ed), BookmarkToWord(OUString(permissionName))); 2053 } 2054 } 2055 2056 2057 // For construction of the special bookmark name template for permissions: 2058 // see, PermInsertPosition::createBookmarkName() 2059 // 2060 // Syntax: 2061 // - "permission-for-user:<permission-id>:<permission-user-name>" 2062 // - "permission-for-group:<permission-id>:<permission-group-name>" 2063 // 2064 void DocxAttributeOutput::DoWritePermissionTagEnd(std::u16string_view permission) 2065 { 2066 std::u16string_view permissionIdAndName; 2067 2068 auto const ok = o3tl::starts_with(permission, u"permission-for-group:", &permissionIdAndName) || 2069 o3tl::starts_with(permission, u"permission-for-user:", &permissionIdAndName); 2070 assert(ok); (void)ok; 2071 2072 const std::size_t separatorIndex = permissionIdAndName.find(u':'); 2073 assert(separatorIndex != std::u16string_view::npos); 2074 const std::u16string_view permissionId = permissionIdAndName.substr(0, separatorIndex); 2075 2076 m_pSerializer->singleElementNS(XML_w, XML_permEnd, 2077 FSNS(XML_w, XML_id), BookmarkToWord(OUString(permissionId))); 2078 } 2079 2080 /// Write the start permissions 2081 void DocxAttributeOutput::DoWritePermissionsStart() 2082 { 2083 for (const OUString & permission : m_rPermissionsStart) 2084 { 2085 DoWritePermissionTagStart(permission); 2086 } 2087 m_rPermissionsStart.clear(); 2088 } 2089 2090 /// export the end permissions 2091 void DocxAttributeOutput::DoWritePermissionsEnd() 2092 { 2093 for (const OUString & permission : m_rPermissionsEnd) 2094 { 2095 DoWritePermissionTagEnd(permission); 2096 } 2097 m_rPermissionsEnd.clear(); 2098 } 2099 2100 void DocxAttributeOutput::DoWriteAnnotationMarks() 2101 { 2102 // Write the start annotation marks 2103 for ( const auto & rName : m_rAnnotationMarksStart ) 2104 { 2105 // Output the annotation mark 2106 /* Ensure that the existing Annotation Marks are not overwritten 2107 as it causes discrepancy when DocxAttributeOutput::PostitField 2108 refers to this map & while mapping comment id's in document.xml & 2109 comment.xml. 2110 */ 2111 if ( m_rOpenedAnnotationMarksIds.end() == m_rOpenedAnnotationMarksIds.find( rName ) ) 2112 { 2113 const sal_Int32 nId = m_nNextAnnotationMarkId++; 2114 m_rOpenedAnnotationMarksIds[rName] = nId; 2115 m_pSerializer->singleElementNS( XML_w, XML_commentRangeStart, 2116 FSNS( XML_w, XML_id ), OString::number(nId) ); 2117 m_sLastOpenedAnnotationMark = rName; 2118 } 2119 } 2120 m_rAnnotationMarksStart.clear(); 2121 2122 // export the end annotation marks 2123 for ( const auto & rName : m_rAnnotationMarksEnd ) 2124 { 2125 // Get the id of the annotation mark 2126 std::map< OString, sal_Int32 >::iterator pPos = m_rOpenedAnnotationMarksIds.find( rName ); 2127 if ( pPos != m_rOpenedAnnotationMarksIds.end( ) ) 2128 { 2129 const sal_Int32 nId = ( *pPos ).second; 2130 m_pSerializer->singleElementNS( XML_w, XML_commentRangeEnd, 2131 FSNS( XML_w, XML_id ), OString::number(nId) ); 2132 m_rOpenedAnnotationMarksIds.erase( rName ); 2133 2134 m_pSerializer->startElementNS(XML_w, XML_r); 2135 m_pSerializer->singleElementNS( XML_w, XML_commentReference, FSNS( XML_w, XML_id ), 2136 OString::number(nId) ); 2137 m_pSerializer->endElementNS(XML_w, XML_r); 2138 } 2139 } 2140 m_rAnnotationMarksEnd.clear(); 2141 } 2142 2143 void DocxAttributeOutput::WriteFFData( const FieldInfos& rInfos ) 2144 { 2145 const ::sw::mark::IFieldmark& rFieldmark = *rInfos.pFieldmark; 2146 FieldMarkParamsHelper params( rFieldmark ); 2147 2148 OUString sEntryMacro; 2149 params.extractParam("EntryMacro", sEntryMacro); 2150 OUString sExitMacro; 2151 params.extractParam("ExitMacro", sExitMacro); 2152 OUString sHelp; 2153 params.extractParam("Help", sHelp); 2154 OUString sHint; 2155 params.extractParam("Hint", sHint); // .docx StatusText 2156 if ( sHint.isEmpty() ) 2157 params.extractParam("Description", sHint); // .doc StatusText 2158 2159 if ( rInfos.eType == ww::eFORMDROPDOWN ) 2160 { 2161 uno::Sequence< OUString> vListEntries; 2162 OUString sName, sSelected; 2163 2164 params.extractParam( ODF_FORMDROPDOWN_LISTENTRY, vListEntries ); 2165 if (vListEntries.getLength() > ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT) 2166 vListEntries = uno::Sequence< OUString>(vListEntries.getArray(), ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT); 2167 2168 sName = params.getName(); 2169 sal_Int32 nSelectedIndex = 0; 2170 2171 if ( params.extractParam( ODF_FORMDROPDOWN_RESULT, nSelectedIndex ) ) 2172 { 2173 if (nSelectedIndex < vListEntries.getLength() ) 2174 sSelected = vListEntries[ nSelectedIndex ]; 2175 } 2176 2177 GetExport().DoComboBox( sName, OUString(), OUString(), sSelected, vListEntries ); 2178 } 2179 else if ( rInfos.eType == ww::eFORMCHECKBOX ) 2180 { 2181 OUString sName; 2182 bool bChecked = false; 2183 2184 params.extractParam( ODF_FORMCHECKBOX_NAME, sName ); 2185 2186 const sw::mark::ICheckboxFieldmark* pCheckboxFm = dynamic_cast<const sw::mark::ICheckboxFieldmark*>(&rFieldmark); 2187 if ( pCheckboxFm && pCheckboxFm->IsChecked() ) 2188 bChecked = true; 2189 2190 FFDataWriterHelper ffdataOut( m_pSerializer ); 2191 ffdataOut.WriteFormCheckbox( sName, sEntryMacro, sExitMacro, sHelp, sHint, bChecked ); 2192 } 2193 else if ( rInfos.eType == ww::eFORMTEXT ) 2194 { 2195 OUString sType; 2196 params.extractParam("Type", sType); 2197 OUString sDefaultText; 2198 params.extractParam("Content", sDefaultText); 2199 sal_uInt16 nMaxLength = 0; 2200 params.extractParam("MaxLength", nMaxLength); 2201 OUString sFormat; 2202 params.extractParam("Format", sFormat); 2203 FFDataWriterHelper ffdataOut( m_pSerializer ); 2204 ffdataOut.WriteFormText( params.getName(), sEntryMacro, sExitMacro, sHelp, sHint, 2205 sType, sDefaultText, nMaxLength, sFormat ); 2206 } 2207 } 2208 2209 void DocxAttributeOutput::WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt) 2210 { 2211 m_pSerializer->startElementNS(XML_w, XML_sdt); 2212 m_pSerializer->startElementNS(XML_w, XML_sdtPr); 2213 2214 if(!sFullDate.isEmpty()) 2215 m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sFullDate); 2216 else 2217 m_pSerializer->startElementNS(XML_w, XML_date); 2218 2219 // Replace quotation mark used for marking static strings in date format 2220 OUString sDateFormat1 = sDateFormat.replaceAll("\"", "'"); 2221 m_pSerializer->singleElementNS(XML_w, XML_dateFormat, 2222 FSNS(XML_w, XML_val), sDateFormat1); 2223 m_pSerializer->singleElementNS(XML_w, XML_lid, 2224 FSNS(XML_w, XML_val), sLang); 2225 m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs, 2226 FSNS(XML_w, XML_val), "dateTime"); 2227 m_pSerializer->singleElementNS(XML_w, XML_calendar, 2228 FSNS(XML_w, XML_val), "gregorian"); 2229 m_pSerializer->endElementNS(XML_w, XML_date); 2230 2231 if (aGrabBagSdt.hasElements()) 2232 { 2233 // There are some extra sdt parameters came from grab bag 2234 SdtBlockHelper aSdtBlock; 2235 aSdtBlock.GetSdtParamsFromGrabBag(aGrabBagSdt); 2236 aSdtBlock.WriteExtraParams(m_pSerializer); 2237 } 2238 2239 m_pSerializer->endElementNS(XML_w, XML_sdtPr); 2240 2241 m_pSerializer->startElementNS(XML_w, XML_sdtContent); 2242 } 2243 2244 void DocxAttributeOutput::WriteSdtPlainText(const OUString & sValue, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt) 2245 { 2246 m_pSerializer->startElementNS(XML_w, XML_sdt); 2247 m_pSerializer->startElementNS(XML_w, XML_sdtPr); 2248 2249 if (aGrabBagSdt.hasElements()) 2250 { 2251 // There are some extra sdt parameters came from grab bag 2252 SdtBlockHelper aSdtBlock; 2253 aSdtBlock.GetSdtParamsFromGrabBag(aGrabBagSdt); 2254 aSdtBlock.WriteExtraParams(m_pSerializer); 2255 2256 if (aSdtBlock.m_nSdtPrToken && aSdtBlock.m_nSdtPrToken != FSNS(XML_w, XML_id)) 2257 { 2258 // Write <w:text/> or whatsoever from grabbag 2259 m_pSerializer->singleElement(aSdtBlock.m_nSdtPrToken); 2260 } 2261 2262 // Store databindings data for later writing to corresponding XMLs 2263 OUString sPrefixMapping, sXpath; 2264 for (const auto& rProp : std::as_const(aGrabBagSdt)) 2265 { 2266 if (rProp.Name == "ooxml:CT_SdtPr_dataBinding") 2267 { 2268 uno::Sequence<beans::PropertyValue> aDataBindingProps; 2269 rProp.Value >>= aDataBindingProps; 2270 for (const auto& rDBProp : std::as_const(aDataBindingProps)) 2271 { 2272 if (rDBProp.Name == "ooxml:CT_DataBinding_prefixMappings") 2273 sPrefixMapping = rDBProp.Value.get<OUString>(); 2274 else if (rDBProp.Name == "ooxml:CT_DataBinding_xpath") 2275 sXpath = rDBProp.Value.get<OUString>(); 2276 } 2277 } 2278 } 2279 2280 if (sXpath.getLength()) 2281 { 2282 // Given xpath is sufficient 2283 m_rExport.AddSdtData(sPrefixMapping, sXpath, sValue); 2284 } 2285 } 2286 2287 m_pSerializer->endElementNS(XML_w, XML_sdtPr); 2288 2289 m_pSerializer->startElementNS(XML_w, XML_sdtContent); 2290 } 2291 2292 void DocxAttributeOutput::WriteSdtEnd() 2293 { 2294 m_pSerializer->endElementNS(XML_w, XML_sdtContent); 2295 m_pSerializer->endElementNS(XML_w, XML_sdt); 2296 } 2297 2298 void DocxAttributeOutput::WriteSdtDropDownStart( 2299 std::u16string_view rName, 2300 OUString const& rSelected, 2301 uno::Sequence<OUString> const& rListItems) 2302 { 2303 m_pSerializer->startElementNS(XML_w, XML_sdt); 2304 m_pSerializer->startElementNS(XML_w, XML_sdtPr); 2305 2306 m_pSerializer->singleElementNS(XML_w, XML_alias, 2307 FSNS(XML_w, XML_val), OUStringToOString(rName, RTL_TEXTENCODING_UTF8)); 2308 2309 sal_Int32 nId = comphelper::findValue(rListItems, rSelected); 2310 if (nId == -1) 2311 { 2312 nId = 0; 2313 } 2314 2315 m_pSerializer->startElementNS(XML_w, XML_dropDownList, 2316 FSNS(XML_w, XML_lastValue), OString::number(nId)); 2317 2318 for (auto const& rItem : rListItems) 2319 { 2320 auto const item(OUStringToOString(rItem, RTL_TEXTENCODING_UTF8)); 2321 m_pSerializer->singleElementNS(XML_w, XML_listItem, 2322 FSNS(XML_w, XML_value), item, 2323 FSNS(XML_w, XML_displayText), item); 2324 } 2325 2326 m_pSerializer->endElementNS(XML_w, XML_dropDownList); 2327 m_pSerializer->endElementNS(XML_w, XML_sdtPr); 2328 2329 m_pSerializer->startElementNS(XML_w, XML_sdtContent); 2330 } 2331 2332 void DocxAttributeOutput::WriteSdtDropDownEnd(OUString const& rSelected, 2333 uno::Sequence<OUString> const& rListItems) 2334 { 2335 // note: rSelected might be empty? 2336 sal_Int32 nId = comphelper::findValue(rListItems, rSelected); 2337 if (nId == -1) 2338 { 2339 nId = 0; 2340 } 2341 2342 // the lastValue only identifies the entry in the list, also export 2343 // currently selected item's displayText as run content (if one exists) 2344 if (rListItems.size()) 2345 { 2346 m_pSerializer->startElementNS(XML_w, XML_r); 2347 m_pSerializer->startElementNS(XML_w, XML_t); 2348 m_pSerializer->writeEscaped(rListItems[nId]); 2349 m_pSerializer->endElementNS(XML_w, XML_t); 2350 m_pSerializer->endElementNS(XML_w, XML_r); 2351 } 2352 2353 WriteSdtEnd(); 2354 } 2355 2356 void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun ) 2357 { 2358 if ( rInfos.pField && rInfos.eType == ww::eUNKNOWN ) 2359 { 2360 // Expand unsupported fields 2361 RunText( rInfos.pField->GetFieldName() ); 2362 return; 2363 } 2364 else if ( rInfos.eType == ww::eFORMDATE ) 2365 { 2366 const sw::mark::IDateFieldmark& rFieldmark = dynamic_cast<const sw::mark::IDateFieldmark&>(*rInfos.pFieldmark); 2367 FieldMarkParamsHelper params(rFieldmark); 2368 2369 OUString sFullDate; 2370 OUString sCurrentDate; 2371 params.extractParam( ODF_FORMDATE_CURRENTDATE, sCurrentDate ); 2372 if(!sCurrentDate.isEmpty()) 2373 { 2374 sFullDate = sCurrentDate + "T00:00:00Z"; 2375 } 2376 else 2377 { 2378 std::pair<bool, double> aResult = rFieldmark.GetCurrentDate(); 2379 if(aResult.first) 2380 { 2381 sFullDate = rFieldmark.GetDateInStandardDateFormat(aResult.second) + "T00:00:00Z"; 2382 } 2383 } 2384 2385 OUString sDateFormat; 2386 params.extractParam( ODF_FORMDATE_DATEFORMAT, sDateFormat ); 2387 OUString sLang; 2388 params.extractParam( ODF_FORMDATE_DATEFORMAT_LANGUAGE, sLang ); 2389 2390 uno::Sequence<beans::PropertyValue> aSdtParams; 2391 params.extractParam(UNO_NAME_MISC_OBJ_INTEROPGRABBAG, aSdtParams); 2392 2393 WriteFormDateStart( sFullDate, sDateFormat, sLang, aSdtParams); 2394 return; 2395 } 2396 else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField) 2397 { 2398 assert(!rInfos.pFieldmark); 2399 SwDropDownField const& rField2(*static_cast<SwDropDownField const*>(rInfos.pField.get())); 2400 WriteSdtDropDownStart(rField2.GetName(), 2401 rField2.GetSelectedItem(), 2402 rField2.GetItemSequence()); 2403 return; 2404 } 2405 else if (rInfos.eType == ww::eFILLIN) 2406 { 2407 SwInputField const& rField(*static_cast<SwInputField const*>(rInfos.pField.get())); 2408 if (rField.getGrabBagParams().hasElements()) 2409 { 2410 WriteSdtPlainText(rField.GetPar1(), rField.getGrabBagParams()); 2411 m_sRawText = rField.GetPar1(); // Write field content also as a fallback 2412 return; 2413 } 2414 } 2415 2416 if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands 2417 { 2418 if ( bWriteRun ) 2419 m_pSerializer->startElementNS(XML_w, XML_r); 2420 2421 if ( rInfos.eType == ww::eFORMDROPDOWN ) 2422 { 2423 m_pSerializer->startElementNS( XML_w, XML_fldChar, 2424 FSNS( XML_w, XML_fldCharType ), "begin" ); 2425 assert( rInfos.pFieldmark && !rInfos.pField ); 2426 WriteFFData(rInfos); 2427 m_pSerializer->endElementNS( XML_w, XML_fldChar ); 2428 2429 if ( bWriteRun ) 2430 m_pSerializer->endElementNS( XML_w, XML_r ); 2431 2432 CmdField_Impl( pNode, nPos, rInfos, bWriteRun ); 2433 } 2434 else 2435 { 2436 // Write the field start 2437 if ( rInfos.pField && (rInfos.pField->Which() == SwFieldIds::DateTime) && rInfos.pField->GetSubType() & FIXEDFLD ) 2438 { 2439 m_pSerializer->startElementNS( XML_w, XML_fldChar, 2440 FSNS( XML_w, XML_fldCharType ), "begin", 2441 FSNS( XML_w, XML_fldLock ), "true" ); 2442 } 2443 else 2444 { 2445 m_pSerializer->startElementNS( XML_w, XML_fldChar, 2446 FSNS( XML_w, XML_fldCharType ), "begin" ); 2447 } 2448 2449 if ( rInfos.pFieldmark ) 2450 WriteFFData( rInfos ); 2451 2452 m_pSerializer->endElementNS( XML_w, XML_fldChar ); 2453 2454 if ( bWriteRun ) 2455 m_pSerializer->endElementNS( XML_w, XML_r ); 2456 2457 // The hyperlinks fields can't be expanded: the value is 2458 // normally in the text run 2459 if ( !rInfos.pField ) 2460 CmdField_Impl( pNode, nPos, rInfos, bWriteRun ); 2461 else 2462 m_bWritingField = true; 2463 } 2464 } 2465 } 2466 2467 void DocxAttributeOutput::DoWriteCmd( const OUString& rCmd ) 2468 { 2469 OUString sCmd = rCmd.trim(); 2470 if (sCmd.startsWith("SEQ")) 2471 { 2472 OUString sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\').trim(); 2473 m_aSeqBookmarksNames[sSeqName].push_back(m_sLastOpenedBookmark); 2474 } 2475 // Write the Field command 2476 sal_Int32 nTextToken = XML_instrText; 2477 if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete ) 2478 nTextToken = XML_delInstrText; 2479 2480 m_pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve"); 2481 m_pSerializer->writeEscaped( rCmd ); 2482 m_pSerializer->endElementNS( XML_w, nTextToken ); 2483 2484 } 2485 2486 void DocxAttributeOutput::CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun ) 2487 { 2488 // Write the Field instruction 2489 if ( bWriteRun ) 2490 { 2491 bool bWriteCombChars(false); 2492 m_pSerializer->startElementNS(XML_w, XML_r); 2493 2494 if (rInfos.eType == ww::eEQ) 2495 bWriteCombChars = true; 2496 2497 DoWriteFieldRunProperties( pNode, nPos, bWriteCombChars ); 2498 } 2499 2500 sal_Int32 nIdx { rInfos.sCmd.isEmpty() ? -1 : 0 }; 2501 while ( nIdx >= 0 ) 2502 { 2503 OUString sToken = rInfos.sCmd.getToken( 0, '\t', nIdx ); 2504 if ( rInfos.eType == ww::eCREATEDATE 2505 || rInfos.eType == ww::eSAVEDATE 2506 || rInfos.eType == ww::ePRINTDATE 2507 || rInfos.eType == ww::eDATE 2508 || rInfos.eType == ww::eTIME ) 2509 { 2510 sToken = sToken.replaceAll("NNNN", "dddd"); 2511 sToken = sToken.replaceAll("NN", "ddd"); 2512 } 2513 else if ( rInfos.eType == ww::eEquals ) 2514 { 2515 // Use original OOXML formula, if it exists and its conversion hasn't been changed 2516 bool bIsChanged = true; 2517 if ( pNode->GetTableBox() ) 2518 { 2519 if ( const SfxGrabBagItem* pItem = pNode->GetTableBox()->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG) ) 2520 { 2521 OUString sActualFormula = sToken.trim(); 2522 const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag(); 2523 std::map<OUString, uno::Any>::const_iterator aStoredFormula = rGrabBag.find("CellFormulaConverted"); 2524 if ( aStoredFormula != rGrabBag.end() && sActualFormula.indexOf('=') == 0 && 2525 sActualFormula.copy(1).trim() == aStoredFormula->second.get<OUString>().trim() ) 2526 { 2527 aStoredFormula = rGrabBag.find("CellFormula"); 2528 if ( aStoredFormula != rGrabBag.end() ) 2529 { 2530 sToken = " =" + aStoredFormula->second.get<OUString>(); 2531 bIsChanged = false; 2532 } 2533 } 2534 } 2535 } 2536 2537 if ( bIsChanged ) 2538 { 2539 UErrorCode nErr(U_ZERO_ERROR); 2540 icu::UnicodeString sInput(sToken.getStr()); 2541 // remove < and > around cell references, e.g. <A1> to A1, <A1:B2> to A1:B2 2542 icu::RegexMatcher aMatcher("<([A-Z]{1,3}[0-9]+(:[A-Z]{1,3}[0-9]+)?)>", sInput, 0, nErr); 2543 sInput = aMatcher.replaceAll(icu::UnicodeString("$1"), nErr); 2544 // convert MEAN to AVERAGE 2545 icu::RegexMatcher aMatcher2("\\bMEAN\\b", sInput, UREGEX_CASE_INSENSITIVE, nErr); 2546 sToken = aMatcher2.replaceAll(icu::UnicodeString("AVERAGE"), nErr).getTerminatedBuffer(); 2547 } 2548 } 2549 2550 // Write the Field command 2551 DoWriteCmd( sToken ); 2552 2553 // Replace tabs by </instrText><tab/><instrText> 2554 if ( nIdx > 0 ) // Is another token expected? 2555 RunText( "\t" ); 2556 } 2557 2558 if ( bWriteRun ) 2559 { 2560 m_pSerializer->endElementNS( XML_w, XML_r ); 2561 } 2562 } 2563 2564 void DocxAttributeOutput::CmdEndField_Impl(SwTextNode const*const pNode, 2565 sal_Int32 const nPos, bool const bWriteRun) 2566 { 2567 // Write the Field separator 2568 if ( bWriteRun ) 2569 { 2570 m_pSerializer->startElementNS(XML_w, XML_r); 2571 DoWriteFieldRunProperties( pNode, nPos ); 2572 } 2573 2574 m_pSerializer->singleElementNS( XML_w, XML_fldChar, 2575 FSNS( XML_w, XML_fldCharType ), "separate" ); 2576 2577 if ( bWriteRun ) 2578 { 2579 m_pSerializer->endElementNS( XML_w, XML_r ); 2580 } 2581 } 2582 2583 /// Writes properties for run that is used to separate field implementation. 2584 /// There are several runs are used: 2585 /// <w:r> 2586 /// <w:rPr> 2587 /// <!-- properties written with StartRunProperties() / EndRunProperties(). 2588 /// </w:rPr> 2589 /// <w:fldChar w:fldCharType="begin" /> 2590 /// </w:r> 2591 /// <w:r> 2592 /// <w:rPr> 2593 /// <!-- properties written with DoWriteFieldRunProperties() 2594 /// </w:rPr> 2595 /// <w:instrText>TIME \@"HH:mm:ss"</w:instrText> 2596 /// </w:r> 2597 /// <w:r> 2598 /// <w:rPr> 2599 /// <!-- properties written with DoWriteFieldRunProperties() 2600 /// </w:rPr> 2601 /// <w:fldChar w:fldCharType="separate" /> 2602 /// </w:r> 2603 /// <w:r> 2604 /// <w:rPr> 2605 /// <!-- properties written with DoWriteFieldRunProperties() 2606 /// </w:rPr> 2607 /// <w:t>14:01:13</w:t> 2608 /// </w:r> 2609 /// <w:r> 2610 /// <w:rPr> 2611 /// <!-- properties written with DoWriteFieldRunProperties() 2612 /// </w:rPr> 2613 /// <w:fldChar w:fldCharType="end" /> 2614 /// </w:r> 2615 /// See, tdf#38778 2616 void DocxAttributeOutput::DoWriteFieldRunProperties( const SwTextNode * pNode, sal_Int32 nPos, bool bWriteCombChars) 2617 { 2618 if (! pNode) 2619 { 2620 // nothing to do 2621 return; 2622 } 2623 2624 m_bPreventDoubleFieldsHandling = true; 2625 2626 { 2627 m_pSerializer->startElementNS(XML_w, XML_rPr); 2628 2629 // 1. output webHidden flag 2630 if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() ) 2631 { 2632 m_pSerializer->singleElementNS(XML_w, XML_webHidden); 2633 } 2634 2635 // 2. find all active character properties 2636 SwWW8AttrIter aAttrIt( m_rExport, *pNode ); 2637 aAttrIt.OutAttr( nPos, bWriteCombChars ); 2638 2639 // 3. write the character properties 2640 WriteCollectedRunProperties(); 2641 2642 m_pSerializer->endElementNS( XML_w, XML_rPr ); 2643 } 2644 2645 m_bPreventDoubleFieldsHandling = false; 2646 } 2647 2648 void DocxAttributeOutput::EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos ) 2649 { 2650 if (rInfos.eType == ww::eFORMDATE) 2651 { 2652 WriteSdtEnd(); 2653 return; 2654 } 2655 else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField) 2656 { 2657 // write selected item from End not Start to ensure that any bookmarks 2658 // precede it 2659 SwDropDownField const& rField(*static_cast<SwDropDownField const*>(rInfos.pField.get())); 2660 WriteSdtDropDownEnd(rField.GetSelectedItem(), rField.GetItemSequence()); 2661 return; 2662 } 2663 else if (rInfos.eType == ww::eFILLIN && rInfos.pField) 2664 { 2665 SwInputField const& rField(*static_cast<SwInputField const*>(rInfos.pField.get())); 2666 if (rField.getGrabBagParams().hasElements()) 2667 { 2668 WriteSdtEnd(); 2669 return; 2670 } 2671 } 2672 // The command has to be written before for the hyperlinks 2673 if ( rInfos.pField ) 2674 { 2675 CmdField_Impl( pNode, nPos, rInfos, true ); 2676 CmdEndField_Impl( pNode, nPos, true ); 2677 } 2678 2679 // Write the bookmark start if any 2680 if ( !m_sFieldBkm.isEmpty() ) 2681 { 2682 DoWriteBookmarkTagStart(m_sFieldBkm); 2683 } 2684 2685 if (rInfos.pField ) // For hyperlinks and TOX 2686 { 2687 // Write the Field latest value 2688 m_pSerializer->startElementNS(XML_w, XML_r); 2689 DoWriteFieldRunProperties( pNode, nPos ); 2690 2691 OUString sExpand; 2692 if(rInfos.eType == ww::eCITATION) 2693 { 2694 sExpand = static_cast<SwAuthorityField const*>(rInfos.pField.get()) 2695 ->ExpandCitation(AUTH_FIELD_TITLE, nullptr); 2696 } 2697 else if(rInfos.eType != ww::eFORMDROPDOWN) 2698 { 2699 sExpand = rInfos.pField->ExpandField(true, nullptr); 2700 } 2701 // newlines embedded in fields are 0x0B in MSO and 0x0A for us 2702 RunText(sExpand.replace(0x0A, 0x0B)); 2703 2704 m_pSerializer->endElementNS( XML_w, XML_r ); 2705 } 2706 2707 // Write the bookmark end if any 2708 if ( !m_sFieldBkm.isEmpty() ) 2709 { 2710 DoWriteBookmarkTagEnd(m_nNextBookmarkId); 2711 2712 m_nNextBookmarkId++; 2713 } 2714 2715 // Write the Field end 2716 if ( rInfos.bClose ) 2717 { 2718 m_bWritingField = false; 2719 m_pSerializer->startElementNS(XML_w, XML_r); 2720 DoWriteFieldRunProperties( pNode, nPos ); 2721 m_pSerializer->singleElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "end"); 2722 m_pSerializer->endElementNS( XML_w, XML_r ); 2723 } 2724 // Write the ref field if a bookmark had to be set and the field 2725 // should be visible 2726 if ( !rInfos.pField ) 2727 { 2728 m_sFieldBkm.clear(); 2729 return; 2730 } 2731 2732 sal_uInt16 nSubType = rInfos.pField->GetSubType( ); 2733 bool bIsSetField = rInfos.pField->GetTyp( )->Which( ) == SwFieldIds::SetExp; 2734 bool bShowRef = bIsSetField && ( nSubType & nsSwExtendedSubType::SUB_INVISIBLE ) == 0; 2735 2736 if (!bShowRef) 2737 { 2738 m_sFieldBkm.clear(); 2739 } 2740 2741 if (m_sFieldBkm.isEmpty()) 2742 return; 2743 2744 // Write the field beginning 2745 m_pSerializer->startElementNS(XML_w, XML_r); 2746 m_pSerializer->singleElementNS( XML_w, XML_fldChar, 2747 FSNS( XML_w, XML_fldCharType ), "begin" ); 2748 m_pSerializer->endElementNS( XML_w, XML_r ); 2749 2750 rInfos.sCmd = FieldString( ww::eREF ); 2751 rInfos.sCmd += "\""; 2752 rInfos.sCmd += m_sFieldBkm; 2753 rInfos.sCmd += "\" "; 2754 2755 // Clean the field bookmark data to avoid infinite loop 2756 m_sFieldBkm = OUString( ); 2757 2758 // Write the end of the field 2759 EndField_Impl( pNode, nPos, rInfos ); 2760 } 2761 2762 void DocxAttributeOutput::StartRunProperties() 2763 { 2764 // postpone the output so that we can later [in EndRunProperties()] 2765 // prepend the properties before the text 2766 m_pSerializer->mark(Tag_StartRunProperties); 2767 2768 m_pSerializer->startElementNS(XML_w, XML_rPr); 2769 2770 if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() ) 2771 { 2772 m_pSerializer->singleElementNS(XML_w, XML_webHidden); 2773 } 2774 InitCollectedRunProperties(); 2775 2776 assert( !m_pPostponedGraphic ); 2777 m_pPostponedGraphic.reset(new std::vector<PostponedGraphic>); 2778 2779 assert( !m_pPostponedDiagrams ); 2780 m_pPostponedDiagrams.reset(new std::vector<PostponedDiagram>); 2781 2782 assert(!m_pPostponedDMLDrawings); 2783 m_pPostponedDMLDrawings.reset(new std::vector<PostponedDrawing>); 2784 2785 assert( !m_pPostponedOLEs ); 2786 m_pPostponedOLEs.reset(new std::vector<PostponedOLE>); 2787 } 2788 2789 void DocxAttributeOutput::InitCollectedRunProperties() 2790 { 2791 m_pFontsAttrList = nullptr; 2792 m_pEastAsianLayoutAttrList = nullptr; 2793 m_pCharLangAttrList = nullptr; 2794 2795 // Write the elements in the spec order 2796 static const sal_Int32 aOrder[] = 2797 { 2798 FSNS( XML_w, XML_rStyle ), 2799 FSNS( XML_w, XML_rFonts ), 2800 FSNS( XML_w, XML_b ), 2801 FSNS( XML_w, XML_bCs ), 2802 FSNS( XML_w, XML_i ), 2803 FSNS( XML_w, XML_iCs ), 2804 FSNS( XML_w, XML_caps ), 2805 FSNS( XML_w, XML_smallCaps ), 2806 FSNS( XML_w, XML_strike ), 2807 FSNS( XML_w, XML_dstrike ), 2808 FSNS( XML_w, XML_outline ), 2809 FSNS( XML_w, XML_shadow ), 2810 FSNS( XML_w, XML_emboss ), 2811 FSNS( XML_w, XML_imprint ), 2812 FSNS( XML_w, XML_noProof ), 2813 FSNS( XML_w, XML_snapToGrid ), 2814 FSNS( XML_w, XML_vanish ), 2815 FSNS( XML_w, XML_webHidden ), 2816 FSNS( XML_w, XML_color ), 2817 FSNS( XML_w, XML_spacing ), 2818 FSNS( XML_w, XML_w ), 2819 FSNS( XML_w, XML_kern ), 2820 FSNS( XML_w, XML_position ), 2821 FSNS( XML_w, XML_sz ), 2822 FSNS( XML_w, XML_szCs ), 2823 FSNS( XML_w, XML_highlight ), 2824 FSNS( XML_w, XML_u ), 2825 FSNS( XML_w, XML_effect ), 2826 FSNS( XML_w, XML_bdr ), 2827 FSNS( XML_w, XML_shd ), 2828 FSNS( XML_w, XML_fitText ), 2829 FSNS( XML_w, XML_vertAlign ), 2830 FSNS( XML_w, XML_rtl ), 2831 FSNS( XML_w, XML_cs ), 2832 FSNS( XML_w, XML_em ), 2833 FSNS( XML_w, XML_lang ), 2834 FSNS( XML_w, XML_eastAsianLayout ), 2835 FSNS( XML_w, XML_specVanish ), 2836 FSNS( XML_w, XML_oMath ), 2837 FSNS( XML_w, XML_rPrChange ), 2838 FSNS( XML_w, XML_del ), 2839 FSNS( XML_w14, XML_glow ), 2840 FSNS( XML_w14, XML_shadow ), 2841 FSNS( XML_w14, XML_reflection ), 2842 FSNS( XML_w14, XML_textOutline ), 2843 FSNS( XML_w14, XML_textFill ), 2844 FSNS( XML_w14, XML_scene3d ), 2845 FSNS( XML_w14, XML_props3d ), 2846 FSNS( XML_w14, XML_ligatures ), 2847 FSNS( XML_w14, XML_numForm ), 2848 FSNS( XML_w14, XML_numSpacing ), 2849 FSNS( XML_w14, XML_stylisticSets ), 2850 FSNS( XML_w14, XML_cntxtAlts ), 2851 }; 2852 2853 // postpone the output so that we can later [in EndParagraphProperties()] 2854 // prepend the properties before the run 2855 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence 2856 m_pSerializer->mark(Tag_InitCollectedRunProperties, comphelper::containerToSequence(aOrder)); 2857 } 2858 2859 namespace 2860 { 2861 2862 struct NameToId 2863 { 2864 OUString maName; 2865 sal_Int32 maId; 2866 }; 2867 2868 const NameToId constNameToIdMapping[] = 2869 { 2870 { OUString("glow"), FSNS( XML_w14, XML_glow ) }, 2871 { OUString("shadow"), FSNS( XML_w14, XML_shadow ) }, 2872 { OUString("reflection"), FSNS( XML_w14, XML_reflection ) }, 2873 { OUString("textOutline"), FSNS( XML_w14, XML_textOutline ) }, 2874 { OUString("textFill"), FSNS( XML_w14, XML_textFill ) }, 2875 { OUString("scene3d"), FSNS( XML_w14, XML_scene3d ) }, 2876 { OUString("props3d"), FSNS( XML_w14, XML_props3d ) }, 2877 { OUString("ligatures"), FSNS( XML_w14, XML_ligatures ) }, 2878 { OUString("numForm"), FSNS( XML_w14, XML_numForm ) }, 2879 { OUString("numSpacing"), FSNS( XML_w14, XML_numSpacing ) }, 2880 { OUString("stylisticSets"),FSNS( XML_w14, XML_stylisticSets ) }, 2881 { OUString("cntxtAlts"), FSNS( XML_w14, XML_cntxtAlts ) }, 2882 2883 { OUString("val"), FSNS( XML_w14, XML_val ) }, 2884 { OUString("rad"), FSNS( XML_w14, XML_rad ) }, 2885 { OUString("blurRad"), FSNS( XML_w14, XML_blurRad ) }, 2886 { OUString("stA"), FSNS( XML_w14, XML_stA ) }, 2887 { OUString("stPos"), FSNS( XML_w14, XML_stPos ) }, 2888 { OUString("endA"), FSNS( XML_w14, XML_endA ) }, 2889 { OUString("endPos"), FSNS( XML_w14, XML_endPos ) }, 2890 { OUString("dist"), FSNS( XML_w14, XML_dist ) }, 2891 { OUString("dir"), FSNS( XML_w14, XML_dir ) }, 2892 { OUString("fadeDir"), FSNS( XML_w14, XML_fadeDir ) }, 2893 { OUString("sx"), FSNS( XML_w14, XML_sx ) }, 2894 { OUString("sy"), FSNS( XML_w14, XML_sy ) }, 2895 { OUString("kx"), FSNS( XML_w14, XML_kx ) }, 2896 { OUString("ky"), FSNS( XML_w14, XML_ky ) }, 2897 { OUString("algn"), FSNS( XML_w14, XML_algn ) }, 2898 { OUString("w"), FSNS( XML_w14, XML_w ) }, 2899 { OUString("cap"), FSNS( XML_w14, XML_cap ) }, 2900 { OUString("cmpd"), FSNS( XML_w14, XML_cmpd ) }, 2901 { OUString("pos"), FSNS( XML_w14, XML_pos ) }, 2902 { OUString("ang"), FSNS( XML_w14, XML_ang ) }, 2903 { OUString("scaled"), FSNS( XML_w14, XML_scaled ) }, 2904 { OUString("path"), FSNS( XML_w14, XML_path ) }, 2905 { OUString("l"), FSNS( XML_w14, XML_l ) }, 2906 { OUString("t"), FSNS( XML_w14, XML_t ) }, 2907 { OUString("r"), FSNS( XML_w14, XML_r ) }, 2908 { OUString("b"), FSNS( XML_w14, XML_b ) }, 2909 { OUString("lim"), FSNS( XML_w14, XML_lim ) }, 2910 { OUString("prst"), FSNS( XML_w14, XML_prst ) }, 2911 { OUString("rig"), FSNS( XML_w14, XML_rig ) }, 2912 { OUString("lat"), FSNS( XML_w14, XML_lat ) }, 2913 { OUString("lon"), FSNS( XML_w14, XML_lon ) }, 2914 { OUString("rev"), FSNS( XML_w14, XML_rev ) }, 2915 { OUString("h"), FSNS( XML_w14, XML_h ) }, 2916 { OUString("extrusionH"), FSNS( XML_w14, XML_extrusionH ) }, 2917 { OUString("contourW"), FSNS( XML_w14, XML_contourW ) }, 2918 { OUString("prstMaterial"), FSNS( XML_w14, XML_prstMaterial ) }, 2919 { OUString("id"), FSNS( XML_w14, XML_id ) }, 2920 2921 { OUString("schemeClr"), FSNS( XML_w14, XML_schemeClr ) }, 2922 { OUString("srgbClr"), FSNS( XML_w14, XML_srgbClr ) }, 2923 { OUString("tint"), FSNS( XML_w14, XML_tint ) }, 2924 { OUString("shade"), FSNS( XML_w14, XML_shade ) }, 2925 { OUString("alpha"), FSNS( XML_w14, XML_alpha ) }, 2926 { OUString("hueMod"), FSNS( XML_w14, XML_hueMod ) }, 2927 { OUString("sat"), FSNS( XML_w14, XML_sat ) }, 2928 { OUString("satOff"), FSNS( XML_w14, XML_satOff ) }, 2929 { OUString("satMod"), FSNS( XML_w14, XML_satMod ) }, 2930 { OUString("lum"), FSNS( XML_w14, XML_lum ) }, 2931 { OUString("lumOff"), FSNS( XML_w14, XML_lumOff ) }, 2932 { OUString("lumMod"), FSNS( XML_w14, XML_lumMod ) }, 2933 { OUString("noFill"), FSNS( XML_w14, XML_noFill ) }, 2934 { OUString("solidFill"), FSNS( XML_w14, XML_solidFill ) }, 2935 { OUString("gradFill"), FSNS( XML_w14, XML_gradFill ) }, 2936 { OUString("gsLst"), FSNS( XML_w14, XML_gsLst ) }, 2937 { OUString("gs"), FSNS( XML_w14, XML_gs ) }, 2938 { OUString("pos"), FSNS( XML_w14, XML_pos ) }, 2939 { OUString("lin"), FSNS( XML_w14, XML_lin ) }, 2940 { OUString("path"), FSNS( XML_w14, XML_path ) }, 2941 { OUString("fillToRect"), FSNS( XML_w14, XML_fillToRect ) }, 2942 { OUString("prstDash"), FSNS( XML_w14, XML_prstDash ) }, 2943 { OUString("round"), FSNS( XML_w14, XML_round ) }, 2944 { OUString("bevel"), FSNS( XML_w14, XML_bevel ) }, 2945 { OUString("miter"), FSNS( XML_w14, XML_miter ) }, 2946 { OUString("camera"), FSNS( XML_w14, XML_camera ) }, 2947 { OUString("lightRig"), FSNS( XML_w14, XML_lightRig ) }, 2948 { OUString("rot"), FSNS( XML_w14, XML_rot ) }, 2949 { OUString("bevelT"), FSNS( XML_w14, XML_bevelT ) }, 2950 { OUString("bevelB"), FSNS( XML_w14, XML_bevelB ) }, 2951 { OUString("extrusionClr"), FSNS( XML_w14, XML_extrusionClr ) }, 2952 { OUString("contourClr"), FSNS( XML_w14, XML_contourClr ) }, 2953 { OUString("styleSet"), FSNS( XML_w14, XML_styleSet ) }, 2954 }; 2955 2956 std::optional<sal_Int32> lclGetElementIdForName(std::u16string_view rName) 2957 { 2958 for (auto const & i : constNameToIdMapping) 2959 { 2960 if (rName == i.maName) 2961 { 2962 return i.maId; 2963 } 2964 } 2965 return std::optional<sal_Int32>(); 2966 } 2967 2968 void lclProcessRecursiveGrabBag(sal_Int32 aElementId, const css::uno::Sequence<css::beans::PropertyValue>& rElements, sax_fastparser::FSHelperPtr const & pSerializer) 2969 { 2970 css::uno::Sequence<css::beans::PropertyValue> aAttributes; 2971 rtl::Reference<FastAttributeList> pAttributes = FastSerializerHelper::createAttrList(); 2972 2973 for (const auto& rElement : rElements) 2974 { 2975 if (rElement.Name == "attributes") 2976 { 2977 rElement.Value >>= aAttributes; 2978 } 2979 } 2980 2981 for (const auto& rAttribute : std::as_const(aAttributes)) 2982 { 2983 uno::Any aAny = rAttribute.Value; 2984 OString aValue; 2985 2986 if(aAny.getValueType() == cppu::UnoType<sal_Int32>::get()) 2987 { 2988 aValue = OString::number(aAny.get<sal_Int32>()); 2989 } 2990 else if(aAny.getValueType() == cppu::UnoType<OUString>::get()) 2991 { 2992 aValue = OUStringToOString(aAny.get<OUString>(), RTL_TEXTENCODING_ASCII_US); 2993 } 2994 2995 std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rAttribute.Name); 2996 if(aSubElementId) 2997 pAttributes->add(*aSubElementId, aValue); 2998 } 2999 3000 pSerializer->startElement(aElementId, pAttributes); 3001 3002 for (const auto& rElement : rElements) 3003 { 3004 css::uno::Sequence<css::beans::PropertyValue> aSumElements; 3005 3006 std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rElement.Name); 3007 if(aSubElementId) 3008 { 3009 rElement.Value >>= aSumElements; 3010 lclProcessRecursiveGrabBag(*aSubElementId, aSumElements, pSerializer); 3011 } 3012 } 3013 3014 pSerializer->endElement(aElementId); 3015 } 3016 3017 } 3018 3019 void DocxAttributeOutput::WriteCollectedRunProperties() 3020 { 3021 // Write all differed properties 3022 if ( m_pFontsAttrList.is() ) 3023 { 3024 rtl::Reference<FastAttributeList> xAttrList = std::move( m_pFontsAttrList ); 3025 m_pSerializer->singleElementNS( XML_w, XML_rFonts, xAttrList ); 3026 } 3027 3028 if ( m_pColorAttrList.is() ) 3029 { 3030 rtl::Reference<FastAttributeList> xAttrList( m_pColorAttrList ); 3031 3032 m_pSerializer->singleElementNS( XML_w, XML_color, xAttrList ); 3033 } 3034 3035 if ( m_pEastAsianLayoutAttrList.is() ) 3036 { 3037 rtl::Reference<FastAttributeList> xAttrList = std::move( m_pEastAsianLayoutAttrList ); 3038 m_pSerializer->singleElementNS( XML_w, XML_eastAsianLayout, xAttrList ); 3039 } 3040 3041 if ( m_pCharLangAttrList.is() ) 3042 { 3043 rtl::Reference<FastAttributeList> xAttrList = std::move( m_pCharLangAttrList ); 3044 m_pSerializer->singleElementNS( XML_w, XML_lang, xAttrList ); 3045 } 3046 3047 if (m_nCharTransparence != 0 && m_pColorAttrList && m_aTextEffectsGrabBag.empty()) 3048 { 3049 const char* pVal = nullptr; 3050 m_pColorAttrList->getAsChar(FSNS(XML_w, XML_val), pVal); 3051 if (std::string_view("auto") != pVal) 3052 { 3053 m_pSerializer->startElementNS(XML_w14, XML_textFill); 3054 m_pSerializer->startElementNS(XML_w14, XML_solidFill); 3055 m_pSerializer->startElementNS(XML_w14, XML_srgbClr, FSNS(XML_w14, XML_val), pVal); 3056 sal_Int32 nTransparence = m_nCharTransparence * oox::drawingml::MAX_PERCENT / 255.0; 3057 m_pSerializer->singleElementNS(XML_w14, XML_alpha, FSNS(XML_w14, XML_val), OString::number(nTransparence)); 3058 m_pSerializer->endElementNS(XML_w14, XML_srgbClr); 3059 m_pSerializer->endElementNS(XML_w14, XML_solidFill); 3060 m_pSerializer->endElementNS(XML_w14, XML_textFill); 3061 m_nCharTransparence = 0; 3062 } 3063 } 3064 m_pColorAttrList.clear(); 3065 for (const beans::PropertyValue & i : m_aTextEffectsGrabBag) 3066 { 3067 std::optional<sal_Int32> aElementId = lclGetElementIdForName(i.Name); 3068 if(aElementId) 3069 { 3070 uno::Sequence<beans::PropertyValue> aGrabBagSeq; 3071 i.Value >>= aGrabBagSeq; 3072 lclProcessRecursiveGrabBag(*aElementId, aGrabBagSeq, m_pSerializer); 3073 } 3074 } 3075 m_aTextEffectsGrabBag.clear(); 3076 } 3077 3078 void DocxAttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) 3079 { 3080 // Call the 'Redline' function. This will add redline (change-tracking) information that regards to run properties. 3081 // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc. 3082 3083 // If there is RedlineData present, call WriteCollectedRunProperties() for writing rPr before calling Redline(). 3084 // As there will be another rPr for redline and LO might mix both. 3085 if(pRedlineData) 3086 WriteCollectedRunProperties(); 3087 Redline( pRedlineData ); 3088 3089 WriteCollectedRunProperties(); 3090 3091 // Merge the marks for the ordered elements 3092 m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties); 3093 3094 m_pSerializer->endElementNS( XML_w, XML_rPr ); 3095 3096 // write footnotes/endnotes if we have any 3097 FootnoteEndnoteReference(); 3098 3099 // merge the properties _before_ the run text (strictly speaking, just 3100 // after the start of the run) 3101 m_pSerializer->mergeTopMarks(Tag_StartRunProperties, sax_fastparser::MergeMarks::PREPEND); 3102 3103 WritePostponedGraphic(); 3104 3105 WritePostponedDiagram(); 3106 //We need to write w:drawing tag after the w:rPr. 3107 WritePostponedChart(); 3108 3109 //We need to write w:pict tag after the w:rPr. 3110 WritePostponedDMLDrawing(); 3111 3112 WritePostponedOLE(); 3113 3114 WritePostponedActiveXControl(true); 3115 } 3116 3117 void DocxAttributeOutput::GetSdtEndBefore(const SdrObject* pSdrObj) 3118 { 3119 if (!pSdrObj) 3120 return; 3121 3122 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObj)->getUnoShape()); 3123 uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); 3124 if( !xPropSet.is() ) 3125 return; 3126 3127 uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); 3128 uno::Sequence< beans::PropertyValue > aGrabBag; 3129 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag")) 3130 { 3131 xPropSet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag; 3132 } 3133 else if(xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag")) 3134 { 3135 xPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag; 3136 } 3137 3138 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag), 3139 [this](const beans::PropertyValue& rProp) { 3140 return "SdtEndBefore" == rProp.Name && m_aRunSdt.m_bStartedSdt && !m_bEndCharSdt; }); 3141 if (pProp != std::cend(aGrabBag)) 3142 pProp->Value >>= m_bEndCharSdt; 3143 } 3144 3145 void DocxAttributeOutput::WritePostponedGraphic() 3146 { 3147 for (const auto & rPostponedDiagram : *m_pPostponedGraphic) 3148 FlyFrameGraphic(rPostponedDiagram.grfNode, rPostponedDiagram.size, 3149 nullptr, nullptr, 3150 rPostponedDiagram.pSdrObj); 3151 m_pPostponedGraphic.reset(); 3152 } 3153 3154 void DocxAttributeOutput::WritePostponedDiagram() 3155 { 3156 for( const auto & rPostponedDiagram : *m_pPostponedDiagrams ) 3157 m_rExport.SdrExporter().writeDiagram(rPostponedDiagram.object, 3158 *rPostponedDiagram.frame, m_anchorId++); 3159 m_pPostponedDiagrams.reset(); 3160 } 3161 3162 bool DocxAttributeOutput::FootnoteEndnoteRefTag() 3163 { 3164 if( m_footnoteEndnoteRefTag == 0 ) 3165 return false; 3166 3167 // output the character style for MS Word's benefit 3168 const SwEndNoteInfo& rInfo = m_footnoteEndnoteRefTag == XML_footnoteRef ? 3169 m_rExport.m_rDoc.GetFootnoteInfo() : m_rExport.m_rDoc.GetEndNoteInfo(); 3170 const SwCharFormat* pCharFormat = rInfo.GetCharFormat( m_rExport.m_rDoc ); 3171 if ( pCharFormat ) 3172 { 3173 const OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat))); 3174 m_pSerializer->startElementNS(XML_w, XML_rPr); 3175 m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); 3176 m_pSerializer->endElementNS( XML_w, XML_rPr ); 3177 } 3178 3179 if (m_footnoteCustomLabel.isEmpty()) 3180 m_pSerializer->singleElementNS(XML_w, m_footnoteEndnoteRefTag); 3181 else 3182 RunText(m_footnoteCustomLabel); 3183 m_footnoteEndnoteRefTag = 0; 3184 return true; 3185 } 3186 3187 /** Output sal_Unicode* as a run text (<t>the text</t>). 3188 3189 When bMove is true, update rBegin to point _after_ the end of the text + 3190 1, meaning that it skips one character after the text. This is to make 3191 the switch in DocxAttributeOutput::RunText() nicer ;-) 3192 */ 3193 static bool impl_WriteRunText( FSHelperPtr const & pSerializer, sal_Int32 nTextToken, 3194 const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true ) 3195 { 3196 const sal_Unicode *pBegin = rBegin; 3197 3198 // skip one character after the end 3199 if ( bMove ) 3200 rBegin = pEnd + 1; 3201 3202 if ( pBegin >= pEnd ) 3203 return false; // we want to write at least one character 3204 3205 // we have to add 'preserve' when starting/ending with space 3206 if ( *pBegin == ' ' || *( pEnd - 1 ) == ' ' ) 3207 { 3208 pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve"); 3209 } 3210 else 3211 pSerializer->startElementNS(XML_w, nTextToken); 3212 3213 pSerializer->writeEscaped( std::u16string_view( pBegin, pEnd - pBegin ) ); 3214 3215 pSerializer->endElementNS( XML_w, nTextToken ); 3216 3217 return true; 3218 } 3219 3220 void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCharSet*/ ) 3221 { 3222 if( m_closeHyperlinkInThisRun ) 3223 { 3224 m_closeHyperlinkInPreviousRun = true; 3225 } 3226 m_bRunTextIsOn = true; 3227 // one text can be split into more <w:t>blah</w:t>'s by line breaks etc. 3228 const sal_Unicode *pBegin = rText.getStr(); 3229 const sal_Unicode *pEnd = pBegin + rText.getLength(); 3230 3231 // the text run is usually XML_t, with the exception of the deleted (and not moved) text 3232 sal_Int32 nTextToken = XML_t; 3233 if ( m_pRedlineData && !m_pRedlineData->IsMoved() && 3234 m_pRedlineData->GetType() == RedlineType::Delete ) 3235 { 3236 nTextToken = XML_delText; 3237 } 3238 3239 sal_Unicode prevUnicode = *pBegin; 3240 3241 for ( const sal_Unicode *pIt = pBegin; pIt < pEnd; ++pIt ) 3242 { 3243 switch ( *pIt ) 3244 { 3245 case 0x09: // tab 3246 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ); 3247 m_pSerializer->singleElementNS(XML_w, XML_tab); 3248 prevUnicode = *pIt; 3249 break; 3250 case 0x0b: // line break 3251 { 3252 if (impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ) || prevUnicode < 0x0020) 3253 { 3254 m_pSerializer->singleElementNS(XML_w, XML_br); 3255 prevUnicode = *pIt; 3256 } 3257 } 3258 break; 3259 case 0x1E: //non-breaking hyphen 3260 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ); 3261 m_pSerializer->singleElementNS(XML_w, XML_noBreakHyphen); 3262 prevUnicode = *pIt; 3263 break; 3264 case 0x1F: //soft (on demand) hyphen 3265 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ); 3266 m_pSerializer->singleElementNS(XML_w, XML_softHyphen); 3267 prevUnicode = *pIt; 3268 break; 3269 default: 3270 if ( *pIt < 0x0020 ) // filter out the control codes 3271 { 3272 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ); 3273 SAL_INFO("sw.ww8", "Ignored control code in a text run: " << unsigned(*pIt) ); 3274 } 3275 prevUnicode = *pIt; 3276 break; 3277 } 3278 } 3279 3280 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false ); 3281 } 3282 3283 void DocxAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding /*eCharSet*/) 3284 { 3285 m_sRawText = rText; 3286 } 3287 3288 void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby ) 3289 { 3290 WW8Ruby aWW8Ruby( rNode, rRuby, GetExport() ); 3291 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRuby( const SwTextNode& rNode, const SwFormatRuby& rRuby )" ); 3292 EndRun( &rNode, nPos ); // end run before starting ruby to avoid nested runs, and overlap 3293 assert(!m_closeHyperlinkInThisRun); // check that no hyperlink overlaps ruby 3294 assert(!m_closeHyperlinkInPreviousRun); 3295 m_pSerializer->startElementNS(XML_w, XML_r); 3296 m_pSerializer->startElementNS(XML_w, XML_ruby); 3297 m_pSerializer->startElementNS(XML_w, XML_rubyPr); 3298 3299 m_pSerializer->singleElementNS( XML_w, XML_rubyAlign, 3300 FSNS( XML_w, XML_val ), lclConvertWW8JCToOOXMLRubyAlign(aWW8Ruby.GetJC()) ); 3301 sal_uInt32 nHps = (aWW8Ruby.GetRubyHeight() + 5) / 10; 3302 sal_uInt32 nHpsBaseText = (aWW8Ruby.GetBaseHeight() + 5) / 10; 3303 m_pSerializer->singleElementNS(XML_w, XML_hps, FSNS(XML_w, XML_val), OString::number(nHps)); 3304 3305 m_pSerializer->singleElementNS( XML_w, XML_hpsRaise, 3306 FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) ); 3307 3308 m_pSerializer->singleElementNS( XML_w, XML_hpsBaseText, 3309 FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) ); 3310 3311 lang::Locale aLocale( SwBreakIt::Get()->GetLocale( 3312 rNode.GetLang( nPos ) ) ); 3313 OUString sLang( LanguageTag::convertToBcp47( aLocale) ); 3314 m_pSerializer->singleElementNS(XML_w, XML_lid, FSNS(XML_w, XML_val), sLang); 3315 3316 m_pSerializer->endElementNS( XML_w, XML_rubyPr ); 3317 3318 m_pSerializer->startElementNS(XML_w, XML_rt); 3319 StartRun( nullptr, nPos ); 3320 StartRunProperties( ); 3321 3322 if (rRuby.GetTextRuby() && rRuby.GetTextRuby()->GetCharFormat()) 3323 { 3324 const SwCharFormat* pFormat = rRuby.GetTextRuby()->GetCharFormat(); 3325 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(rRuby.GetText(), 0); 3326 sal_uInt16 nWhichFont = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONT : RES_CHRATR_CJK_FONT; 3327 sal_uInt16 nWhichFontSize = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONTSIZE : RES_CHRATR_CJK_FONTSIZE; 3328 3329 CharFont(ItemGet<SvxFontItem>(*pFormat, nWhichFont)); 3330 CharFontSize(ItemGet<SvxFontHeightItem>(*pFormat, nWhichFontSize)); 3331 CharFontSize(ItemGet<SvxFontHeightItem>(*pFormat, RES_CHRATR_CTL_FONTSIZE)); 3332 } 3333 3334 EndRunProperties( nullptr ); 3335 RunText( rRuby.GetText( ) ); 3336 EndRun( &rNode, nPos ); 3337 m_pSerializer->endElementNS( XML_w, XML_rt ); 3338 3339 m_pSerializer->startElementNS(XML_w, XML_rubyBase); 3340 StartRun( nullptr, nPos ); 3341 } 3342 3343 void DocxAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos) 3344 { 3345 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRuby()" ); 3346 EndRun( &rNode, nPos ); 3347 m_pSerializer->endElementNS( XML_w, XML_rubyBase ); 3348 m_pSerializer->endElementNS( XML_w, XML_ruby ); 3349 m_pSerializer->endElementNS( XML_w, XML_r ); 3350 StartRun(nullptr, nPos); // open Run again so OutputTextNode loop can close it 3351 } 3352 3353 bool DocxAttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) 3354 { 3355 bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark ); 3356 3357 if ( !pMark->isEmpty() ) 3358 { 3359 OUString sURL = *pLinkURL; 3360 3361 if ( bBookMarkOnly ) 3362 sURL = FieldString( ww::eHYPERLINK ); 3363 else 3364 sURL = FieldString( ww::eHYPERLINK ) + "\"" + sURL + "\""; 3365 3366 sURL += " \\l \"" + *pMark + "\""; 3367 3368 if ( !rTarget.isEmpty() ) 3369 sURL += " \\n " + rTarget; 3370 3371 *pLinkURL = sURL; 3372 } 3373 3374 return bBookMarkOnly; 3375 } 3376 3377 void DocxAttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) 3378 { 3379 m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName)); 3380 m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName)); 3381 } 3382 3383 bool DocxAttributeOutput::StartURL( const OUString& rUrl, const OUString& rTarget ) 3384 { 3385 OUString sMark; 3386 OUString sUrl; 3387 3388 bool bBookmarkOnly = AnalyzeURL( rUrl, rTarget, &sUrl, &sMark ); 3389 3390 m_hyperLinkAnchor = sMark; 3391 3392 if ( !sMark.isEmpty() && !bBookmarkOnly ) 3393 { 3394 m_rExport.OutputField( nullptr, ww::eHYPERLINK, sUrl ); 3395 } 3396 else 3397 { 3398 // Output a hyperlink XML element 3399 m_pHyperlinkAttrList = FastSerializerHelper::createAttrList(); 3400 3401 if ( !bBookmarkOnly ) 3402 { 3403 OString sId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(), 3404 oox::getRelationship(Relationship::HYPERLINK), 3405 sUrl, true ), RTL_TEXTENCODING_UTF8 ); 3406 3407 m_pHyperlinkAttrList->add(FSNS(XML_r, XML_id), sId); 3408 } 3409 else 3410 { 3411 // Is this a link to a sequence? Then try to replace that with a 3412 // normal bookmark, as Word won't understand our special 3413 // <seqname>!<index>|sequence syntax. 3414 if (sMark.endsWith("|sequence")) 3415 { 3416 sal_Int32 nPos = sMark.indexOf('!'); 3417 if (nPos != -1) 3418 { 3419 // Extract <seqname>, the field instruction text has the name quoted. 3420 OUString aSequenceName = sMark.copy(0, nPos); 3421 // Extract <index>. 3422 sal_uInt32 nIndex = sMark.copy(nPos + 1, sMark.getLength() - nPos - sizeof("|sequence")).toUInt32(); 3423 std::map<OUString, std::vector<OString> >::iterator it = m_aSeqBookmarksNames.find(aSequenceName); 3424 if (it != m_aSeqBookmarksNames.end()) 3425 { 3426 std::vector<OString>& rNames = it->second; 3427 if (rNames.size() > nIndex) 3428 // We know the bookmark name for this sequence and this index, do the replacement. 3429 sMark = OStringToOUString(rNames[nIndex], RTL_TEXTENCODING_UTF8); 3430 } 3431 } 3432 } 3433 else if (sMark.endsWith("|toxmark")) 3434 { 3435 if (auto const it = GetExport().m_TOXMarkBookmarksByURL.find(sMark); 3436 it != GetExport().m_TOXMarkBookmarksByURL.end()) 3437 { 3438 sMark = it->second; 3439 } 3440 } 3441 // Spaces are prohibited in bookmark name. 3442 sMark = sMark.replace(' ', '_'); 3443 m_pHyperlinkAttrList->add( FSNS( XML_w, XML_anchor ), 3444 OUStringToOString( sMark, RTL_TEXTENCODING_UTF8 ) ); 3445 } 3446 3447 if ( !rTarget.isEmpty() ) 3448 { 3449 OString soTarget = OUStringToOString( rTarget, RTL_TEXTENCODING_UTF8 ); 3450 m_pHyperlinkAttrList->add(FSNS(XML_w, XML_tgtFrame), soTarget); 3451 } 3452 } 3453 3454 return true; 3455 } 3456 3457 bool DocxAttributeOutput::EndURL(bool const) 3458 { 3459 m_closeHyperlinkInThisRun = true; 3460 if(m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc")) 3461 { 3462 m_endPageRef = true; 3463 } 3464 return true; 3465 } 3466 3467 void DocxAttributeOutput::FieldVanish(const OUString& rText, 3468 ww::eField const eType, OUString const*const pBookmarkName) 3469 { 3470 WriteField_Impl(nullptr, eType, rText, FieldFlags::All, pBookmarkName); 3471 } 3472 3473 // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that: 3474 // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node) 3475 // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node) 3476 void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData) 3477 { 3478 if ( !pRedlineData ) 3479 return; 3480 3481 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet( 3482 SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ); 3483 3484 OString aId( OString::number( pRedlineData->GetSeqNo() ) ); 3485 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) ); 3486 OString aDate( DateTimeToOString( bRemovePersonalInfo 3487 ? DateTime(Date( 1, 1, 1970 )) // Epoch time 3488 : pRedlineData->GetTimeStamp() ) ); 3489 3490 switch( pRedlineData->GetType() ) 3491 { 3492 case RedlineType::Insert: 3493 break; 3494 3495 case RedlineType::Delete: 3496 break; 3497 3498 case RedlineType::Format: 3499 m_pSerializer->startElementNS( XML_w, XML_rPrChange, 3500 FSNS( XML_w, XML_id ), aId, 3501 FSNS( XML_w, XML_author ), bRemovePersonalInfo 3502 ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) 3503 : rAuthor, 3504 FSNS( XML_w, XML_date ), aDate ); 3505 3506 // Check if there is any extra data stored in the redline object 3507 if (pRedlineData->GetExtraData()) 3508 { 3509 const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData(); 3510 const SwRedlineExtraData_FormatColl* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData); 3511 3512 // Check if the extra data is of type 'formatting changes' 3513 if (pFormattingChanges) 3514 { 3515 // Get the item set that holds all the changes properties 3516 const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet(); 3517 if (pChangesSet) 3518 { 3519 m_pSerializer->mark(Tag_Redline_1); 3520 3521 m_pSerializer->startElementNS(XML_w, XML_rPr); 3522 3523 // Output the redline item set 3524 if (pChangesSet) 3525 m_rExport.OutputItemSet( *pChangesSet, false, true, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF ); 3526 3527 m_pSerializer->endElementNS( XML_w, XML_rPr ); 3528 3529 m_pSerializer->mergeTopMarks(Tag_Redline_1, sax_fastparser::MergeMarks::PREPEND); 3530 } 3531 } 3532 } 3533 3534 m_pSerializer->endElementNS( XML_w, XML_rPrChange ); 3535 break; 3536 3537 case RedlineType::ParagraphFormat: 3538 m_pSerializer->startElementNS( XML_w, XML_pPrChange, 3539 FSNS( XML_w, XML_id ), aId, 3540 FSNS( XML_w, XML_author ), bRemovePersonalInfo 3541 ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) 3542 : rAuthor, 3543 FSNS( XML_w, XML_date ), aDate ); 3544 3545 // Check if there is any extra data stored in the redline object 3546 if (pRedlineData->GetExtraData()) 3547 { 3548 const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData(); 3549 const SwRedlineExtraData_FormatColl* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData); 3550 3551 // Check if the extra data is of type 'formatting changes' 3552 if (pFormattingChanges) 3553 { 3554 // Get the item set that holds all the changes properties 3555 const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet(); 3556 const OUString & sParaStyleName = pFormattingChanges->GetFormatName(); 3557 if (pChangesSet || !sParaStyleName.isEmpty()) 3558 { 3559 m_pSerializer->mark(Tag_Redline_2); 3560 3561 m_pSerializer->startElementNS(XML_w, XML_pPr); 3562 3563 OString sStyleName = MSWordStyles::CreateStyleId( sParaStyleName ); 3564 if ( !sStyleName.isEmpty() ) 3565 m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), sStyleName); 3566 3567 // The 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' are used to hold information 3568 // that should be collected by different properties in the core, and are all flushed together 3569 // to the DOCX when the function 'WriteCollectedParagraphProperties' gets called. 3570 // So we need to store the current status of these lists, so that we can revert back to them when 3571 // we are done exporting the redline attributes. 3572 rtl::Reference<sax_fastparser::FastAttributeList> pFlyAttrList_Original(m_rExport.SdrExporter().getFlyAttrList()); 3573 m_rExport.SdrExporter().getFlyAttrList().clear(); 3574 rtl::Reference<sax_fastparser::FastAttributeList> pParagraphSpacingAttrList_Original(m_pParagraphSpacingAttrList); 3575 m_pParagraphSpacingAttrList.clear(); 3576 3577 // Output the redline item set 3578 if (pChangesSet) 3579 m_rExport.OutputItemSet( *pChangesSet, true, false, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF ); 3580 3581 // Write the collected paragraph properties that are stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' 3582 WriteCollectedParagraphProperties(); 3583 3584 // Revert back the original values that were stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' 3585 m_rExport.SdrExporter().getFlyAttrList() = pFlyAttrList_Original; 3586 m_pParagraphSpacingAttrList = pParagraphSpacingAttrList_Original; 3587 3588 m_pSerializer->endElementNS( XML_w, XML_pPr ); 3589 3590 m_pSerializer->mergeTopMarks(Tag_Redline_2, sax_fastparser::MergeMarks::PREPEND); 3591 } 3592 } 3593 } 3594 m_pSerializer->endElementNS( XML_w, XML_pPrChange ); 3595 break; 3596 3597 default: 3598 SAL_WARN("sw.ww8", "Unhandled redline type for export " << SwRedlineTypeToOUString(pRedlineData->GetType())); 3599 break; 3600 } 3601 } 3602 3603 // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that: 3604 // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node) 3605 // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node) 3606 void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData ) 3607 { 3608 if ( !pRedlineData ) 3609 return; 3610 3611 // write out stack of this redline recursively (first the oldest) 3612 StartRedline( pRedlineData->Next() ); 3613 3614 OString aId( OString::number( m_nRedlineId++ ) ); 3615 3616 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet( 3617 SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ); 3618 3619 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) ); 3620 OString aAuthor( OUStringToOString( bRemovePersonalInfo 3621 ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) 3622 : rAuthor, RTL_TEXTENCODING_UTF8 ) ); 3623 3624 OString aDate( DateTimeToOString( bRemovePersonalInfo 3625 ? DateTime(Date( 1, 1, 1970 )) // Epoch time 3626 : pRedlineData->GetTimeStamp() ) ); 3627 3628 bool bMoved = pRedlineData->IsMoved(); 3629 switch ( pRedlineData->GetType() ) 3630 { 3631 case RedlineType::Insert: 3632 m_pSerializer->startElementNS( XML_w, bMoved ? XML_moveTo : XML_ins, 3633 FSNS( XML_w, XML_id ), aId, 3634 FSNS( XML_w, XML_author ), aAuthor, 3635 FSNS( XML_w, XML_date ), aDate ); 3636 break; 3637 3638 case RedlineType::Delete: 3639 m_pSerializer->startElementNS( XML_w, bMoved ? XML_moveFrom : XML_del, 3640 FSNS( XML_w, XML_id ), aId, 3641 FSNS( XML_w, XML_author ), aAuthor, 3642 FSNS( XML_w, XML_date ), aDate ); 3643 break; 3644 3645 case RedlineType::Format: 3646 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRedline()" ); 3647 break; 3648 default: 3649 break; 3650 } 3651 } 3652 3653 void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData ) 3654 { 3655 if ( !pRedlineData || m_bWritingField ) 3656 return; 3657 3658 bool bMoved = pRedlineData->IsMoved(); 3659 switch ( pRedlineData->GetType() ) 3660 { 3661 case RedlineType::Insert: 3662 m_pSerializer->endElementNS( XML_w, bMoved ? XML_moveTo : XML_ins ); 3663 break; 3664 3665 case RedlineType::Delete: 3666 m_pSerializer->endElementNS( XML_w, bMoved ? XML_moveFrom : XML_del ); 3667 break; 3668 3669 case RedlineType::Format: 3670 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRedline()" ); 3671 break; 3672 default: 3673 break; 3674 } 3675 3676 // write out stack of this redline recursively (first the newest) 3677 EndRedline( pRedlineData->Next() ); 3678 } 3679 3680 void DocxAttributeOutput::FormatDrop( const SwTextNode& /*rNode*/, const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t ) 3681 { 3682 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle )" ); 3683 } 3684 3685 void DocxAttributeOutput::ParagraphStyle( sal_uInt16 nStyle ) 3686 { 3687 OString aStyleId(m_rExport.m_pStyles->GetStyleId(nStyle)); 3688 3689 m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), aStyleId); 3690 } 3691 3692 static void impl_borderLine( FSHelperPtr const & pSerializer, sal_Int32 elementToken, const SvxBorderLine* pBorderLine, sal_uInt16 nDist, 3693 bool bWriteShadow, const table::BorderLine2* rStyleProps = nullptr ) 3694 { 3695 // Compute val attribute value 3696 // Can be one of: 3697 // single, double, 3698 // basicWideOutline, basicWideInline 3699 // OOXml also supports those types of borders, but we'll try to play with the first ones. 3700 // thickThinMediumGap, thickThinLargeGap, thickThinSmallGap 3701 // thinThickLargeGap, thinThickMediumGap, thinThickSmallGap 3702 const char* pVal = "nil"; 3703 if ( pBorderLine && !pBorderLine->isEmpty( ) ) 3704 { 3705 switch (pBorderLine->GetBorderLineStyle()) 3706 { 3707 case SvxBorderLineStyle::SOLID: 3708 pVal = "single"; 3709 break; 3710 case SvxBorderLineStyle::DOTTED: 3711 pVal = "dotted"; 3712 break; 3713 case SvxBorderLineStyle::DASHED: 3714 pVal = "dashed"; 3715 break; 3716 case SvxBorderLineStyle::DOUBLE: 3717 case SvxBorderLineStyle::DOUBLE_THIN: 3718 pVal = "double"; 3719 break; 3720 case SvxBorderLineStyle::THINTHICK_SMALLGAP: 3721 pVal = "thinThickSmallGap"; 3722 break; 3723 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: 3724 pVal = "thinThickMediumGap"; 3725 break; 3726 case SvxBorderLineStyle::THINTHICK_LARGEGAP: 3727 pVal = "thinThickLargeGap"; 3728 break; 3729 case SvxBorderLineStyle::THICKTHIN_SMALLGAP: 3730 pVal = "thickThinSmallGap"; 3731 break; 3732 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: 3733 pVal = "thickThinMediumGap"; 3734 break; 3735 case SvxBorderLineStyle::THICKTHIN_LARGEGAP: 3736 pVal = "thickThinLargeGap"; 3737 break; 3738 case SvxBorderLineStyle::EMBOSSED: 3739 pVal = "threeDEmboss"; 3740 break; 3741 case SvxBorderLineStyle::ENGRAVED: 3742 pVal = "threeDEngrave"; 3743 break; 3744 case SvxBorderLineStyle::OUTSET: 3745 pVal = "outset"; 3746 break; 3747 case SvxBorderLineStyle::INSET: 3748 pVal = "inset"; 3749 break; 3750 case SvxBorderLineStyle::FINE_DASHED: 3751 pVal = "dashSmallGap"; 3752 break; 3753 case SvxBorderLineStyle::DASH_DOT: 3754 pVal = "dotDash"; 3755 break; 3756 case SvxBorderLineStyle::DASH_DOT_DOT: 3757 pVal = "dotDotDash"; 3758 break; 3759 case SvxBorderLineStyle::NONE: 3760 default: 3761 break; 3762 } 3763 } 3764 else if ( !rStyleProps || !rStyleProps->LineWidth ) 3765 // no line, and no line set by the style either: 3766 // there is no need to write the property 3767 return; 3768 3769 // compare the properties with the theme properties before writing them: 3770 // if they are equal, it means that they were style-defined and there is 3771 // no need to write them. 3772 if( rStyleProps != nullptr && pBorderLine && !pBorderLine->isEmpty() && 3773 pBorderLine->GetBorderLineStyle() == static_cast<SvxBorderLineStyle>(rStyleProps->LineStyle) && 3774 pBorderLine->GetColor() == Color(ColorTransparency, rStyleProps->Color) && 3775 pBorderLine->GetWidth() == o3tl::toTwips(rStyleProps->LineWidth, o3tl::Length::mm100) ) 3776 return; 3777 3778 rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList(); 3779 pAttr->add( FSNS( XML_w, XML_val ), OString( pVal ) ); 3780 3781 if ( pBorderLine && !pBorderLine->isEmpty() ) 3782 { 3783 // Compute the sz attribute 3784 3785 double const fConverted( ::editeng::ConvertBorderWidthToWord( 3786 pBorderLine->GetBorderLineStyle(), pBorderLine->GetWidth())); 3787 // The unit is the 8th of point 3788 sal_Int32 nWidth = sal_Int32( fConverted / 2.5 ); 3789 const sal_Int32 nMinWidth = 2; 3790 const sal_Int32 nMaxWidth = 96; 3791 3792 if ( nWidth > nMaxWidth ) 3793 nWidth = nMaxWidth; 3794 else if ( nWidth < nMinWidth ) 3795 nWidth = nMinWidth; 3796 3797 pAttr->add( FSNS( XML_w, XML_sz ), OString::number( nWidth ) ); 3798 3799 // Get the distance (in pt) 3800 pAttr->add(FSNS(XML_w, XML_space), OString::number(rtl::math::round(nDist / 20.0))); 3801 3802 // Get the color code as an RRGGBB hex value 3803 OString sColor( msfilter::util::ConvertColor( pBorderLine->GetColor( ) ) ); 3804 pAttr->add( FSNS( XML_w, XML_color ), sColor ); 3805 } 3806 3807 if (bWriteShadow) 3808 { 3809 // Set the shadow value 3810 pAttr->add( FSNS( XML_w, XML_shadow ), "1" ); 3811 } 3812 3813 pSerializer->singleElementNS( XML_w, elementToken, pAttr ); 3814 } 3815 3816 static OutputBorderOptions lcl_getTableCellBorderOptions(bool bEcma) 3817 { 3818 OutputBorderOptions rOptions; 3819 3820 rOptions.tag = XML_tcBorders; 3821 rOptions.bUseStartEnd = !bEcma; 3822 rOptions.bWriteTag = true; 3823 rOptions.bWriteDistance = false; 3824 3825 return rOptions; 3826 } 3827 3828 static OutputBorderOptions lcl_getBoxBorderOptions() 3829 { 3830 OutputBorderOptions rOptions; 3831 3832 rOptions.tag = XML_pBdr; 3833 rOptions.bUseStartEnd = false; 3834 rOptions.bWriteTag = false; 3835 rOptions.bWriteDistance = true; 3836 3837 return rOptions; 3838 } 3839 3840 static void impl_borders( FSHelperPtr const & pSerializer, 3841 const SvxBoxItem& rBox, 3842 const OutputBorderOptions& rOptions, 3843 std::map<SvxBoxItemLine, 3844 css::table::BorderLine2> &rTableStyleConf ) 3845 { 3846 static const SvxBoxItemLine aBorders[] = 3847 { 3848 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT 3849 }; 3850 3851 const sal_Int32 aXmlElements[] = 3852 { 3853 XML_top, 3854 rOptions.bUseStartEnd ? XML_start : XML_left, 3855 XML_bottom, 3856 rOptions.bUseStartEnd ? XML_end : XML_right 3857 }; 3858 bool tagWritten = false; 3859 const SvxBoxItemLine* pBrd = aBorders; 3860 3861 for( int i = 0; i < 4; ++i, ++pBrd ) 3862 { 3863 const SvxBorderLine* pLn = rBox.GetLine( *pBrd ); 3864 const table::BorderLine2 *aStyleProps = nullptr; 3865 if( rTableStyleConf.find( *pBrd ) != rTableStyleConf.end() ) 3866 aStyleProps = &rTableStyleConf[ *pBrd ]; 3867 3868 if (!tagWritten && rOptions.bWriteTag) 3869 { 3870 pSerializer->startElementNS(XML_w, rOptions.tag); 3871 tagWritten = true; 3872 } 3873 3874 bool bWriteShadow = false; 3875 if (rOptions.aShadowLocation == SvxShadowLocation::NONE) 3876 { 3877 // The border has no shadow 3878 } 3879 else if (rOptions.aShadowLocation == SvxShadowLocation::BottomRight) 3880 { 3881 // Special case of 'Bottom-Right' shadow: 3882 // If the shadow location is 'Bottom-Right' - then turn on the shadow 3883 // for ALL the sides. This is because in Word - if you select a shadow 3884 // for a border - it turn on the shadow for ALL the sides (but shows only 3885 // the bottom-right one). 3886 // This is so that no information will be lost if passed through LibreOffice 3887 bWriteShadow = true; 3888 } 3889 else 3890 { 3891 // If there is a shadow, and it's not the regular 'Bottom-Right', 3892 // then write only the 'shadowed' sides of the border 3893 if ( 3894 ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::TOP ) || 3895 ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::BottomLeft) && *pBrd == SvxBoxItemLine::LEFT ) || 3896 ((rOptions.aShadowLocation == SvxShadowLocation::BottomLeft ) && *pBrd == SvxBoxItemLine::BOTTOM) || 3897 ((rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::RIGHT ) 3898 ) 3899 { 3900 bWriteShadow = true; 3901 } 3902 } 3903 3904 sal_uInt16 nDist = 0; 3905 if (rOptions.bWriteDistance) 3906 { 3907 if (rOptions.pDistances) 3908 { 3909 if ( *pBrd == SvxBoxItemLine::TOP) 3910 nDist = rOptions.pDistances->nTop; 3911 else if ( *pBrd == SvxBoxItemLine::LEFT) 3912 nDist = rOptions.pDistances->nLeft; 3913 else if ( *pBrd == SvxBoxItemLine::BOTTOM) 3914 nDist = rOptions.pDistances->nBottom; 3915 else if ( *pBrd == SvxBoxItemLine::RIGHT) 3916 nDist = rOptions.pDistances->nRight; 3917 } 3918 else 3919 { 3920 nDist = rBox.GetDistance(*pBrd); 3921 } 3922 } 3923 3924 impl_borderLine( pSerializer, aXmlElements[i], pLn, nDist, bWriteShadow, aStyleProps ); 3925 } 3926 if (tagWritten && rOptions.bWriteTag) { 3927 pSerializer->endElementNS( XML_w, rOptions.tag ); 3928 } 3929 } 3930 3931 static void impl_cellMargins( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins = nullptr) 3932 { 3933 static const SvxBoxItemLine aBorders[] = 3934 { 3935 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT 3936 }; 3937 3938 const sal_Int32 aXmlElements[] = 3939 { 3940 XML_top, 3941 bUseStartEnd ? XML_start : XML_left, 3942 XML_bottom, 3943 bUseStartEnd ? XML_end : XML_right 3944 }; 3945 bool tagWritten = false; 3946 const SvxBoxItemLine* pBrd = aBorders; 3947 for( int i = 0; i < 4; ++i, ++pBrd ) 3948 { 3949 sal_Int32 nDist = sal_Int32( rBox.GetDistance( *pBrd ) ); 3950 3951 if (pDefaultMargins) 3952 { 3953 // Skip output if cell margin == table default margin 3954 if (sal_Int32( pDefaultMargins->GetDistance( *pBrd ) ) == nDist) 3955 continue; 3956 } 3957 3958 if (!tagWritten) { 3959 pSerializer->startElementNS(XML_w, tag); 3960 tagWritten = true; 3961 } 3962 pSerializer->singleElementNS( XML_w, aXmlElements[i], 3963 FSNS( XML_w, XML_w ), OString::number(nDist), 3964 FSNS( XML_w, XML_type ), "dxa" ); 3965 } 3966 if (tagWritten) { 3967 pSerializer->endElementNS( XML_w, tag ); 3968 } 3969 } 3970 3971 void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow ) 3972 { 3973 m_pSerializer->startElementNS(XML_w, XML_tcPr); 3974 3975 const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( ); 3976 3977 bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; 3978 3979 // Output any table cell redlines if there are any attached to this specific cell 3980 TableCellRedline( pTableTextNodeInfoInner ); 3981 3982 // Cell preferred width 3983 SwTwips nWidth = GetGridCols( pTableTextNodeInfoInner )->at( nCell ); 3984 if ( nCell ) 3985 nWidth = nWidth - GetGridCols( pTableTextNodeInfoInner )->at( nCell - 1 ); 3986 m_pSerializer->singleElementNS( XML_w, XML_tcW, 3987 FSNS( XML_w, XML_w ), OString::number(nWidth), 3988 FSNS( XML_w, XML_type ), "dxa" ); 3989 3990 // Horizontal spans 3991 const SwWriteTableRows& rRows = m_xTableWrt->GetRows( ); 3992 SwWriteTableRow *pRow = rRows[ nRow ].get(); 3993 const SwWriteTableCells& rTableCells = pRow->GetCells(); 3994 if (nCell < rTableCells.size() ) 3995 { 3996 const SwWriteTableCell& rCell = *rTableCells[nCell]; 3997 const sal_uInt16 nColSpan = rCell.GetColSpan(); 3998 if ( nColSpan > 1 ) 3999 m_pSerializer->singleElementNS( XML_w, XML_gridSpan, 4000 FSNS( XML_w, XML_val ), OString::number(nColSpan) ); 4001 } 4002 4003 // Vertical merges 4004 ww8::RowSpansPtr xRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow(); 4005 sal_Int32 vSpan = (*xRowSpans)[nCell]; 4006 if ( vSpan > 1 ) 4007 { 4008 m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "restart"); 4009 } 4010 else if ( vSpan < 0 ) 4011 { 4012 m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "continue"); 4013 } 4014 4015 if (const SfxGrabBagItem* pItem = pTableBox->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)) 4016 { 4017 const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag(); 4018 std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("CellCnfStyle"); 4019 if (it != rGrabBag.end()) 4020 { 4021 uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >(); 4022 m_pTableStyleExport->CnfStyle(aAttributes); 4023 } 4024 } 4025 4026 4027 const SvxBoxItem& rBox = pTableBox->GetFrameFormat( )->GetBox( ); 4028 const SvxBoxItem& rDefaultBox = (*tableFirstCells.rbegin())->getTableBox( )->GetFrameFormat( )->GetBox( ); 4029 { 4030 // The cell borders 4031 impl_borders(m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma), 4032 m_aTableStyleConfs.back()); 4033 } 4034 4035 TableBackgrounds( pTableTextNodeInfoInner ); 4036 4037 { 4038 // Cell margins 4039 impl_cellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox ); 4040 } 4041 4042 TableVerticalCell( pTableTextNodeInfoInner ); 4043 4044 m_pSerializer->endElementNS( XML_w, XML_tcPr ); 4045 } 4046 4047 void DocxAttributeOutput::InitTableHelper( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) 4048 { 4049 const SwTable* pTable = pTableTextNodeInfoInner->getTable(); 4050 if (m_xTableWrt && pTable == m_xTableWrt->GetTable()) 4051 return; 4052 4053 tools::Long nPageSize = 0; 4054 bool bRelBoxSize = false; 4055 4056 // Create the SwWriteTable instance to use col spans (and maybe other infos) 4057 GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize ); 4058 4059 const SwFrameFormat *pFormat = pTable->GetFrameFormat( ); 4060 const sal_uInt32 nTableSz = static_cast<sal_uInt32>(pFormat->GetFrameSize( ).GetWidth( )); 4061 4062 const SwHTMLTableLayout *pLayout = pTable->GetHTMLTableLayout(); 4063 if( pLayout && pLayout->IsExportable() ) 4064 m_xTableWrt.reset(new SwWriteTable(pTable, pLayout)); 4065 else 4066 m_xTableWrt.reset(new SwWriteTable(pTable, pTable->GetTabLines(), nPageSize, nTableSz, false)); 4067 } 4068 4069 void DocxAttributeOutput::StartTable( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) 4070 { 4071 m_aTableStyleConfs.push_back({}); 4072 4073 // In case any paragraph SDT's are open, close them here. 4074 EndParaSdtBlock(); 4075 4076 m_pSerializer->startElementNS(XML_w, XML_tbl); 4077 4078 tableFirstCells.push_back(pTableTextNodeInfoInner); 4079 lastOpenCell.push_back(-1); 4080 lastClosedCell.push_back(-1); 4081 4082 InitTableHelper( pTableTextNodeInfoInner ); 4083 TableDefinition( pTableTextNodeInfoInner ); 4084 } 4085 4086 void DocxAttributeOutput::EndTable() 4087 { 4088 m_pSerializer->endElementNS( XML_w, XML_tbl ); 4089 4090 if ( m_tableReference->m_nTableDepth > 0 ) 4091 --m_tableReference->m_nTableDepth; 4092 4093 lastClosedCell.pop_back(); 4094 lastOpenCell.pop_back(); 4095 tableFirstCells.pop_back(); 4096 4097 // We closed the table; if it is a nested table, the cell that contains it 4098 // still continues 4099 // set to true only if we were in a nested table, not otherwise. 4100 if( !tableFirstCells.empty() ) 4101 m_tableReference->m_bTableCellOpen = true; 4102 4103 // Cleans the table helper 4104 m_xTableWrt.reset(); 4105 4106 m_aTableStyleConfs.pop_back(); 4107 } 4108 4109 void DocxAttributeOutput::StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) 4110 { 4111 m_pSerializer->startElementNS(XML_w, XML_tr); 4112 4113 // Output the row properties 4114 m_pSerializer->startElementNS(XML_w, XML_trPr); 4115 4116 // Header row: tblHeader 4117 const SwTable *pTable = pTableTextNodeInfoInner->getTable( ); 4118 if ( pTable->GetRowsToRepeat( ) > pTableTextNodeInfoInner->getRow( ) ) 4119 m_pSerializer->singleElementNS(XML_w, XML_tblHeader, FSNS(XML_w, XML_val), "true"); // TODO to overwrite table style may need explicit false 4120 4121 TableRowRedline( pTableTextNodeInfoInner ); 4122 TableHeight( pTableTextNodeInfoInner ); 4123 TableCanSplit( pTableTextNodeInfoInner ); 4124 4125 const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox(); 4126 const SwTableLine* pTableLine = pTableBox->GetUpper(); 4127 if (const SfxGrabBagItem* pItem = pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)) 4128 { 4129 const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag(); 4130 std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("RowCnfStyle"); 4131 if (it != rGrabBag.end()) 4132 { 4133 uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >(); 4134 m_pTableStyleExport->CnfStyle(aAttributes); 4135 } 4136 } 4137 4138 4139 m_pSerializer->endElementNS( XML_w, XML_trPr ); 4140 } 4141 4142 void DocxAttributeOutput::EndTableRow( ) 4143 { 4144 m_pSerializer->endElementNS( XML_w, XML_tr ); 4145 lastOpenCell.back() = -1; 4146 lastClosedCell.back() = -1; 4147 } 4148 4149 void DocxAttributeOutput::StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow ) 4150 { 4151 lastOpenCell.back() = nCell; 4152 4153 InitTableHelper( pTableTextNodeInfoInner ); 4154 4155 m_pSerializer->startElementNS(XML_w, XML_tc); 4156 4157 // Write the cell properties here 4158 TableCellProperties( pTableTextNodeInfoInner, nCell, nRow ); 4159 4160 m_tableReference->m_bTableCellOpen = true; 4161 } 4162 4163 void DocxAttributeOutput::EndTableCell(sal_uInt32 nCell) 4164 { 4165 lastClosedCell.back() = nCell; 4166 lastOpenCell.back() = -1; 4167 4168 if (m_tableReference->m_bTableCellParaSdtOpen) 4169 EndParaSdtBlock(); 4170 4171 m_pSerializer->endElementNS( XML_w, XML_tc ); 4172 4173 m_tableReference->m_bTableCellOpen = false; 4174 m_tableReference->m_bTableCellParaSdtOpen = false; 4175 } 4176 4177 void DocxAttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) 4178 { 4179 } 4180 4181 void DocxAttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/ ) 4182 { 4183 } 4184 4185 namespace 4186 { 4187 4188 /// Does the same as comphelper::string::padToLength(), but extends the start, not the end. 4189 OString lcl_padStartToLength(OString const & aString, sal_Int32 nLen, char cFill) 4190 { 4191 if (nLen > aString.getLength()) 4192 { 4193 sal_Int32 nDiff = nLen - aString.getLength(); 4194 OStringBuffer aBuffer; 4195 comphelper::string::padToLength(aBuffer, nDiff, cFill); 4196 aBuffer.append(aString); 4197 return aBuffer.makeStringAndClear(); 4198 } 4199 else 4200 return aString; 4201 } 4202 4203 //Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx 4204 //Since this is not import code, "-1" needs to be handled as the mode that LO will save as. 4205 //To identify how your code should handle a "-1", look in DocxExport::WriteSettings(). 4206 sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& rDocExport) 4207 { 4208 sal_Int32 nWordCompatibilityMode = rDocExport.getWordCompatibilityModeFromGrabBag(); 4209 4210 // TODO: this is duplicated, better store it in DocxExport member? 4211 if (!rDocExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING)) 4212 { 4213 if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode) 4214 { 4215 nWordCompatibilityMode = 14; 4216 } 4217 } 4218 4219 return nWordCompatibilityMode; 4220 } 4221 4222 } 4223 4224 void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) 4225 { 4226 bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; 4227 4228 // Write the table properties 4229 m_pSerializer->startElementNS(XML_w, XML_tblPr); 4230 4231 static const sal_Int32 aOrder[] = 4232 { 4233 FSNS( XML_w, XML_tblStyle ), 4234 FSNS( XML_w, XML_tblpPr ), 4235 FSNS( XML_w, XML_tblOverlap ), 4236 FSNS( XML_w, XML_bidiVisual ), 4237 FSNS( XML_w, XML_tblStyleRowBandSize ), 4238 FSNS( XML_w, XML_tblStyleColBandSize ), 4239 FSNS( XML_w, XML_tblW ), 4240 FSNS( XML_w, XML_jc ), 4241 FSNS( XML_w, XML_tblCellSpacing ), 4242 FSNS( XML_w, XML_tblInd ), 4243 FSNS( XML_w, XML_tblBorders ), 4244 FSNS( XML_w, XML_shd ), 4245 FSNS( XML_w, XML_tblLayout ), 4246 FSNS( XML_w, XML_tblCellMar ), 4247 FSNS( XML_w, XML_tblLook ), 4248 FSNS( XML_w, XML_tblPrChange ) 4249 }; 4250 4251 // postpone the output so that we can later [] 4252 // prepend the properties before the run 4253 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence 4254 m_pSerializer->mark(Tag_TableDefinition, comphelper::containerToSequence(aOrder)); 4255 4256 tools::Long nPageSize = 0; 4257 const char* widthType = "dxa"; 4258 4259 // If actual width of table is relative it should export is as "pct".` 4260 const SwTable *pTable = pTableTextNodeInfoInner->getTable(); 4261 SwFrameFormat *pTableFormat = pTable->GetFrameFormat( ); 4262 const SwFormatFrameSize &rSize = pTableFormat->GetFrameSize(); 4263 int nWidthPercent = rSize.GetWidthPercent(); 4264 // If we export a floating table: we use the widthPercent of the surrounding frame 4265 const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame(); 4266 if (pFloatingTableFrame) 4267 { 4268 const SwFormatFrameSize &rFrameSize = pFloatingTableFrame->GetFrameFormat().GetFrameSize(); 4269 nWidthPercent = rFrameSize.GetWidthPercent(); 4270 } 4271 4272 uno::Reference<beans::XPropertySet> xPropertySet(SwXTextTables::GetObject(*pTable->GetFrameFormat( )),uno::UNO_QUERY); 4273 bool isWidthRelative = false; 4274 xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative; 4275 if (!isWidthRelative && !nWidthPercent) 4276 { 4277 // The best fit for "automatic" table placement is relative 100% 4278 short nHoriOrient = -1; 4279 xPropertySet->getPropertyValue("HoriOrient") >>= nHoriOrient; 4280 isWidthRelative = nHoriOrient == text::HoriOrientation::FULL; 4281 if (isWidthRelative) 4282 nWidthPercent = 100; 4283 } 4284 4285 if(isWidthRelative) 4286 { 4287 /** 4288 * As per ECMA Specification : ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference [ 17.18.90 ST_TableWidth (Table Width Units)] 4289 * http://www.schemacentral.com/sc/ooxml/a-w_type-7.html 4290 * 4291 * Fiftieths of a Percent : 4292 * http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/ 4293 * pct Width is in Fiftieths of a Percent 4294 * 4295 * ex. If the Table width is 50% then 4296 * Width in Fiftieths of a percent is (50 * 50) % or 0.5 * 5000 = 2500pct 4297 **/ 4298 nPageSize = nWidthPercent * 50 ; 4299 widthType = "pct" ; 4300 } 4301 else 4302 { 4303 bool bRelBoxSize = false; 4304 // Create the SwWriteTable instance to use col spans (and maybe other infos) 4305 GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize ); 4306 if(nPageSize == 0) 4307 widthType = "auto"; 4308 } 4309 4310 // Output the table preferred width 4311 m_pSerializer->singleElementNS( XML_w, XML_tblW, 4312 FSNS( XML_w, XML_w ), OString::number(nPageSize), 4313 FSNS( XML_w, XML_type ), widthType ); 4314 4315 // Disable layout autofit, as it does not exist in LibreOffice yet 4316 m_pSerializer->singleElementNS( XML_w, XML_tblLayout, 4317 FSNS( XML_w, XML_type ), "fixed" ); 4318 4319 // Look for the table style property in the table grab bag 4320 std::map<OUString, css::uno::Any> aGrabBag = 4321 pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag(); 4322 4323 // We should clear the TableStyle map. In case of Table inside multiple tables it contains the 4324 // table border style of the previous table. 4325 std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = m_aTableStyleConfs.back(); 4326 rTableStyleConf.clear(); 4327 4328 // Extract properties from grab bag 4329 for( const auto & rGrabBagElement : aGrabBag ) 4330 { 4331 if( rGrabBagElement.first == "TableStyleName") 4332 { 4333 OString sStyleName = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 ); 4334 m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, XML_val), sStyleName); 4335 } 4336 else if( rGrabBagElement.first == "TableStyleTopBorder" ) 4337 rTableStyleConf[SvxBoxItemLine::TOP] = rGrabBagElement.second.get<table::BorderLine2>(); 4338 else if( rGrabBagElement.first == "TableStyleBottomBorder" ) 4339 rTableStyleConf[SvxBoxItemLine::BOTTOM] 4340 = rGrabBagElement.second.get<table::BorderLine2>(); 4341 else if( rGrabBagElement.first == "TableStyleLeftBorder" ) 4342 rTableStyleConf[SvxBoxItemLine::LEFT] 4343 = rGrabBagElement.second.get<table::BorderLine2>(); 4344 else if( rGrabBagElement.first == "TableStyleRightBorder" ) 4345 rTableStyleConf[SvxBoxItemLine::RIGHT] 4346 = rGrabBagElement.second.get<table::BorderLine2>(); 4347 else if (rGrabBagElement.first == "TableStyleLook") 4348 { 4349 rtl::Reference<FastAttributeList> pAttributeList = FastSerializerHelper::createAttrList(); 4350 const uno::Sequence<beans::PropertyValue> aAttributeList = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); 4351 4352 for (const auto& rAttribute : aAttributeList) 4353 { 4354 if (rAttribute.Name == "val") 4355 pAttributeList->add(FSNS(XML_w, XML_val), lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16), 4, '0')); 4356 else 4357 { 4358 static DocxStringTokenMap const aTokens[] = 4359 { 4360 {"firstRow", XML_firstRow}, 4361 {"lastRow", XML_lastRow}, 4362 {"firstColumn", XML_firstColumn}, 4363 {"lastColumn", XML_lastColumn}, 4364 {"noHBand", XML_noHBand}, 4365 {"noVBand", XML_noVBand}, 4366 {nullptr, 0} 4367 }; 4368 4369 if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name)) 4370 pAttributeList->add(FSNS(XML_w, nToken), (rAttribute.Value.get<sal_Int32>() ? "1" : "0")); 4371 } 4372 } 4373 4374 m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList); 4375 } 4376 else if (rGrabBagElement.first == "TablePosition" && 4377 // skip empty table position (tables in footnotes converted to 4378 // floating tables temporarily, don't export this) 4379 rGrabBagElement.second != uno::Any() ) 4380 { 4381 rtl::Reference<FastAttributeList> attrListTablePos = FastSerializerHelper::createAttrList( ); 4382 const uno::Sequence<beans::PropertyValue> aTablePosition = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue> >(); 4383 // look for a surrounding frame and take it's position values 4384 const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame(); 4385 if( pFrame ) 4386 { 4387 // we export the values of the surrounding Frame 4388 OString sOrientation; 4389 sal_Int32 nValue; 4390 4391 // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY! 4392 OString sTblpXSpec = convertToOOXMLHoriOrient( pFrame->GetFrameFormat().GetHoriOrient().GetHoriOrient(), pFrame->GetFrameFormat().GetHoriOrient().IsPosToggle() ); 4393 OString sTblpYSpec = convertToOOXMLVertOrient( pFrame->GetFrameFormat().GetVertOrient().GetVertOrient() ); 4394 4395 sOrientation = convertToOOXMLVertOrientRel( pFrame->GetFrameFormat().GetVertOrient().GetRelationOrient() ); 4396 attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), sOrientation); 4397 4398 if( !sTblpYSpec.isEmpty() ) 4399 attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), sTblpYSpec); 4400 4401 sOrientation = convertToOOXMLHoriOrientRel( pFrame->GetFrameFormat().GetHoriOrient().GetRelationOrient() ); 4402 attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), sOrientation); 4403 4404 if( !sTblpXSpec.isEmpty() ) 4405 attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), sTblpXSpec); 4406 4407 nValue = pFrame->GetFrameFormat().GetULSpace().GetLower(); 4408 if( nValue != 0 ) 4409 attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) ); 4410 4411 nValue = pFrame->GetFrameFormat().GetLRSpace().GetLeft(); 4412 if( nValue != 0 ) 4413 attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) ); 4414 4415 nValue = pFrame->GetFrameFormat().GetLRSpace().GetRight(); 4416 if( nValue != 0 ) 4417 attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) ); 4418 4419 nValue = pFrame->GetFrameFormat().GetULSpace().GetUpper(); 4420 if( nValue != 0 ) 4421 attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) ); 4422 4423 if( sTblpXSpec.isEmpty() ) // do not write tblpX if tblpXSpec is present 4424 { 4425 nValue = pFrame->GetFrameFormat().GetHoriOrient().GetPos(); 4426 // we need to revert the additional shift introduced by 4427 // lcl_DecrementHoriOrientPosition() in writerfilter 4428 // 1st: left distance of the table 4429 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); 4430 const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat(); 4431 const SvxBoxItem& rBox = pFrameFormat->GetBox( ); 4432 sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT); 4433 nValue += nLeftDistance; 4434 4435 // 2nd: if a left border is given, revert the shift by half the width 4436 // from lcl_DecrementHoriOrientPosition() in writerfilter 4437 if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft()) 4438 { 4439 tools::Long nWidth = pLeftBorder->GetWidth(); 4440 nValue += (nWidth / 2); 4441 } 4442 4443 attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) ); 4444 } 4445 4446 if( sTblpYSpec.isEmpty() ) // do not write tblpY if tblpYSpec is present 4447 { 4448 nValue = pFrame->GetFrameFormat().GetVertOrient().GetPos(); 4449 attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) ); 4450 } 4451 } 4452 else // ( pFrame = 0 ) 4453 { 4454 // we export the values from the grabBag 4455 for (const auto& rProp : aTablePosition) 4456 { 4457 if (rProp.Name == "vertAnchor" && !rProp.Value.get<OUString>().isEmpty()) 4458 { 4459 OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); 4460 attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), sOrientation); 4461 } 4462 else if (rProp.Name == "tblpYSpec" && !rProp.Value.get<OUString>().isEmpty()) 4463 { 4464 OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); 4465 attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), sOrientation); 4466 } 4467 else if (rProp.Name == "horzAnchor" && !rProp.Value.get<OUString>().isEmpty()) 4468 { 4469 OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); 4470 attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), sOrientation); 4471 } 4472 else if (rProp.Name == "tblpXSpec" && !rProp.Value.get<OUString>().isEmpty()) 4473 { 4474 OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); 4475 attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), sOrientation); 4476 } 4477 else if (rProp.Name == "bottomFromText") 4478 { 4479 sal_Int32 nValue = rProp.Value.get<sal_Int32>(); 4480 attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) ); 4481 } 4482 else if (rProp.Name == "leftFromText") 4483 { 4484 sal_Int32 nValue = rProp.Value.get<sal_Int32>(); 4485 attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) ); 4486 } 4487 else if (rProp.Name == "rightFromText") 4488 { 4489 sal_Int32 nValue = rProp.Value.get<sal_Int32>(); 4490 attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) ); 4491 } 4492 else if (rProp.Name == "topFromText") 4493 { 4494 sal_Int32 nValue = rProp.Value.get<sal_Int32>(); 4495 attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) ); 4496 } 4497 else if (rProp.Name == "tblpX") 4498 { 4499 sal_Int32 nValue = rProp.Value.get<sal_Int32>(); 4500 attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) ); 4501 } 4502 else if (rProp.Name == "tblpY") 4503 { 4504 sal_Int32 nValue = rProp.Value.get<sal_Int32>(); 4505 attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) ); 4506 } 4507 } 4508 } 4509 4510 m_pSerializer->singleElementNS( XML_w, XML_tblpPr, attrListTablePos); 4511 attrListTablePos = nullptr; 4512 } 4513 else 4514 SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: unhandled property: " << rGrabBagElement.first); 4515 } 4516 4517 // Output the table alignment 4518 const char* pJcVal; 4519 sal_Int32 nIndent = 0; 4520 switch ( pTableFormat->GetHoriOrient( ).GetHoriOrient( ) ) 4521 { 4522 case text::HoriOrientation::CENTER: 4523 pJcVal = "center"; 4524 break; 4525 case text::HoriOrientation::RIGHT: 4526 if ( bEcma ) 4527 pJcVal = "right"; 4528 else 4529 pJcVal = "end"; 4530 break; 4531 default: 4532 case text::HoriOrientation::NONE: 4533 case text::HoriOrientation::LEFT_AND_WIDTH: 4534 { 4535 if ( bEcma ) 4536 pJcVal = "left"; 4537 else 4538 pJcVal = "start"; 4539 nIndent = sal_Int32( pTableFormat->GetLRSpace().GetLeft() ); 4540 4541 // Table indentation has different meaning in Word, depending if the table is nested or not. 4542 // If nested, tblInd is added to parent table's left spacing and defines left edge position 4543 // If not nested, text position of left-most cell must be at absolute X = tblInd 4544 // so, table_spacing + table_spacing_to_content = tblInd 4545 4546 // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables; 4547 // if no compatibilityMode is defined (which now should only happen on a new export to .docx), 4548 // LO uses a higher compatibility than 2010's 14. 4549 sal_Int32 nMode = lcl_getWordCompatibilityMode(m_rExport); 4550 4551 const SwFrameFormat* pFrameFormat = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat(); 4552 if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth == 0) 4553 nIndent += pFrameFormat->GetBox().GetDistance( SvxBoxItemLine::LEFT ); 4554 else 4555 { 4556 // adjust for SW considering table to start mid-border instead of nested/2013's left-side-of-border. 4557 nIndent -= pFrameFormat->GetBox().CalcLineWidth( SvxBoxItemLine::LEFT ) / 2; 4558 } 4559 4560 break; 4561 } 4562 } 4563 m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pJcVal); 4564 4565 // Output the table background color (although cell value still needs to be specified) 4566 const SvxBrushItem *pColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); 4567 Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO; 4568 if ( aColor != COL_AUTO ) 4569 { 4570 OString sColor = msfilter::util::ConvertColor( aColor ); 4571 m_pSerializer->singleElementNS( XML_w, XML_shd, 4572 FSNS( XML_w, XML_fill ), sColor, 4573 FSNS( XML_w, XML_val ), "clear" ); 4574 } 4575 4576 // Output the table borders 4577 TableDefaultBorders( pTableTextNodeInfoInner ); 4578 4579 // Output the default cell margins 4580 TableDefaultCellMargins( pTableTextNodeInfoInner ); 4581 4582 TableBidi( pTableTextNodeInfoInner ); 4583 4584 // Table indent (need to get written even if == 0) 4585 m_pSerializer->singleElementNS( XML_w, XML_tblInd, 4586 FSNS( XML_w, XML_w ), OString::number(nIndent), 4587 FSNS( XML_w, XML_type ), "dxa" ); 4588 4589 // Merge the marks for the ordered elements 4590 m_pSerializer->mergeTopMarks(Tag_TableDefinition); 4591 4592 m_pSerializer->endElementNS( XML_w, XML_tblPr ); 4593 4594 // Write the table grid infos 4595 m_pSerializer->startElementNS(XML_w, XML_tblGrid); 4596 sal_Int32 nPrv = 0; 4597 ww8::WidthsPtr pColumnWidths = GetColumnWidths( pTableTextNodeInfoInner ); 4598 for ( auto aColumnWidth : *pColumnWidths ) 4599 { 4600 sal_Int32 nWidth = sal_Int32( aColumnWidth ) - nPrv; 4601 m_pSerializer->singleElementNS( XML_w, XML_gridCol, 4602 FSNS( XML_w, XML_w ), OString::number(nWidth) ); 4603 nPrv = sal_Int32( aColumnWidth ); 4604 } 4605 4606 m_pSerializer->endElementNS( XML_w, XML_tblGrid ); 4607 } 4608 4609 void DocxAttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) 4610 { 4611 // Table defaults should only be created IF m_aTableStyleConf contents haven't come from a table style. 4612 // Previously this function wrote out Cell A1 as the table default, causing problems with no benefit. 4613 } 4614 4615 void DocxAttributeOutput::TableDefaultCellMargins( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) 4616 { 4617 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); 4618 const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat(); 4619 const SvxBoxItem& rBox = pFrameFormat->GetBox( ); 4620 const bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; 4621 4622 impl_cellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma); 4623 } 4624 4625 void DocxAttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) 4626 { 4627 const SwTable *pTable = pTableTextNodeInfoInner->getTable(); 4628 const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( ); 4629 const SwTableLine *pTableRow = pTableBox->GetUpper(); 4630 const SwFrameFormat *pFormat = pTableBox->GetFrameFormat( ); 4631 4632 const SvxBrushItem *pColorProp = pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); 4633 Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO; 4634 4635 const SwFrameFormat *pRowFormat = pTableRow->GetFrameFormat( ); 4636 const SvxBrushItem *pRowColorProp = pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); 4637 if ( pRowColorProp && aColor == COL_AUTO) 4638 aColor = pRowColorProp->GetColor(); 4639 4640 const SwFrameFormat *pTableFormat = pTable->GetFrameFormat( ); 4641 const SvxBrushItem *pTableColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); 4642 if ( pTableColorProp && aColor == COL_AUTO ) 4643 aColor = pTableColorProp->GetColor(); 4644 4645 const OString sColor = msfilter::util::ConvertColor( aColor ); 4646 4647 std::map<OUString, css::uno::Any> aGrabBag = 4648 pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag(); 4649 4650 OString sOriginalColor; 4651 std::map<OUString, css::uno::Any>::iterator aGrabBagElement = aGrabBag.find("originalColor"); 4652 if( aGrabBagElement != aGrabBag.end() ) 4653 sOriginalColor = OUStringToOString( aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 ); 4654 4655 if ( sOriginalColor != sColor ) 4656 { 4657 // color changed by the user, or no grab bag: write sColor 4658 if ( sColor != "auto" ) 4659 { 4660 m_pSerializer->singleElementNS( XML_w, XML_shd, 4661 FSNS( XML_w, XML_fill ), sColor, 4662 FSNS( XML_w, XML_val ), "clear" ); 4663 } 4664 } 4665 else 4666 { 4667 rtl::Reference<sax_fastparser::FastAttributeList> pAttrList; 4668 4669 for( const auto & rGrabBagElement : aGrabBag ) 4670 { 4671 if (!rGrabBagElement.second.has<OUString>()) 4672 continue; 4673 4674 OString sValue = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 ); 4675 if( rGrabBagElement.first == "themeFill") 4676 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFill ), sValue.getStr() ); 4677 else if( rGrabBagElement.first == "themeFillTint") 4678 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillTint ), sValue.getStr() ); 4679 else if( rGrabBagElement.first == "themeFillShade") 4680 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillShade ), sValue.getStr() ); 4681 else if( rGrabBagElement.first == "fill" ) 4682 AddToAttrList( pAttrList, FSNS( XML_w, XML_fill ), sValue.getStr() ); 4683 else if( rGrabBagElement.first == "themeColor") 4684 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeColor ), sValue.getStr() ); 4685 else if( rGrabBagElement.first == "themeTint") 4686 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeTint ), sValue.getStr() ); 4687 else if( rGrabBagElement.first == "themeShade") 4688 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeShade ), sValue.getStr() ); 4689 else if( rGrabBagElement.first == "color") 4690 AddToAttrList( pAttrList, FSNS( XML_w, XML_color ), sValue.getStr() ); 4691 else if( rGrabBagElement.first == "val") 4692 AddToAttrList( pAttrList, FSNS( XML_w, XML_val ), sValue.getStr() ); 4693 } 4694 m_pSerializer->singleElementNS( XML_w, XML_shd, pAttrList.get() ); 4695 } 4696 } 4697 4698 void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) 4699 { 4700 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); 4701 const SwTableLine * pTabLine = pTabBox->GetUpper(); 4702 4703 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet( 4704 SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ); 4705 4706 // check table row property "HasTextChangesOnly" 4707 SwRedlineTable::size_type nPos(0); 4708 SwRedlineTable::size_type nChange = pTabLine->UpdateTextChangesOnly(nPos); 4709 if ( nChange != SwRedlineTable::npos ) 4710 { 4711 const SwRedlineTable& aRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable(); 4712 const SwRangeRedline* pRedline = aRedlineTable[ nChange ]; 4713 SwTableRowRedline* pTableRowRedline = nullptr; 4714 bool bIsInExtra = false; 4715 4716 // use the original DOCX redline data stored in ExtraRedlineTable, 4717 // if it exists and its type wasn't changed 4718 const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable(); 4719 for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos ) 4720 { 4721 SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos); 4722 pTableRowRedline = dynamic_cast<SwTableRowRedline*>(pExtraRedline); 4723 if (pTableRowRedline && &pTableRowRedline->GetTableLine() == pTabLine) 4724 { 4725 bIsInExtra = true; 4726 break; 4727 } 4728 } 4729 4730 const SwRedlineData& aRedlineData = bIsInExtra && 4731 // still the same type (an inserted row could become a tracked deleted one) 4732 pTableRowRedline->GetRedlineData().GetType() == pRedline->GetRedlineData().GetType() 4733 ? pTableRowRedline->GetRedlineData() 4734 : pRedline->GetRedlineData(); 4735 4736 // Note: all redline ranges and table row redline (with the same author and timestamp) 4737 // use the same redline id in OOXML exported by MSO, but it seems, the recent solution 4738 // (different IDs for different ranges, also row changes) is also portable. 4739 OString aId( OString::number( m_nRedlineId++ ) ); 4740 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) ); 4741 OString aAuthor( OUStringToOString( bRemovePersonalInfo 4742 ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) 4743 : rAuthor, RTL_TEXTENCODING_UTF8 ) ); 4744 4745 OString aDate( DateTimeToOString( bRemovePersonalInfo 4746 ? DateTime(Date( 1, 1, 1970 )) // Epoch time 4747 : aRedlineData.GetTimeStamp() ) ); 4748 4749 m_pSerializer->singleElementNS( XML_w, 4750 RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins, 4751 FSNS( XML_w, XML_id ), aId, 4752 FSNS( XML_w, XML_author ), aAuthor, 4753 FSNS( XML_w, XML_date ), aDate ); 4754 return; 4755 } 4756 } 4757 4758 void DocxAttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) 4759 { 4760 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); 4761 4762 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet( 4763 SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ); 4764 4765 // search next Redline 4766 const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable(); 4767 for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos ) 4768 { 4769 SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos); 4770 const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline); 4771 if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox) 4772 { 4773 // Redline for this table cell 4774 const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData(); 4775 RedlineType nRedlineType = aRedlineData.GetType(); 4776 switch (nRedlineType) 4777 { 4778 case RedlineType::TableCellInsert: 4779 case RedlineType::TableCellDelete: 4780 { 4781 OString aId( OString::number( m_nRedlineId++ ) ); 4782 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) ); 4783 OString aAuthor( OUStringToOString( bRemovePersonalInfo 4784 ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) 4785 : rAuthor, RTL_TEXTENCODING_UTF8 ) ); 4786 4787 OString aDate( DateTimeToOString( bRemovePersonalInfo 4788 ? DateTime(Date( 1, 1, 1970 )) // Epoch time 4789 : aRedlineData.GetTimeStamp() ) ); 4790 4791 if (nRedlineType == RedlineType::TableCellInsert) 4792 m_pSerializer->singleElementNS( XML_w, XML_cellIns, 4793 FSNS( XML_w, XML_id ), aId, 4794 FSNS( XML_w, XML_author ), aAuthor, 4795 FSNS( XML_w, XML_date ), aDate ); 4796 else if (nRedlineType == RedlineType::TableCellDelete) 4797 m_pSerializer->singleElementNS( XML_w, XML_cellDel, 4798 FSNS( XML_w, XML_id ), aId, 4799 FSNS( XML_w, XML_author ), aAuthor, 4800 FSNS( XML_w, XML_date ), aDate ); 4801 } 4802 break; 4803 default: break; 4804 } 4805 } 4806 } 4807 } 4808 4809 void DocxAttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) 4810 { 4811 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); 4812 const SwTableLine * pTabLine = pTabBox->GetUpper(); 4813 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat(); 4814 4815 const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize(); 4816 if ( !(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()) ) 4817 return; 4818 4819 sal_Int32 nHeight = rLSz.GetHeight(); 4820 const char *pRule = nullptr; 4821 4822 switch ( rLSz.GetHeightSizeType() ) 4823 { 4824 case SwFrameSize::Fixed: pRule = "exact"; break; 4825 case SwFrameSize::Minimum: pRule = "atLeast"; break; 4826 default: break; 4827 } 4828 4829 if ( pRule ) 4830 m_pSerializer->singleElementNS( XML_w, XML_trHeight, 4831 FSNS( XML_w, XML_val ), OString::number(nHeight), 4832 FSNS( XML_w, XML_hRule ), pRule ); 4833 } 4834 4835 void DocxAttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) 4836 { 4837 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); 4838 const SwTableLine * pTabLine = pTabBox->GetUpper(); 4839 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat(); 4840 4841 const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit( ); 4842 // if rSplittable is true then no need to write <w:cantSplit w:val="false"/> 4843 // as default row prop is allow row to break across page. 4844 if( !rSplittable.GetValue( ) ) 4845 m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w, XML_val), "true"); 4846 } 4847 4848 void DocxAttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) 4849 { 4850 const SwTable * pTable = pTableTextNodeInfoInner->getTable(); 4851 const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat(); 4852 4853 if ( m_rExport.TrueFrameDirection( *pFrameFormat ) == SvxFrameDirection::Horizontal_RL_TB ) 4854 { 4855 m_pSerializer->singleElementNS(XML_w, XML_bidiVisual, FSNS(XML_w, XML_val), "true"); 4856 } 4857 } 4858 4859 void DocxAttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) 4860 { 4861 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); 4862 const SwFrameFormat *pFrameFormat = pTabBox->GetFrameFormat( ); 4863 4864 if ( SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection( *pFrameFormat ) ) 4865 m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "tbRl"); 4866 else if ( SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection( *pFrameFormat ) ) 4867 { 4868 m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "btLr"); 4869 } 4870 4871 const SwWriteTableRows& rRows = m_xTableWrt->GetRows( ); 4872 SwWriteTableRow *pRow = rRows[ pTableTextNodeInfoInner->getRow( ) ].get(); 4873 sal_uInt32 nCell = pTableTextNodeInfoInner->getCell(); 4874 const SwWriteTableCells& rTableCells = pRow->GetCells(); 4875 if (nCell >= rTableCells.size() ) 4876 return; 4877 4878 const SwWriteTableCell *const pCell = pRow->GetCells()[ nCell ].get(); 4879 switch( pCell->GetVertOri()) 4880 { 4881 case text::VertOrientation::TOP: 4882 break; 4883 case text::VertOrientation::CENTER: 4884 m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center"); 4885 break; 4886 case text::VertOrientation::BOTTOM: 4887 m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom"); 4888 break; 4889 } 4890 } 4891 4892 void DocxAttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner ) 4893 { 4894 // This is called when the nested table ends in a cell, and there's no 4895 // paragraph behind that; so we must check for the ends of cell, rows, 4896 // tables 4897 // ['true' to write an empty paragraph, MS Word insists on that] 4898 FinishTableRowCell( pNodeInfoInner, true ); 4899 } 4900 4901 void DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) 4902 { 4903 SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" ); 4904 } 4905 4906 void DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) 4907 { 4908 SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" ); 4909 } 4910 4911 void DocxAttributeOutput::TableRowEnd( sal_uInt32 /*nDepth*/ ) 4912 { 4913 SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 nDepth = 1 )" ); 4914 } 4915 4916 void DocxAttributeOutput::StartStyles() 4917 { 4918 m_pSerializer->startElementNS( XML_w, XML_styles, 4919 FSNS( XML_xmlns, XML_w ), GetExport().GetFilter().getNamespaceURL(OOX_NS(doc)), 4920 FSNS( XML_xmlns, XML_w14 ), GetExport().GetFilter().getNamespaceURL(OOX_NS(w14)), 4921 FSNS( XML_xmlns, XML_mc ), GetExport().GetFilter().getNamespaceURL(OOX_NS(mce)), 4922 FSNS( XML_mc, XML_Ignorable ), "w14" ); 4923 4924 DocDefaults(); 4925 LatentStyles(); 4926 } 4927 4928 sal_Int32 DocxStringGetToken(DocxStringTokenMap const * pMap, std::u16string_view rName) 4929 { 4930 OString sName = OUStringToOString(rName, RTL_TEXTENCODING_UTF8); 4931 while (pMap->pToken) 4932 { 4933 if (sName == pMap->pToken) 4934 return pMap->nToken; 4935 ++pMap; 4936 } 4937 return 0; 4938 } 4939 4940 namespace 4941 { 4942 4943 DocxStringTokenMap const aDefaultTokens[] = { 4944 {"defQFormat", XML_defQFormat}, 4945 {"defUnhideWhenUsed", XML_defUnhideWhenUsed}, 4946 {"defSemiHidden", XML_defSemiHidden}, 4947 {"count", XML_count}, 4948 {"defUIPriority", XML_defUIPriority}, 4949 {"defLockedState", XML_defLockedState}, 4950 {nullptr, 0} 4951 }; 4952 4953 DocxStringTokenMap const aExceptionTokens[] = { 4954 {"name", XML_name}, 4955 {"locked", XML_locked}, 4956 {"uiPriority", XML_uiPriority}, 4957 {"semiHidden", XML_semiHidden}, 4958 {"unhideWhenUsed", XML_unhideWhenUsed}, 4959 {"qFormat", XML_qFormat}, 4960 {nullptr, 0} 4961 }; 4962 4963 } 4964 4965 void DocxAttributeOutput::LatentStyles() 4966 { 4967 // Do we have latent styles available? 4968 uno::Reference<beans::XPropertySet> xPropertySet(m_rExport.m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW); 4969 uno::Sequence<beans::PropertyValue> aInteropGrabBag; 4970 xPropertySet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag; 4971 uno::Sequence<beans::PropertyValue> aLatentStyles; 4972 auto pProp = std::find_if(std::cbegin(aInteropGrabBag), std::cend(aInteropGrabBag), 4973 [](const beans::PropertyValue& rProp) { return rProp.Name == "latentStyles"; }); 4974 if (pProp != std::cend(aInteropGrabBag)) 4975 pProp->Value >>= aLatentStyles; 4976 if (!aLatentStyles.hasElements()) 4977 return; 4978 4979 // Extract default attributes first. 4980 rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList = FastSerializerHelper::createAttrList(); 4981 uno::Sequence<beans::PropertyValue> aLsdExceptions; 4982 for (const auto& rLatentStyle : std::as_const(aLatentStyles)) 4983 { 4984 if (sal_Int32 nToken = DocxStringGetToken(aDefaultTokens, rLatentStyle.Name)) 4985 pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(rLatentStyle.Value.get<OUString>(), RTL_TEXTENCODING_UTF8)); 4986 else if (rLatentStyle.Name == "lsdExceptions") 4987 rLatentStyle.Value >>= aLsdExceptions; 4988 } 4989 4990 m_pSerializer->startElementNS(XML_w, XML_latentStyles, pAttributeList); 4991 pAttributeList = nullptr; 4992 4993 // Then handle the exceptions. 4994 for (const auto& rLsdException : std::as_const(aLsdExceptions)) 4995 { 4996 pAttributeList = FastSerializerHelper::createAttrList(); 4997 4998 uno::Sequence<beans::PropertyValue> aAttributes; 4999 rLsdException.Value >>= aAttributes; 5000 for (const auto& rAttribute : std::as_const(aAttributes)) 5001 if (sal_Int32 nToken = DocxStringGetToken(aExceptionTokens, rAttribute.Name)) 5002 pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(rAttribute.Value.get<OUString>(), RTL_TEXTENCODING_UTF8)); 5003 5004 m_pSerializer->singleElementNS(XML_w, XML_lsdException, pAttributeList); 5005 pAttributeList = nullptr; 5006 } 5007 5008 m_pSerializer->endElementNS(XML_w, XML_latentStyles); 5009 } 5010 5011 void DocxAttributeOutput::OutputDefaultItem(const SfxPoolItem& rHt) 5012 { 5013 bool bMustWrite = true; 5014 switch (rHt.Which()) 5015 { 5016 case RES_CHRATR_CASEMAP: 5017 bMustWrite = static_cast< const SvxCaseMapItem& >(rHt).GetCaseMap() != SvxCaseMap::NotMapped; 5018 break; 5019 case RES_CHRATR_COLOR: 5020 bMustWrite = static_cast< const SvxColorItem& >(rHt).GetValue() != COL_AUTO; 5021 break; 5022 case RES_CHRATR_CONTOUR: 5023 bMustWrite = static_cast< const SvxContourItem& >(rHt).GetValue(); 5024 break; 5025 case RES_CHRATR_CROSSEDOUT: 5026 bMustWrite = static_cast< const SvxCrossedOutItem& >(rHt).GetStrikeout() != STRIKEOUT_NONE; 5027 break; 5028 case RES_CHRATR_ESCAPEMENT: 5029 bMustWrite = static_cast< const SvxEscapementItem& >(rHt).GetEscapement() != SvxEscapement::Off; 5030 break; 5031 case RES_CHRATR_FONT: 5032 bMustWrite = true; 5033 break; 5034 case RES_CHRATR_FONTSIZE: 5035 bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default 5036 break; 5037 case RES_CHRATR_KERNING: 5038 bMustWrite = static_cast< const SvxKerningItem& >(rHt).GetValue() != 0; 5039 break; 5040 case RES_CHRATR_LANGUAGE: 5041 bMustWrite = true; 5042 break; 5043 case RES_CHRATR_POSTURE: 5044 bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE; 5045 break; 5046 case RES_CHRATR_SHADOWED: 5047 bMustWrite = static_cast< const SvxShadowedItem& >(rHt).GetValue(); 5048 break; 5049 case RES_CHRATR_UNDERLINE: 5050 bMustWrite = static_cast< const SvxUnderlineItem& >(rHt).GetLineStyle() != LINESTYLE_NONE; 5051 break; 5052 case RES_CHRATR_WEIGHT: 5053 bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL; 5054 break; 5055 case RES_CHRATR_AUTOKERN: 5056 bMustWrite = static_cast< const SvxAutoKernItem& >(rHt).GetValue(); 5057 break; 5058 case RES_CHRATR_BLINK: 5059 bMustWrite = static_cast< const SvxBlinkItem& >(rHt).GetValue(); 5060 break; 5061 case RES_CHRATR_BACKGROUND: 5062 { 5063 const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt); 5064 bMustWrite = (rBrushItem.GetColor() != COL_AUTO || 5065 rBrushItem.GetShadingValue() != ShadingPattern::CLEAR || 5066 rBrushItem.GetGraphic() != nullptr || 5067 rBrushItem.GetGraphicObject() != nullptr); 5068 } 5069 break; 5070 5071 case RES_CHRATR_CJK_FONT: 5072 bMustWrite = true; 5073 break; 5074 case RES_CHRATR_CJK_FONTSIZE: 5075 bMustWrite = false; // we have written it already as RES_CHRATR_FONTSIZE 5076 break; 5077 case RES_CHRATR_CJK_LANGUAGE: 5078 bMustWrite = true; 5079 break; 5080 case RES_CHRATR_CJK_POSTURE: 5081 bMustWrite = false; // we have written it already as RES_CHRATR_POSTURE 5082 break; 5083 case RES_CHRATR_CJK_WEIGHT: 5084 bMustWrite = false; // we have written it already as RES_CHRATR_WEIGHT 5085 break; 5086 5087 case RES_CHRATR_CTL_FONT: 5088 bMustWrite = true; 5089 break; 5090 case RES_CHRATR_CTL_FONTSIZE: 5091 bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default 5092 break; 5093 case RES_CHRATR_CTL_LANGUAGE: 5094 bMustWrite = true; 5095 break; 5096 case RES_CHRATR_CTL_POSTURE: 5097 bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE; 5098 break; 5099 case RES_CHRATR_CTL_WEIGHT: 5100 bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL; 5101 break; 5102 5103 case RES_CHRATR_ROTATE: 5104 bMustWrite = static_cast< const SvxCharRotateItem& >(rHt).GetValue() != 0_deg10; 5105 break; 5106 case RES_CHRATR_EMPHASIS_MARK: 5107 bMustWrite = static_cast< const SvxEmphasisMarkItem& >(rHt).GetEmphasisMark() != FontEmphasisMark::NONE; 5108 break; 5109 case RES_CHRATR_TWO_LINES: 5110 bMustWrite = static_cast< const SvxTwoLinesItem& >(rHt).GetValue(); 5111 break; 5112 case RES_CHRATR_SCALEW: 5113 bMustWrite = static_cast< const SvxCharScaleWidthItem& >(rHt).GetValue() != 100; 5114 break; 5115 case RES_CHRATR_RELIEF: 5116 bMustWrite = static_cast< const SvxCharReliefItem& >(rHt).GetValue() != FontRelief::NONE; 5117 break; 5118 case RES_CHRATR_HIDDEN: 5119 bMustWrite = static_cast< const SvxCharHiddenItem& >(rHt).GetValue(); 5120 break; 5121 case RES_CHRATR_BOX: 5122 { 5123 const SvxBoxItem& rBoxItem = static_cast< const SvxBoxItem& >(rHt); 5124 bMustWrite = rBoxItem.GetTop() || rBoxItem.GetLeft() || 5125 rBoxItem.GetBottom() || rBoxItem.GetRight() || 5126 rBoxItem.GetSmallestDistance(); 5127 } 5128 break; 5129 case RES_CHRATR_HIGHLIGHT: 5130 { 5131 const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt); 5132 bMustWrite = (rBrushItem.GetColor() != COL_AUTO || 5133 rBrushItem.GetShadingValue() != ShadingPattern::CLEAR || 5134 rBrushItem.GetGraphic() != nullptr || 5135 rBrushItem.GetGraphicObject() != nullptr); 5136 } 5137 break; 5138 5139 case RES_PARATR_LINESPACING: 5140 bMustWrite = static_cast< const SvxLineSpacingItem& >(rHt).GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off; 5141 break; 5142 case RES_PARATR_ADJUST: 5143 bMustWrite = static_cast< const SvxAdjustItem& >(rHt).GetAdjust() != SvxAdjust::Left; 5144 break; 5145 case RES_PARATR_SPLIT: 5146 bMustWrite = !static_cast< const SvxFormatSplitItem& >(rHt).GetValue(); 5147 break; 5148 case RES_PARATR_WIDOWS: 5149 bMustWrite = static_cast< const SvxWidowsItem& >(rHt).GetValue(); 5150 break; 5151 case RES_PARATR_TABSTOP: 5152 bMustWrite = static_cast< const SvxTabStopItem& >(rHt).Count() != 0; 5153 break; 5154 case RES_PARATR_HYPHENZONE: 5155 bMustWrite = true; 5156 break; 5157 case RES_PARATR_NUMRULE: 5158 bMustWrite = !static_cast< const SwNumRuleItem& >(rHt).GetValue().isEmpty(); 5159 break; 5160 case RES_PARATR_SCRIPTSPACE: 5161 bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue(); 5162 break; 5163 case RES_PARATR_HANGINGPUNCTUATION: 5164 bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue(); 5165 break; 5166 case RES_PARATR_FORBIDDEN_RULES: 5167 bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue(); 5168 break; 5169 case RES_PARATR_VERTALIGN: 5170 bMustWrite = static_cast< const SvxParaVertAlignItem& >(rHt).GetValue() != SvxParaVertAlignItem::Align::Automatic; 5171 break; 5172 case RES_PARATR_SNAPTOGRID: 5173 bMustWrite = !static_cast< const SvxParaGridItem& >(rHt).GetValue(); 5174 break; 5175 case RES_CHRATR_GRABBAG: 5176 bMustWrite = true; 5177 break; 5178 5179 default: 5180 SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() ); 5181 break; 5182 } 5183 5184 if (bMustWrite) 5185 OutputItem(rHt); 5186 } 5187 5188 void DocxAttributeOutput::DocDefaults( ) 5189 { 5190 // Write the '<w:docDefaults>' section here 5191 m_pSerializer->startElementNS(XML_w, XML_docDefaults); 5192 5193 // Output the default run properties 5194 m_pSerializer->startElementNS(XML_w, XML_rPrDefault); 5195 5196 StartStyleProperties(false, 0); 5197 5198 for (int i = int(RES_CHRATR_BEGIN); i < int(RES_CHRATR_END); ++i) 5199 OutputDefaultItem(m_rExport.m_rDoc.GetDefault(i)); 5200 5201 EndStyleProperties(false); 5202 5203 m_pSerializer->endElementNS(XML_w, XML_rPrDefault); 5204 5205 // Output the default paragraph properties 5206 m_pSerializer->startElementNS(XML_w, XML_pPrDefault); 5207 5208 StartStyleProperties(true, 0); 5209 5210 for (int i = int(RES_PARATR_BEGIN); i < int(RES_PARATR_END); ++i) 5211 OutputDefaultItem(m_rExport.m_rDoc.GetDefault(i)); 5212 5213 EndStyleProperties(true); 5214 5215 m_pSerializer->endElementNS(XML_w, XML_pPrDefault); 5216 5217 m_pSerializer->endElementNS(XML_w, XML_docDefaults); 5218 } 5219 5220 void DocxAttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles ) 5221 { 5222 // HACK 5223 // Ms Office seems to have an internal limitation of 4091 styles 5224 // and refuses to load .docx with more, even though the spec seems to allow that; 5225 // so simply if there are more styles, don't export those 5226 const sal_Int32 nCountStylesToWrite = MSWORD_MAX_STYLES_LIMIT - nNumberOfStyles; 5227 m_pTableStyleExport->TableStyles(nCountStylesToWrite); 5228 m_pSerializer->endElementNS( XML_w, XML_styles ); 5229 } 5230 5231 void DocxAttributeOutput::DefaultStyle() 5232 { 5233 // are these the values of enum ww::sti (see ../inc/wwstyles.hxx)? 5234 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::DefaultStyle()"); 5235 } 5236 5237 /* Writes <a:srcRect> tag back to document.xml if a file contains a cropped image. 5238 * NOTE : Tested on images of type JPEG,EMF/WMF,BMP, PNG and GIF. 5239 */ 5240 void DocxAttributeOutput::WriteSrcRect( 5241 const css::uno::Reference<css::beans::XPropertySet>& xShapePropSet, 5242 const SwFrameFormat* pFrameFormat) 5243 { 5244 uno::Reference<graphic::XGraphic> xGraphic; 5245 xShapePropSet->getPropertyValue("Graphic") >>= xGraphic; 5246 const Graphic aGraphic(xGraphic); 5247 5248 Size aOriginalSize(aGraphic.GetPrefSize()); 5249 5250 const MapMode aMap100mm( MapUnit::Map100thMM ); 5251 const MapMode& rMapMode = aGraphic.GetPrefMapMode(); 5252 if (rMapMode.GetMapUnit() == MapUnit::MapPixel) 5253 { 5254 aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, aMap100mm); 5255 } 5256 5257 css::text::GraphicCrop aGraphicCropStruct; 5258 xShapePropSet->getPropertyValue("GraphicCrop") >>= aGraphicCropStruct; 5259 sal_Int32 nCropL = aGraphicCropStruct.Left; 5260 sal_Int32 nCropR = aGraphicCropStruct.Right; 5261 sal_Int32 nCropT = aGraphicCropStruct.Top; 5262 sal_Int32 nCropB = aGraphicCropStruct.Bottom; 5263 5264 // simulate border padding as a negative crop. 5265 const SfxPoolItem* pItem; 5266 if (pFrameFormat && SfxItemState::SET == pFrameFormat->GetItemState(RES_BOX, false, &pItem)) 5267 { 5268 const SvxBoxItem& rBox = *static_cast<const SvxBoxItem*>(pItem); 5269 nCropL -= rBox.GetDistance( SvxBoxItemLine::LEFT ); 5270 nCropR -= rBox.GetDistance( SvxBoxItemLine::RIGHT ); 5271 nCropT -= rBox.GetDistance( SvxBoxItemLine::TOP ); 5272 nCropB -= rBox.GetDistance( SvxBoxItemLine::BOTTOM ); 5273 } 5274 5275 if ( !((0 != nCropL) || (0 != nCropT) || (0 != nCropR) || (0 != nCropB)) ) 5276 return; 5277 5278 double widthMultiplier = 100000.0/aOriginalSize.Width(); 5279 double heightMultiplier = 100000.0/aOriginalSize.Height(); 5280 5281 sal_Int32 left = static_cast<sal_Int32>(rtl::math::round(nCropL * widthMultiplier)); 5282 sal_Int32 right = static_cast<sal_Int32>(rtl::math::round(nCropR * widthMultiplier)); 5283 sal_Int32 top = static_cast<sal_Int32>(rtl::math::round(nCropT * heightMultiplier)); 5284 sal_Int32 bottom = static_cast<sal_Int32>(rtl::math::round(nCropB * heightMultiplier)); 5285 5286 m_pSerializer->singleElementNS( XML_a, XML_srcRect, 5287 XML_l, OString::number(left), 5288 XML_t, OString::number(top), 5289 XML_r, OString::number(right), 5290 XML_b, OString::number(bottom) ); 5291 } 5292 5293 void DocxAttributeOutput::PushRelIdCache() 5294 { 5295 m_aRelIdCache.emplace(); 5296 m_aSdrRelIdCache.emplace(); 5297 } 5298 5299 OUString DocxAttributeOutput::FindRelId(BitmapChecksum nChecksum) 5300 { 5301 OUString aRet; 5302 5303 if (!m_aSdrRelIdCache.empty() && m_aSdrRelIdCache.top().find(nChecksum) != m_aSdrRelIdCache.top().end()) 5304 aRet = m_aSdrRelIdCache.top()[nChecksum].first; 5305 5306 return aRet; 5307 } 5308 5309 OUString DocxAttributeOutput::FindFileName(BitmapChecksum nChecksum) 5310 { 5311 OUString aRet; 5312 5313 if (!m_aSdrRelIdCache.empty() && m_aSdrRelIdCache.top().find(nChecksum) != m_aSdrRelIdCache.top().end()) 5314 aRet = m_aSdrRelIdCache.top()[nChecksum].second; 5315 5316 return aRet; 5317 } 5318 5319 void DocxAttributeOutput::CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId, const OUString& rFileName) 5320 { 5321 if (!m_aSdrRelIdCache.empty()) 5322 m_aSdrRelIdCache.top()[nChecksum] = std::pair(rRelId, rFileName); 5323 } 5324 5325 uno::Reference<css::text::XTextFrame> DocxAttributeOutput::GetUnoTextFrame( 5326 css::uno::Reference<css::drawing::XShape> xShape) 5327 { 5328 return SwTextBoxHelper::getUnoTextFrame(xShape); 5329 } 5330 5331 std::pair<OString, OUString> DocxAttributeOutput::getExistingGraphicRelId(BitmapChecksum nChecksum) 5332 { 5333 std::pair<OString, OUString> aResult; 5334 5335 if (m_aRelIdCache.empty()) 5336 return aResult; 5337 5338 auto pIterator = m_aRelIdCache.top().find(nChecksum); 5339 5340 if (pIterator != m_aRelIdCache.top().end()) 5341 { 5342 aResult = pIterator->second; 5343 } 5344 5345 return aResult; 5346 } 5347 5348 void DocxAttributeOutput::cacheGraphicRelId(BitmapChecksum nChecksum, OString const & rRelId, OUString const & rFileName) 5349 { 5350 if (!m_aRelIdCache.empty()) 5351 m_aRelIdCache.top().emplace(nChecksum, std::pair(rRelId, rFileName)); 5352 } 5353 5354 void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj ) 5355 { 5356 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj ) - some stuff still missing" ); 5357 5358 GetSdtEndBefore(pSdrObj); 5359 5360 // detect mis-use of the API 5361 assert(pGrfNode || (pOLEFrameFormat && pOLENode)); 5362 const SwFrameFormat* pFrameFormat = pGrfNode ? pGrfNode->GetFlyFormat() : pOLEFrameFormat; 5363 // create the relation ID 5364 OString aRelId; 5365 sal_Int32 nImageType; 5366 if ( pGrfNode && pGrfNode->IsLinkedFile() ) 5367 { 5368 // linked image, just create the relation 5369 OUString aFileName; 5370 pGrfNode->GetFileFilterNms( &aFileName, nullptr ); 5371 5372 sal_Int32 const nFragment(aFileName.indexOf('#')); 5373 sal_Int32 const nForbiddenU(aFileName.indexOf("%5C")); 5374 sal_Int32 const nForbiddenL(aFileName.indexOf("%5c")); 5375 if ( (nForbiddenU != -1 && (nFragment == -1 || nForbiddenU < nFragment)) 5376 || (nForbiddenL != -1 && (nFragment == -1 || nForbiddenL < nFragment))) 5377 { 5378 SAL_WARN("sw.ww8", "DocxAttributeOutput::FlyFrameGraphic: ignoring image with invalid link URL"); 5379 return; 5380 } 5381 5382 // TODO Convert the file name to relative for better interoperability 5383 5384 aRelId = m_rExport.AddRelation( 5385 oox::getRelationship(Relationship::IMAGE), 5386 aFileName ); 5387 5388 nImageType = XML_link; 5389 } 5390 else 5391 { 5392 // inline, we also have to write the image itself 5393 Graphic aGraphic; 5394 if (pGrfNode) 5395 aGraphic = pGrfNode->GetGrf(); 5396 else 5397 aGraphic = *pOLENode->GetGraphic(); 5398 5399 BitmapChecksum aChecksum = aGraphic.GetChecksum(); 5400 OUString aFileName; 5401 std::tie(aRelId, aFileName) = getExistingGraphicRelId(aChecksum); 5402 OUString aImageId; 5403 5404 if (aRelId.isEmpty()) 5405 { 5406 // Not in cache, then need to write it. 5407 m_rDrawingML.SetFS( m_pSerializer ); // to be sure that we write to the right stream 5408 5409 aImageId = m_rDrawingML.WriteImage(aGraphic, false, &aFileName); 5410 5411 aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 ); 5412 cacheGraphicRelId(aChecksum, aRelId, aFileName); 5413 } 5414 else 5415 { 5416 // Include the same relation again. This makes it possible to 5417 // reuse an image across different headers. 5418 aImageId = m_rDrawingML.GetFB()->addRelation( m_pSerializer->getOutputStream(), 5419 oox::getRelationship(Relationship::IMAGE), 5420 aFileName ); 5421 5422 aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 ); 5423 } 5424 5425 nImageType = XML_embed; 5426 } 5427 5428 // In case there are any grab-bag items on the graphic frame, emit them now. 5429 // These are always character grab-bags, as graphics are at-char or as-char in Word. 5430 const SfxPoolItem* pItem = nullptr; 5431 if (pFrameFormat->GetAttrSet().HasItem(RES_FRMATR_GRABBAG, &pItem)) 5432 { 5433 const SfxGrabBagItem* pGrabBag = static_cast<const SfxGrabBagItem*>(pItem); 5434 CharGrabBag(*pGrabBag); 5435 } 5436 5437 rtl::Reference<sax_fastparser::FastAttributeList> xFrameAttributes( 5438 FastSerializerHelper::createAttrList()); 5439 if (pGrfNode) 5440 { 5441 const SwAttrSet& rSet = pGrfNode->GetSwAttrSet(); 5442 MirrorGraph eMirror = rSet.Get(RES_GRFATR_MIRRORGRF).GetValue(); 5443 if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both) 5444 // Mirror on the vertical axis is a horizontal flip. 5445 xFrameAttributes->add(XML_flipH, "1"); 5446 // RES_GRFATR_ROTATION is sal_uInt16; use sal_uInt32 for multiplication later 5447 if (Degree10 nRot = rSet.Get(RES_GRFATR_ROTATION).GetValue()) 5448 { 5449 // RES_GRFATR_ROTATION is in 10ths of degree; convert to 100ths for macro 5450 sal_uInt32 mOOXMLRot = oox::drawingml::ExportRotateClockwisify(to<Degree100>(nRot)); 5451 xFrameAttributes->add(XML_rot, OString::number(mOOXMLRot)); 5452 } 5453 } 5454 5455 css::uno::Reference<css::beans::XPropertySet> xShapePropSet; 5456 if (pSdrObj) 5457 { 5458 css::uno::Reference<css::drawing::XShape> xShape( 5459 const_cast<SdrObject*>(pSdrObj)->getUnoShape(), css::uno::UNO_QUERY); 5460 xShapePropSet.set(xShape, css::uno::UNO_QUERY); 5461 assert(xShapePropSet); 5462 } 5463 5464 Size aSize = rSize; 5465 // We need the original (cropped, but unrotated) size of object. So prefer the object data, 5466 // and only use passed frame size as fallback. 5467 if (xShapePropSet) 5468 { 5469 if (css::awt::Size val; xShapePropSet->getPropertyValue("Size") >>= val) 5470 aSize = Size(o3tl::toTwips(val.Width, o3tl::Length::mm100), o3tl::toTwips(val.Height, o3tl::Length::mm100)); 5471 } 5472 5473 m_rExport.SdrExporter().startDMLAnchorInline(pFrameFormat, aSize); 5474 5475 // picture description (used for pic:cNvPr later too) 5476 rtl::Reference<::sax_fastparser::FastAttributeList> docPrattrList = FastSerializerHelper::createAttrList(); 5477 docPrattrList->add( XML_id, OString::number( m_anchorId++).getStr()); 5478 docPrattrList->add( XML_name, OUStringToOString( pFrameFormat->GetName(), RTL_TEXTENCODING_UTF8 ) ); 5479 docPrattrList->add( XML_descr, OUStringToOString( pGrfNode ? pGrfNode->GetDescription() : pOLEFrameFormat->GetObjDescription(), RTL_TEXTENCODING_UTF8 )); 5480 if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT ) 5481 docPrattrList->add( XML_title, OUStringToOString( pGrfNode ? pGrfNode->GetTitle() : pOLEFrameFormat->GetObjTitle(), RTL_TEXTENCODING_UTF8 )); 5482 m_pSerializer->startElementNS( XML_wp, XML_docPr, docPrattrList ); 5483 5484 OUString sURL, sRelId; 5485 if (xShapePropSet) 5486 { 5487 xShapePropSet->getPropertyValue("HyperLinkURL") >>= sURL; 5488 if(!sURL.isEmpty()) 5489 { 5490 if (sURL.startsWith("#") && sURL.indexOf(' ') != -1 && !sURL.endsWith("|outline") && !sURL.endsWith("|table") && 5491 !sURL.endsWith("|frame") && !sURL.endsWith("|graphic") && !sURL.endsWith("|ole") && !sURL.endsWith("|region")) 5492 { 5493 // Spaces are prohibited in bookmark name. 5494 sURL = sURL.replace(' ', '_'); 5495 } 5496 sRelId = GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(), 5497 oox::getRelationship(Relationship::HYPERLINK), 5498 sURL, !sURL.startsWith("#") ); 5499 m_pSerializer->singleElementNS( XML_a, XML_hlinkClick, 5500 FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main", 5501 FSNS( XML_r, XML_id ), sRelId); 5502 } 5503 } 5504 5505 m_pSerializer->endElementNS( XML_wp, XML_docPr ); 5506 5507 m_pSerializer->startElementNS(XML_wp, XML_cNvGraphicFramePr); 5508 // TODO change aspect? 5509 m_pSerializer->singleElementNS( XML_a, XML_graphicFrameLocks, 5510 FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)), 5511 XML_noChangeAspect, "1" ); 5512 m_pSerializer->endElementNS( XML_wp, XML_cNvGraphicFramePr ); 5513 5514 m_pSerializer->startElementNS( XML_a, XML_graphic, 5515 FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)) ); 5516 m_pSerializer->startElementNS( XML_a, XML_graphicData, 5517 XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/picture" ); 5518 5519 m_pSerializer->startElementNS( XML_pic, XML_pic, 5520 FSNS( XML_xmlns, XML_pic ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlPicture)) ); 5521 5522 m_pSerializer->startElementNS(XML_pic, XML_nvPicPr); 5523 // It seems pic:cNvpr and wp:docPr are pretty much the same thing with the same attributes 5524 m_pSerializer->startElementNS(XML_pic, XML_cNvPr, docPrattrList); 5525 5526 if(!sURL.isEmpty()) 5527 m_pSerializer->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId); 5528 5529 m_pSerializer->endElementNS( XML_pic, XML_cNvPr ); 5530 5531 m_pSerializer->startElementNS(XML_pic, XML_cNvPicPr); 5532 // TODO change aspect? 5533 m_pSerializer->singleElementNS( XML_a, XML_picLocks, 5534 XML_noChangeAspect, "1", XML_noChangeArrowheads, "1" ); 5535 m_pSerializer->endElementNS( XML_pic, XML_cNvPicPr ); 5536 m_pSerializer->endElementNS( XML_pic, XML_nvPicPr ); 5537 5538 // the actual picture 5539 m_pSerializer->startElementNS(XML_pic, XML_blipFill); 5540 5541 /* At this point we are certain that, WriteImage returns empty RelId 5542 for unhandled graphic type. Therefore we write the picture description 5543 and not the relation( coz there ain't any), so that the user knows 5544 there is an image/graphic in the doc but it is broken instead of 5545 completely discarding it. 5546 */ 5547 if ( aRelId.isEmpty() ) 5548 m_pSerializer->startElementNS(XML_a, XML_blip); 5549 else 5550 m_pSerializer->startElementNS(XML_a, XML_blip, FSNS(XML_r, nImageType), aRelId); 5551 5552 pItem = nullptr; 5553 5554 if ( pGrfNode && SfxItemState::SET == pGrfNode->GetSwAttrSet().GetItemState(RES_GRFATR_DRAWMODE, true, &pItem)) 5555 { 5556 GraphicDrawMode nMode = static_cast<GraphicDrawMode>(static_cast<const SfxEnumItemInterface*>(pItem)->GetEnumValue()); 5557 if (nMode == GraphicDrawMode::Greys) 5558 m_pSerializer->singleElementNS (XML_a, XML_grayscl); 5559 else if (nMode == GraphicDrawMode::Mono) //black/white has a 0,5 threshold in LibreOffice 5560 m_pSerializer->singleElementNS (XML_a, XML_biLevel, XML_thresh, OString::number(50000)); 5561 else if (nMode == GraphicDrawMode::Watermark) //watermark has a brightness/luminance of 0,5 and contrast of -0.7 in LibreOffice 5562 m_pSerializer->singleElementNS( XML_a, XML_lum, XML_bright, OString::number(70000), XML_contrast, OString::number(-70000) ); 5563 } 5564 m_pSerializer->endElementNS( XML_a, XML_blip ); 5565 5566 if (xShapePropSet) 5567 WriteSrcRect(xShapePropSet, pFrameFormat); 5568 5569 m_pSerializer->startElementNS(XML_a, XML_stretch); 5570 m_pSerializer->singleElementNS(XML_a, XML_fillRect); 5571 m_pSerializer->endElementNS( XML_a, XML_stretch ); 5572 m_pSerializer->endElementNS( XML_pic, XML_blipFill ); 5573 5574 // TODO setup the right values below 5575 m_pSerializer->startElementNS(XML_pic, XML_spPr, XML_bwMode, "auto"); 5576 5577 m_pSerializer->startElementNS(XML_a, XML_xfrm, xFrameAttributes); 5578 5579 m_pSerializer->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0"); 5580 OString aWidth( OString::number( TwipsToEMU( aSize.Width() ) ) ); 5581 OString aHeight( OString::number( TwipsToEMU( aSize.Height() ) ) ); 5582 m_pSerializer->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight); 5583 m_pSerializer->endElementNS( XML_a, XML_xfrm ); 5584 m_pSerializer->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect"); 5585 m_pSerializer->singleElementNS(XML_a, XML_avLst); 5586 m_pSerializer->endElementNS( XML_a, XML_prstGeom ); 5587 5588 const SvxBoxItem& rBoxItem = pFrameFormat->GetBox(); 5589 const SvxBorderLine* pLeft = rBoxItem.GetLine(SvxBoxItemLine::LEFT); 5590 const SvxBorderLine* pRight = rBoxItem.GetLine(SvxBoxItemLine::RIGHT); 5591 const SvxBorderLine* pTop = rBoxItem.GetLine(SvxBoxItemLine::TOP); 5592 const SvxBorderLine* pBottom = rBoxItem.GetLine(SvxBoxItemLine::BOTTOM); 5593 if (pLeft || pRight || pTop || pBottom) 5594 m_rExport.SdrExporter().writeBoxItemLine(rBoxItem); 5595 5596 m_rExport.SdrExporter().writeDMLEffectLst(*pFrameFormat); 5597 5598 m_pSerializer->endElementNS( XML_pic, XML_spPr ); 5599 5600 m_pSerializer->endElementNS( XML_pic, XML_pic ); 5601 5602 m_pSerializer->endElementNS( XML_a, XML_graphicData ); 5603 m_pSerializer->endElementNS( XML_a, XML_graphic ); 5604 m_rExport.SdrExporter().endDMLAnchorInline(pFrameFormat); 5605 } 5606 5607 void DocxAttributeOutput::WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rOLENode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat, const sal_Int8 nFormulaAlignment ) 5608 { 5609 if( WriteOLEChart( pSdrObj, rSize, pFlyFrameFormat )) 5610 return; 5611 if( WriteOLEMath( rOLENode , nFormulaAlignment)) 5612 return; 5613 PostponeOLE( rOLENode, rSize, pFlyFrameFormat ); 5614 } 5615 5616 bool DocxAttributeOutput::WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat ) 5617 { 5618 uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY ); 5619 if (!xShape.is()) 5620 return false; 5621 5622 uno::Reference<beans::XPropertySet> const xPropSet(xShape, uno::UNO_QUERY); 5623 if (!xPropSet.is()) 5624 return false; 5625 5626 OUString clsid; // why is the property of type string, not sequence<byte>? 5627 xPropSet->getPropertyValue("CLSID") >>= clsid; 5628 assert(!clsid.isEmpty()); 5629 SvGlobalName aClassID; 5630 bool const isValid(aClassID.MakeId(clsid)); 5631 assert(isValid); (void)isValid; 5632 5633 if (!SotExchange::IsChart(aClassID)) 5634 return false; 5635 5636 m_aPostponedCharts.push_back(PostponedChart(pSdrObj, rSize, pFlyFrameFormat)); 5637 return true; 5638 } 5639 5640 /* 5641 * Write chart hierarchy in w:drawing after end element of w:rPr tag. 5642 */ 5643 void DocxAttributeOutput::WritePostponedChart() 5644 { 5645 if (m_aPostponedCharts.empty()) 5646 return; 5647 5648 for (const PostponedChart& rChart : m_aPostponedCharts) 5649 { 5650 uno::Reference< chart2::XChartDocument > xChartDoc; 5651 uno::Reference< drawing::XShape > xShape(const_cast<SdrObject*>(rChart.object)->getUnoShape(), uno::UNO_QUERY ); 5652 if( xShape.is() ) 5653 { 5654 uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); 5655 if( xPropSet.is() ) 5656 xChartDoc.set( xPropSet->getPropertyValue( "Model" ), uno::UNO_QUERY ); 5657 } 5658 5659 if( xChartDoc.is() ) 5660 { 5661 SAL_INFO("sw.ww8", "DocxAttributeOutput::WriteOLE2Obj: export chart "); 5662 5663 m_rExport.SdrExporter().startDMLAnchorInline(rChart.frame, rChart.size); 5664 5665 OUString sName("Object 1"); 5666 uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY ); 5667 if( xNamed.is() ) 5668 sName = xNamed->getName(); 5669 5670 /* If there is a scenario where a chart is followed by a shape 5671 which is being exported as an alternate content then, the 5672 docPr Id is being repeated, ECMA 20.4.2.5 says that the 5673 docPr Id should be unique, ensuring the same here. 5674 */ 5675 m_pSerializer->singleElementNS( XML_wp, XML_docPr, 5676 XML_id, OString::number(m_anchorId++), 5677 XML_name, sName ); 5678 5679 m_pSerializer->singleElementNS(XML_wp, XML_cNvGraphicFramePr); 5680 5681 m_pSerializer->startElementNS( XML_a, XML_graphic, 5682 FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)) ); 5683 5684 m_pSerializer->startElementNS( XML_a, XML_graphicData, 5685 XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart" ); 5686 5687 OString aRelId; 5688 m_nChartCount++; 5689 aRelId = m_rExport.OutputChart( xChartDoc, m_nChartCount, m_pSerializer ); 5690 5691 m_pSerializer->singleElementNS( XML_c, XML_chart, 5692 FSNS( XML_xmlns, XML_c ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlChart)), 5693 FSNS( XML_xmlns, XML_r ), GetExport().GetFilter().getNamespaceURL(OOX_NS(officeRel)), 5694 FSNS( XML_r, XML_id ), aRelId ); 5695 5696 m_pSerializer->endElementNS( XML_a, XML_graphicData ); 5697 m_pSerializer->endElementNS( XML_a, XML_graphic ); 5698 5699 m_rExport.SdrExporter().endDMLAnchorInline(rChart.frame); 5700 } 5701 } 5702 5703 m_aPostponedCharts.clear(); 5704 } 5705 5706 bool DocxAttributeOutput::WriteOLEMath( const SwOLENode& rOLENode ,const sal_Int8 nAlign) 5707 { 5708 uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode&>(rOLENode).GetOLEObj().GetOleRef()); 5709 SvGlobalName aObjName(xObj->getClassID()); 5710 5711 if( !SotExchange::IsMath(aObjName) ) 5712 return false; 5713 5714 try 5715 { 5716 PostponedMathObjects aPostponedMathObject; 5717 aPostponedMathObject.pMathObject = const_cast<SwOLENode*>( &rOLENode); 5718 aPostponedMathObject.nMathObjAlignment = nAlign; 5719 m_aPostponedMaths.push_back(aPostponedMathObject); 5720 } 5721 catch (const uno::Exception&) 5722 { 5723 } 5724 return true; 5725 } 5726 5727 void DocxAttributeOutput::WritePostponedMath(const SwOLENode* pPostponedMath, sal_Int8 nAlign) 5728 { 5729 uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode*>(pPostponedMath)->GetOLEObj().GetOleRef()); 5730 if (embed::EmbedStates::LOADED == xObj->getCurrentState()) 5731 { 5732 // must be running so there is a Component 5733 try 5734 { 5735 xObj->changeState(embed::EmbedStates::RUNNING); 5736 } 5737 catch (const uno::Exception&) 5738 { 5739 } 5740 } 5741 uno::Reference< uno::XInterface > xInterface( xObj->getComponent(), uno::UNO_QUERY ); 5742 if (!xInterface.is()) 5743 { 5744 SAL_WARN("sw.ww8", "Broken math object"); 5745 return; 5746 } 5747 // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class, 5748 // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated 5749 // to RTLD_GLOBAL, so most probably a gcc bug. 5750 oox::FormulaExportBase* formulaexport = dynamic_cast<oox::FormulaExportBase*>(dynamic_cast<SfxBaseModel*>(xInterface.get())); 5751 assert( formulaexport != nullptr ); 5752 if (formulaexport) 5753 formulaexport->writeFormulaOoxml( m_pSerializer, GetExport().GetFilter().getVersion(), 5754 oox::drawingml::DOCUMENT_DOCX, nAlign); 5755 } 5756 5757 void DocxAttributeOutput::WritePostponedFormControl(const SdrObject* pObject) 5758 { 5759 if (!pObject || pObject->GetObjInventor() != SdrInventor::FmForm) 5760 return; 5761 5762 SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject)); 5763 if (!pFormObj) 5764 return; 5765 5766 uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel(); 5767 uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY); 5768 if (!xInfo.is()) 5769 return; 5770 5771 if (xInfo->supportsService("com.sun.star.form.component.DateField")) 5772 { 5773 // gather component properties 5774 5775 OUString sDateFormat; 5776 uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY); 5777 5778 OString sDate; 5779 OUString aContentText; 5780 bool bHasDate = false; 5781 css::util::Date aUNODate; 5782 if (xPropertySet->getPropertyValue("Date") >>= aUNODate) 5783 { 5784 bHasDate = true; 5785 Date aDate(aUNODate.Day, aUNODate.Month, aUNODate.Year); 5786 sDate = DateToOString(aDate); 5787 aContentText = OUString::createFromAscii(DateToDDMMYYYYOString(aDate).getStr()); 5788 sDateFormat = "dd/MM/yyyy"; 5789 } 5790 else 5791 { 5792 aContentText = xPropertySet->getPropertyValue("HelpText").get<OUString>(); 5793 if(sDateFormat.isEmpty()) 5794 sDateFormat = "dd/MM/yyyy"; // Need to set date format even if there is no date set 5795 } 5796 5797 // output component 5798 5799 m_pSerializer->startElementNS(XML_w, XML_sdt); 5800 m_pSerializer->startElementNS(XML_w, XML_sdtPr); 5801 5802 if (bHasDate) 5803 m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sDate); 5804 else 5805 m_pSerializer->startElementNS(XML_w, XML_date); 5806 5807 m_pSerializer->singleElementNS(XML_w, XML_dateFormat, FSNS(XML_w, XML_val), sDateFormat); 5808 m_pSerializer->singleElementNS(XML_w, XML_lid, 5809 FSNS(XML_w, XML_val), "en-US"); 5810 m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs, 5811 FSNS(XML_w, XML_val), "dateTime"); 5812 m_pSerializer->singleElementNS(XML_w, XML_calendar, 5813 FSNS(XML_w, XML_val), "gregorian"); 5814 5815 m_pSerializer->endElementNS(XML_w, XML_date); 5816 m_pSerializer->endElementNS(XML_w, XML_sdtPr); 5817 5818 m_pSerializer->startElementNS(XML_w, XML_sdtContent); 5819 m_pSerializer->startElementNS(XML_w, XML_r); 5820 5821 RunText(aContentText); 5822 m_pSerializer->endElementNS(XML_w, XML_r); 5823 m_pSerializer->endElementNS(XML_w, XML_sdtContent); 5824 5825 m_pSerializer->endElementNS(XML_w, XML_sdt); 5826 } 5827 else if (xInfo->supportsService("com.sun.star.form.component.ComboBox")) 5828 { 5829 // gather component properties 5830 5831 uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY); 5832 OUString sText = xPropertySet->getPropertyValue("Text").get<OUString>(); 5833 const uno::Sequence<OUString> aItems = xPropertySet->getPropertyValue("StringItemList").get< uno::Sequence<OUString> >(); 5834 5835 // output component 5836 5837 m_pSerializer->startElementNS(XML_w, XML_sdt); 5838 m_pSerializer->startElementNS(XML_w, XML_sdtPr); 5839 5840 m_pSerializer->startElementNS(XML_w, XML_dropDownList); 5841 5842 for (const auto& rItem : aItems) 5843 { 5844 m_pSerializer->singleElementNS(XML_w, XML_listItem, 5845 FSNS(XML_w, XML_displayText), rItem, 5846 FSNS(XML_w, XML_value), rItem); 5847 } 5848 5849 m_pSerializer->endElementNS(XML_w, XML_dropDownList); 5850 m_pSerializer->endElementNS(XML_w, XML_sdtPr); 5851 5852 m_pSerializer->startElementNS(XML_w, XML_sdtContent); 5853 m_pSerializer->startElementNS(XML_w, XML_r); 5854 RunText(sText); 5855 m_pSerializer->endElementNS(XML_w, XML_r); 5856 m_pSerializer->endElementNS(XML_w, XML_sdtContent); 5857 5858 m_pSerializer->endElementNS(XML_w, XML_sdt); 5859 } 5860 } 5861 5862 void DocxAttributeOutput::WritePostponedActiveXControl(bool bInsideRun) 5863 { 5864 for( const auto & rPostponedDrawing : m_aPostponedActiveXControls ) 5865 { 5866 WriteActiveXControl(rPostponedDrawing.object, *rPostponedDrawing.frame, bInsideRun); 5867 } 5868 m_aPostponedActiveXControls.clear(); 5869 } 5870 5871 5872 void DocxAttributeOutput::WriteActiveXControl(const SdrObject* pObject, const SwFrameFormat& rFrameFormat, bool bInsideRun) 5873 { 5874 SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject)); 5875 if (!pFormObj) 5876 return; 5877 5878 uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel(); 5879 if (!xControlModel.is()) 5880 return; 5881 5882 const bool bAnchoredInline = rFrameFormat.GetAnchor().GetAnchorId() == static_cast<RndStdIds>(css::text::TextContentAnchorType_AS_CHARACTER); 5883 5884 if(!bInsideRun) 5885 { 5886 m_pSerializer->startElementNS(XML_w, XML_r); 5887 } 5888 5889 // w:pict for floating embedded control and w:object for inline embedded control 5890 if(bAnchoredInline) 5891 m_pSerializer->startElementNS(XML_w, XML_object); 5892 else 5893 m_pSerializer->startElementNS(XML_w, XML_pict); 5894 5895 // write ActiveX fragment and ActiveX binary 5896 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(), uno::UNO_QUERY); 5897 std::pair<OString,OString> sRelIdAndName = m_rExport.WriteActiveXObject(xShape, xControlModel); 5898 5899 // VML shape definition 5900 m_rExport.VMLExporter().SetSkipwzName(true); 5901 m_rExport.VMLExporter().SetHashMarkForType(true); 5902 m_rExport.VMLExporter().OverrideShapeIDGen(true, "control_shape_"); 5903 OString sShapeId; 5904 if(bAnchoredInline) 5905 { 5906 sShapeId = m_rExport.VMLExporter().AddInlineSdrObject(*pObject, true); 5907 } 5908 else 5909 { 5910 SwFormatFollowTextFlow const& rFlow(rFrameFormat.GetFollowTextFlow()); 5911 const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient(); 5912 const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient(); 5913 SwFormatSurround const& rSurround(rFrameFormat.GetSurround()); 5914 rtl::Reference<sax_fastparser::FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround)); 5915 sShapeId = m_rExport.VMLExporter().AddSdrObject(*pObject, 5916 rFlow.GetValue(), 5917 rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(), 5918 rHoriOri.GetRelationOrient(), 5919 rVertOri.GetRelationOrient(), 5920 pAttrList.get(), 5921 true); 5922 } 5923 // Restore default values 5924 m_rExport.VMLExporter().SetSkipwzName(false); 5925 m_rExport.VMLExporter().SetHashMarkForType(false); 5926 m_rExport.VMLExporter().OverrideShapeIDGen(false); 5927 5928 // control 5929 m_pSerializer->singleElementNS(XML_w, XML_control, 5930 FSNS(XML_r, XML_id), sRelIdAndName.first, 5931 FSNS(XML_w, XML_name), sRelIdAndName.second, 5932 FSNS(XML_w, XML_shapeid), sShapeId); 5933 5934 if(bAnchoredInline) 5935 m_pSerializer->endElementNS(XML_w, XML_object); 5936 else 5937 m_pSerializer->endElementNS(XML_w, XML_pict); 5938 5939 if(!bInsideRun) 5940 { 5941 m_pSerializer->endElementNS(XML_w, XML_r); 5942 } 5943 } 5944 5945 bool DocxAttributeOutput::ExportAsActiveXControl(const SdrObject* pObject) const 5946 { 5947 SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject)); 5948 if (!pFormObj) 5949 return false; 5950 5951 uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel(); 5952 if (!xControlModel.is()) 5953 return false; 5954 5955 uno::Reference< css::frame::XModel > xModel( m_rExport.m_rDoc.GetDocShell() ? m_rExport.m_rDoc.GetDocShell()->GetModel() : nullptr ); 5956 if (!xModel.is()) 5957 return false; 5958 5959 uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY); 5960 if (!xInfo.is()) 5961 return false; 5962 5963 // See WritePostponedFormControl 5964 // By now date field and combobox is handled on a different way, so let's not interfere with the other method. 5965 if(xInfo->supportsService("com.sun.star.form.component.DateField") || 5966 xInfo->supportsService("com.sun.star.form.component.ComboBox")) 5967 return false; 5968 5969 oox::ole::OleFormCtrlExportHelper exportHelper(comphelper::getProcessComponentContext(), xModel, xControlModel); 5970 return exportHelper.isValid(); 5971 } 5972 5973 void DocxAttributeOutput::PostponeOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat ) 5974 { 5975 if( !m_pPostponedOLEs ) 5976 //cannot be postponed, try to write now 5977 WriteOLE( rNode, rSize, pFlyFrameFormat ); 5978 else 5979 m_pPostponedOLEs->push_back( PostponedOLE( &rNode, rSize, pFlyFrameFormat ) ); 5980 } 5981 5982 /* 5983 * Write w:object hierarchy for embedded objects after end element of w:rPr tag. 5984 */ 5985 void DocxAttributeOutput::WritePostponedOLE() 5986 { 5987 if( !m_pPostponedOLEs ) 5988 return; 5989 5990 for( const auto & rPostponedOLE : *m_pPostponedOLEs ) 5991 { 5992 WriteOLE( *rPostponedOLE.object, rPostponedOLE.size, rPostponedOLE.frame ); 5993 } 5994 5995 // clear list of postponed objects 5996 m_pPostponedOLEs.reset(); 5997 } 5998 5999 void DocxAttributeOutput::WriteOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat ) 6000 { 6001 OSL_ASSERT(pFlyFrameFormat); 6002 6003 // get interoperability information about embedded objects 6004 uno::Reference< beans::XPropertySet > xPropSet( m_rExport.m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); 6005 uno::Sequence< beans::PropertyValue > aGrabBag, aObjectsInteropList,aObjectInteropAttributes; 6006 xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= aGrabBag; 6007 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag), 6008 [](const beans::PropertyValue& rProp) { return rProp.Name == "EmbeddedObjects"; }); 6009 if (pProp != std::cend(aGrabBag)) 6010 pProp->Value >>= aObjectsInteropList; 6011 6012 SwOLEObj& aObject = rNode.GetOLEObj(); 6013 uno::Reference < embed::XEmbeddedObject > xObj( aObject.GetOleRef() ); 6014 comphelper::EmbeddedObjectContainer* aContainer = aObject.GetObject().GetContainer(); 6015 OUString sObjectName = aContainer->GetEmbeddedObjectName( xObj ); 6016 6017 // set some attributes according to the type of the embedded object 6018 OUString sProgID, sDrawAspect; 6019 switch (rNode.GetAspect()) 6020 { 6021 case embed::Aspects::MSOLE_CONTENT: sDrawAspect = "Content"; break; 6022 case embed::Aspects::MSOLE_DOCPRINT: sDrawAspect = "DocPrint"; break; 6023 case embed::Aspects::MSOLE_ICON: sDrawAspect = "Icon"; break; 6024 case embed::Aspects::MSOLE_THUMBNAIL: sDrawAspect = "Thumbnail"; break; 6025 default: 6026 SAL_WARN("sw.ww8", "DocxAttributeOutput::WriteOLE: invalid aspect value"); 6027 } 6028 auto pObjectsInterop = std::find_if(std::cbegin(aObjectsInteropList), std::cend(aObjectsInteropList), 6029 [&sObjectName](const beans::PropertyValue& rProp) { return rProp.Name == sObjectName; }); 6030 if (pObjectsInterop != std::cend(aObjectsInteropList)) 6031 pObjectsInterop->Value >>= aObjectInteropAttributes; 6032 6033 for( const auto& rObjectInteropAttribute : std::as_const(aObjectInteropAttributes) ) 6034 { 6035 if ( rObjectInteropAttribute.Name == "ProgID" ) 6036 { 6037 rObjectInteropAttribute.Value >>= sProgID; 6038 } 6039 } 6040 6041 // write embedded file 6042 OString sId = m_rExport.WriteOLEObject(aObject, sProgID); 6043 6044 if( sId.isEmpty() ) 6045 { 6046 // the embedded file could not be saved 6047 // fallback: save as an image 6048 FlyFrameGraphic( nullptr, rSize, pFlyFrameFormat, &rNode ); 6049 return; 6050 } 6051 6052 // write preview image 6053 const Graphic* pGraphic = rNode.GetGraphic(); 6054 m_rDrawingML.SetFS(m_pSerializer); 6055 OUString sImageId = m_rDrawingML.WriteImage( *pGraphic ); 6056 6057 if ( sDrawAspect == "Content" ) 6058 { 6059 try 6060 { 6061 awt::Size aSize = xObj->getVisualAreaSize( rNode.GetAspect() ); 6062 6063 MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( rNode.GetAspect() ) ); 6064 Size aOriginalSize( OutputDevice::LogicToLogic(Size( aSize.Width, aSize.Height), 6065 MapMode(aUnit), MapMode(MapUnit::MapTwip))); 6066 6067 m_pSerializer->startElementNS( XML_w, XML_object, 6068 FSNS(XML_w, XML_dxaOrig), OString::number(aOriginalSize.Width()), 6069 FSNS(XML_w, XML_dyaOrig), OString::number(aOriginalSize.Height()) ); 6070 } 6071 catch ( uno::Exception& ) 6072 { 6073 m_pSerializer->startElementNS(XML_w, XML_object); 6074 } 6075 } 6076 else 6077 { 6078 m_pSerializer->startElementNS(XML_w, XML_object); 6079 } 6080 6081 OString sShapeId = "ole_" + sId; 6082 6083 //OLE Shape definition 6084 WriteOLEShape(*pFlyFrameFormat, rSize, sShapeId, sImageId); 6085 6086 //OLE Object definition 6087 m_pSerializer->singleElementNS(XML_o, XML_OLEObject, 6088 XML_Type, "Embed", 6089 XML_ProgID, sProgID, 6090 XML_ShapeID, sShapeId.getStr(), 6091 XML_DrawAspect, sDrawAspect, 6092 XML_ObjectID, "_" + OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())), 6093 FSNS( XML_r, XML_id ), sId ); 6094 6095 m_pSerializer->endElementNS(XML_w, XML_object); 6096 } 6097 6098 void DocxAttributeOutput::WriteOLEShape(const SwFlyFrameFormat& rFrameFormat, const Size& rSize, 6099 const OString& rShapeId, const OUString& rImageId) 6100 { 6101 assert(m_pSerializer); 6102 6103 //Here is an attribute list where we collect the attributes what we want to export 6104 rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList(); 6105 pAttr->add(XML_id, rShapeId); 6106 6107 //export the fixed shape type for picture frame 6108 m_pSerializer->write(vml::VMLExport::GetVMLShapeTypeDefinition(rShapeId, true)); 6109 pAttr->add(XML_type, "_x0000_t" + rShapeId); 6110 6111 //Export the style attribute for position and size 6112 pAttr->add(XML_style, GetOLEStyle(rFrameFormat, rSize)); 6113 //Get the OLE frame 6114 const SvxBoxItem& rBox = rFrameFormat.GetAttrSet().GetBox(); 6115 OString sLineType; 6116 OString sDashType; 6117 //Word does not handle differently the four sides, 6118 //so we have to choose, and the left one is the winner: 6119 if (rBox.GetLeft()) 6120 { 6121 //Get the left border color and width 6122 const Color aLineColor = rBox.GetLeft()->GetColor(); 6123 const tools::Long aLineWidth = rBox.GetLeft()->GetWidth(); 6124 6125 //Convert the left OLE border style to OOXML 6126 //FIXME improve if it's necessary 6127 switch (rBox.GetLeft()->GetBorderLineStyle()) 6128 { 6129 case SvxBorderLineStyle::SOLID: 6130 sLineType = OString("Single"); 6131 sDashType = OString("Solid"); 6132 break; 6133 case SvxBorderLineStyle::DASHED: 6134 sLineType = OString("Single"); 6135 sDashType = OString("Dash"); 6136 break; 6137 case SvxBorderLineStyle::DASH_DOT: 6138 sLineType = OString("Single"); 6139 sDashType = OString("DashDot"); 6140 break; 6141 case SvxBorderLineStyle::DASH_DOT_DOT: 6142 sLineType = OString("Single"); 6143 sDashType = OString("ShortDashDotDot"); 6144 break; 6145 case SvxBorderLineStyle::DOTTED: 6146 sLineType = OString("Single"); 6147 sDashType = OString("Dot"); 6148 break; 6149 case SvxBorderLineStyle::DOUBLE: 6150 sLineType = OString("ThinThin"); 6151 sDashType = OString("Solid"); 6152 break; 6153 case SvxBorderLineStyle::DOUBLE_THIN: 6154 sLineType = OString("ThinThin"); 6155 sDashType = OString("Solid"); 6156 break; 6157 case SvxBorderLineStyle::EMBOSSED: 6158 sLineType = OString("Single"); 6159 sDashType = OString("Solid"); 6160 break; 6161 case SvxBorderLineStyle::ENGRAVED: 6162 sLineType = OString("Single"); 6163 sDashType = OString("Solid"); 6164 break; 6165 case SvxBorderLineStyle::FINE_DASHED: 6166 sLineType = OString("Single"); 6167 sDashType = OString("Dot"); 6168 break; 6169 case SvxBorderLineStyle::INSET: 6170 sLineType = OString("Single"); 6171 sDashType = OString("Solid"); 6172 break; 6173 case SvxBorderLineStyle::OUTSET: 6174 sLineType = OString("Single"); 6175 sDashType = OString("Solid"); 6176 break; 6177 case SvxBorderLineStyle::THICKTHIN_LARGEGAP: 6178 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: 6179 case SvxBorderLineStyle::THICKTHIN_SMALLGAP: 6180 sLineType = OString("ThickThin"); 6181 sDashType = OString("Solid"); 6182 break; 6183 case SvxBorderLineStyle::THINTHICK_LARGEGAP: 6184 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: 6185 case SvxBorderLineStyle::THINTHICK_SMALLGAP: 6186 sLineType = OString("ThinThick"); 6187 sDashType = OString("Solid"); 6188 break; 6189 case SvxBorderLineStyle::NONE: 6190 sLineType = OString(""); 6191 sDashType = OString(""); 6192 break; 6193 default: 6194 SAL_WARN("sw.ww8", "Unknown line type on OOXML ELE export!"); 6195 break; 6196 } 6197 6198 //If there is a line add it for export 6199 if (!sLineType.isEmpty() && !sDashType.isEmpty()) 6200 { 6201 pAttr->add(XML_stroked, "t"); 6202 pAttr->add(XML_strokecolor, "#" + msfilter::util::ConvertColor(aLineColor)); 6203 pAttr->add(XML_strokeweight, OString::number(aLineWidth / 20) + "pt"); 6204 } 6205 } 6206 6207 //Let's check the filltype of the OLE 6208 switch (rFrameFormat.GetAttrSet().Get(XATTR_FILLSTYLE).GetValue()) 6209 { 6210 case drawing::FillStyle::FillStyle_SOLID: 6211 { 6212 //If solid, we get the color and add it to the exporter 6213 const Color rShapeColor = rFrameFormat.GetAttrSet().Get(XATTR_FILLCOLOR).GetColorValue(); 6214 pAttr->add(XML_filled, "t"); 6215 pAttr->add(XML_fillcolor, "#" + msfilter::util::ConvertColor(rShapeColor)); 6216 break; 6217 } 6218 case drawing::FillStyle::FillStyle_GRADIENT: 6219 case drawing::FillStyle::FillStyle_HATCH: 6220 case drawing::FillStyle::FillStyle_BITMAP: 6221 //TODO 6222 break; 6223 case drawing::FillStyle::FillStyle_NONE: 6224 { 6225 pAttr->add(XML_filled, "f"); 6226 break; 6227 } 6228 default: 6229 SAL_WARN("sw.ww8", "Unknown fill type on OOXML OLE export!"); 6230 break; 6231 } 6232 pAttr->addNS(XML_o, XML_ole, ""); //compulsory, even if it's empty 6233 m_pSerializer->startElementNS(XML_v, XML_shape, pAttr);//Write the collected attrs... 6234 6235 if (!sLineType.isEmpty() && !sDashType.isEmpty()) //If there is a line/dash style it is time to export it 6236 { 6237 m_pSerializer->singleElementNS(XML_v, XML_stroke, XML_linestyle, sLineType, XML_dashstyle, sDashType); 6238 } 6239 6240 // shape filled with the preview image 6241 m_pSerializer->singleElementNS(XML_v, XML_imagedata, 6242 FSNS(XML_r, XML_id), rImageId, 6243 FSNS(XML_o, XML_title), ""); 6244 6245 //export wrap settings 6246 if (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) //As-char objs does not have surround. 6247 ExportOLESurround(rFrameFormat.GetSurround()); 6248 6249 m_pSerializer->endElementNS(XML_v, XML_shape); 6250 } 6251 6252 OString DocxAttributeOutput::GetOLEStyle(const SwFlyFrameFormat& rFormat, const Size& rSize) 6253 { 6254 //tdf#131539: Export OLE positions in docx: 6255 //This string will store the position output for the xml 6256 OString aPos; 6257 //This string will store the relative position for aPos 6258 OString aAnch; 6259 6260 if (rFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) 6261 { 6262 //Get the horizontal alignment of the OLE via the frame format, to aHAlign 6263 OString aHAlign = convertToOOXMLHoriOrient(rFormat.GetHoriOrient().GetHoriOrient(), 6264 rFormat.GetHoriOrient().IsPosToggle()); 6265 //Get the vertical alignment of the OLE via the frame format to aVAlign 6266 OString aVAlign = convertToOOXMLVertOrient(rFormat.GetVertOrient().GetVertOrient()); 6267 6268 // Check if the OLE anchored to page: 6269 const bool bIsPageAnchor = rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE; 6270 6271 //Get the relative horizontal positions for the anchors 6272 OString aHAnch 6273 = bIsPageAnchor 6274 ? OString("page") 6275 : convertToOOXMLHoriOrientRel(rFormat.GetHoriOrient().GetRelationOrient()); 6276 //Get the relative vertical positions for the anchors 6277 OString aVAnch = convertToOOXMLVertOrientRel(rFormat.GetVertOrient().GetRelationOrient()); 6278 6279 //Choice that the horizontal position is relative or not 6280 if (!aHAlign.isEmpty()) 6281 aHAlign = ";mso-position-horizontal:" + aHAlign; 6282 aHAlign = ";mso-position-horizontal-relative:" + aHAnch; 6283 6284 //Choice that the vertical position is relative or not 6285 if (!aVAlign.isEmpty()) 6286 aVAlign = ";mso-position-vertical:" + aVAlign; 6287 aVAlign = ";mso-position-vertical-relative:" + aVAnch; 6288 6289 //Set the anchoring information into one string for aPos 6290 aAnch = aHAlign + aVAlign; 6291 6292 //Query the positions to aPos from frameformat 6293 aPos = 6294 "position:absolute;margin-left:" + OString::number(double(rFormat.GetHoriOrient().GetPos()) / 20) + 6295 "pt;margin-top:" + OString::number(double(rFormat.GetVertOrient().GetPos()) / 20) + "pt;"; 6296 } 6297 6298 OString sShapeStyle = "width:" + OString::number( double( rSize.Width() ) / 20 ) + 6299 "pt;height:" + OString::number( double( rSize.Height() ) / 20 ) + 6300 "pt"; //from VMLExport::AddRectangleDimensions(), it does: value/20 6301 6302 const SvxLRSpaceItem& rLRSpace = rFormat.GetLRSpace(); 6303 if (rLRSpace.IsExplicitZeroMarginValLeft() || rLRSpace.GetLeft()) 6304 sShapeStyle += ";mso-wrap-distance-left:" + OString::number(double(rLRSpace.GetLeft()) / 20) + "pt"; 6305 if (rLRSpace.IsExplicitZeroMarginValRight() || rLRSpace.GetRight()) 6306 sShapeStyle += ";mso-wrap-distance-right:" + OString::number(double(rLRSpace.GetRight()) / 20) + "pt"; 6307 const SvxULSpaceItem& rULSpace = rFormat.GetULSpace(); 6308 if (rULSpace.GetUpper()) 6309 sShapeStyle += ";mso-wrap-distance-top:" + OString::number(double(rULSpace.GetUpper()) / 20) + "pt"; 6310 if (rULSpace.GetLower()) 6311 sShapeStyle += ";mso-wrap-distance-bottom:" + OString::number(double(rULSpace.GetLower()) / 20) + "pt"; 6312 6313 //Export anchor setting, if it exists 6314 if (!aPos.isEmpty() && !aAnch.isEmpty()) 6315 sShapeStyle = aPos + sShapeStyle + aAnch; 6316 6317 return sShapeStyle; 6318 } 6319 6320 void DocxAttributeOutput::ExportOLESurround(const SwFormatSurround& rWrap) 6321 { 6322 const bool bIsContour = rWrap.IsContour(); //Has the shape contour or not 6323 OString sSurround; 6324 OString sSide; 6325 6326 //Map the ODF wrap settings to OOXML one 6327 switch (rWrap.GetSurround()) 6328 { 6329 case text::WrapTextMode::WrapTextMode_NONE: 6330 sSurround = OString("topAndBottom"); 6331 break; 6332 case text::WrapTextMode::WrapTextMode_PARALLEL: 6333 sSurround = bIsContour ? OString("tight") : OString("square"); 6334 break; 6335 case text::WrapTextMode::WrapTextMode_DYNAMIC: 6336 sSide = OString("largest"); 6337 sSurround = bIsContour ? OString("tight") : OString("square"); 6338 break; 6339 case text::WrapTextMode::WrapTextMode_LEFT: 6340 sSide = OString("left"); 6341 sSurround = bIsContour ? OString("tight") : OString("square"); 6342 break; 6343 case text::WrapTextMode::WrapTextMode_RIGHT: 6344 sSide = OString("right"); 6345 sSurround = bIsContour ? OString("tight") : OString("square"); 6346 break; 6347 default: 6348 SAL_WARN("sw.ww8", "Unknown surround type on OOXML export!"); 6349 break; 6350 } 6351 6352 //if there is a setting export it: 6353 if (!sSurround.isEmpty()) 6354 { 6355 if (sSide.isEmpty()) 6356 m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, sSurround); 6357 else 6358 m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, sSurround, XML_side, sSide); 6359 } 6360 } 6361 6362 void DocxAttributeOutput::WritePostponedCustomShape() 6363 { 6364 if (!m_pPostponedCustomShape) 6365 return; 6366 6367 for( const auto & rPostponedDrawing : *m_pPostponedCustomShape) 6368 { 6369 if ( IsAlternateContentChoiceOpen() ) 6370 m_rExport.SdrExporter().writeDMLDrawing(rPostponedDrawing.object, rPostponedDrawing.frame, m_anchorId++); 6371 else 6372 m_rExport.SdrExporter().writeDMLAndVMLDrawing(rPostponedDrawing.object, *rPostponedDrawing.frame, m_anchorId++); 6373 } 6374 m_pPostponedCustomShape.reset(); 6375 } 6376 6377 void DocxAttributeOutput::WritePostponedDMLDrawing() 6378 { 6379 if (!m_pPostponedDMLDrawings) 6380 return; 6381 6382 // Clear the list early, this method may be called recursively. 6383 std::unique_ptr< std::vector<PostponedDrawing> > pPostponedDMLDrawings(std::move(m_pPostponedDMLDrawings)); 6384 std::unique_ptr< std::vector<PostponedOLE> > pPostponedOLEs(std::move(m_pPostponedOLEs)); 6385 6386 for( const auto & rPostponedDrawing : *pPostponedDMLDrawings ) 6387 { 6388 // Avoid w:drawing within another w:drawing. 6389 if ( IsAlternateContentChoiceOpen() && !( m_rExport.SdrExporter().IsDrawingOpen()) ) 6390 m_rExport.SdrExporter().writeDMLDrawing(rPostponedDrawing.object, rPostponedDrawing.frame, m_anchorId++); 6391 else 6392 m_rExport.SdrExporter().writeDMLAndVMLDrawing(rPostponedDrawing.object, *rPostponedDrawing.frame, m_anchorId++); 6393 } 6394 6395 m_pPostponedOLEs = std::move(pPostponedOLEs); 6396 } 6397 6398 void DocxAttributeOutput::WriteFlyFrame(const ww8::Frame& rFrame) 6399 { 6400 m_pSerializer->mark(Tag_OutputFlyFrame); 6401 6402 switch ( rFrame.GetWriterType() ) 6403 { 6404 case ww8::Frame::eGraphic: 6405 { 6406 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject(); 6407 const SwNode *pNode = rFrame.GetContent(); 6408 const SwGrfNode *pGrfNode = pNode ? pNode->GetGrfNode() : nullptr; 6409 if ( pGrfNode ) 6410 { 6411 if (!m_pPostponedGraphic) 6412 { 6413 m_bPostponedProcessingFly = false ; 6414 FlyFrameGraphic( pGrfNode, rFrame.GetLayoutSize(), nullptr, nullptr, pSdrObj); 6415 } 6416 else // we are writing out attributes, but w:drawing should not be inside w:rPr, 6417 { // so write it out later 6418 m_bPostponedProcessingFly = true ; 6419 m_pPostponedGraphic->push_back(PostponedGraphic(pGrfNode, rFrame.GetLayoutSize(), pSdrObj)); 6420 } 6421 } 6422 } 6423 break; 6424 case ww8::Frame::eDrawing: 6425 { 6426 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject(); 6427 if ( pSdrObj ) 6428 { 6429 uno::Reference<drawing::XShape> xShape( 6430 const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY); 6431 6432 if (xShape.is() && oox::drawingml::DrawingML::IsDiagram(xShape)) 6433 { 6434 if ( !m_pPostponedDiagrams ) 6435 { 6436 m_bPostponedProcessingFly = false ; 6437 m_rExport.SdrExporter().writeDiagram( pSdrObj, rFrame.GetFrameFormat(), m_anchorId++); 6438 } 6439 else // we are writing out attributes, but w:drawing should not be inside w:rPr, 6440 { // so write it out later 6441 m_bPostponedProcessingFly = true ; 6442 m_pPostponedDiagrams->push_back( PostponedDiagram( pSdrObj, &(rFrame.GetFrameFormat()) )); 6443 } 6444 } 6445 else 6446 { 6447 if (!m_pPostponedDMLDrawings) 6448 { 6449 if ( IsAlternateContentChoiceOpen() ) 6450 { 6451 // Do not write w:drawing inside w:drawing. Instead Postpone the Inner Drawing. 6452 if( m_rExport.SdrExporter().IsDrawingOpen() ) 6453 m_pPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat()))); 6454 else 6455 m_rExport.SdrExporter().writeDMLDrawing( pSdrObj, &rFrame.GetFrameFormat(), m_anchorId++); 6456 } 6457 else 6458 m_rExport.SdrExporter().writeDMLAndVMLDrawing( pSdrObj, rFrame.GetFrameFormat(), m_anchorId++); 6459 6460 m_bPostponedProcessingFly = false ; 6461 } 6462 // IsAlternateContentChoiceOpen(): check is to ensure that only one object is getting added. Without this check, plus one object gets added 6463 // m_bParagraphFrameOpen: check if the frame is open. 6464 else if (IsAlternateContentChoiceOpen() && m_bParagraphFrameOpen) 6465 m_pPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat()))); 6466 else 6467 { 6468 // we are writing out attributes, but w:drawing should not be inside w:rPr, so write it out later 6469 m_bPostponedProcessingFly = true ; 6470 m_pPostponedDMLDrawings->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat()))); 6471 } 6472 } 6473 } 6474 } 6475 break; 6476 case ww8::Frame::eTextBox: 6477 { 6478 // If this is a TextBox of a shape, then ignore: it's handled in WriteTextBox(). 6479 if (DocxSdrExport::isTextBox(rFrame.GetFrameFormat())) 6480 break; 6481 6482 // If this is a TextBox containing a table which we already exported directly, ignore it 6483 if (m_aFloatingTablesOfParagraph.find(&rFrame.GetFrameFormat()) != m_aFloatingTablesOfParagraph.end()) 6484 break; 6485 6486 // The frame output is postponed to the end of the anchor paragraph 6487 bool bDuplicate = false; 6488 const OUString& rName = rFrame.GetFrameFormat().GetName(); 6489 unsigned nSize = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0; 6490 for( unsigned nIndex = 0; nIndex < nSize; ++nIndex ) 6491 { 6492 const OUString& rNameExisting = m_aFramesOfParagraph.top()[nIndex].GetFrameFormat().GetName(); 6493 6494 if (!rName.isEmpty() && !rNameExisting.isEmpty()) 6495 { 6496 if (rName == rNameExisting) 6497 bDuplicate = true; 6498 } 6499 } 6500 6501 if( !bDuplicate ) 6502 { 6503 m_bPostponedProcessingFly = true ; 6504 if ( m_aFramesOfParagraph.size() ) 6505 m_aFramesOfParagraph.top().emplace_back(rFrame); 6506 } 6507 } 6508 break; 6509 case ww8::Frame::eOle: 6510 { 6511 const SwFrameFormat &rFrameFormat = rFrame.GetFrameFormat(); 6512 const SdrObject *pSdrObj = rFrameFormat.FindRealSdrObject(); 6513 if ( pSdrObj ) 6514 { 6515 SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1); 6516 SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode(); 6517 6518 //output variable for the formula alignment (default inline) 6519 sal_Int8 nAlign(FormulaExportBase::eFormulaAlign::INLINE); 6520 auto xObj(rOLENd.GetOLEObj().GetOleRef()); //get the xObject of the formula 6521 6522 //tdf133030: Export formula position 6523 //If we have a formula with inline anchor... 6524 if(SotExchange::IsMath(xObj->getClassID()) && rFrame.IsInline()) 6525 { 6526 SwPosition const* const aAPos = rFrameFormat.GetAnchor().GetContentAnchor(); 6527 if(aAPos) 6528 { 6529 //Get the text node what the formula anchored to 6530 const SwTextNode* pTextNode = aAPos->nNode.GetNode().GetTextNode(); 6531 if(pTextNode && pTextNode->Len() == 1) 6532 { 6533 //Get the paragraph alignment 6534 auto aParaAdjust = pTextNode->GetSwAttrSet().GetAdjust().GetAdjust(); 6535 //And set the formula according to the paragraph alignment 6536 if (aParaAdjust == SvxAdjust::Center) 6537 nAlign = FormulaExportBase::eFormulaAlign::CENTER; 6538 else if (aParaAdjust == SvxAdjust::Right) 6539 nAlign = FormulaExportBase::eFormulaAlign::RIGHT; 6540 else // left in the case of left and justified paragraph alignments 6541 nAlign = FormulaExportBase::eFormulaAlign::LEFT; 6542 } 6543 } 6544 } 6545 WriteOLE2Obj( pSdrObj, rOLENd, rFrame.GetLayoutSize(), dynamic_cast<const SwFlyFrameFormat*>( &rFrameFormat ), nAlign); 6546 m_bPostponedProcessingFly = false ; 6547 } 6548 } 6549 break; 6550 case ww8::Frame::eFormControl: 6551 { 6552 const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject(); 6553 if(ExportAsActiveXControl(pObject)) 6554 m_aPostponedActiveXControls.emplace_back(pObject, &(rFrame.GetFrameFormat())); 6555 else 6556 m_aPostponedFormControls.push_back(pObject); 6557 m_bPostponedProcessingFly = true ; 6558 } 6559 break; 6560 default: 6561 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame& rFrame ) - frame type " << 6562 ( rFrame.GetWriterType() == ww8::Frame::eTextBox ? "eTextBox": 6563 ( rFrame.GetWriterType() == ww8::Frame::eOle ? "eOle": "???" ) ) ); 6564 break; 6565 } 6566 6567 m_pSerializer->mergeTopMarks(Tag_OutputFlyFrame); 6568 } 6569 6570 void DocxAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/) 6571 { 6572 /// The old OutputFlyFrame_Impl() moved to WriteFlyFrame(). 6573 /// Now if a frame anchored inside another frame, it will 6574 /// not be exported immediately, because OOXML does not 6575 /// support that feature, instead it postponed and exported 6576 /// later when the original shape closed. 6577 6578 if (rFrame.GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR 6579 || rFrame.IsInline()) 6580 { 6581 m_nEmbedFlyLevel++; 6582 WriteFlyFrame(rFrame); 6583 m_nEmbedFlyLevel--; 6584 return; 6585 } 6586 6587 if (m_nEmbedFlyLevel == 0) 6588 { 6589 if (m_vPostponedFlys.empty()) 6590 { 6591 m_nEmbedFlyLevel++; 6592 WriteFlyFrame(rFrame); 6593 m_nEmbedFlyLevel--; 6594 } 6595 else 6596 for (auto it = m_vPostponedFlys.begin(); it != m_vPostponedFlys.end();) 6597 { 6598 m_nEmbedFlyLevel++; 6599 WriteFlyFrame(*it); 6600 it = m_vPostponedFlys.erase(it); 6601 m_nEmbedFlyLevel--; 6602 } 6603 } 6604 else 6605 { 6606 bool bFound = false; 6607 for (const auto& i : m_vPostponedFlys) 6608 { 6609 if (i.RefersToSameFrameAs(rFrame)) 6610 { 6611 bFound = true; 6612 break; 6613 } 6614 } 6615 if (!bFound) 6616 { 6617 if (auto pParentFly = rFrame.GetContentNode()->GetFlyFormat()) 6618 { 6619 auto aHori(rFrame.GetFrameFormat().GetHoriOrient()); 6620 aHori.SetPos(aHori.GetPos() + pParentFly->GetHoriOrient().GetPos()); 6621 auto aVori(rFrame.GetFrameFormat().GetVertOrient()); 6622 aVori.SetPos(aVori.GetPos() + pParentFly->GetVertOrient().GetPos()); 6623 6624 const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(aHori); 6625 const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(aVori); 6626 const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(pParentFly->GetAnchor()); 6627 6628 m_vPostponedFlys.push_back(rFrame); 6629 } 6630 6631 } 6632 } 6633 } 6634 6635 void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj) 6636 { 6637 const EditTextObject& rEditObj = rParaObj.GetTextObject(); 6638 MSWord_SdrAttrIter aAttrIter( m_rExport, rEditObj, TXT_HFTXTBOX ); 6639 6640 sal_Int32 nPara = rEditObj.GetParagraphCount(); 6641 6642 m_pSerializer->startElementNS(XML_w, XML_txbxContent); 6643 for (sal_Int32 n = 0; n < nPara; ++n) 6644 { 6645 if( n ) 6646 aAttrIter.NextPara( n ); 6647 6648 OUString aStr( rEditObj.GetText( n )); 6649 sal_Int32 nCurrentPos = 0; 6650 sal_Int32 nEnd = aStr.getLength(); 6651 6652 StartParagraph(ww8::WW8TableNodeInfo::Pointer_t(), false); 6653 6654 // Write paragraph properties. 6655 StartParagraphProperties(); 6656 aAttrIter.OutParaAttr(false); 6657 SfxItemSet aParagraphMarkerProperties(m_rExport.m_rDoc.GetAttrPool()); 6658 EndParagraphProperties(aParagraphMarkerProperties, nullptr, nullptr, nullptr); 6659 6660 do { 6661 const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd); 6662 6663 m_pSerializer->startElementNS(XML_w, XML_r); 6664 6665 // Write run properties. 6666 m_pSerializer->startElementNS(XML_w, XML_rPr); 6667 aAttrIter.OutAttr(nCurrentPos); 6668 WriteCollectedRunProperties(); 6669 m_pSerializer->endElementNS(XML_w, XML_rPr); 6670 6671 bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos ); 6672 if( !bTextAtr ) 6673 { 6674 OUString aOut( aStr.copy( nCurrentPos, nNextAttr - nCurrentPos ) ); 6675 RunText(aOut); 6676 } 6677 6678 if ( !m_sRawText.isEmpty() ) 6679 { 6680 RunText( m_sRawText ); 6681 m_sRawText.clear(); 6682 } 6683 6684 m_pSerializer->endElementNS( XML_w, XML_r ); 6685 6686 nCurrentPos = nNextAttr; 6687 aAttrIter.NextPos(); 6688 } 6689 while( nCurrentPos < nEnd ); 6690 EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t()); 6691 } 6692 m_pSerializer->endElementNS( XML_w, XML_txbxContent ); 6693 } 6694 6695 void DocxAttributeOutput::pushToTableExportContext(DocxTableExportContext& rContext) 6696 { 6697 rContext.m_pTableInfo = m_rExport.m_pTableInfo; 6698 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>(); 6699 6700 rContext.m_bTableCellOpen = m_tableReference->m_bTableCellOpen; 6701 m_tableReference->m_bTableCellOpen = false; 6702 6703 rContext.m_nTableDepth = m_tableReference->m_nTableDepth; 6704 m_tableReference->m_nTableDepth = 0; 6705 6706 rContext.m_bStartedParaSdt = m_aParagraphSdt.m_bStartedSdt; 6707 m_aParagraphSdt.m_bStartedSdt = false; 6708 } 6709 6710 void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext const & rContext) 6711 { 6712 m_rExport.m_pTableInfo = rContext.m_pTableInfo; 6713 m_tableReference->m_bTableCellOpen = rContext.m_bTableCellOpen; 6714 m_tableReference->m_nTableDepth = rContext.m_nTableDepth; 6715 m_aParagraphSdt.m_bStartedSdt = rContext.m_bStartedParaSdt; 6716 } 6717 6718 void DocxAttributeOutput::WriteTextBox(uno::Reference<drawing::XShape> xShape) 6719 { 6720 DocxTableExportContext aTableExportContext(*this); 6721 6722 SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape); 6723 assert(pTextBox); 6724 const SwPosition* pAnchor = nullptr; 6725 const bool bFlyAtPage = pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE; 6726 if (bFlyAtPage) //tdf135711 6727 { 6728 auto pNdIdx = pTextBox->GetContent().GetContentIdx(); 6729 if (pNdIdx) //Is that possible it is null? 6730 pAnchor = new SwPosition(*pNdIdx); 6731 } 6732 else 6733 { 6734 pAnchor = pTextBox->GetAnchor().GetContentAnchor();//This might be null 6735 } 6736 6737 if (pAnchor) //pAnchor can be null, so that's why not assert here. 6738 { 6739 ww8::Frame aFrame(*pTextBox, *pAnchor); 6740 m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++, /*bTextBoxOnly=*/true); 6741 if (bFlyAtPage) 6742 { 6743 delete pAnchor; 6744 } 6745 } 6746 } 6747 6748 void DocxAttributeOutput::WriteVMLTextBox(uno::Reference<drawing::XShape> xShape) 6749 { 6750 DocxTableExportContext aTableExportContext(*this); 6751 6752 SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape); 6753 assert(pTextBox); 6754 const SwPosition* pAnchor = nullptr; 6755 if (pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE) //tdf135711 6756 { 6757 auto pNdIdx = pTextBox->GetContent().GetContentIdx(); 6758 if (pNdIdx) //Is that possible it is null? 6759 pAnchor = new SwPosition(*pNdIdx); 6760 } 6761 else 6762 { 6763 pAnchor = pTextBox->GetAnchor().GetContentAnchor();//This might be null 6764 } 6765 6766 if (pAnchor) //pAnchor can be null, so that's why not assert here. 6767 { 6768 ww8::Frame aFrame(*pTextBox, *pAnchor); 6769 m_rExport.SdrExporter().writeVMLTextFrame(&aFrame, /*bTextBoxOnly=*/true); 6770 if (pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE) 6771 { 6772 delete pAnchor; 6773 } 6774 } 6775 } 6776 6777 oox::drawingml::DrawingML& DocxAttributeOutput::GetDrawingML() 6778 { 6779 return m_rDrawingML; 6780 } 6781 6782 bool DocxAttributeOutput::MaybeOutputBrushItem(SfxItemSet const& rSet) 6783 { 6784 const XFillStyleItem* pXFillStyleItem(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE)); 6785 6786 if ((pXFillStyleItem && pXFillStyleItem->GetValue() != drawing::FillStyle_NONE) 6787 || !m_rExport.SdrExporter().getDMLTextFrameSyntax()) 6788 { 6789 return false; 6790 } 6791 6792 // sw text frames are opaque by default, even with fill none! 6793 std::unique_ptr<SfxItemSet> const pClone(rSet.Clone()); 6794 XFillColorItem const aColor(OUString(), COL_WHITE); 6795 pClone->Put(aColor); 6796 // call getSvxBrushItemForSolid - this also takes XFillTransparenceItem into account 6797 XFillStyleItem const aSolid(drawing::FillStyle_SOLID); 6798 pClone->Put(aSolid); 6799 std::unique_ptr<SvxBrushItem> const pBrush(getSvxBrushItemFromSourceSet(*pClone, RES_BACKGROUND)); 6800 FormatBackground(*pBrush); 6801 return true; 6802 } 6803 6804 namespace { 6805 6806 /// Functor to do case-insensitive ordering of OUString instances. 6807 struct OUStringIgnoreCase 6808 { 6809 bool operator() (const OUString& lhs, std::u16string_view rhs) const 6810 { 6811 return lhs.compareToIgnoreAsciiCase(rhs) < 0; 6812 } 6813 }; 6814 6815 } 6816 6817 /// Guesses if a style created in Writer (no grab-bag) should be qFormat or not. 6818 static bool lcl_guessQFormat(const OUString& rName, sal_uInt16 nWwId) 6819 { 6820 // If the style has no dedicated STI number, then it's probably a custom style -> qFormat. 6821 if (nWwId == ww::stiUser) 6822 return true; 6823 6824 // Allow exported built-in styles UI language neutral 6825 if ( nWwId == ww::stiNormal || 6826 ( nWwId>= ww::stiLev1 && nWwId <= ww::stiLev9 ) || 6827 nWwId == ww::stiCaption || nWwId == ww::stiTitle || 6828 nWwId == ww::stiSubtitle || nWwId == ww::stiStrong || 6829 nWwId == ww::stiEmphasis ) 6830 return true; 6831 6832 static o3tl::sorted_vector<OUString, OUStringIgnoreCase> const aAllowlist 6833 { 6834 "No Spacing", 6835 "List Paragraph", 6836 "Quote", 6837 "Intense Quote", 6838 "Subtle Emphasis,", 6839 "Intense Emphasis", 6840 "Subtle Reference", 6841 "Intense Reference", 6842 "Book Title", 6843 "TOC Heading", 6844 }; 6845 // Not custom style? Then we have a list of standard styles which should be qFormat. 6846 return aAllowlist.find(rName) != aAllowlist.end(); 6847 } 6848 6849 void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType, 6850 sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nLink, sal_uInt16 nWwId, sal_uInt16 nId, bool bAutoUpdate ) 6851 { 6852 bool bQFormat = false, bUnhideWhenUsed = false, bSemiHidden = false, bLocked = false, bDefault = false, bCustomStyle = false; 6853 OUString aRsid, aUiPriority; 6854 rtl::Reference<FastAttributeList> pStyleAttributeList = FastSerializerHelper::createAttrList(); 6855 uno::Any aAny; 6856 if (eType == STYLE_TYPE_PARA || eType == STYLE_TYPE_CHAR) 6857 { 6858 const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nId); 6859 pFormat->GetGrabBagItem(aAny); 6860 } 6861 else 6862 { 6863 const SwNumRule* pRule = m_rExport.m_pStyles->GetSwNumRule(nId); 6864 pRule->GetGrabBagItem(aAny); 6865 } 6866 const uno::Sequence<beans::PropertyValue>& rGrabBag = aAny.get< uno::Sequence<beans::PropertyValue> >(); 6867 6868 for (const auto& rProp : rGrabBag) 6869 { 6870 if (rProp.Name == "uiPriority") 6871 aUiPriority = rProp.Value.get<OUString>(); 6872 else if (rProp.Name == "qFormat") 6873 bQFormat = true; 6874 else if (rProp.Name == "rsid") 6875 aRsid = rProp.Value.get<OUString>(); 6876 else if (rProp.Name == "unhideWhenUsed") 6877 bUnhideWhenUsed = true; 6878 else if (rProp.Name == "semiHidden") 6879 bSemiHidden = true; 6880 else if (rProp.Name == "locked") 6881 bLocked = true; 6882 else if (rProp.Name == "default") 6883 bDefault = rProp.Value.get<bool>(); 6884 else if (rProp.Name == "customStyle") 6885 bCustomStyle = rProp.Value.get<bool>(); 6886 else 6887 SAL_WARN("sw.ww8", "Unhandled style property: " << rProp.Name); 6888 } 6889 6890 // MSO exports English names and writerfilter only recognize them. 6891 const char *pEnglishName = nullptr; 6892 const char* pType = nullptr; 6893 switch (eType) 6894 { 6895 case STYLE_TYPE_PARA: 6896 pType = "paragraph"; 6897 if ( nWwId < ww::stiMax) 6898 pEnglishName = ww::GetEnglishNameFromSti( static_cast<ww::sti>(nWwId ) ); 6899 break; 6900 case STYLE_TYPE_CHAR: pType = "character"; break; 6901 case STYLE_TYPE_LIST: pType = "numbering"; break; 6902 } 6903 pStyleAttributeList->add(FSNS( XML_w, XML_type ), pType); 6904 pStyleAttributeList->add(FSNS(XML_w, XML_styleId), m_rExport.m_pStyles->GetStyleId(nId)); 6905 if (bDefault) 6906 pStyleAttributeList->add(FSNS(XML_w, XML_default), "1"); 6907 if (bCustomStyle) 6908 pStyleAttributeList->add(FSNS(XML_w, XML_customStyle), "1"); 6909 m_pSerializer->startElementNS( XML_w, XML_style, pStyleAttributeList); 6910 m_pSerializer->singleElementNS( XML_w, XML_name, 6911 FSNS( XML_w, XML_val ), pEnglishName ? pEnglishName : rName.toUtf8() ); 6912 6913 if ( nBase != 0x0FFF && eType != STYLE_TYPE_LIST) 6914 { 6915 m_pSerializer->singleElementNS( XML_w, XML_basedOn, 6916 FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nBase) ); 6917 } 6918 6919 if ( nNext != nId && eType != STYLE_TYPE_LIST) 6920 { 6921 m_pSerializer->singleElementNS( XML_w, XML_next, 6922 FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nNext) ); 6923 } 6924 6925 if (nLink != 0x0FFF && (eType == STYLE_TYPE_PARA || eType == STYLE_TYPE_CHAR)) 6926 { 6927 m_pSerializer->singleElementNS(XML_w, XML_link, FSNS(XML_w, XML_val), 6928 m_rExport.m_pStyles->GetStyleId(nLink)); 6929 } 6930 6931 if ( bAutoUpdate ) 6932 m_pSerializer->singleElementNS(XML_w, XML_autoRedefine); 6933 6934 if (!aUiPriority.isEmpty()) 6935 m_pSerializer->singleElementNS(XML_w, XML_uiPriority, FSNS(XML_w, XML_val), aUiPriority); 6936 if (bSemiHidden) 6937 m_pSerializer->singleElementNS(XML_w, XML_semiHidden); 6938 if (bUnhideWhenUsed) 6939 m_pSerializer->singleElementNS(XML_w, XML_unhideWhenUsed); 6940 6941 if (bQFormat || lcl_guessQFormat(rName, nWwId)) 6942 m_pSerializer->singleElementNS(XML_w, XML_qFormat); 6943 if (bLocked) 6944 m_pSerializer->singleElementNS(XML_w, XML_locked); 6945 if (!aRsid.isEmpty()) 6946 m_pSerializer->singleElementNS(XML_w, XML_rsid, FSNS(XML_w, XML_val), aRsid); 6947 } 6948 6949 void DocxAttributeOutput::EndStyle() 6950 { 6951 m_pSerializer->endElementNS( XML_w, XML_style ); 6952 } 6953 6954 void DocxAttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 /*nStyle*/ ) 6955 { 6956 if ( bParProp ) 6957 { 6958 m_pSerializer->startElementNS(XML_w, XML_pPr); 6959 InitCollectedParagraphProperties(); 6960 } 6961 else 6962 { 6963 m_pSerializer->startElementNS(XML_w, XML_rPr); 6964 InitCollectedRunProperties(); 6965 } 6966 } 6967 6968 void DocxAttributeOutput::EndStyleProperties( bool bParProp ) 6969 { 6970 if ( bParProp ) 6971 { 6972 WriteCollectedParagraphProperties(); 6973 6974 // Merge the marks for the ordered elements 6975 m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties); 6976 6977 m_pSerializer->endElementNS( XML_w, XML_pPr ); 6978 } 6979 else 6980 { 6981 WriteCollectedRunProperties(); 6982 6983 // Merge the marks for the ordered elements 6984 m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties); 6985 6986 m_pSerializer->endElementNS( XML_w, XML_rPr ); 6987 } 6988 } 6989 6990 void DocxAttributeOutput::OutlineNumbering(sal_uInt8 const /*nLvl*/) 6991 { 6992 // Handled by ParaOutlineLevel() instead. 6993 } 6994 6995 void DocxAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& rItem) 6996 { 6997 sal_uInt16 nOutLvl = std::min(rItem.GetValue(), sal_uInt16(WW8ListManager::nMaxLevel)); 6998 // Outline Level: in LO Body Text = 0, in MS Body Text = 9 6999 nOutLvl = nOutLvl ? nOutLvl - 1 : 9; 7000 m_pSerializer->singleElementNS(XML_w, XML_outlineLvl, FSNS(XML_w, XML_val), OString::number(nOutLvl)); 7001 } 7002 7003 void DocxAttributeOutput::PageBreakBefore( bool bBreak ) 7004 { 7005 if ( bBreak ) 7006 m_pSerializer->singleElementNS(XML_w, XML_pageBreakBefore); 7007 else 7008 m_pSerializer->singleElementNS( XML_w, XML_pageBreakBefore, 7009 FSNS( XML_w, XML_val ), "false" ); 7010 } 7011 7012 void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo, bool bExtraPageBreak) 7013 { 7014 switch ( nC ) 7015 { 7016 case msword::ColumnBreak: 7017 // The column break should be output in the next paragraph... 7018 if ( m_nColBreakStatus == COLBRK_WRITE ) 7019 m_nColBreakStatus = COLBRK_WRITEANDPOSTPONE; 7020 else 7021 m_nColBreakStatus = COLBRK_POSTPONE; 7022 break; 7023 case msword::PageBreak: 7024 if ( pSectionInfo ) 7025 { 7026 // Detect when the current node is the last node in the 7027 // document: the last section is written explicitly in 7028 // DocxExport::WriteMainText(), don't duplicate that here. 7029 SwNodeIndex aCurrentNode(m_rExport.m_pCurPam->GetNode()); 7030 SwNodeIndex aLastNode(m_rExport.m_rDoc.GetNodes().GetEndOfContent(), -1); 7031 bool bEmit = aCurrentNode != aLastNode; 7032 7033 if (!bEmit) 7034 { 7035 // Need to still emit an empty section at the end of the 7036 // document in case balanced columns are wanted, since the last 7037 // section in Word is always balanced. 7038 sal_uInt16 nColumns = 1; 7039 bool bBalance = false; 7040 if (const SwSectionFormat* pFormat = pSectionInfo->pSectionFormat) 7041 { 7042 if (pFormat != reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1))) 7043 { 7044 nColumns = pFormat->GetCol().GetNumCols(); 7045 const SwFormatNoBalancedColumns& rNoBalanced = pFormat->GetBalancedColumns(); 7046 bBalance = !rNoBalanced.GetValue(); 7047 } 7048 } 7049 bEmit = (nColumns > 1 && bBalance); 7050 } 7051 7052 // don't add section properties if this will be the first 7053 // paragraph in the document 7054 if ( !m_bParagraphOpened && !m_bIsFirstParagraph && bEmit ) 7055 { 7056 // Create a dummy paragraph if needed 7057 m_pSerializer->startElementNS(XML_w, XML_p); 7058 m_pSerializer->startElementNS(XML_w, XML_pPr); 7059 7060 m_rExport.SectionProperties( *pSectionInfo ); 7061 7062 m_pSerializer->endElementNS( XML_w, XML_pPr ); 7063 if (bExtraPageBreak) 7064 { 7065 m_pSerializer->startElementNS(XML_w, XML_r); 7066 m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page"); 7067 m_pSerializer->endElementNS(XML_w, XML_r); 7068 } 7069 m_pSerializer->endElementNS( XML_w, XML_p ); 7070 } 7071 else 7072 { 7073 if (bExtraPageBreak && m_bParagraphOpened) 7074 { 7075 m_pSerializer->startElementNS(XML_w, XML_r); 7076 m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page"); 7077 m_pSerializer->endElementNS(XML_w, XML_r); 7078 } 7079 // postpone the output of this; it has to be done inside the 7080 // paragraph properties, so remember it until then 7081 m_pSectionInfo.reset( new WW8_SepInfo( *pSectionInfo )); 7082 } 7083 } 7084 else if ( m_bParagraphOpened ) 7085 { 7086 if (bBreakAfter) 7087 // tdf#128889 7088 m_bPageBreakAfter = true; 7089 else 7090 { 7091 m_pSerializer->startElementNS(XML_w, XML_r); 7092 m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page"); 7093 m_pSerializer->endElementNS(XML_w, XML_r); 7094 } 7095 } 7096 else 7097 m_bPostponedPageBreak = true; 7098 7099 break; 7100 default: 7101 SAL_INFO("sw.ww8", "Unknown section break to write: " << nC ); 7102 break; 7103 } 7104 } 7105 7106 void DocxAttributeOutput::EndParaSdtBlock() 7107 { 7108 if (m_aParagraphSdt.m_bStartedSdt) 7109 { 7110 // Paragraph-level SDT still open? Close it now. 7111 m_aParagraphSdt.EndSdtBlock(m_pSerializer); 7112 } 7113 } 7114 7115 void DocxAttributeOutput::StartSection() 7116 { 7117 m_pSerializer->startElementNS(XML_w, XML_sectPr); 7118 m_bOpenedSectPr = true; 7119 7120 // Write the elements in the spec order 7121 static const sal_Int32 aOrder[] = 7122 { 7123 FSNS( XML_w, XML_headerReference ), 7124 FSNS( XML_w, XML_footerReference ), 7125 FSNS( XML_w, XML_footnotePr ), 7126 FSNS( XML_w, XML_endnotePr ), 7127 FSNS( XML_w, XML_type ), 7128 FSNS( XML_w, XML_pgSz ), 7129 FSNS( XML_w, XML_pgMar ), 7130 FSNS( XML_w, XML_paperSrc ), 7131 FSNS( XML_w, XML_pgBorders ), 7132 FSNS( XML_w, XML_lnNumType ), 7133 FSNS( XML_w, XML_pgNumType ), 7134 FSNS( XML_w, XML_cols ), 7135 FSNS( XML_w, XML_formProt ), 7136 FSNS( XML_w, XML_vAlign ), 7137 FSNS( XML_w, XML_noEndnote ), 7138 FSNS( XML_w, XML_titlePg ), 7139 FSNS( XML_w, XML_textDirection ), 7140 FSNS( XML_w, XML_bidi ), 7141 FSNS( XML_w, XML_rtlGutter ), 7142 FSNS( XML_w, XML_docGrid ), 7143 FSNS( XML_w, XML_printerSettings ), 7144 FSNS( XML_w, XML_sectPrChange ) 7145 }; 7146 7147 // postpone the output so that we can later [in EndParagraphProperties()] 7148 // prepend the properties before the run 7149 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence 7150 m_pSerializer->mark(Tag_StartSection, comphelper::containerToSequence(aOrder)); 7151 m_bHadSectPr = true; 7152 } 7153 7154 void DocxAttributeOutput::EndSection() 7155 { 7156 // Write the section properties 7157 if ( m_pSectionSpacingAttrList.is() ) 7158 { 7159 rtl::Reference<FastAttributeList> xAttrList = std::move( m_pSectionSpacingAttrList ); 7160 m_pSerializer->singleElementNS( XML_w, XML_pgMar, xAttrList ); 7161 } 7162 7163 // Order the elements 7164 m_pSerializer->mergeTopMarks(Tag_StartSection); 7165 7166 m_pSerializer->endElementNS( XML_w, XML_sectPr ); 7167 m_bOpenedSectPr = false; 7168 } 7169 7170 void DocxAttributeOutput::SectionFormProtection( bool bProtected ) 7171 { 7172 if ( bProtected ) 7173 m_pSerializer->singleElementNS(XML_w, XML_formProt, FSNS(XML_w, XML_val), "true"); 7174 else 7175 m_pSerializer->singleElementNS(XML_w, XML_formProt, FSNS(XML_w, XML_val), "false"); 7176 } 7177 7178 void DocxAttributeOutput::SectionRtlGutter(const SfxBoolItem& rRtlGutter) 7179 { 7180 if (!rRtlGutter.GetValue()) 7181 { 7182 return; 7183 } 7184 7185 m_pSerializer->singleElementNS(XML_w, XML_rtlGutter); 7186 } 7187 7188 void DocxAttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo ) 7189 { 7190 rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList(); 7191 pAttr->add( FSNS( XML_w, XML_countBy ), OString::number(rLnNumInfo.GetCountBy()).getStr()); 7192 pAttr->add( FSNS( XML_w, XML_restart ), rLnNumInfo.IsRestartEachPage() ? "newPage" : "continuous" ); 7193 if( rLnNumInfo.GetPosFromLeft()) 7194 pAttr->add( FSNS( XML_w, XML_distance ), OString::number(rLnNumInfo.GetPosFromLeft()).getStr()); 7195 if (nRestartNo > 0) 7196 // Writer is 1-based, Word is 0-based. 7197 pAttr->add(FSNS(XML_w, XML_start), OString::number(nRestartNo - 1).getStr()); 7198 m_pSerializer->singleElementNS( XML_w, XML_lnNumType, pAttr ); 7199 } 7200 7201 void DocxAttributeOutput::SectionTitlePage() 7202 { 7203 m_pSerializer->singleElementNS(XML_w, XML_titlePg); 7204 } 7205 7206 void DocxAttributeOutput::SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* /*pFirstPageFormat*/ ) 7207 { 7208 // Output the margins 7209 7210 const SvxBoxItem& rBox = pFormat->GetBox( ); 7211 7212 const SvxBorderLine* pLeft = rBox.GetLeft( ); 7213 const SvxBorderLine* pTop = rBox.GetTop( ); 7214 const SvxBorderLine* pRight = rBox.GetRight( ); 7215 const SvxBorderLine* pBottom = rBox.GetBottom( ); 7216 7217 if ( !(pBottom || pTop || pLeft || pRight) ) 7218 return; 7219 7220 OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions(); 7221 7222 // Check if there is a shadow item 7223 const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW ); 7224 if ( pItem ) 7225 { 7226 const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem); 7227 aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation(); 7228 } 7229 7230 // By top margin, impl_borders() means the distance between the top of the page and the header frame. 7231 editeng::WordPageMargins aMargins = m_pageMargins; 7232 HdFtDistanceGlue aGlue(pFormat->GetAttrSet()); 7233 if (aGlue.HasHeader()) 7234 aMargins.nTop = aGlue.dyaHdrTop; 7235 // Ditto for bottom margin. 7236 if (aGlue.HasFooter()) 7237 aMargins.nBottom = aGlue.dyaHdrBottom; 7238 7239 if (pFormat->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::GUTTER_AT_TOP)) 7240 { 7241 aMargins.nTop += pFormat->GetLRSpace().GetGutterMargin(); 7242 } 7243 else 7244 { 7245 aMargins.nLeft += pFormat->GetLRSpace().GetGutterMargin(); 7246 } 7247 7248 aOutputBorderOptions.pDistances = std::make_shared<editeng::WordBorderDistances>(); 7249 editeng::BorderDistancesToWord(rBox, aMargins, *aOutputBorderOptions.pDistances); 7250 7251 // All distances are relative to the text margins 7252 m_pSerializer->startElementNS(XML_w, XML_pgBorders, 7253 FSNS(XML_w, XML_display), "allPages", 7254 FSNS(XML_w, XML_offsetFrom), aOutputBorderOptions.pDistances->bFromEdge ? "page" : "text"); 7255 7256 std::map<SvxBoxItemLine, css::table::BorderLine2> aEmptyMap; // empty styles map 7257 impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aEmptyMap ); 7258 7259 m_pSerializer->endElementNS( XML_w, XML_pgBorders ); 7260 7261 } 7262 7263 void DocxAttributeOutput::SectionBiDi( bool bBiDi ) 7264 { 7265 if ( bBiDi ) 7266 m_pSerializer->singleElementNS(XML_w, XML_bidi); 7267 } 7268 7269 // Converting Numbering Format Code to string 7270 static OString lcl_ConvertNumberingType(sal_Int16 nNumberingType, const SfxItemSet* pOutSet, OString& rFormat, const OString& sDefault = "" ) 7271 { 7272 OString aType = sDefault; 7273 7274 switch ( nNumberingType ) 7275 { 7276 case SVX_NUM_CHARS_UPPER_LETTER: 7277 case SVX_NUM_CHARS_UPPER_LETTER_N: aType = "upperLetter"; break; 7278 7279 case SVX_NUM_CHARS_LOWER_LETTER: 7280 case SVX_NUM_CHARS_LOWER_LETTER_N: aType = "lowerLetter"; break; 7281 7282 case SVX_NUM_ROMAN_UPPER: aType = "upperRoman"; break; 7283 case SVX_NUM_ROMAN_LOWER: aType = "lowerRoman"; break; 7284 case SVX_NUM_ARABIC: aType = "decimal"; break; 7285 7286 case SVX_NUM_BITMAP: 7287 case SVX_NUM_CHAR_SPECIAL: aType = "bullet"; break; 7288 7289 case style::NumberingType::CHARS_HEBREW: aType = "hebrew2"; break; 7290 case style::NumberingType::NUMBER_HEBREW: aType = "hebrew1"; break; 7291 case style::NumberingType::NUMBER_NONE: aType = "none"; break; 7292 case style::NumberingType::FULLWIDTH_ARABIC: aType="decimalFullWidth"; break; 7293 case style::NumberingType::TIAN_GAN_ZH: aType="ideographTraditional"; break; 7294 case style::NumberingType::DI_ZI_ZH: aType="ideographZodiac"; break; 7295 case style::NumberingType::NUMBER_LOWER_ZH: 7296 aType="taiwaneseCountingThousand"; 7297 if (pOutSet) { 7298 const SvxLanguageItem& rLang = pOutSet->Get( RES_CHRATR_CJK_LANGUAGE); 7299 const LanguageType eLang = rLang.GetLanguage(); 7300 7301 if (LANGUAGE_CHINESE_SIMPLIFIED == eLang) { 7302 aType="chineseCountingThousand"; 7303 } 7304 } 7305 break; 7306 case style::NumberingType::NUMBER_UPPER_ZH_TW: aType="ideographLegalTraditional";break; 7307 case style::NumberingType::NUMBER_UPPER_ZH: aType="chineseLegalSimplified"; break; 7308 case style::NumberingType::NUMBER_TRADITIONAL_JA: aType="japaneseLegal";break; 7309 case style::NumberingType::AIU_FULLWIDTH_JA: aType="aiueoFullWidth";break; 7310 case style::NumberingType::AIU_HALFWIDTH_JA: aType="aiueo";break; 7311 case style::NumberingType::IROHA_FULLWIDTH_JA: aType="iroha";break; 7312 case style::NumberingType::IROHA_HALFWIDTH_JA: aType="irohaFullWidth";break; 7313 case style::NumberingType::HANGUL_SYLLABLE_KO: aType="ganada";break; 7314 case style::NumberingType::HANGUL_JAMO_KO: aType="chosung";break; 7315 case style::NumberingType::NUMBER_HANGUL_KO: aType="koreanCounting"; break; 7316 case style::NumberingType::NUMBER_LEGAL_KO: aType = "koreanLegal"; break; 7317 case style::NumberingType::NUMBER_DIGITAL_KO: aType = "koreanDigital"; break; 7318 case style::NumberingType::NUMBER_DIGITAL2_KO: aType = "koreanDigital2"; break; 7319 case style::NumberingType::CIRCLE_NUMBER: aType="decimalEnclosedCircle"; break; 7320 case style::NumberingType::CHARS_ARABIC: aType="arabicAlpha"; break; 7321 case style::NumberingType::CHARS_ARABIC_ABJAD: aType="arabicAbjad"; break; 7322 case style::NumberingType::CHARS_THAI: aType="thaiLetters"; break; 7323 case style::NumberingType::CHARS_PERSIAN: 7324 case style::NumberingType::CHARS_NEPALI: aType="hindiVowels"; break; 7325 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU: 7326 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_RU: aType = "russianUpper"; break; 7327 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU: 7328 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_RU: aType = "russianLower"; break; 7329 case style::NumberingType::TEXT_NUMBER: aType="ordinal"; break; 7330 case style::NumberingType::TEXT_CARDINAL: aType="cardinalText"; break; 7331 case style::NumberingType::TEXT_ORDINAL: aType="ordinalText"; break; 7332 case style::NumberingType::SYMBOL_CHICAGO: aType="chicago"; break; 7333 case style::NumberingType::ARABIC_ZERO: aType = "decimalZero"; break; 7334 case style::NumberingType::ARABIC_ZERO3: 7335 aType = "custom"; 7336 rFormat = "001, 002, 003, ..."; 7337 break; 7338 case style::NumberingType::ARABIC_ZERO4: 7339 aType = "custom"; 7340 rFormat = "0001, 0002, 0003, ..."; 7341 break; 7342 case style::NumberingType::ARABIC_ZERO5: 7343 aType = "custom"; 7344 rFormat = "00001, 00002, 00003, ..."; 7345 break; 7346 /* 7347 Fallback the rest to the suggested default. 7348 case style::NumberingType::NATIVE_NUMBERING: 7349 case style::NumberingType::HANGUL_CIRCLED_JAMO_KO: 7350 case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO: 7351 case style::NumberingType::CHARS_GREEK_UPPER_LETTER: 7352 case style::NumberingType::CHARS_GREEK_LOWER_LETTER: 7353 case style::NumberingType::PAGE_DESCRIPTOR: 7354 case style::NumberingType::TRANSLITERATION: 7355 case style::NumberingType::CHARS_KHMER: 7356 case style::NumberingType::CHARS_LAO: 7357 case style::NumberingType::CHARS_TIBETAN: 7358 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_BG: 7359 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_BG: 7360 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_BG: 7361 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_BG: 7362 case style::NumberingType::CHARS_MYANMAR: 7363 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_SR: 7364 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_SR: 7365 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_SR: 7366 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_SR: 7367 */ 7368 default: break; 7369 } 7370 return aType; 7371 } 7372 7373 7374 void DocxAttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber ) 7375 { 7376 // FIXME Not called properly with page styles like "First Page" 7377 7378 rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList(); 7379 7380 // std::nullopt means no restart: then don't output that attribute if it is negative 7381 if ( oPageRestartNumber ) 7382 pAttr->add( FSNS( XML_w, XML_start ), OString::number( *oPageRestartNumber ) ); 7383 7384 // nNumType corresponds to w:fmt. See WW8Export::GetNumId() for more precisions 7385 OString aCustomFormat; 7386 OString aFormat(lcl_ConvertNumberingType(nNumType, nullptr, aCustomFormat)); 7387 if (!aFormat.isEmpty() && aCustomFormat.isEmpty()) 7388 pAttr->add(FSNS(XML_w, XML_fmt), aFormat); 7389 7390 m_pSerializer->singleElementNS( XML_w, XML_pgNumType, pAttr ); 7391 7392 // see 2.6.12 pgNumType (Page Numbering Settings) 7393 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::SectionPageNumbering()" ); 7394 } 7395 7396 void DocxAttributeOutput::SectionType( sal_uInt8 nBreakCode ) 7397 { 7398 /* break code: 0 No break, 1 New column 7399 2 New page, 3 Even page, 4 Odd page 7400 */ 7401 const char* pType; 7402 switch ( nBreakCode ) 7403 { 7404 case 1: pType = "nextColumn"; break; 7405 case 2: pType = "nextPage"; break; 7406 case 3: pType = "evenPage"; break; 7407 case 4: pType = "oddPage"; break; 7408 default: pType = "continuous"; break; 7409 } 7410 7411 m_pSerializer->singleElementNS(XML_w, XML_type, FSNS(XML_w, XML_val), pType); 7412 } 7413 7414 void DocxAttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA ) 7415 { 7416 switch( nVA ) 7417 { 7418 case drawing::TextVerticalAdjust_CENTER: 7419 m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center"); 7420 break; 7421 case drawing::TextVerticalAdjust_BOTTOM: 7422 m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom"); 7423 break; 7424 case drawing::TextVerticalAdjust_BLOCK: //justify 7425 m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "both"); 7426 break; 7427 default: 7428 break; 7429 } 7430 } 7431 7432 void DocxAttributeOutput::StartFont( const OUString& rFamilyName ) const 7433 { 7434 m_pSerializer->startElementNS(XML_w, XML_font, FSNS(XML_w, XML_name), rFamilyName); 7435 } 7436 7437 void DocxAttributeOutput::EndFont() const 7438 { 7439 m_pSerializer->endElementNS( XML_w, XML_font ); 7440 } 7441 7442 void DocxAttributeOutput::FontAlternateName( const OUString& rName ) const 7443 { 7444 m_pSerializer->singleElementNS(XML_w, XML_altName, FSNS(XML_w, XML_val), rName); 7445 } 7446 7447 void DocxAttributeOutput::FontCharset( sal_uInt8 nCharSet, rtl_TextEncoding nEncoding ) const 7448 { 7449 rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList(); 7450 7451 OString aCharSet( OString::number( nCharSet, 16 ) ); 7452 if ( aCharSet.getLength() == 1 ) 7453 aCharSet = "0" + aCharSet; 7454 pAttr->add(FSNS(XML_w, XML_val), aCharSet); 7455 7456 if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT ) 7457 { 7458 if( const char* charset = rtl_getMimeCharsetFromTextEncoding( nEncoding )) 7459 pAttr->add( FSNS( XML_w, XML_characterSet ), charset ); 7460 } 7461 7462 m_pSerializer->singleElementNS( XML_w, XML_charset, pAttr ); 7463 } 7464 7465 void DocxAttributeOutput::FontFamilyType( FontFamily eFamily ) const 7466 { 7467 const char* pFamily; 7468 switch ( eFamily ) 7469 { 7470 case FAMILY_ROMAN: pFamily = "roman"; break; 7471 case FAMILY_SWISS: pFamily = "swiss"; break; 7472 case FAMILY_MODERN: pFamily = "modern"; break; 7473 case FAMILY_SCRIPT: pFamily = "script"; break; 7474 case FAMILY_DECORATIVE: pFamily = "decorative"; break; 7475 default: pFamily = "auto"; break; // no font family 7476 } 7477 7478 m_pSerializer->singleElementNS(XML_w, XML_family, FSNS(XML_w, XML_val), pFamily); 7479 } 7480 7481 void DocxAttributeOutput::FontPitchType( FontPitch ePitch ) const 7482 { 7483 const char* pPitch; 7484 switch ( ePitch ) 7485 { 7486 case PITCH_VARIABLE: pPitch = "variable"; break; 7487 case PITCH_FIXED: pPitch = "fixed"; break; 7488 default: pPitch = "default"; break; // no info about the pitch 7489 } 7490 7491 m_pSerializer->singleElementNS(XML_w, XML_pitch, FSNS(XML_w, XML_val), pPitch); 7492 } 7493 7494 void DocxAttributeOutput::EmbedFont( std::u16string_view name, FontFamily family, FontPitch pitch ) 7495 { 7496 if( !m_rExport.m_rDoc.getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS )) 7497 return; // no font embedding with this document 7498 EmbedFontStyle( name, XML_embedRegular, family, ITALIC_NONE, WEIGHT_NORMAL, pitch ); 7499 EmbedFontStyle( name, XML_embedBold, family, ITALIC_NONE, WEIGHT_BOLD, pitch ); 7500 EmbedFontStyle( name, XML_embedItalic, family, ITALIC_NORMAL, WEIGHT_NORMAL, pitch ); 7501 EmbedFontStyle( name, XML_embedBoldItalic, family, ITALIC_NORMAL, WEIGHT_BOLD, pitch ); 7502 } 7503 7504 static char toHexChar( int value ) 7505 { 7506 return value >= 10 ? value + 'A' - 10 : value + '0'; 7507 } 7508 7509 void DocxAttributeOutput::EmbedFontStyle( std::u16string_view name, int tag, FontFamily family, FontItalic italic, 7510 FontWeight weight, FontPitch pitch ) 7511 { 7512 // Embed font if at least viewing is allowed (in which case the opening app must check 7513 // the font license rights too and open either read-only or not use the font for editing). 7514 OUString fontUrl = EmbeddedFontsHelper::fontFileUrl( name, family, italic, weight, pitch, 7515 EmbeddedFontsHelper::FontRights::ViewingAllowed ); 7516 if( fontUrl.isEmpty()) 7517 return; 7518 // TODO IDocumentSettingAccess::EMBED_SYSTEM_FONTS 7519 if( !fontFilesMap.count( fontUrl )) 7520 { 7521 osl::File file( fontUrl ); 7522 if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None ) 7523 return; 7524 uno::Reference< css::io::XOutputStream > xOutStream = m_rExport.GetFilter().openFragmentStream( 7525 "word/fonts/font" + OUString::number(m_nextFontId) + ".odttf", 7526 "application/vnd.openxmlformats-officedocument.obfuscatedFont" ); 7527 // Not much point in trying hard with the obfuscation key, whoever reads the spec can read the font anyway, 7528 // so just alter the first and last part of the key. 7529 char fontKeyStr[] = "{00014A78-CABC-4EF0-12AC-5CD89AEFDE00}"; 7530 sal_uInt8 fontKey[ 16 ] = { 0, 0xDE, 0xEF, 0x9A, 0xD8, 0x5C, 0xAC, 0x12, 0xF0, 0x4E, 7531 0xBC, 0xCA, 0x78, 0x4A, 0x01, 0 }; 7532 fontKey[ 0 ] = fontKey[ 15 ] = m_nextFontId % 256; 7533 fontKeyStr[ 1 ] = fontKeyStr[ 35 ] = toHexChar(( m_nextFontId % 256 ) / 16 ); 7534 fontKeyStr[ 2 ] = fontKeyStr[ 36 ] = toHexChar(( m_nextFontId % 256 ) % 16 ); 7535 unsigned char buffer[ 4096 ]; 7536 sal_uInt64 readSize; 7537 file.read( buffer, 32, readSize ); 7538 if( readSize < 32 ) 7539 { 7540 SAL_WARN( "sw.ww8", "Font file size too small (" << fontUrl << ")" ); 7541 xOutStream->closeOutput(); 7542 return; 7543 } 7544 for( int i = 0; 7545 i < 16; 7546 ++i ) 7547 { 7548 buffer[ i ] ^= fontKey[ i ]; 7549 buffer[ i + 16 ] ^= fontKey[ i ]; 7550 } 7551 xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), 32 )); 7552 for(;;) 7553 { 7554 sal_Bool eof; 7555 if( file.isEndOfFile( &eof ) != osl::File::E_None ) 7556 { 7557 SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl ); 7558 xOutStream->closeOutput(); 7559 return; 7560 } 7561 if( eof ) 7562 break; 7563 if( file.read( buffer, 4096, readSize ) != osl::File::E_None ) 7564 { 7565 SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl ); 7566 xOutStream->closeOutput(); 7567 return; 7568 } 7569 if( readSize == 0 ) 7570 break; 7571 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence 7572 xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), readSize )); 7573 } 7574 xOutStream->closeOutput(); 7575 OString relId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(), 7576 oox::getRelationship(Relationship::FONT), 7577 OUStringConcatenation("fonts/font" + OUString::number( m_nextFontId ) + ".odttf") ), RTL_TEXTENCODING_UTF8 ); 7578 EmbeddedFontRef ref; 7579 ref.relId = relId; 7580 ref.fontKey = fontKeyStr; 7581 fontFilesMap[ fontUrl ] = ref; 7582 ++m_nextFontId; 7583 } 7584 m_pSerializer->singleElementNS( XML_w, tag, 7585 FSNS( XML_r, XML_id ), fontFilesMap[ fontUrl ].relId, 7586 FSNS( XML_w, XML_fontKey ), fontFilesMap[ fontUrl ].fontKey ); 7587 } 7588 7589 OString DocxAttributeOutput::TransHighlightColor( sal_uInt8 nIco ) 7590 { 7591 switch (nIco) 7592 { 7593 case 0: return "none"; break; 7594 case 1: return "black"; break; 7595 case 2: return "blue"; break; 7596 case 3: return "cyan"; break; 7597 case 4: return "green"; break; 7598 case 5: return "magenta"; break; 7599 case 6: return "red"; break; 7600 case 7: return "yellow"; break; 7601 case 8: return "white"; break; 7602 case 9: return "darkBlue"; break; 7603 case 10: return "darkCyan"; break; 7604 case 11: return "darkGreen"; break; 7605 case 12: return "darkMagenta"; break; 7606 case 13: return "darkRed"; break; 7607 case 14: return "darkYellow"; break; 7608 case 15: return "darkGray"; break; 7609 case 16: return "lightGray"; break; 7610 default: return OString(); break; 7611 } 7612 } 7613 7614 void DocxAttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) 7615 { 7616 // nId is the same both for abstract numbering definition as well as the 7617 // numbering definition itself 7618 // TODO check that this is actually true & fix if not ;-) 7619 OString aId( OString::number( nId ) ); 7620 7621 m_pSerializer->startElementNS(XML_w, XML_num, FSNS(XML_w, XML_numId), aId); 7622 7623 m_pSerializer->singleElementNS(XML_w, XML_abstractNumId, FSNS(XML_w, XML_val), aId); 7624 7625 #if OSL_DEBUG_LEVEL > 1 7626 // TODO ww8 version writes this, anything to do about it here? 7627 if ( rRule.IsContinusNum() ) 7628 SAL_INFO("sw", "TODO DocxAttributeOutput::NumberingDefinition()" ); 7629 #else 7630 (void) rRule; // to quiet the warning... 7631 #endif 7632 7633 m_pSerializer->endElementNS( XML_w, XML_num ); 7634 } 7635 7636 // Not all attributes of SwNumFormat are important for export, so can't just use embedded in 7637 // that classes comparison. 7638 static bool lcl_ListLevelsAreDifferentForExport(const SwNumFormat & rFormat1, const SwNumFormat & rFormat2) 7639 { 7640 if (rFormat1 == rFormat2) 7641 // They are equal, nothing to do 7642 return false; 7643 7644 if (!rFormat1.GetCharFormat() != !rFormat2.GetCharFormat()) 7645 // One has charformat, other not. they are different 7646 return true; 7647 7648 if (rFormat1.GetCharFormat() && rFormat2.GetCharFormat()) 7649 { 7650 const SwAttrSet & a1 = rFormat1.GetCharFormat()->GetAttrSet(); 7651 const SwAttrSet & a2 = rFormat2.GetCharFormat()->GetAttrSet(); 7652 7653 if (!(a1 == a2)) 7654 // Difference in charformat: they are different 7655 return true; 7656 } 7657 7658 // Compare numformats with empty charformats 7659 SwNumFormat modified1 = rFormat1; 7660 SwNumFormat modified2 = rFormat2; 7661 modified1.SetCharFormatName(OUString()); 7662 modified2.SetCharFormatName(OUString()); 7663 modified1.SetCharFormat(nullptr); 7664 modified2.SetCharFormat(nullptr); 7665 return modified1 != modified2; 7666 } 7667 7668 void DocxAttributeOutput::OverrideNumberingDefinition( 7669 SwNumRule const& rRule, 7670 sal_uInt16 const nNum, sal_uInt16 const nAbstractNum, const std::map< size_t, size_t > & rLevelOverrides ) 7671 { 7672 m_pSerializer->startElementNS(XML_w, XML_num, FSNS(XML_w, XML_numId), OString::number(nNum)); 7673 7674 m_pSerializer->singleElementNS(XML_w, XML_abstractNumId, FSNS(XML_w, XML_val), OString::number(nAbstractNum)); 7675 7676 SwNumRule const& rAbstractRule = *(*m_rExport.m_pUsedNumTable)[nAbstractNum - 1]; 7677 sal_uInt8 const nLevels = static_cast<sal_uInt8>(rRule.IsContinusNum() 7678 ? WW8ListManager::nMinLevel : WW8ListManager::nMaxLevel); 7679 for (sal_uInt8 nLevel = 0; nLevel < nLevels; ++nLevel) 7680 { 7681 const auto levelOverride = rLevelOverrides.find(nLevel); 7682 bool bListsAreDifferent = lcl_ListLevelsAreDifferentForExport(rRule.Get(nLevel), rAbstractRule.Get(nLevel)); 7683 7684 // Export list override only if it is different to abstract one 7685 // or we have a level numbering override 7686 if (bListsAreDifferent || levelOverride != rLevelOverrides.end()) 7687 { 7688 m_pSerializer->startElementNS(XML_w, XML_lvlOverride, FSNS(XML_w, XML_ilvl), OString::number(nLevel)); 7689 7690 if (bListsAreDifferent) 7691 { 7692 GetExport().NumberingLevel(rRule, nLevel); 7693 } 7694 if (levelOverride != rLevelOverrides.end()) 7695 { 7696 // list numbering restart override 7697 m_pSerializer->singleElementNS(XML_w, XML_startOverride, 7698 FSNS(XML_w, XML_val), OString::number(levelOverride->second)); 7699 } 7700 7701 m_pSerializer->endElementNS(XML_w, XML_lvlOverride); 7702 } 7703 } 7704 7705 m_pSerializer->endElementNS( XML_w, XML_num ); 7706 } 7707 7708 void DocxAttributeOutput::StartAbstractNumbering( sal_uInt16 nId ) 7709 { 7710 const SwNumRule* pRule = (*m_rExport.m_pUsedNumTable)[nId - 1]; 7711 m_bExportingOutline = pRule && pRule->IsOutlineRule(); 7712 m_pSerializer->startElementNS( XML_w, XML_abstractNum, 7713 FSNS( XML_w, XML_abstractNumId ), OString::number(nId) ); 7714 } 7715 7716 void DocxAttributeOutput::EndAbstractNumbering() 7717 { 7718 m_pSerializer->endElementNS( XML_w, XML_abstractNum ); 7719 } 7720 7721 void DocxAttributeOutput::NumberingLevel( sal_uInt8 nLevel, 7722 sal_uInt16 nStart, 7723 sal_uInt16 nNumberingType, 7724 SvxAdjust eAdjust, 7725 const sal_uInt8 * /*pNumLvlPos*/, 7726 sal_uInt8 nFollow, 7727 const wwFont *pFont, 7728 const SfxItemSet *pOutSet, 7729 sal_Int16 nIndentAt, 7730 sal_Int16 nFirstLineIndex, 7731 sal_Int16 nListTabPos, 7732 const OUString &rNumberingString, 7733 const SvxBrushItem* pBrush) 7734 { 7735 m_pSerializer->startElementNS(XML_w, XML_lvl, FSNS(XML_w, XML_ilvl), OString::number(nLevel)); 7736 7737 // start with the nStart value. Do not write w:start if Numbered Lists 7738 // starts from zero.As it's an optional parameter. 7739 // refer ECMA 376 Second edition Part-1 7740 if(0 != nLevel || 0 != nStart) 7741 { 7742 m_pSerializer->singleElementNS( XML_w, XML_start, 7743 FSNS( XML_w, XML_val ), OString::number(nStart) ); 7744 } 7745 7746 if (m_bExportingOutline) 7747 { 7748 sal_uInt16 nId = m_rExport.m_pStyles->GetHeadingParagraphStyleId( nLevel ); 7749 if ( nId != SAL_MAX_UINT16 ) 7750 m_pSerializer->singleElementNS( XML_w, XML_pStyle , 7751 FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nId) ); 7752 } 7753 // format 7754 OString aCustomFormat; 7755 OString aFormat(lcl_ConvertNumberingType(nNumberingType, pOutSet, aCustomFormat, "decimal")); 7756 7757 { 7758 if (aCustomFormat.isEmpty()) 7759 { 7760 m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), aFormat); 7761 } 7762 else 7763 { 7764 m_pSerializer->startElementNS(XML_mc, XML_AlternateContent); 7765 m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "w14"); 7766 7767 m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), aFormat, 7768 FSNS(XML_w, XML_format), aCustomFormat); 7769 7770 m_pSerializer->endElementNS(XML_mc, XML_Choice); 7771 m_pSerializer->startElementNS(XML_mc, XML_Fallback); 7772 m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), "decimal"); 7773 m_pSerializer->endElementNS(XML_mc, XML_Fallback); 7774 m_pSerializer->endElementNS(XML_mc, XML_AlternateContent); 7775 } 7776 } 7777 7778 // suffix 7779 const char *pSuffix = nullptr; 7780 switch ( nFollow ) 7781 { 7782 case 1: pSuffix = "space"; break; 7783 case 2: pSuffix = "nothing"; break; 7784 default: /*pSuffix = "tab";*/ break; 7785 } 7786 if ( pSuffix ) 7787 m_pSerializer->singleElementNS(XML_w, XML_suff, FSNS(XML_w, XML_val), pSuffix); 7788 7789 // text 7790 OUStringBuffer aBuffer( rNumberingString.getLength() + WW8ListManager::nMaxLevel ); 7791 7792 const sal_Unicode *pPrev = rNumberingString.getStr(); 7793 const sal_Unicode *pIt = rNumberingString.getStr(); 7794 while ( pIt < rNumberingString.getStr() + rNumberingString.getLength() ) 7795 { 7796 // convert the level values to %NUMBER form 7797 // (we don't use pNumLvlPos at all) 7798 // FIXME so far we support the ww8 limit of levels only 7799 if ( *pIt < sal_Unicode( WW8ListManager::nMaxLevel ) ) 7800 { 7801 aBuffer.append( pPrev, pIt - pPrev ); 7802 aBuffer.append( '%' ); 7803 aBuffer.append( sal_Int32( *pIt ) + 1 ); 7804 7805 pPrev = pIt + 1; 7806 } 7807 ++pIt; 7808 } 7809 if ( pPrev < pIt ) 7810 aBuffer.append( pPrev, pIt - pPrev ); 7811 7812 // If bullet char is empty, set lvlText as empty 7813 if ( rNumberingString == OUStringChar('\0') && nNumberingType == SVX_NUM_CHAR_SPECIAL ) 7814 { 7815 m_pSerializer->singleElementNS(XML_w, XML_lvlText, FSNS(XML_w, XML_val), ""); 7816 } 7817 else 7818 { 7819 // Writer's "zero width space" suffix is necessary, so that LabelFollowedBy shows up, but Word doesn't require that. 7820 OUString aLevelText = aBuffer.makeStringAndClear(); 7821 static OUString aZeroWidthSpace(u'\x200B'); 7822 if (aLevelText == aZeroWidthSpace) 7823 aLevelText.clear(); 7824 m_pSerializer->singleElementNS(XML_w, XML_lvlText, FSNS(XML_w, XML_val), aLevelText); 7825 } 7826 7827 // bullet 7828 if (nNumberingType == SVX_NUM_BITMAP && pBrush) 7829 { 7830 int nIndex = m_rExport.GetGrfIndex(*pBrush); 7831 if (nIndex != -1) 7832 { 7833 m_pSerializer->singleElementNS(XML_w, XML_lvlPicBulletId, 7834 FSNS(XML_w, XML_val), OString::number(nIndex)); 7835 } 7836 } 7837 7838 // justification 7839 const char *pJc; 7840 bool ecmaDialect = ( m_rExport.GetFilter().getVersion() == oox::core::ECMA_DIALECT ); 7841 switch ( eAdjust ) 7842 { 7843 case SvxAdjust::Center: pJc = "center"; break; 7844 case SvxAdjust::Right: pJc = !ecmaDialect ? "end" : "right"; break; 7845 default: pJc = !ecmaDialect ? "start" : "left"; break; 7846 } 7847 m_pSerializer->singleElementNS(XML_w, XML_lvlJc, FSNS(XML_w, XML_val), pJc); 7848 7849 // indentation 7850 m_pSerializer->startElementNS(XML_w, XML_pPr); 7851 if( nListTabPos >= 0 ) 7852 { 7853 m_pSerializer->startElementNS(XML_w, XML_tabs); 7854 m_pSerializer->singleElementNS( XML_w, XML_tab, 7855 FSNS( XML_w, XML_val ), "num", 7856 FSNS( XML_w, XML_pos ), OString::number(nListTabPos) ); 7857 m_pSerializer->endElementNS( XML_w, XML_tabs ); 7858 } 7859 7860 sal_Int32 nToken = ecmaDialect ? XML_left : XML_start; 7861 sal_Int32 nIndentToken = nFirstLineIndex > 0 ? XML_firstLine : XML_hanging; 7862 m_pSerializer->singleElementNS( XML_w, XML_ind, 7863 FSNS( XML_w, nToken ), OString::number(nIndentAt), 7864 FSNS( XML_w, nIndentToken ), OString::number(abs(nFirstLineIndex)) ); 7865 m_pSerializer->endElementNS( XML_w, XML_pPr ); 7866 7867 // font 7868 if ( pOutSet ) 7869 { 7870 m_pSerializer->startElementNS(XML_w, XML_rPr); 7871 7872 SfxItemSet aTempSet(*pOutSet); 7873 if ( pFont ) 7874 { 7875 GetExport().GetId( *pFont ); // ensure font info is written to fontTable.xml 7876 OString aFamilyName( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); 7877 m_pSerializer->singleElementNS( XML_w, XML_rFonts, 7878 FSNS( XML_w, XML_ascii ), aFamilyName, 7879 FSNS( XML_w, XML_hAnsi ), aFamilyName, 7880 FSNS( XML_w, XML_cs ), aFamilyName, 7881 FSNS( XML_w, XML_hint ), "default" ); 7882 aTempSet.ClearItem(RES_CHRATR_FONT); 7883 aTempSet.ClearItem(RES_CHRATR_CTL_FONT); 7884 } 7885 m_rExport.OutputItemSet(aTempSet, false, true, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF); 7886 7887 WriteCollectedRunProperties(); 7888 7889 m_pSerializer->endElementNS( XML_w, XML_rPr ); 7890 } 7891 7892 // TODO anything to do about nListTabPos? 7893 7894 m_pSerializer->endElementNS( XML_w, XML_lvl ); 7895 } 7896 7897 void DocxAttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap ) 7898 { 7899 switch ( rCaseMap.GetValue() ) 7900 { 7901 case SvxCaseMap::SmallCaps: 7902 m_pSerializer->singleElementNS(XML_w, XML_smallCaps); 7903 break; 7904 case SvxCaseMap::Uppercase: 7905 m_pSerializer->singleElementNS(XML_w, XML_caps); 7906 break; 7907 default: // Something that ooxml does not support 7908 m_pSerializer->singleElementNS(XML_w, XML_smallCaps, FSNS(XML_w, XML_val), "false"); 7909 m_pSerializer->singleElementNS(XML_w, XML_caps, FSNS(XML_w, XML_val), "false"); 7910 break; 7911 } 7912 } 7913 7914 void DocxAttributeOutput::CharColor( const SvxColorItem& rColor ) 7915 { 7916 const Color aColor( rColor.GetValue() ); 7917 OString aColorString = msfilter::util::ConvertColor( aColor ); 7918 7919 const char* pExistingValue(nullptr); 7920 if (m_pColorAttrList.is() && m_pColorAttrList->getAsChar(FSNS(XML_w, XML_val), pExistingValue)) 7921 { 7922 assert(aColorString.equalsL(pExistingValue, rtl_str_getLength(pExistingValue))); 7923 return; 7924 } 7925 7926 AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_val ), aColorString.getStr() ); 7927 m_nCharTransparence = 255 - aColor.GetAlpha(); 7928 } 7929 7930 void DocxAttributeOutput::CharContour( const SvxContourItem& rContour ) 7931 { 7932 if ( rContour.GetValue() ) 7933 m_pSerializer->singleElementNS(XML_w, XML_outline); 7934 else 7935 m_pSerializer->singleElementNS(XML_w, XML_outline, FSNS(XML_w, XML_val), "false"); 7936 } 7937 7938 void DocxAttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossedOut ) 7939 { 7940 switch ( rCrossedOut.GetStrikeout() ) 7941 { 7942 case STRIKEOUT_DOUBLE: 7943 m_pSerializer->singleElementNS(XML_w, XML_dstrike); 7944 break; 7945 case STRIKEOUT_NONE: 7946 m_pSerializer->singleElementNS(XML_w, XML_dstrike, FSNS(XML_w, XML_val), "false"); 7947 m_pSerializer->singleElementNS(XML_w, XML_strike, FSNS(XML_w, XML_val), "false"); 7948 break; 7949 default: 7950 m_pSerializer->singleElementNS(XML_w, XML_strike); 7951 break; 7952 } 7953 } 7954 7955 void DocxAttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement ) 7956 { 7957 OString sIss; 7958 short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight(); 7959 7960 // Simplify styles to avoid impossible complexity. Import and export as defaults only 7961 if ( m_rExport.m_bStyDef && nEsc ) 7962 { 7963 nProp = DFLT_ESC_PROP; 7964 nEsc = (nEsc > 0) ? DFLT_ESC_AUTO_SUPER : DFLT_ESC_AUTO_SUB; 7965 } 7966 7967 if ( !nEsc ) 7968 { 7969 sIss = OString( "baseline" ); 7970 nEsc = 0; 7971 nProp = 100; 7972 } 7973 else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 ) 7974 { 7975 if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc ) 7976 sIss = OString( "subscript" ); 7977 else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc ) 7978 sIss = OString( "superscript" ); 7979 } 7980 else if ( DFLT_ESC_AUTO_SUPER == nEsc ) 7981 { 7982 // Raised by the differences between the ascenders (ascent = baseline to top of highest letter). 7983 // The ascent is generally about 80% of the total font height. 7984 // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER) 7985 nEsc = .8 * (100 - nProp); 7986 } 7987 else if ( DFLT_ESC_AUTO_SUB == nEsc ) 7988 { 7989 // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter). 7990 // The descent is generally about 20% of the total font height. 7991 // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB) 7992 nEsc = .2 * -(100 - nProp); 7993 } 7994 7995 if ( !sIss.isEmpty() ) 7996 m_pSerializer->singleElementNS(XML_w, XML_vertAlign, FSNS(XML_w, XML_val), sIss); 7997 7998 if (!(sIss.isEmpty() || sIss.match("baseline"))) 7999 return; 8000 8001 const SvxFontHeightItem& rItem = m_rExport.GetItem(RES_CHRATR_FONTSIZE); 8002 float fHeight = rItem.GetHeight(); 8003 OString sPos = OString::number( round(( fHeight * nEsc ) / 1000) ); 8004 m_pSerializer->singleElementNS(XML_w, XML_position, FSNS(XML_w, XML_val), sPos); 8005 8006 if( ( 100 != nProp || sIss.match( "baseline" ) ) && !m_rExport.m_bFontSizeWritten ) 8007 { 8008 OString sSize = OString::number( round(( fHeight * nProp ) / 1000) ); 8009 m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), sSize); 8010 } 8011 } 8012 8013 void DocxAttributeOutput::CharFont( const SvxFontItem& rFont) 8014 { 8015 GetExport().GetId( rFont ); // ensure font info is written to fontTable.xml 8016 const OUString& sFontName(rFont.GetFamilyName()); 8017 const OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8); 8018 if (sFontNameUtf8.isEmpty()) 8019 return; 8020 8021 if (m_pFontsAttrList && 8022 ( m_pFontsAttrList->hasAttribute(FSNS( XML_w, XML_ascii )) || 8023 m_pFontsAttrList->hasAttribute(FSNS( XML_w, XML_hAnsi )) ) 8024 ) 8025 { 8026 // tdf#38778: do to fields output into DOC the font could be added before and after field declaration 8027 // that all sub runs of the field will have correct font inside. 8028 // For DOCX we should do not add the same font information twice in the same node 8029 return; 8030 } 8031 8032 AddToAttrList( m_pFontsAttrList, 2, 8033 FSNS( XML_w, XML_ascii ), sFontNameUtf8.getStr(), 8034 FSNS( XML_w, XML_hAnsi ), sFontNameUtf8.getStr() ); 8035 } 8036 8037 void DocxAttributeOutput::CharFontSize( const SvxFontHeightItem& rFontSize) 8038 { 8039 OString fontSize = OString::number( ( rFontSize.GetHeight() + 5 ) / 10 ); 8040 8041 switch ( rFontSize.Which() ) 8042 { 8043 case RES_CHRATR_FONTSIZE: 8044 case RES_CHRATR_CJK_FONTSIZE: 8045 m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), fontSize); 8046 break; 8047 case RES_CHRATR_CTL_FONTSIZE: 8048 m_pSerializer->singleElementNS(XML_w, XML_szCs, FSNS(XML_w, XML_val), fontSize); 8049 break; 8050 } 8051 } 8052 8053 void DocxAttributeOutput::CharKerning( const SvxKerningItem& rKerning ) 8054 { 8055 OString aKerning = OString::number( rKerning.GetValue() ); 8056 m_pSerializer->singleElementNS(XML_w, XML_spacing, FSNS(XML_w, XML_val), aKerning); 8057 } 8058 8059 void DocxAttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage ) 8060 { 8061 OString aLanguageCode( OUStringToOString( 8062 LanguageTag( rLanguage.GetLanguage()).getBcp47MS(), 8063 RTL_TEXTENCODING_UTF8)); 8064 8065 switch ( rLanguage.Which() ) 8066 { 8067 case RES_CHRATR_LANGUAGE: 8068 AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_val ), aLanguageCode.getStr() ); 8069 break; 8070 case RES_CHRATR_CJK_LANGUAGE: 8071 AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_eastAsia ), aLanguageCode.getStr() ); 8072 break; 8073 case RES_CHRATR_CTL_LANGUAGE: 8074 AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_bidi ), aLanguageCode.getStr() ); 8075 break; 8076 } 8077 } 8078 8079 void DocxAttributeOutput::CharPosture( const SvxPostureItem& rPosture ) 8080 { 8081 if ( rPosture.GetPosture() != ITALIC_NONE ) 8082 m_pSerializer->singleElementNS(XML_w, XML_i); 8083 else 8084 m_pSerializer->singleElementNS(XML_w, XML_i, FSNS(XML_w, XML_val), "false"); 8085 } 8086 8087 void DocxAttributeOutput::CharShadow( const SvxShadowedItem& rShadow ) 8088 { 8089 if ( rShadow.GetValue() ) 8090 m_pSerializer->singleElementNS(XML_w, XML_shadow); 8091 else 8092 m_pSerializer->singleElementNS(XML_w, XML_shadow, FSNS(XML_w, XML_val), "false"); 8093 } 8094 8095 void DocxAttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline ) 8096 { 8097 const char *pUnderlineValue; 8098 8099 switch ( rUnderline.GetLineStyle() ) 8100 { 8101 case LINESTYLE_SINGLE: pUnderlineValue = "single"; break; 8102 case LINESTYLE_BOLD: pUnderlineValue = "thick"; break; 8103 case LINESTYLE_DOUBLE: pUnderlineValue = "double"; break; 8104 case LINESTYLE_DOTTED: pUnderlineValue = "dotted"; break; 8105 case LINESTYLE_DASH: pUnderlineValue = "dash"; break; 8106 case LINESTYLE_DASHDOT: pUnderlineValue = "dotDash"; break; 8107 case LINESTYLE_DASHDOTDOT: pUnderlineValue = "dotDotDash"; break; 8108 case LINESTYLE_WAVE: pUnderlineValue = "wave"; break; 8109 case LINESTYLE_BOLDDOTTED: pUnderlineValue = "dottedHeavy"; break; 8110 case LINESTYLE_BOLDDASH: pUnderlineValue = "dashedHeavy"; break; 8111 case LINESTYLE_LONGDASH: pUnderlineValue = "dashLongHeavy"; break; 8112 case LINESTYLE_BOLDLONGDASH: pUnderlineValue = "dashLongHeavy"; break; 8113 case LINESTYLE_BOLDDASHDOT: pUnderlineValue = "dashDotHeavy"; break; 8114 case LINESTYLE_BOLDDASHDOTDOT: pUnderlineValue = "dashDotDotHeavy"; break; 8115 case LINESTYLE_BOLDWAVE: pUnderlineValue = "wavyHeavy"; break; 8116 case LINESTYLE_DOUBLEWAVE: pUnderlineValue = "wavyDouble"; break; 8117 case LINESTYLE_NONE: // fall through 8118 default: pUnderlineValue = "none"; break; 8119 } 8120 8121 Color aUnderlineColor = rUnderline.GetColor(); 8122 bool bUnderlineHasColor = !aUnderlineColor.IsTransparent(); 8123 if (bUnderlineHasColor) 8124 { 8125 // Underline has a color 8126 m_pSerializer->singleElementNS( XML_w, XML_u, 8127 FSNS( XML_w, XML_val ), pUnderlineValue, 8128 FSNS( XML_w, XML_color ), msfilter::util::ConvertColor(aUnderlineColor) ); 8129 } 8130 else 8131 { 8132 // Underline has no color 8133 m_pSerializer->singleElementNS(XML_w, XML_u, FSNS(XML_w, XML_val), pUnderlineValue); 8134 } 8135 } 8136 8137 void DocxAttributeOutput::CharWeight( const SvxWeightItem& rWeight ) 8138 { 8139 if ( rWeight.GetWeight() == WEIGHT_BOLD ) 8140 m_pSerializer->singleElementNS(XML_w, XML_b); 8141 else 8142 m_pSerializer->singleElementNS(XML_w, XML_b, FSNS(XML_w, XML_val), "false"); 8143 } 8144 8145 void DocxAttributeOutput::CharAutoKern( const SvxAutoKernItem& rAutoKern ) 8146 { 8147 // auto kerning is bound to a minimum font size in Word - but is just a boolean in Writer :-( 8148 // kerning is based on half-point sizes, so 2 enables kerning for fontsize 1pt or higher. (1 is treated as size 12, and 0 is treated as disabled.) 8149 const OString sFontSize = OString::number( static_cast<sal_uInt32>(rAutoKern.GetValue()) * 2 ); 8150 m_pSerializer->singleElementNS(XML_w, XML_kern, FSNS(XML_w, XML_val), sFontSize); 8151 } 8152 8153 void DocxAttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink ) 8154 { 8155 if ( rBlink.GetValue() ) 8156 m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS(XML_w, XML_val), "blinkBackground"); 8157 else 8158 m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS(XML_w, XML_val), "none"); 8159 } 8160 8161 constexpr OUStringLiteral MSWORD_CH_SHADING_FILL = u"FFFFFF"; // The attribute w:fill of w:shd, for MS-Word's character shading, 8162 constexpr OUStringLiteral MSWORD_CH_SHADING_COLOR = u"auto"; // The attribute w:color of w:shd, for MS-Word's character shading, 8163 constexpr OUStringLiteral MSWORD_CH_SHADING_VAL = u"pct15"; // The attribute w:value of w:shd, for MS-Word's character shading, 8164 8165 void DocxAttributeOutput::CharBackground( const SvxBrushItem& rBrush ) 8166 { 8167 // Check if the brush shading pattern is 'PCT15'. If so - write it back to the DOCX 8168 if (rBrush.GetShadingValue() == ShadingPattern::PCT15) 8169 { 8170 m_pSerializer->singleElementNS( XML_w, XML_shd, 8171 FSNS( XML_w, XML_val ), MSWORD_CH_SHADING_VAL, 8172 FSNS( XML_w, XML_color ), MSWORD_CH_SHADING_COLOR, 8173 FSNS( XML_w, XML_fill ), MSWORD_CH_SHADING_FILL ); 8174 } 8175 else 8176 { 8177 m_pSerializer->singleElementNS( XML_w, XML_shd, 8178 FSNS( XML_w, XML_fill ), msfilter::util::ConvertColor(rBrush.GetColor()), 8179 FSNS( XML_w, XML_val ), "clear" ); 8180 } 8181 } 8182 8183 void DocxAttributeOutput::CharFontCJK( const SvxFontItem& rFont ) 8184 { 8185 if (m_pFontsAttrList && m_pFontsAttrList->hasAttribute(FSNS(XML_w, XML_eastAsia))) 8186 { 8187 // tdf#38778: do to fields output into DOC the font could be added before and after field declaration 8188 // that all sub runs of the field will have correct font inside. 8189 // For DOCX we should do not add the same font information twice in the same node 8190 return; 8191 } 8192 8193 const OUString& sFontName(rFont.GetFamilyName()); 8194 OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8); 8195 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsia ), sFontNameUtf8.getStr() ); 8196 } 8197 8198 void DocxAttributeOutput::CharPostureCJK( const SvxPostureItem& rPosture ) 8199 { 8200 if ( rPosture.GetPosture() != ITALIC_NONE ) 8201 m_pSerializer->singleElementNS(XML_w, XML_i); 8202 else 8203 m_pSerializer->singleElementNS(XML_w, XML_i, FSNS(XML_w, XML_val), "false"); 8204 } 8205 8206 void DocxAttributeOutput::CharWeightCJK( const SvxWeightItem& rWeight ) 8207 { 8208 if ( rWeight.GetWeight() == WEIGHT_BOLD ) 8209 m_pSerializer->singleElementNS(XML_w, XML_b); 8210 else 8211 m_pSerializer->singleElementNS(XML_w, XML_b, FSNS(XML_w, XML_val), "false"); 8212 } 8213 8214 void DocxAttributeOutput::CharFontCTL( const SvxFontItem& rFont ) 8215 { 8216 if (m_pFontsAttrList && m_pFontsAttrList->hasAttribute(FSNS(XML_w, XML_cs))) 8217 { 8218 // tdf#38778: do to fields output into DOC the font could be added before and after field declaration 8219 // that all sub runs of the field will have correct font inside. 8220 // For DOCX we should do not add the same font information twice in the same node 8221 return; 8222 } 8223 8224 const OUString& sFontName(rFont.GetFamilyName()); 8225 OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8); 8226 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cs ), sFontNameUtf8.getStr() ); 8227 } 8228 8229 void DocxAttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture) 8230 { 8231 if ( rPosture.GetPosture() != ITALIC_NONE ) 8232 m_pSerializer->singleElementNS(XML_w, XML_iCs); 8233 else 8234 m_pSerializer->singleElementNS(XML_w, XML_iCs, FSNS(XML_w, XML_val), "false"); 8235 } 8236 8237 void DocxAttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight ) 8238 { 8239 if ( rWeight.GetWeight() == WEIGHT_BOLD ) 8240 m_pSerializer->singleElementNS(XML_w, XML_bCs); 8241 else 8242 m_pSerializer->singleElementNS(XML_w, XML_bCs, FSNS(XML_w, XML_val), "false"); 8243 } 8244 8245 void DocxAttributeOutput::CharBidiRTL( const SfxPoolItem& ) 8246 { 8247 } 8248 8249 void DocxAttributeOutput::CharIdctHint( const SfxPoolItem& ) 8250 { 8251 } 8252 8253 void DocxAttributeOutput::CharRotate( const SvxCharRotateItem& rRotate) 8254 { 8255 // Not rotated? 8256 if ( !rRotate.GetValue()) 8257 return; 8258 8259 AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vert ), "true" ); 8260 8261 if (rRotate.IsFitToLine()) 8262 AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vertCompress ), "true" ); 8263 } 8264 8265 void DocxAttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark ) 8266 { 8267 const char *pEmphasis; 8268 const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark(); 8269 8270 if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove)) 8271 pEmphasis = "dot"; 8272 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove)) 8273 pEmphasis = "comma"; 8274 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove)) 8275 pEmphasis = "circle"; 8276 else if (v == (FontEmphasisMark::Dot|FontEmphasisMark::PosBelow)) 8277 pEmphasis = "underDot"; 8278 else 8279 pEmphasis = "none"; 8280 8281 m_pSerializer->singleElementNS(XML_w, XML_em, FSNS(XML_w, XML_val), pEmphasis); 8282 } 8283 8284 void DocxAttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines ) 8285 { 8286 if ( !rTwoLines.GetValue() ) 8287 return; 8288 8289 AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combine ), "true" ); 8290 8291 sal_Unicode cStart = rTwoLines.GetStartBracket(); 8292 sal_Unicode cEnd = rTwoLines.GetEndBracket(); 8293 8294 if (!cStart && !cEnd) 8295 return; 8296 8297 OString sBracket; 8298 if ((cStart == '{') || (cEnd == '}')) 8299 sBracket = const_cast<char *>("curly"); 8300 else if ((cStart == '<') || (cEnd == '>')) 8301 sBracket = const_cast<char *>("angle"); 8302 else if ((cStart == '[') || (cEnd == ']')) 8303 sBracket = const_cast<char *>("square"); 8304 else 8305 sBracket = const_cast<char *>("round"); 8306 AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combineBrackets ), sBracket.getStr() ); 8307 } 8308 8309 void DocxAttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth ) 8310 { 8311 // Clamp CharScaleWidth to OOXML limits ([1..600]) 8312 const sal_Int16 nScaleWidth( std::max<sal_Int16>( 1, 8313 std::min<sal_Int16>( rScaleWidth.GetValue(), 600 ) ) ); 8314 m_pSerializer->singleElementNS( XML_w, XML_w, 8315 FSNS( XML_w, XML_val ), OString::number(nScaleWidth) ); 8316 } 8317 8318 void DocxAttributeOutput::CharRelief( const SvxCharReliefItem& rRelief ) 8319 { 8320 switch ( rRelief.GetValue() ) 8321 { 8322 case FontRelief::Embossed: 8323 m_pSerializer->singleElementNS(XML_w, XML_emboss); 8324 break; 8325 case FontRelief::Engraved: 8326 m_pSerializer->singleElementNS(XML_w, XML_imprint); 8327 break; 8328 default: 8329 m_pSerializer->singleElementNS(XML_w, XML_emboss, FSNS(XML_w, XML_val), "false"); 8330 m_pSerializer->singleElementNS(XML_w, XML_imprint, FSNS(XML_w, XML_val), "false"); 8331 break; 8332 } 8333 } 8334 8335 void DocxAttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden ) 8336 { 8337 if ( rHidden.GetValue() ) 8338 m_pSerializer->singleElementNS(XML_w, XML_vanish); 8339 else 8340 m_pSerializer->singleElementNS(XML_w, XML_vanish, FSNS(XML_w, XML_val), "false"); 8341 } 8342 8343 void DocxAttributeOutput::CharBorder( 8344 const SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow ) 8345 { 8346 css::table::BorderLine2 rStyleBorder; 8347 const SvxBoxItem* pInherited = nullptr; 8348 if ( GetExport().m_bStyDef && GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() ) 8349 pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxBoxItem>(RES_CHRATR_BOX); 8350 else if ( m_rExport.m_pChpIter ) // incredibly undocumented, but this is the character-style info, right? 8351 { 8352 if (const SfxPoolItem* pPoolItem = GetExport().m_pChpIter->HasTextItem(RES_CHRATR_BOX)) 8353 { 8354 pInherited = &pPoolItem->StaticWhichCast(RES_CHRATR_BOX); 8355 } 8356 } 8357 8358 if ( pInherited ) 8359 rStyleBorder = SvxBoxItem::SvxLineToLine(pInherited->GetRight(), false); 8360 8361 impl_borderLine( m_pSerializer, XML_bdr, pAllBorder, nDist, bShadow, &rStyleBorder ); 8362 } 8363 8364 void DocxAttributeOutput::CharHighlight( const SvxBrushItem& rHighlight ) 8365 { 8366 const OString sColor = TransHighlightColor( msfilter::util::TransColToIco(rHighlight.GetColor()) ); 8367 if ( !sColor.isEmpty() ) 8368 { 8369 m_pSerializer->singleElementNS(XML_w, XML_highlight, FSNS(XML_w, XML_val), sColor); 8370 } 8371 } 8372 8373 void DocxAttributeOutput::TextINetFormat( const SwFormatINetFormat& rLink ) 8374 { 8375 OString aStyleId = MSWordStyles::CreateStyleId(rLink.GetINetFormat()); 8376 if (!aStyleId.isEmpty() && !aStyleId.equalsIgnoreAsciiCase("DefaultStyle")) 8377 m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); 8378 } 8379 8380 void DocxAttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat ) 8381 { 8382 OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(rCharFormat.GetCharFormat()))); 8383 8384 m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); 8385 } 8386 8387 void DocxAttributeOutput::RefField( const SwField& rField, const OUString& rRef ) 8388 { 8389 SwFieldIds nType = rField.GetTyp( )->Which( ); 8390 if ( nType == SwFieldIds::GetExp ) 8391 { 8392 OUString sCmd = FieldString( ww::eREF ) + 8393 "\"" + rRef + "\" "; 8394 8395 m_rExport.OutputField( &rField, ww::eREF, sCmd ); 8396 } 8397 8398 // There is nothing to do here for the set fields 8399 } 8400 8401 void DocxAttributeOutput::HiddenField(const SwField& rField) 8402 { 8403 auto eSubType = static_cast<SwFieldTypesEnum>(rField.GetSubType()); 8404 if (eSubType == SwFieldTypesEnum::ConditionalText) 8405 { 8406 OUString aCond = rField.GetPar1(); 8407 OUString aTrueFalse = rField.GetPar2(); 8408 sal_Int32 nPos = aTrueFalse.indexOf('|'); 8409 OUString aTrue; 8410 OUString aFalse; 8411 if (nPos == -1) 8412 { 8413 aTrue = aTrueFalse; 8414 } 8415 else 8416 { 8417 aTrue = aTrueFalse.subView(0, nPos); 8418 aFalse = aTrueFalse.subView(nPos + 1); 8419 } 8420 OUString aCmd = FieldString(ww::eIF) + aCond + " \"" + aTrue + "\" \"" + aFalse + "\""; 8421 m_rExport.OutputField(&rField, ww::eIF, aCmd); 8422 return; 8423 } 8424 8425 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::HiddenField()" ); 8426 } 8427 8428 void DocxAttributeOutput::PostitField( const SwField* pField ) 8429 { 8430 assert( dynamic_cast< const SwPostItField* >( pField )); 8431 const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pField); 8432 OString aName = OUStringToOString(pPostItField->GetName(), RTL_TEXTENCODING_UTF8); 8433 sal_Int32 nId = 0; 8434 std::map< OString, sal_Int32 >::iterator it = m_rOpenedAnnotationMarksIds.find(aName); 8435 if (it != m_rOpenedAnnotationMarksIds.end()) 8436 // If the postit field has an annotation mark associated, we already have an id. 8437 nId = it->second; 8438 else 8439 // Otherwise get a new one. 8440 nId = m_nNextAnnotationMarkId++; 8441 m_postitFields.emplace_back(pPostItField, PostItDOCXData{ nId }); 8442 } 8443 8444 void DocxAttributeOutput::WritePostitFieldReference() 8445 { 8446 while( m_postitFieldsMaxId < m_postitFields.size()) 8447 { 8448 OString idstr = OString::number(m_postitFields[m_postitFieldsMaxId].second.id); 8449 8450 // In case this file is inside annotation marks, we want to write the 8451 // comment reference after the annotation mark is closed, not here. 8452 OString idname = OUStringToOString(m_postitFields[m_postitFieldsMaxId].first->GetName(), RTL_TEXTENCODING_UTF8); 8453 std::map< OString, sal_Int32 >::iterator it = m_rOpenedAnnotationMarksIds.find( idname ); 8454 if ( it == m_rOpenedAnnotationMarksIds.end( ) ) 8455 m_pSerializer->singleElementNS(XML_w, XML_commentReference, FSNS(XML_w, XML_id), idstr); 8456 ++m_postitFieldsMaxId; 8457 } 8458 } 8459 8460 DocxAttributeOutput::hasResolved DocxAttributeOutput::WritePostitFields() 8461 { 8462 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet( 8463 SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ); 8464 8465 hasResolved eResult = hasResolved::no; 8466 for (auto& [f, data] : m_postitFields) 8467 { 8468 OString idstr = OString::number(data.id); 8469 m_pSerializer->startElementNS( XML_w, XML_comment, FSNS( XML_w, XML_id ), idstr, 8470 FSNS( XML_w, XML_author ), bRemovePersonalInfo 8471 ? "Author" + OUString::number( GetExport().GetInfoID(f->GetPar1()) ) 8472 : f->GetPar1(), 8473 FSNS( XML_w, XML_date ), DateTimeToOString( bRemovePersonalInfo 8474 ? util::DateTime() // "no date" time 8475 : f->GetDateTime() ), 8476 FSNS( XML_w, XML_initials ), bRemovePersonalInfo 8477 ? OUString::number( GetExport().GetInfoID(f->GetInitials()) ) 8478 : f->GetInitials() ); 8479 8480 const bool bNeedParaId = f->GetResolved(); 8481 if (bNeedParaId) 8482 eResult = hasResolved::yes; 8483 8484 if (f->GetTextObject() != nullptr) 8485 { 8486 // richtext 8487 data.lastParaId = GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN, bNeedParaId); 8488 } 8489 else 8490 { 8491 // just plain text - eg. when the field was created via the 8492 // .uno:InsertAnnotation API 8493 std::optional<OUString> aParaId; 8494 if (bNeedParaId) 8495 { 8496 data.lastParaId = m_nNextParaId++; 8497 aParaId = NumberToHexBinary(data.lastParaId); 8498 } 8499 m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId); 8500 m_pSerializer->startElementNS(XML_w, XML_r); 8501 RunText(f->GetText()); 8502 m_pSerializer->endElementNS(XML_w, XML_r); 8503 m_pSerializer->endElementNS(XML_w, XML_p); 8504 } 8505 8506 m_pSerializer->endElementNS( XML_w, XML_comment ); 8507 } 8508 return eResult; 8509 } 8510 8511 void DocxAttributeOutput::WritePostItFieldsResolved() 8512 { 8513 for (auto& [f, data] : m_postitFields) 8514 { 8515 if (!f->GetResolved()) 8516 continue; 8517 OUString idstr = NumberToHexBinary(data.lastParaId); 8518 m_pSerializer->singleElementNS(XML_w15, XML_commentEx, FSNS(XML_w15, XML_paraId), idstr, 8519 FSNS(XML_w15, XML_done), "1"); 8520 } 8521 } 8522 8523 bool DocxAttributeOutput::DropdownField( const SwField* pField ) 8524 { 8525 ww::eField eType = ww::eFORMDROPDOWN; 8526 OUString sCmd = FieldString( eType ); 8527 GetExport( ).OutputField( pField, eType, sCmd ); 8528 8529 return false; 8530 } 8531 8532 bool DocxAttributeOutput::PlaceholderField( const SwField* pField ) 8533 { 8534 assert( pendingPlaceholder == nullptr ); 8535 pendingPlaceholder = pField; 8536 return false; // do not expand 8537 } 8538 8539 void DocxAttributeOutput::WritePendingPlaceholder() 8540 { 8541 if( pendingPlaceholder == nullptr ) 8542 return; 8543 const SwField* pField = pendingPlaceholder; 8544 pendingPlaceholder = nullptr; 8545 m_pSerializer->startElementNS(XML_w, XML_sdt); 8546 m_pSerializer->startElementNS(XML_w, XML_sdtPr); 8547 if( !pField->GetPar2().isEmpty()) 8548 m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), pField->GetPar2()); 8549 m_pSerializer->singleElementNS(XML_w, XML_temporary); 8550 m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr); 8551 m_pSerializer->singleElementNS(XML_w, XML_text); 8552 m_pSerializer->endElementNS( XML_w, XML_sdtPr ); 8553 m_pSerializer->startElementNS(XML_w, XML_sdtContent); 8554 m_pSerializer->startElementNS(XML_w, XML_r); 8555 RunText( pField->GetPar1()); 8556 m_pSerializer->endElementNS( XML_w, XML_r ); 8557 m_pSerializer->endElementNS( XML_w, XML_sdtContent ); 8558 m_pSerializer->endElementNS( XML_w, XML_sdt ); 8559 } 8560 8561 void DocxAttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd ) 8562 { 8563 // field bookmarks are handled in the EndRun method 8564 GetExport().OutputField(&rField, eType, rCmd ); 8565 } 8566 8567 void DocxAttributeOutput::WriteExpand( const SwField* pField ) 8568 { 8569 // Will be written in the next End Run 8570 m_rExport.OutputField( pField, ww::eUNKNOWN, OUString() ); 8571 } 8572 8573 void DocxAttributeOutput::WriteField_Impl(const SwField *const pField, 8574 ww::eField const eType, const OUString& rFieldCmd, FieldFlags const nMode, 8575 OUString const*const pBookmarkName) 8576 { 8577 if (m_bPreventDoubleFieldsHandling) 8578 return; 8579 8580 struct FieldInfos infos; 8581 if (pField) 8582 infos.pField = pField->CopyField(); 8583 infos.sCmd = rFieldCmd; 8584 infos.eType = eType; 8585 infos.bClose = bool(FieldFlags::Close & nMode); 8586 infos.bSep = bool(FieldFlags::CmdEnd & nMode); 8587 infos.bOpen = bool(FieldFlags::Start & nMode); 8588 m_Fields.push_back( infos ); 8589 8590 if (pBookmarkName) 8591 { 8592 m_sFieldBkm = *pBookmarkName; 8593 } 8594 8595 if ( !pField ) 8596 return; 8597 8598 SwFieldIds nType = pField->GetTyp( )->Which( ); 8599 sal_uInt16 nSubType = pField->GetSubType(); 8600 8601 // TODO Any other field types here ? 8602 if ( ( nType == SwFieldIds::SetExp ) && ( nSubType & nsSwGetSetExpType::GSE_STRING ) ) 8603 { 8604 const SwSetExpField *pSet = static_cast<const SwSetExpField*>( pField ); 8605 m_sFieldBkm = pSet->GetPar1( ); 8606 } 8607 else if ( nType == SwFieldIds::Dropdown ) 8608 { 8609 const SwDropDownField* pDropDown = static_cast<const SwDropDownField*>( pField ); 8610 m_sFieldBkm = pDropDown->GetName( ); 8611 } 8612 } 8613 8614 void DocxAttributeOutput::WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark ) 8615 { 8616 if ( !m_Fields.empty() ) 8617 m_Fields.begin()->pFieldmark = &rFieldmark; 8618 } 8619 8620 void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds, const SwRedlineData* pRedlineData ) 8621 { 8622 for ( const OUString & name : rStarts ) 8623 { 8624 if (name.startsWith("permission-for-group:") || 8625 name.startsWith("permission-for-user:")) 8626 { 8627 m_rPermissionsStart.push_back(name); 8628 } 8629 else 8630 { 8631 m_rBookmarksStart.push_back(name); 8632 m_pMoveRedlineData = const_cast<SwRedlineData*>(pRedlineData); 8633 } 8634 } 8635 rStarts.clear(); 8636 8637 for ( const OUString & name : rEnds ) 8638 { 8639 if (name.startsWith("permission-for-group:") || 8640 name.startsWith("permission-for-user:")) 8641 { 8642 m_rPermissionsEnd.push_back(name); 8643 } 8644 else 8645 { 8646 m_rBookmarksEnd.push_back(name); 8647 } 8648 } 8649 rEnds.clear(); 8650 } 8651 8652 void DocxAttributeOutput::WriteFinalBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ) 8653 { 8654 for ( const OUString & name : rStarts ) 8655 { 8656 if (name.startsWith("permission-for-group:") || 8657 name.startsWith("permission-for-user:")) 8658 { 8659 m_rPermissionsStart.push_back(name); 8660 } 8661 else 8662 { 8663 m_rFinalBookmarksStart.push_back(name); 8664 } 8665 } 8666 rStarts.clear(); 8667 8668 for ( const OUString & name : rEnds ) 8669 { 8670 if (name.startsWith("permission-for-group:") || 8671 name.startsWith("permission-for-user:")) 8672 { 8673 m_rPermissionsEnd.push_back(name); 8674 } 8675 else 8676 { 8677 m_rFinalBookmarksEnd.push_back(name); 8678 } 8679 } 8680 rEnds.clear(); 8681 } 8682 8683 void DocxAttributeOutput::WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts, 8684 std::vector< OUString >& rEnds ) 8685 { 8686 for ( const auto & rAnnotationName : rStarts ) 8687 { 8688 OString rName = OUStringToOString(rAnnotationName, RTL_TEXTENCODING_UTF8 ).getStr( ); 8689 m_rAnnotationMarksStart.push_back( rName ); 8690 } 8691 rStarts.clear(); 8692 8693 for ( const auto & rAnnotationName : rEnds ) 8694 { 8695 OString rName = OUStringToOString( rAnnotationName, RTL_TEXTENCODING_UTF8 ).getStr( ); 8696 m_rAnnotationMarksEnd.push_back( rName ); 8697 } 8698 rEnds.clear(); 8699 } 8700 8701 void DocxAttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote ) 8702 { 8703 const SwEndNoteInfo& rInfo = rFootnote.IsEndNote()? 8704 m_rExport.m_rDoc.GetEndNoteInfo(): m_rExport.m_rDoc.GetFootnoteInfo(); 8705 8706 // footnote/endnote run properties 8707 const SwCharFormat* pCharFormat = rInfo.GetAnchorCharFormat( m_rExport.m_rDoc ); 8708 8709 OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat))); 8710 8711 m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); 8712 8713 // remember the footnote/endnote to 8714 // 1) write the footnoteReference/endnoteReference in EndRunProperties() 8715 // 2) be able to dump them all to footnotes.xml/endnotes.xml 8716 if ( !rFootnote.IsEndNote() && m_rExport.m_rDoc.GetFootnoteInfo().m_ePos != FTNPOS_CHAPTER ) 8717 m_pFootnotesList->add( rFootnote ); 8718 else 8719 m_pEndnotesList->add( rFootnote ); 8720 } 8721 8722 void DocxAttributeOutput::FootnoteEndnoteReference() 8723 { 8724 sal_Int32 nId; 8725 const SwFormatFootnote *pFootnote = m_pFootnotesList->getCurrent( nId ); 8726 sal_Int32 nToken = XML_footnoteReference; 8727 8728 // both cannot be set at the same time - if they are, it's a bug 8729 if ( !pFootnote ) 8730 { 8731 pFootnote = m_pEndnotesList->getCurrent( nId ); 8732 nToken = XML_endnoteReference; 8733 } 8734 8735 if ( !pFootnote ) 8736 return; 8737 8738 // write it 8739 if ( pFootnote->GetNumStr().isEmpty() ) 8740 { 8741 // autonumbered 8742 m_pSerializer->singleElementNS(XML_w, nToken, FSNS(XML_w, XML_id), OString::number(nId)); 8743 } 8744 else 8745 { 8746 // not autonumbered 8747 m_pSerializer->singleElementNS( XML_w, nToken, 8748 FSNS( XML_w, XML_customMarkFollows ), "1", 8749 FSNS( XML_w, XML_id ), OString::number(nId) ); 8750 8751 RunText( pFootnote->GetNumStr() ); 8752 } 8753 } 8754 8755 static void WriteFootnoteSeparatorHeight( 8756 ::sax_fastparser::FSHelperPtr const& pSerializer, SwTwips const nHeight) 8757 { 8758 // try to get the height by setting font size of the paragraph 8759 if (nHeight != 0) 8760 { 8761 pSerializer->startElementNS(XML_w, XML_pPr); 8762 pSerializer->startElementNS(XML_w, XML_rPr); 8763 pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), 8764 OString::number((nHeight + 5) / 10)); 8765 pSerializer->endElementNS(XML_w, XML_rPr); 8766 pSerializer->endElementNS(XML_w, XML_pPr); 8767 } 8768 } 8769 8770 void DocxAttributeOutput::FootnotesEndnotes( bool bFootnotes ) 8771 { 8772 const FootnotesVector& rVector = bFootnotes? m_pFootnotesList->getVector(): m_pEndnotesList->getVector(); 8773 8774 sal_Int32 nBody = bFootnotes? XML_footnotes: XML_endnotes; 8775 sal_Int32 nItem = bFootnotes? XML_footnote: XML_endnote; 8776 8777 m_pSerializer->startElementNS( XML_w, nBody, m_rExport.MainXmlNamespaces() ); 8778 8779 sal_Int32 nIndex = 0; 8780 8781 // separator 8782 // note: can only be defined for the whole document, not per section 8783 m_pSerializer->startElementNS( XML_w, nItem, 8784 FSNS( XML_w, XML_id ), OString::number(nIndex++), 8785 FSNS( XML_w, XML_type ), "separator" ); 8786 m_pSerializer->startElementNS(XML_w, XML_p); 8787 8788 bool bSeparator = true; 8789 SwTwips nHeight(0); 8790 if (bFootnotes) 8791 { 8792 const SwPageFootnoteInfo& rFootnoteInfo = m_rExport.m_rDoc.GetPageDesc(0).GetFootnoteInfo(); 8793 // Request separator only if both width and thickness are non-zero. 8794 bSeparator = rFootnoteInfo.GetLineStyle() != SvxBorderLineStyle::NONE 8795 && rFootnoteInfo.GetLineWidth() > 0 8796 && double(rFootnoteInfo.GetWidth()) > 0; 8797 nHeight = sw::FootnoteSeparatorHeight(rFootnoteInfo); 8798 } 8799 8800 WriteFootnoteSeparatorHeight(m_pSerializer, nHeight); 8801 8802 m_pSerializer->startElementNS(XML_w, XML_r); 8803 if (bSeparator) 8804 m_pSerializer->singleElementNS(XML_w, XML_separator); 8805 m_pSerializer->endElementNS( XML_w, XML_r ); 8806 m_pSerializer->endElementNS( XML_w, XML_p ); 8807 m_pSerializer->endElementNS( XML_w, nItem ); 8808 8809 // separator 8810 m_pSerializer->startElementNS( XML_w, nItem, 8811 FSNS( XML_w, XML_id ), OString::number(nIndex++), 8812 FSNS( XML_w, XML_type ), "continuationSeparator" ); 8813 m_pSerializer->startElementNS(XML_w, XML_p); 8814 8815 WriteFootnoteSeparatorHeight(m_pSerializer, nHeight); 8816 8817 m_pSerializer->startElementNS(XML_w, XML_r); 8818 if (bSeparator) 8819 { 8820 m_pSerializer->singleElementNS(XML_w, XML_continuationSeparator); 8821 } 8822 m_pSerializer->endElementNS( XML_w, XML_r ); 8823 m_pSerializer->endElementNS( XML_w, XML_p ); 8824 m_pSerializer->endElementNS( XML_w, nItem ); 8825 8826 // if new special ones are added, update also WriteFootnoteEndnotePr() 8827 8828 // footnotes/endnotes themselves 8829 for ( const auto& rpItem : rVector ) 8830 { 8831 m_footnoteEndnoteRefTag = bFootnotes ? XML_footnoteRef : XML_endnoteRef; 8832 m_footnoteCustomLabel = rpItem->GetNumStr(); 8833 8834 m_pSerializer->startElementNS(XML_w, nItem, FSNS(XML_w, XML_id), OString::number(nIndex)); 8835 8836 const SwNodeIndex* pIndex = rpItem->GetTextFootnote()->GetStartNode(); 8837 m_rExport.WriteSpecialText( pIndex->GetIndex() + 1, 8838 pIndex->GetNode().EndOfSectionIndex(), 8839 bFootnotes? TXT_FTN: TXT_EDN ); 8840 8841 m_pSerializer->endElementNS( XML_w, nItem ); 8842 ++nIndex; 8843 } 8844 8845 m_pSerializer->endElementNS( XML_w, nBody ); 8846 8847 } 8848 8849 void DocxAttributeOutput::WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr const & fs, int tag, 8850 const SwEndNoteInfo& info, int listtag ) 8851 { 8852 fs->startElementNS(XML_w, tag); 8853 OString aCustomFormat; 8854 OString fmt = lcl_ConvertNumberingType(info.m_aFormat.GetNumberingType(), nullptr, aCustomFormat); 8855 if (!fmt.isEmpty() && aCustomFormat.isEmpty()) 8856 fs->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), fmt); 8857 if( info.m_nFootnoteOffset != 0 ) 8858 fs->singleElementNS( XML_w, XML_numStart, FSNS( XML_w, XML_val ), 8859 OString::number(info.m_nFootnoteOffset + 1) ); 8860 8861 const SwFootnoteInfo* pFootnoteInfo = dynamic_cast<const SwFootnoteInfo*>(&info); 8862 if( pFootnoteInfo ) 8863 { 8864 switch( pFootnoteInfo->m_eNum ) 8865 { 8866 case FTNNUM_PAGE: fmt = "eachPage"; break; 8867 case FTNNUM_CHAPTER: fmt = "eachSect"; break; 8868 default: fmt.clear(); break; 8869 } 8870 if (!fmt.isEmpty()) 8871 fs->singleElementNS(XML_w, XML_numRestart, FSNS(XML_w, XML_val), fmt); 8872 } 8873 8874 if( listtag != 0 ) // we are writing to settings.xml, write also special footnote/endnote list 8875 { // there are currently only two hardcoded ones ( see FootnotesEndnotes()) 8876 fs->singleElementNS(XML_w, listtag, FSNS(XML_w, XML_id), "0"); 8877 fs->singleElementNS(XML_w, listtag, FSNS(XML_w, XML_id), "1"); 8878 } 8879 fs->endElementNS( XML_w, tag ); 8880 } 8881 8882 void DocxAttributeOutput::SectFootnoteEndnotePr() 8883 { 8884 if( HasFootnotes()) 8885 WriteFootnoteEndnotePr( m_pSerializer, XML_footnotePr, m_rExport.m_rDoc.GetFootnoteInfo(), 0 ); 8886 if( HasEndnotes()) 8887 WriteFootnoteEndnotePr( m_pSerializer, XML_endnotePr, m_rExport.m_rDoc.GetEndNoteInfo(), 0 ); 8888 } 8889 8890 void DocxAttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti ) 8891 { 8892 if ( nSpace < 0 ) 8893 { 8894 AddToAttrList( m_pParagraphSpacingAttrList, 2, 8895 FSNS( XML_w, XML_lineRule ), "exact", 8896 FSNS( XML_w, XML_line ), OString::number( -nSpace ).getStr() ); 8897 } 8898 else if( nSpace > 0 && nMulti ) 8899 { 8900 AddToAttrList( m_pParagraphSpacingAttrList, 2, 8901 FSNS( XML_w, XML_lineRule ), "auto", 8902 FSNS( XML_w, XML_line ), OString::number( nSpace ).getStr() ); 8903 } 8904 else 8905 { 8906 AddToAttrList( m_pParagraphSpacingAttrList, 2, 8907 FSNS( XML_w, XML_lineRule ), "atLeast", 8908 FSNS( XML_w, XML_line ), OString::number( nSpace ).getStr() ); 8909 } 8910 } 8911 8912 void DocxAttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust ) 8913 { 8914 const char *pAdjustString; 8915 8916 bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; 8917 8918 const SfxItemSet* pItems = GetExport().GetCurItemSet(); 8919 const SvxFrameDirectionItem* rFrameDir = pItems? 8920 pItems->GetItem( RES_FRAMEDIR ) : nullptr; 8921 8922 SvxFrameDirection nDir = SvxFrameDirection::Environment; 8923 if( rFrameDir != nullptr ) 8924 nDir = rFrameDir->GetValue(); 8925 if ( nDir == SvxFrameDirection::Environment ) 8926 nDir = GetExport( ).GetDefaultFrameDirection( ); 8927 bool bRtl = ( nDir == SvxFrameDirection::Horizontal_RL_TB ); 8928 8929 switch ( rAdjust.GetAdjust() ) 8930 { 8931 case SvxAdjust::Left: 8932 if ( bEcma ) 8933 { 8934 if ( bRtl ) 8935 pAdjustString = "right"; 8936 else 8937 pAdjustString = "left"; 8938 } 8939 else if ( bRtl ) 8940 pAdjustString = "end"; 8941 else 8942 pAdjustString = "start"; 8943 break; 8944 case SvxAdjust::Right: 8945 if ( bEcma ) 8946 { 8947 if ( bRtl ) 8948 pAdjustString = "left"; 8949 else 8950 pAdjustString = "right"; 8951 } 8952 else if ( bRtl ) 8953 pAdjustString = "start"; 8954 else 8955 pAdjustString = "end"; 8956 break; 8957 case SvxAdjust::BlockLine: 8958 case SvxAdjust::Block: 8959 if (rAdjust.GetLastBlock() == SvxAdjust::Block) 8960 pAdjustString = "distribute"; 8961 else 8962 pAdjustString = "both"; 8963 break; 8964 case SvxAdjust::Center: 8965 pAdjustString = "center"; 8966 break; 8967 default: 8968 return; // not supported attribute 8969 } 8970 m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pAdjustString); 8971 } 8972 8973 void DocxAttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit ) 8974 { 8975 if (rSplit.GetValue()) 8976 m_pSerializer->singleElementNS(XML_w, XML_keepLines, FSNS(XML_w, XML_val), "false"); 8977 else 8978 m_pSerializer->singleElementNS(XML_w, XML_keepLines); 8979 } 8980 8981 void DocxAttributeOutput::ParaWidows( const SvxWidowsItem& rWidows ) 8982 { 8983 if (rWidows.GetValue()) 8984 m_pSerializer->singleElementNS(XML_w, XML_widowControl); 8985 else 8986 m_pSerializer->singleElementNS(XML_w, XML_widowControl, FSNS(XML_w, XML_val), "false"); 8987 } 8988 8989 static void impl_WriteTabElement( FSHelperPtr const & pSerializer, 8990 const SvxTabStop& rTab, tools::Long tabsOffset ) 8991 { 8992 rtl::Reference<FastAttributeList> pTabElementAttrList = FastSerializerHelper::createAttrList(); 8993 8994 switch (rTab.GetAdjustment()) 8995 { 8996 case SvxTabAdjust::Right: 8997 pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "right" ) ); 8998 break; 8999 case SvxTabAdjust::Decimal: 9000 pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "decimal" ) ); 9001 break; 9002 case SvxTabAdjust::Center: 9003 pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "center" ) ); 9004 break; 9005 case SvxTabAdjust::Default: 9006 case SvxTabAdjust::Left: 9007 default: 9008 pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "left" ) ); 9009 break; 9010 } 9011 9012 // Write position according to used offset of the whole paragraph. 9013 // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins. 9014 // But in ODT, zero position could be page margins or paragraph indent according to used settings. 9015 // This is handled outside of this method and provided for us in tabsOffset parameter. 9016 pTabElementAttrList->add( FSNS( XML_w, XML_pos ), OString::number( rTab.GetTabPos() + tabsOffset ) ); 9017 9018 sal_Unicode cFillChar = rTab.GetFill(); 9019 9020 if ('.' == cFillChar ) 9021 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "dot" ) ); 9022 else if ( '-' == cFillChar ) 9023 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "hyphen" ) ); 9024 else if ( u'\x00B7' == cFillChar ) // middle dot 9025 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "middleDot" ) ); 9026 else if ( '_' == cFillChar ) 9027 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "underscore" ) ); 9028 else 9029 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "none" ) ); 9030 9031 pSerializer->singleElementNS(XML_w, XML_tab, pTabElementAttrList); 9032 } 9033 9034 void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop ) 9035 { 9036 const SvxTabStopItem* pInheritedTabs = nullptr; 9037 if ( GetExport().m_pStyAttr ) 9038 pInheritedTabs = GetExport().m_pStyAttr->GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP); 9039 else if ( GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() ) 9040 pInheritedTabs = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP); 9041 const sal_uInt16 nInheritedTabCount = pInheritedTabs ? pInheritedTabs->Count() : 0; 9042 const sal_uInt16 nCount = rTabStop.Count(); 9043 9044 // <w:tabs> must contain at least one <w:tab>, so don't write it empty 9045 if ( !nCount && !nInheritedTabCount ) 9046 return; 9047 if( nCount == 1 && rTabStop[ 0 ].GetAdjustment() == SvxTabAdjust::Default ) 9048 { 9049 GetExport().setDefaultTabStop( rTabStop[ 0 ].GetTabPos()); 9050 return; 9051 } 9052 9053 // do not output inherited tabs twice (inside styles and inside inline properties) 9054 if ( nCount == nInheritedTabCount && nCount > 0 ) 9055 { 9056 if ( *pInheritedTabs == rTabStop ) 9057 return; 9058 } 9059 9060 m_pSerializer->startElementNS(XML_w, XML_tabs); 9061 9062 // Get offset for tabs 9063 // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins. 9064 // But in ODT, zero position could be page margins or paragraph indent according to used settings. 9065 tools::Long tabsOffset = 0; 9066 if (m_rExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT)) 9067 tabsOffset = m_rExport.GetItem(RES_LR_SPACE).GetTextLeft(); 9068 9069 // clear unused inherited tabs - otherwise the style will add them back in 9070 sal_Int32 nCurrTab = 0; 9071 for ( sal_uInt16 i = 0; i < nInheritedTabCount; ++i ) 9072 { 9073 while ( nCurrTab < nCount && rTabStop[nCurrTab] < pInheritedTabs->At(i) ) 9074 ++nCurrTab; 9075 9076 if ( nCurrTab == nCount || pInheritedTabs->At(i) < rTabStop[nCurrTab] ) 9077 { 9078 m_pSerializer->singleElementNS( XML_w, XML_tab, 9079 FSNS( XML_w, XML_val ), "clear", 9080 FSNS( XML_w, XML_pos ), OString::number(pInheritedTabs->At(i).GetTabPos()) ); 9081 } 9082 } 9083 9084 for (sal_uInt16 i = 0; i < nCount; i++ ) 9085 { 9086 if( rTabStop[i].GetAdjustment() != SvxTabAdjust::Default ) 9087 impl_WriteTabElement( m_pSerializer, rTabStop[i], tabsOffset ); 9088 else 9089 GetExport().setDefaultTabStop( rTabStop[i].GetTabPos()); 9090 } 9091 9092 m_pSerializer->endElementNS( XML_w, XML_tabs ); 9093 } 9094 9095 void DocxAttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone ) 9096 { 9097 m_pSerializer->singleElementNS( XML_w, XML_suppressAutoHyphens, 9098 FSNS( XML_w, XML_val ), OString::boolean( !rHyphenZone.IsHyphen() ) ); 9099 } 9100 9101 void DocxAttributeOutput::ParaNumRule_Impl( const SwTextNode* pTextNd, sal_Int32 nLvl, sal_Int32 nNumId ) 9102 { 9103 if ( USHRT_MAX == nNumId ) 9104 return; 9105 9106 // LibreOffice is not very flexible with "Outline Numbering" (aka "Outline" numbering style). 9107 // Only ONE numbering rule ("Outline") can be associated with a style-assigned-listLevel, 9108 // and no other style is able to inherit these numId/nLvl settings - only text nodes can. 9109 // So listLevel only exists in paragraph properties EXCEPT for up to ten styles that have been 9110 // assigned to one of these special Chapter Numbering listlevels (by default Heading 1-10). 9111 const sal_Int32 nTableSize = m_rExport.m_pUsedNumTable ? m_rExport.m_pUsedNumTable->size() : 0; 9112 const SwNumRule* pRule = nNumId > 0 && nNumId <= nTableSize ? (*m_rExport.m_pUsedNumTable)[nNumId-1] : nullptr; 9113 const SwTextFormatColl* pColl = pTextNd ? pTextNd->GetTextColl() : nullptr; 9114 // Do not duplicate numbering that is inherited from the (Chapter numbering) style 9115 // (since on import we duplicate style numbering/listlevel to the paragraph). 9116 if (pColl && pColl->IsAssignedToListLevelOfOutlineStyle() 9117 && nLvl == pColl->GetAssignedOutlineStyleLevel() && pRule && pRule->IsOutlineRule()) 9118 { 9119 // By definition of how LO is implemented, assignToListLevel is only possible 9120 // when the style is also using OutlineRule for numbering. Adjust logic if that changes. 9121 assert(pRule->GetName() == pColl->GetNumRule(true).GetValue()); 9122 return; 9123 } 9124 9125 m_pSerializer->startElementNS(XML_w, XML_numPr); 9126 m_pSerializer->singleElementNS(XML_w, XML_ilvl, FSNS(XML_w, XML_val), OString::number(nLvl)); 9127 m_pSerializer->singleElementNS(XML_w, XML_numId, FSNS(XML_w, XML_val), OString::number(nNumId)); 9128 m_pSerializer->endElementNS(XML_w, XML_numPr); 9129 } 9130 9131 void DocxAttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace ) 9132 { 9133 m_pSerializer->singleElementNS( XML_w, XML_autoSpaceDE, 9134 FSNS( XML_w, XML_val ), OString::boolean( rScriptSpace.GetValue() ) ); 9135 } 9136 9137 void DocxAttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem ) 9138 { 9139 m_pSerializer->singleElementNS( XML_w, XML_overflowPunct, 9140 FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) ); 9141 } 9142 9143 void DocxAttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem ) 9144 { 9145 m_pSerializer->singleElementNS( XML_w, XML_kinsoku, 9146 FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) ); 9147 } 9148 9149 void DocxAttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign ) 9150 { 9151 const char *pAlignString; 9152 9153 switch ( rAlign.GetValue() ) 9154 { 9155 case SvxParaVertAlignItem::Align::Baseline: 9156 pAlignString = "baseline"; 9157 break; 9158 case SvxParaVertAlignItem::Align::Top: 9159 pAlignString = "top"; 9160 break; 9161 case SvxParaVertAlignItem::Align::Center: 9162 pAlignString = "center"; 9163 break; 9164 case SvxParaVertAlignItem::Align::Bottom: 9165 pAlignString = "bottom"; 9166 break; 9167 case SvxParaVertAlignItem::Align::Automatic: 9168 pAlignString = "auto"; 9169 break; 9170 default: 9171 return; // not supported attribute 9172 } 9173 m_pSerializer->singleElementNS(XML_w, XML_textAlignment, FSNS(XML_w, XML_val), pAlignString); 9174 } 9175 9176 void DocxAttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid ) 9177 { 9178 m_pSerializer->singleElementNS( XML_w, XML_snapToGrid, 9179 FSNS( XML_w, XML_val ), OString::boolean( rGrid.GetValue() ) ); 9180 } 9181 9182 void DocxAttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize ) 9183 { 9184 if (m_rExport.SdrExporter().getTextFrameSyntax() && m_rExport.SdrExporter().getFlyFrameSize()) 9185 { 9186 const Size* pSize = m_rExport.SdrExporter().getFlyFrameSize(); 9187 m_rExport.SdrExporter().getTextFrameStyle().append(";width:" + OString::number(double(pSize->Width()) / 20)); 9188 m_rExport.SdrExporter().getTextFrameStyle().append("pt;height:" + OString::number(double(pSize->Height()) / 20) + "pt"); 9189 } 9190 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9191 { 9192 } 9193 else if ( m_rExport.m_bOutFlyFrameAttrs ) 9194 { 9195 if ( rSize.GetWidth() && rSize.GetWidthSizeType() == SwFrameSize::Fixed ) 9196 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 9197 FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ).getStr() ); 9198 9199 if ( rSize.GetHeight() ) 9200 { 9201 OString sRule( "exact" ); 9202 if ( rSize.GetHeightSizeType() == SwFrameSize::Minimum ) 9203 sRule = OString( "atLeast" ); 9204 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2, 9205 FSNS( XML_w, XML_hRule ), sRule.getStr(), 9206 FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ).getStr() ); 9207 } 9208 } 9209 else if ( m_rExport.m_bOutPageDescs ) 9210 { 9211 rtl::Reference<FastAttributeList> attrList = FastSerializerHelper::createAttrList( ); 9212 if ( m_rExport.m_pCurrentPageDesc->GetLandscape( ) ) 9213 attrList->add( FSNS( XML_w, XML_orient ), "landscape" ); 9214 9215 attrList->add( FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ) ); 9216 attrList->add( FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ) ); 9217 9218 m_pSerializer->singleElementNS( XML_w, XML_pgSz, attrList ); 9219 } 9220 } 9221 9222 void DocxAttributeOutput::FormatPaperBin( const SvxPaperBinItem& ) 9223 { 9224 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatPaperBin()" ); 9225 } 9226 9227 void DocxAttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLRSpace ) 9228 { 9229 bool bEcma = m_rExport.GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; 9230 9231 if (m_rExport.SdrExporter().getTextFrameSyntax()) 9232 { 9233 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-left:" + OString::number(double(rLRSpace.GetLeft()) / 20) + "pt"); 9234 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-right:" + OString::number(double(rLRSpace.GetRight()) / 20) + "pt"); 9235 } 9236 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9237 { 9238 } 9239 else if ( m_rExport.m_bOutFlyFrameAttrs ) 9240 { 9241 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hSpace ), 9242 OString::number( 9243 ( rLRSpace.GetLeft() + rLRSpace.GetRight() ) / 2 ).getStr() ); 9244 } 9245 else if ( m_rExport.m_bOutPageDescs ) 9246 { 9247 m_pageMargins.nLeft = 0; 9248 m_pageMargins.nRight = 0; 9249 9250 const SfxPoolItem* pPoolItem = m_rExport.HasItem(RES_BOX); 9251 const SvxBoxItem* pBoxItem = pPoolItem ? &pPoolItem->StaticWhichCast(RES_BOX) : nullptr; 9252 if (pBoxItem) 9253 { 9254 m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true ); 9255 m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true ); 9256 } 9257 9258 m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft()); 9259 m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight()); 9260 sal_uInt16 nGutter = rLRSpace.GetGutterMargin(); 9261 9262 AddToAttrList( m_pSectionSpacingAttrList, 3, 9263 FSNS( XML_w, XML_left ), OString::number( m_pageMargins.nLeft ).getStr(), 9264 FSNS( XML_w, XML_right ), OString::number( m_pageMargins.nRight ).getStr(), 9265 FSNS( XML_w, XML_gutter ), OString::number( nGutter ).getStr() ); 9266 } 9267 else 9268 { 9269 SvxLRSpaceItem const* pLRSpace(&rLRSpace); 9270 ::std::optional<SvxLRSpaceItem> oLRSpace; 9271 if (dynamic_cast<SwContentNode const*>(GetExport().m_pOutFormatNode) != nullptr) 9272 { 9273 auto pTextNd(static_cast<SwTextNode const*>(GetExport().m_pOutFormatNode)); 9274 // WW doesn't have a concept of a pararaph that's in a list but not 9275 // counted in the list - see AttributeOutputBase::ParaNumRule() 9276 // forcing non-existent numId="0" in this case. 9277 // This means WW won't apply the indents from the numbering, 9278 // so try to add them as paragraph properties here. 9279 if (!pTextNd->IsCountedInList()) 9280 { 9281 SfxItemSetFixed<RES_LR_SPACE, RES_LR_SPACE> temp(m_rExport.m_rDoc.GetAttrPool()); 9282 pTextNd->GetParaAttr(temp, 0, 0, false, true, true, nullptr); 9283 if (auto *const pItem = temp.GetItem(RES_LR_SPACE)) 9284 { 9285 // but don't use first-line offset from list (should it be 0 or from node?) 9286 oLRSpace.emplace(*pItem); 9287 oLRSpace->SetTextFirstLineOffset(pLRSpace->GetTextFirstLineOffset()); 9288 pLRSpace = &*oLRSpace; 9289 } 9290 } 9291 } 9292 rtl::Reference<FastAttributeList> pLRSpaceAttrList = FastSerializerHelper::createAttrList(); 9293 if ((0 != pLRSpace->GetTextLeft()) || (pLRSpace->IsExplicitZeroMarginValLeft())) 9294 { 9295 pLRSpaceAttrList->add( FSNS(XML_w, (bEcma ? XML_left : XML_start)), OString::number(pLRSpace->GetTextLeft()) ); 9296 } 9297 if ((0 != pLRSpace->GetRight()) || (pLRSpace->IsExplicitZeroMarginValRight())) 9298 { 9299 pLRSpaceAttrList->add( FSNS(XML_w, (bEcma ? XML_right : XML_end)), OString::number(pLRSpace->GetRight()) ); 9300 } 9301 sal_Int32 const nFirstLineAdjustment = pLRSpace->GetTextFirstLineOffset(); 9302 if (nFirstLineAdjustment > 0) 9303 pLRSpaceAttrList->add( FSNS( XML_w, XML_firstLine ), OString::number( nFirstLineAdjustment ) ); 9304 else 9305 pLRSpaceAttrList->add( FSNS( XML_w, XML_hanging ), OString::number( - nFirstLineAdjustment ) ); 9306 m_pSerializer->singleElementNS( XML_w, XML_ind, pLRSpaceAttrList ); 9307 } 9308 } 9309 9310 void DocxAttributeOutput::FormatULSpace( const SvxULSpaceItem& rULSpace ) 9311 { 9312 9313 if (m_rExport.SdrExporter().getTextFrameSyntax()) 9314 { 9315 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-top:" + OString::number(double(rULSpace.GetUpper()) / 20) + "pt"); 9316 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-bottom:" + OString::number(double(rULSpace.GetLower()) / 20) + "pt"); 9317 } 9318 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9319 { 9320 } 9321 else if ( m_rExport.m_bOutFlyFrameAttrs ) 9322 { 9323 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vSpace ), 9324 OString::number( 9325 ( rULSpace.GetLower() + rULSpace.GetUpper() ) / 2 ).getStr() ); 9326 } 9327 else if (m_rExport.m_bOutPageDescs ) 9328 { 9329 OSL_ENSURE( m_rExport.GetCurItemSet(), "Impossible" ); 9330 if ( !m_rExport.GetCurItemSet() ) 9331 return; 9332 9333 HdFtDistanceGlue aDistances( *m_rExport.GetCurItemSet() ); 9334 9335 sal_Int32 nHeader = 0; 9336 if ( aDistances.HasHeader() ) 9337 nHeader = sal_Int32( aDistances.dyaHdrTop ); 9338 else if (m_rExport.m_pFirstPageFormat) 9339 { 9340 HdFtDistanceGlue aFirstPageDistances(m_rExport.m_pFirstPageFormat->GetAttrSet()); 9341 if (aFirstPageDistances.HasHeader()) 9342 { 9343 // The follow page style has no header, but the first page style has. In Word terms, 9344 // this means that the header margin of "the" section is coming from the first page 9345 // style. 9346 nHeader = sal_Int32(aFirstPageDistances.dyaHdrTop); 9347 } 9348 } 9349 9350 // Page top 9351 m_pageMargins.nTop = aDistances.dyaTop; 9352 9353 sal_Int32 nFooter = 0; 9354 if ( aDistances.HasFooter() ) 9355 nFooter = sal_Int32( aDistances.dyaHdrBottom ); 9356 else if (m_rExport.m_pFirstPageFormat) 9357 { 9358 HdFtDistanceGlue aFirstPageDistances(m_rExport.m_pFirstPageFormat->GetAttrSet()); 9359 if (aFirstPageDistances.HasFooter()) 9360 { 9361 // The follow page style has no footer, but the first page style has. In Word terms, 9362 // this means that the footer margin of "the" section is coming from the first page 9363 // style. 9364 nFooter = sal_Int32(aFirstPageDistances.dyaHdrBottom); 9365 } 9366 } 9367 9368 // Page Bottom 9369 m_pageMargins.nBottom = aDistances.dyaBottom; 9370 9371 AddToAttrList( m_pSectionSpacingAttrList, 4, 9372 FSNS( XML_w, XML_header ), OString::number( nHeader ).getStr(), 9373 FSNS( XML_w, XML_top ), OString::number( m_pageMargins.nTop ).getStr(), 9374 FSNS( XML_w, XML_footer ), OString::number( nFooter ).getStr(), 9375 FSNS( XML_w, XML_bottom ), OString::number( m_pageMargins.nBottom ).getStr() ); 9376 } 9377 else 9378 { 9379 SAL_INFO("sw.ww8", "DocxAttributeOutput::FormatULSpace: setting spacing" << rULSpace.GetUpper() ); 9380 // check if before auto spacing was set during import and spacing we get from actual object is same 9381 // that we set in import. If yes just write beforeAutoSpacing tag. 9382 if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == rULSpace.GetUpper()) 9383 { 9384 AddToAttrList( m_pParagraphSpacingAttrList, 9385 FSNS( XML_w, XML_beforeAutospacing ), "1" ); 9386 } 9387 else if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == -1) 9388 { 9389 AddToAttrList( m_pParagraphSpacingAttrList, 9390 FSNS( XML_w, XML_beforeAutospacing ), "0" ); 9391 AddToAttrList( m_pParagraphSpacingAttrList, 9392 FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ).getStr() ); 9393 } 9394 else 9395 { 9396 AddToAttrList( m_pParagraphSpacingAttrList, 9397 FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ).getStr() ); 9398 } 9399 m_bParaBeforeAutoSpacing = false; 9400 // check if after auto spacing was set during import and spacing we get from actual object is same 9401 // that we set in import. If yes just write afterAutoSpacing tag. 9402 if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == rULSpace.GetLower()) 9403 { 9404 AddToAttrList( m_pParagraphSpacingAttrList, 9405 FSNS( XML_w, XML_afterAutospacing ), "1" ); 9406 } 9407 else if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == -1) 9408 { 9409 AddToAttrList( m_pParagraphSpacingAttrList, 9410 FSNS( XML_w, XML_afterAutospacing ), "0" ); 9411 AddToAttrList( m_pParagraphSpacingAttrList, 9412 FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()).getStr() ); 9413 } 9414 else 9415 { 9416 AddToAttrList( m_pParagraphSpacingAttrList, 9417 FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()).getStr() ); 9418 } 9419 m_bParaAfterAutoSpacing = false; 9420 9421 if (rULSpace.GetContext()) 9422 m_pSerializer->singleElementNS(XML_w, XML_contextualSpacing); 9423 else 9424 { 9425 // Write out Contextual Spacing = false if it would have inherited a true. 9426 const SvxULSpaceItem* pInherited = nullptr; 9427 if (auto pNd = dynamic_cast<const SwContentNode*>(m_rExport.m_pOutFormatNode)) //paragraph 9428 pInherited = &static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetULSpace(); 9429 else if (m_rExport.m_bStyDef && m_rExport.m_pCurrentStyle && m_rExport.m_pCurrentStyle->DerivedFrom()) //style 9430 pInherited = &m_rExport.m_pCurrentStyle->DerivedFrom()->GetULSpace(); 9431 9432 if (pInherited && pInherited->GetContext()) 9433 m_pSerializer->singleElementNS(XML_w, XML_contextualSpacing, FSNS(XML_w, XML_val), "false"); 9434 } 9435 } 9436 } 9437 9438 namespace docx { 9439 9440 rtl::Reference<FastAttributeList> SurroundToVMLWrap(SwFormatSurround const& rSurround) 9441 { 9442 rtl::Reference<FastAttributeList> pAttrList; 9443 OString sType; 9444 OString sSide; 9445 switch (rSurround.GetSurround()) 9446 { 9447 case css::text::WrapTextMode_NONE: 9448 sType = "topAndBottom"; 9449 break; 9450 case css::text::WrapTextMode_PARALLEL: 9451 sType = "square"; 9452 break; 9453 case css::text::WrapTextMode_DYNAMIC: 9454 sType = "square"; 9455 sSide = "largest"; 9456 break; 9457 case css::text::WrapTextMode_LEFT: 9458 sType = "square"; 9459 sSide = "left"; 9460 break; 9461 case css::text::WrapTextMode_RIGHT: 9462 sType = "square"; 9463 sSide = "right"; 9464 break; 9465 case css::text::WrapTextMode_THROUGH: 9466 /* empty type and side means through */ 9467 default: 9468 sType = "none"; 9469 break; 9470 } 9471 if (!sType.isEmpty() || !sSide.isEmpty()) 9472 { 9473 pAttrList = FastSerializerHelper::createAttrList(); 9474 if (!sType.isEmpty()) 9475 { 9476 pAttrList->add(XML_type, sType); 9477 } 9478 if (!sSide.isEmpty()) 9479 { 9480 pAttrList->add(XML_side, sSide); 9481 } 9482 } 9483 return pAttrList; 9484 } 9485 9486 } // namespace docx 9487 9488 void DocxAttributeOutput::FormatSurround( const SwFormatSurround& rSurround ) 9489 { 9490 if (m_rExport.SdrExporter().getTextFrameSyntax()) 9491 { 9492 rtl::Reference<FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround)); 9493 if (pAttrList) 9494 { 9495 m_rExport.SdrExporter().setFlyWrapAttrList(pAttrList); 9496 } 9497 } 9498 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9499 { 9500 } 9501 else if ( m_rExport.m_bOutFlyFrameAttrs ) 9502 { 9503 OString sWrap( "auto" ); 9504 switch ( rSurround.GetSurround( ) ) 9505 { 9506 case css::text::WrapTextMode_NONE: 9507 sWrap = OString( "none" ); 9508 break; 9509 case css::text::WrapTextMode_THROUGH: 9510 sWrap = OString( "through" ); 9511 break; 9512 case css::text::WrapTextMode_DYNAMIC: 9513 case css::text::WrapTextMode_PARALLEL: 9514 case css::text::WrapTextMode_LEFT: 9515 case css::text::WrapTextMode_RIGHT: 9516 default: 9517 sWrap = OString( "around" ); 9518 } 9519 9520 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_wrap ), sWrap.getStr() ); 9521 } 9522 } 9523 9524 void DocxAttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert ) 9525 { 9526 OString sAlign = convertToOOXMLVertOrient( rFlyVert.GetVertOrient() ); 9527 OString sVAnchor = convertToOOXMLVertOrientRel( rFlyVert.GetRelationOrient() ); 9528 9529 if (m_rExport.SdrExporter().getTextFrameSyntax()) 9530 { 9531 m_rExport.SdrExporter().getTextFrameStyle().append(";margin-top:" + OString::number(double(rFlyVert.GetPos()) / 20) + "pt"); 9532 if ( !sAlign.isEmpty() ) 9533 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical:" + sAlign); 9534 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical-relative:" + sVAnchor); 9535 } 9536 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9537 { 9538 } 9539 else if ( m_rExport.m_bOutFlyFrameAttrs ) 9540 { 9541 if ( !sAlign.isEmpty() ) 9542 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_yAlign ), sAlign.getStr() ); 9543 else 9544 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_y ), 9545 OString::number( rFlyVert.GetPos() ).getStr() ); 9546 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vAnchor ), sVAnchor.getStr() ); 9547 } 9548 } 9549 9550 void DocxAttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori ) 9551 { 9552 OString sAlign = convertToOOXMLHoriOrient( rFlyHori.GetHoriOrient(), rFlyHori.IsPosToggle() ); 9553 OString sHAnchor = convertToOOXMLHoriOrientRel( rFlyHori.GetRelationOrient() ); 9554 9555 if (m_rExport.SdrExporter().getTextFrameSyntax()) 9556 { 9557 m_rExport.SdrExporter().getTextFrameStyle().append(";margin-left:" + OString::number(double(rFlyHori.GetPos()) / 20) + "pt"); 9558 if ( !sAlign.isEmpty() ) 9559 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal:" + sAlign); 9560 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal-relative:" + sHAnchor); 9561 } 9562 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9563 { 9564 } 9565 else if ( m_rExport.m_bOutFlyFrameAttrs ) 9566 { 9567 if ( !sAlign.isEmpty() ) 9568 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_xAlign ), sAlign.getStr() ); 9569 else 9570 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_x ), 9571 OString::number( rFlyHori.GetPos() ).getStr() ); 9572 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hAnchor ), sHAnchor.getStr() ); 9573 } 9574 } 9575 9576 void DocxAttributeOutput::FormatAnchor( const SwFormatAnchor& ) 9577 { 9578 // Fly frames: anchors here aren't matching the anchors in docx 9579 } 9580 9581 static std::optional<sal_Int32> lcl_getDmlAlpha(const SvxBrushItem& rBrush) 9582 { 9583 std::optional<sal_Int32> oRet; 9584 sal_Int32 nTransparency = 255 - rBrush.GetColor().GetAlpha(); 9585 if (nTransparency) 9586 { 9587 // Convert transparency to percent 9588 sal_Int8 nTransparencyPercent = SvxBrushItem::TransparencyToPercent(nTransparency); 9589 9590 // Calculate alpha value 9591 // Consider oox/source/drawingml/color.cxx : getTransparency() function. 9592 sal_Int32 nAlpha = ::oox::drawingml::MAX_PERCENT - ( ::oox::drawingml::PER_PERCENT * nTransparencyPercent ); 9593 oRet = nAlpha; 9594 } 9595 return oRet; 9596 } 9597 9598 void DocxAttributeOutput::FormatBackground( const SvxBrushItem& rBrush ) 9599 { 9600 const Color aColor = rBrush.GetColor(); 9601 OString sColor = msfilter::util::ConvertColor( aColor.GetRGBColor() ); 9602 std::optional<sal_Int32> oAlpha = lcl_getDmlAlpha(rBrush); 9603 if (m_rExport.SdrExporter().getTextFrameSyntax()) 9604 { 9605 // Handle 'Opacity' 9606 if (oAlpha) 9607 { 9608 // Calculate opacity value 9609 // Consider oox/source/vml/vmlformatting.cxx : decodeColor() function. 9610 double fOpacity = static_cast<double>(*oAlpha) * 65535 / ::oox::drawingml::MAX_PERCENT; 9611 OUString sOpacity = OUString::number(fOpacity) + "f"; 9612 9613 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_opacity, OUStringToOString(sOpacity, RTL_TEXTENCODING_UTF8).getStr() ); 9614 } 9615 9616 sColor = "#" + sColor; 9617 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, sColor.getStr() ); 9618 } 9619 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9620 { 9621 bool bImageBackground = false; 9622 const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE); 9623 if (pItem) 9624 { 9625 const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem); 9626 if(pFillStyle->GetValue() == drawing::FillStyle_BITMAP) 9627 { 9628 bImageBackground = true; 9629 } 9630 } 9631 if (!bImageBackground) 9632 { 9633 m_pSerializer->startElementNS(XML_a, XML_solidFill); 9634 m_pSerializer->startElementNS(XML_a, XML_srgbClr, XML_val, sColor); 9635 if (oAlpha) 9636 m_pSerializer->singleElementNS(XML_a, XML_alpha, 9637 XML_val, OString::number(*oAlpha)); 9638 m_pSerializer->endElementNS(XML_a, XML_srgbClr); 9639 m_pSerializer->endElementNS(XML_a, XML_solidFill); 9640 } 9641 } 9642 else if ( !m_rExport.m_bOutPageDescs ) 9643 { 9644 // compare fill color with the original fill color 9645 OString sOriginalFill = OUStringToOString( 9646 m_sOriginalBackgroundColor, RTL_TEXTENCODING_UTF8 ); 9647 9648 if ( aColor == COL_AUTO ) 9649 sColor = "auto"; 9650 9651 if( !m_pBackgroundAttrList.is() ) 9652 { 9653 m_pBackgroundAttrList = FastSerializerHelper::createAttrList(); 9654 m_pBackgroundAttrList->add(FSNS(XML_w, XML_fill), sColor); 9655 m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" ); 9656 } 9657 else if ( sOriginalFill != sColor ) 9658 { 9659 // fill was modified during edition, theme fill attribute must be dropped 9660 m_pBackgroundAttrList = FastSerializerHelper::createAttrList(); 9661 m_pBackgroundAttrList->add(FSNS(XML_w, XML_fill), sColor); 9662 m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" ); 9663 } 9664 m_sOriginalBackgroundColor.clear(); 9665 } 9666 } 9667 9668 void DocxAttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle ) 9669 { 9670 if (!m_bIgnoreNextFill) 9671 m_oFillStyle = rFillStyle.GetValue(); 9672 else 9673 m_bIgnoreNextFill = false; 9674 9675 // Don't round-trip grabbag OriginalBackground if the background has been cleared. 9676 if ( m_pBackgroundAttrList.is() && m_sOriginalBackgroundColor != "auto" && rFillStyle.GetValue() == drawing::FillStyle_NONE ) 9677 m_pBackgroundAttrList.clear(); 9678 } 9679 9680 void DocxAttributeOutput::FormatFillGradient( const XFillGradientItem& rFillGradient ) 9681 { 9682 if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && !m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9683 { 9684 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_type, "gradient" ); 9685 9686 const XGradient& rGradient = rFillGradient.GetGradientValue(); 9687 OString sStartColor = msfilter::util::ConvertColor(rGradient.GetStartColor()); 9688 OString sEndColor = msfilter::util::ConvertColor(rGradient.GetEndColor()); 9689 9690 // Calculate the angle that was originally in the imported DOCX file 9691 // (reverse calculate the angle that was converted in the file 9692 // /oox/source/vml/vmlformatting.cxx :: FillModel::pushToPropMap 9693 // and also in 9694 // /oox/source/drawingml/fillproperties.cxx :: FillProperties::pushToPropMap 9695 sal_Int32 nReverseAngle = toDegrees(4500_deg10 - rGradient.GetAngle()); 9696 nReverseAngle = (270 - nReverseAngle) % 360; 9697 if (nReverseAngle != 0) 9698 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), 9699 XML_angle, OString::number( nReverseAngle ).getStr() ); 9700 9701 OString sColor1 = sStartColor; 9702 OString sColor2 = sEndColor; 9703 9704 switch (rGradient.GetGradientStyle()) 9705 { 9706 case css::awt::GradientStyle_AXIAL: 9707 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_focus, "50%" ); 9708 // If it is an 'axial' gradient - swap the colors 9709 // (because in the import process they were imported swapped) 9710 sColor1 = sEndColor; 9711 sColor2 = sStartColor; 9712 break; 9713 case css::awt::GradientStyle_LINEAR: break; 9714 case css::awt::GradientStyle_RADIAL: break; 9715 case css::awt::GradientStyle_ELLIPTICAL: break; 9716 case css::awt::GradientStyle_SQUARE: break; 9717 case css::awt::GradientStyle_RECT: break; 9718 default: 9719 break; 9720 } 9721 9722 sColor1 = "#" + sColor1; 9723 sColor2 = "#" + sColor2; 9724 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, sColor1.getStr() ); 9725 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_color2, sColor2.getStr() ); 9726 } 9727 else if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9728 { 9729 SwFrameFormat & rFormat( 9730 const_cast<SwFrameFormat&>(m_rExport.m_pParentFrame->GetFrameFormat())); 9731 uno::Reference<beans::XPropertySet> const xPropertySet( 9732 SwXTextFrame::CreateXTextFrame(*rFormat.GetDoc(), &rFormat), 9733 uno::UNO_QUERY); 9734 m_rDrawingML.SetFS(m_pSerializer); 9735 m_rDrawingML.WriteGradientFill(xPropertySet); 9736 } 9737 m_oFillStyle.reset(); 9738 } 9739 9740 void DocxAttributeOutput::FormatBox( const SvxBoxItem& rBox ) 9741 { 9742 if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9743 { 9744 // ugh, exporting fill here is quite some hack... this OutputItemSet abstraction is quite leaky 9745 // <a:gradFill> should be before <a:ln>. 9746 const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE); 9747 if (pItem) 9748 { 9749 const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem); 9750 FormatFillStyle(*pFillStyle); 9751 if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_BITMAP) 9752 { 9753 const SdrObject* pSdrObj = m_rExport.m_pParentFrame->GetFrameFormat().FindRealSdrObject(); 9754 if (pSdrObj) 9755 { 9756 uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY ); 9757 uno::Reference< beans::XPropertySet > xPropertySet( xShape, uno::UNO_QUERY ); 9758 m_rDrawingML.SetFS(m_pSerializer); 9759 m_rDrawingML.WriteBlipFill(xPropertySet, "BackGraphic"); 9760 } 9761 } 9762 } 9763 9764 pItem = GetExport().HasItem(XATTR_FILLGRADIENT); 9765 if (pItem) 9766 { 9767 const XFillGradientItem* pFillGradient = static_cast<const XFillGradientItem*>(pItem); 9768 FormatFillGradient(*pFillGradient); 9769 } 9770 m_bIgnoreNextFill = true; 9771 } 9772 if (m_rExport.SdrExporter().getTextFrameSyntax() || m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9773 { 9774 const SvxBorderLine* pLeft = rBox.GetLeft( ); 9775 const SvxBorderLine* pTop = rBox.GetTop( ); 9776 const SvxBorderLine* pRight = rBox.GetRight( ); 9777 const SvxBorderLine* pBottom = rBox.GetBottom( ); 9778 9779 if (pLeft && pRight && pTop && pBottom && 9780 *pLeft == *pRight && *pLeft == *pTop && *pLeft == *pBottom) 9781 { 9782 // Check border style 9783 SvxBorderLineStyle eBorderStyle = pTop->GetBorderLineStyle(); 9784 if (eBorderStyle == SvxBorderLineStyle::NONE) 9785 { 9786 if (m_rExport.SdrExporter().getTextFrameSyntax()) 9787 { 9788 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2, 9789 XML_stroked, "f", XML_strokeweight, "0pt" ); 9790 } 9791 } 9792 else 9793 { 9794 OString sColor(msfilter::util::ConvertColor(pTop->GetColor())); 9795 double const fConverted(editeng::ConvertBorderWidthToWord(pTop->GetBorderLineStyle(), pTop->GetWidth())); 9796 9797 if (m_rExport.SdrExporter().getTextFrameSyntax()) 9798 { 9799 sColor = "#" + sColor; 9800 sal_Int32 nWidth = sal_Int32(fConverted / 20); 9801 OString sWidth = OString::number(nWidth) + "pt"; 9802 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2, 9803 XML_strokecolor, sColor.getStr(), 9804 XML_strokeweight, sWidth.getStr() ); 9805 if( SvxBorderLineStyle::DASHED == pTop->GetBorderLineStyle() ) // Line Style is Dash type 9806 AddToAttrList( m_rExport.SdrExporter().getDashLineStyle(), 9807 XML_dashstyle, "dash" ); 9808 } 9809 else 9810 m_rExport.SdrExporter().writeBoxItemLine(rBox); 9811 } 9812 } 9813 9814 if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) 9815 { 9816 m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_lIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::LEFT)))); 9817 m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_tIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::TOP)))); 9818 m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_rIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::RIGHT)))); 9819 m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_bIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::BOTTOM)))); 9820 return; 9821 } 9822 9823 // v:textbox's inset attribute: inner margin values for textbox text - write only non-default values 9824 double fDistanceLeftTwips = double(rBox.GetDistance(SvxBoxItemLine::LEFT)); 9825 double fDistanceTopTwips = double(rBox.GetDistance(SvxBoxItemLine::TOP)); 9826 double fDistanceRightTwips = double(rBox.GetDistance(SvxBoxItemLine::RIGHT)); 9827 double fDistanceBottomTwips = double(rBox.GetDistance(SvxBoxItemLine::BOTTOM)); 9828 9829 // Convert 'TWIPS' to 'INCH' (because in Word the default values are in Inches) 9830 double fDistanceLeftInch = o3tl::convert(fDistanceLeftTwips, o3tl::Length::twip, o3tl::Length::in); 9831 double fDistanceTopInch = o3tl::convert(fDistanceTopTwips, o3tl::Length::twip, o3tl::Length::in); 9832 double fDistanceRightInch = o3tl::convert(fDistanceRightTwips, o3tl::Length::twip, o3tl::Length::in); 9833 double fDistanceBottomInch = o3tl::convert(fDistanceBottomTwips, o3tl::Length::twip, o3tl::Length::in); 9834 9835 // This code will write ONLY the non-default values. The values are in 'left','top','right','bottom' order. 9836 // so 'bottom' is checked if it is default and if it is non-default - all the values will be written 9837 // otherwise - 'right' is checked if it is default and if it is non-default - all the values except for 'bottom' will be written 9838 // and so on. 9839 OStringBuffer aInset; 9840 if(!aInset.isEmpty() || fDistanceBottomInch != 0.05) 9841 aInset.insert(0, OStringConcatenation("," + OString::number(fDistanceBottomInch) + "in")); 9842 9843 if(!aInset.isEmpty() || fDistanceRightInch != 0.1) 9844 aInset.insert(0, OStringConcatenation("," + OString::number(fDistanceRightInch) + "in")); 9845 9846 if(!aInset.isEmpty() || fDistanceTopInch != 0.05) 9847 aInset.insert(0, OStringConcatenation("," + OString::number(fDistanceTopInch) + "in")); 9848 9849 if(!aInset.isEmpty() || fDistanceLeftInch != 0.1) 9850 aInset.insert(0, OStringConcatenation(OString::number(fDistanceLeftInch) + "in")); 9851 9852 if (!aInset.isEmpty()) 9853 m_rExport.SdrExporter().getTextboxAttrList()->add(XML_inset, aInset.makeStringAndClear()); 9854 9855 return; 9856 } 9857 9858 OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions(); 9859 // Check if there is a shadow item 9860 const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW ); 9861 if ( pItem ) 9862 { 9863 const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem); 9864 aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation(); 9865 } 9866 9867 if ( m_bOpenedSectPr && !GetWritingHeaderFooter()) 9868 return; 9869 9870 // Not inside a section 9871 9872 // Open the paragraph's borders tag 9873 m_pSerializer->startElementNS(XML_w, XML_pBdr); 9874 9875 std::map<SvxBoxItemLine, css::table::BorderLine2> aStyleBorders; 9876 const SvxBoxItem* pInherited = nullptr; 9877 if ( GetExport().m_pStyAttr ) 9878 pInherited = GetExport().m_pStyAttr->GetItem<SvxBoxItem>(RES_BOX); 9879 else if ( GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() ) 9880 pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxBoxItem>(RES_BOX); 9881 9882 if ( pInherited ) 9883 { 9884 aStyleBorders[ SvxBoxItemLine::TOP ] = SvxBoxItem::SvxLineToLine(pInherited->GetTop(), /*bConvert=*/false); 9885 aStyleBorders[ SvxBoxItemLine::BOTTOM ] = SvxBoxItem::SvxLineToLine(pInherited->GetBottom(), false); 9886 aStyleBorders[ SvxBoxItemLine::LEFT ] = SvxBoxItem::SvxLineToLine(pInherited->GetLeft(), false); 9887 aStyleBorders[ SvxBoxItemLine::RIGHT ] = SvxBoxItem::SvxLineToLine(pInherited->GetRight(), false); 9888 } 9889 9890 impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aStyleBorders ); 9891 9892 // Close the paragraph's borders tag 9893 m_pSerializer->endElementNS( XML_w, XML_pBdr ); 9894 } 9895 9896 void DocxAttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven, SwTwips nPageSize ) 9897 { 9898 // Get the columns attributes 9899 rtl::Reference<FastAttributeList> pColsAttrList = FastSerializerHelper::createAttrList(); 9900 9901 pColsAttrList->add( FSNS( XML_w, XML_num ), 9902 OString::number( nCols ). getStr( ) ); 9903 9904 const char* pEquals = "false"; 9905 if ( bEven ) 9906 { 9907 sal_uInt16 nWidth = rCol.GetGutterWidth( true ); 9908 pColsAttrList->add( FSNS( XML_w, XML_space ), 9909 OString::number( nWidth ).getStr( ) ); 9910 9911 pEquals = "true"; 9912 } 9913 9914 pColsAttrList->add( FSNS( XML_w, XML_equalWidth ), pEquals ); 9915 9916 bool bHasSep = (COLADJ_NONE != rCol.GetLineAdj()); 9917 9918 pColsAttrList->add( FSNS( XML_w, XML_sep ), OString::boolean( bHasSep ) ); 9919 9920 // Write the element 9921 m_pSerializer->startElementNS( XML_w, XML_cols, pColsAttrList ); 9922 9923 // Write the columns width if non-equals 9924 const SwColumns & rColumns = rCol.GetColumns( ); 9925 if ( !bEven ) 9926 { 9927 for ( sal_uInt16 n = 0; n < nCols; ++n ) 9928 { 9929 rtl::Reference<FastAttributeList> pColAttrList = FastSerializerHelper::createAttrList(); 9930 sal_uInt16 nWidth = rCol.CalcPrtColWidth( n, o3tl::narrowing<sal_uInt16>(nPageSize) ); 9931 pColAttrList->add( FSNS( XML_w, XML_w ), 9932 OString::number( nWidth ).getStr( ) ); 9933 9934 if ( n + 1 != nCols ) 9935 { 9936 sal_uInt16 nSpacing = rColumns[n].GetRight( ) + rColumns[n + 1].GetLeft( ); 9937 pColAttrList->add( FSNS( XML_w, XML_space ), 9938 OString::number( nSpacing ).getStr( ) ); 9939 } 9940 9941 m_pSerializer->singleElementNS( XML_w, XML_col, pColAttrList ); 9942 } 9943 } 9944 9945 m_pSerializer->endElementNS( XML_w, XML_cols ); 9946 } 9947 9948 void DocxAttributeOutput::FormatKeep( const SvxFormatKeepItem& rItem ) 9949 { 9950 m_pSerializer->singleElementNS( XML_w, XML_keepNext, 9951 FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) ); 9952 } 9953 9954 void DocxAttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid ) 9955 { 9956 rtl::Reference<FastAttributeList> pGridAttrList = FastSerializerHelper::createAttrList(); 9957 9958 OString sGridType; 9959 switch ( rGrid.GetGridType( ) ) 9960 { 9961 default: 9962 case GRID_NONE: 9963 sGridType = OString( "default" ); 9964 break; 9965 case GRID_LINES_ONLY: 9966 sGridType = OString( "lines" ); 9967 break; 9968 case GRID_LINES_CHARS: 9969 if ( rGrid.IsSnapToChars( ) ) 9970 sGridType = OString( "snapToChars" ); 9971 else 9972 sGridType = OString( "linesAndChars" ); 9973 break; 9974 } 9975 pGridAttrList->add(FSNS(XML_w, XML_type), sGridType); 9976 9977 sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight(); 9978 pGridAttrList->add( FSNS( XML_w, XML_linePitch ), 9979 OString::number( nHeight ).getStr( ) ); 9980 9981 pGridAttrList->add( FSNS( XML_w, XML_charSpace ), 9982 OString::number( GridCharacterPitch( rGrid ) ).getStr( ) ); 9983 9984 m_pSerializer->singleElementNS( XML_w, XML_docGrid, pGridAttrList ); 9985 } 9986 9987 void DocxAttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering ) 9988 { 9989 if ( !rNumbering.IsCount( ) ) 9990 m_pSerializer->singleElementNS(XML_w, XML_suppressLineNumbers); 9991 } 9992 9993 void DocxAttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection ) 9994 { 9995 OString sTextFlow; 9996 bool bBiDi = false; 9997 SvxFrameDirection nDir = rDirection.GetValue(); 9998 9999 if ( nDir == SvxFrameDirection::Environment ) 10000 nDir = GetExport( ).GetDefaultFrameDirection( ); 10001 10002 switch ( nDir ) 10003 { 10004 default: 10005 case SvxFrameDirection::Horizontal_LR_TB: 10006 sTextFlow = OString( "lrTb" ); 10007 break; 10008 case SvxFrameDirection::Horizontal_RL_TB: 10009 sTextFlow = OString( "lrTb" ); 10010 bBiDi = true; 10011 break; 10012 case SvxFrameDirection::Vertical_LR_TB: // many things but not this one 10013 case SvxFrameDirection::Vertical_RL_TB: 10014 sTextFlow = OString( "tbRl" ); 10015 break; 10016 } 10017 10018 if ( m_rExport.m_bOutPageDescs ) 10019 { 10020 m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), sTextFlow); 10021 if ( bBiDi ) 10022 m_pSerializer->singleElementNS(XML_w, XML_bidi); 10023 } 10024 else if ( !m_rExport.m_bOutFlyFrameAttrs ) 10025 { 10026 if ( bBiDi ) 10027 m_pSerializer->singleElementNS(XML_w, XML_bidi, FSNS(XML_w, XML_val), "1"); 10028 else 10029 m_pSerializer->singleElementNS(XML_w, XML_bidi, FSNS(XML_w, XML_val), "0"); 10030 } 10031 } 10032 10033 void DocxAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem) 10034 { 10035 const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag(); 10036 for ( const auto & rGrabBagElement : rMap ) 10037 { 10038 if (rGrabBagElement.first == "MirrorIndents") 10039 m_pSerializer->singleElementNS(XML_w, XML_mirrorIndents); 10040 else if (rGrabBagElement.first == "ParaTopMarginBeforeAutoSpacing") 10041 { 10042 m_bParaBeforeAutoSpacing = true; 10043 // get fixed value which was set during import 10044 rGrabBagElement.second >>= m_nParaBeforeSpacing; 10045 m_nParaBeforeSpacing = o3tl::toTwips(m_nParaBeforeSpacing, o3tl::Length::mm100); 10046 SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << rGrabBagElement.first << " : m_nParaBeforeSpacing= " << m_nParaBeforeSpacing); 10047 } 10048 else if (rGrabBagElement.first == "ParaBottomMarginAfterAutoSpacing") 10049 { 10050 m_bParaAfterAutoSpacing = true; 10051 // get fixed value which was set during import 10052 rGrabBagElement.second >>= m_nParaAfterSpacing; 10053 m_nParaAfterSpacing = o3tl::toTwips(m_nParaAfterSpacing, o3tl::Length::mm100); 10054 SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << rGrabBagElement.first << " : m_nParaBeforeSpacing= " << m_nParaAfterSpacing); 10055 } 10056 else if (rGrabBagElement.first == "CharThemeFill") 10057 { 10058 uno::Sequence<beans::PropertyValue> aGrabBagSeq; 10059 rGrabBagElement.second >>= aGrabBagSeq; 10060 10061 for (const auto& rProp : std::as_const(aGrabBagSeq)) 10062 { 10063 OString sVal = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); 10064 10065 if (sVal.isEmpty()) 10066 continue; 10067 10068 if (rProp.Name == "val") 10069 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_val), sVal.getStr()); 10070 else if (rProp.Name == "color") 10071 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_color), sVal.getStr()); 10072 else if (rProp.Name == "themeColor") 10073 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeColor), sVal.getStr()); 10074 else if (rProp.Name == "themeTint") 10075 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeTint), sVal.getStr()); 10076 else if (rProp.Name == "themeShade") 10077 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeShade), sVal.getStr()); 10078 else if (rProp.Name == "fill") 10079 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_fill), sVal.getStr()); 10080 else if (rProp.Name == "themeFill") 10081 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFill), sVal.getStr()); 10082 else if (rProp.Name == "themeFillTint") 10083 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillTint), sVal.getStr()); 10084 else if (rProp.Name == "themeFillShade") 10085 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillShade), sVal.getStr()); 10086 else if (rProp.Name == "originalColor") 10087 rProp.Value >>= m_sOriginalBackgroundColor; 10088 } 10089 } 10090 else if (rGrabBagElement.first == "SdtPr") 10091 { 10092 const uno::Sequence<beans::PropertyValue> aGrabBagSdt = 10093 rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); 10094 m_aParagraphSdt.GetSdtParamsFromGrabBag(aGrabBagSdt); 10095 m_aStartedParagraphSdtPrAlias = m_aParagraphSdt.m_aAlias; 10096 } 10097 else if (rGrabBagElement.first == "ParaCnfStyle") 10098 { 10099 uno::Sequence<beans::PropertyValue> aAttributes = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); 10100 m_pTableStyleExport->CnfStyle(aAttributes); 10101 } 10102 else if (rGrabBagElement.first == "ParaSdtEndBefore") 10103 { 10104 // Handled already in StartParagraph(). 10105 } 10106 else 10107 SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled grab bag property " << rGrabBagElement.first ); 10108 } 10109 } 10110 10111 void DocxAttributeOutput::CharGrabBag( const SfxGrabBagItem& rItem ) 10112 { 10113 if (m_bPreventDoubleFieldsHandling) 10114 return; 10115 10116 const std::map< OUString, css::uno::Any >& rMap = rItem.GetGrabBag(); 10117 10118 // get original values of theme-derived properties to check if they have changed during the edition 10119 bool bWriteCSTheme = true; 10120 bool bWriteAsciiTheme = true; 10121 bool bWriteEastAsiaTheme = true; 10122 bool bWriteThemeFontColor = true; 10123 OUString sOriginalValue; 10124 for ( const auto & rGrabBagElement : rMap ) 10125 { 10126 if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameCs" ) 10127 { 10128 if ( rGrabBagElement.second >>= sOriginalValue ) 10129 bWriteCSTheme = 10130 ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_cs ) ) == sOriginalValue ); 10131 } 10132 else if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameAscii" ) 10133 { 10134 if ( rGrabBagElement.second >>= sOriginalValue ) 10135 bWriteAsciiTheme = 10136 ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_ascii ) ) == sOriginalValue ); 10137 } 10138 else if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameEastAsia" ) 10139 { 10140 if ( rGrabBagElement.second >>= sOriginalValue ) 10141 bWriteEastAsiaTheme = 10142 ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_eastAsia ) ) == sOriginalValue ); 10143 } 10144 else if ( m_pColorAttrList.is() && rGrabBagElement.first == "CharThemeOriginalColor" ) 10145 { 10146 if ( rGrabBagElement.second >>= sOriginalValue ) 10147 bWriteThemeFontColor = 10148 ( m_pColorAttrList->getOptionalValue( FSNS( XML_w, XML_val ) ) == sOriginalValue ); 10149 } 10150 } 10151 10152 // save theme attributes back to the run properties 10153 OUString str; 10154 for ( const auto & rGrabBagElement : rMap ) 10155 { 10156 if ( rGrabBagElement.first == "CharThemeNameAscii" && bWriteAsciiTheme ) 10157 { 10158 rGrabBagElement.second >>= str; 10159 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_asciiTheme ), 10160 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); 10161 } 10162 else if ( rGrabBagElement.first == "CharThemeNameCs" && bWriteCSTheme ) 10163 { 10164 rGrabBagElement.second >>= str; 10165 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cstheme ), 10166 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); 10167 } 10168 else if ( rGrabBagElement.first == "CharThemeNameEastAsia" && bWriteEastAsiaTheme ) 10169 { 10170 rGrabBagElement.second >>= str; 10171 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsiaTheme ), 10172 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); 10173 } 10174 else if ( rGrabBagElement.first == "CharThemeNameHAnsi" && bWriteAsciiTheme ) 10175 // this is not a mistake: in LibO we don't directly support the hAnsi family 10176 // of attributes so we save the same value from ascii attributes instead 10177 { 10178 rGrabBagElement.second >>= str; 10179 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_hAnsiTheme ), 10180 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); 10181 } 10182 else if ( rGrabBagElement.first == "CharThemeColor" && bWriteThemeFontColor ) 10183 { 10184 rGrabBagElement.second >>= str; 10185 AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeColor ), 10186 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); 10187 } 10188 else if ( rGrabBagElement.first == "CharThemeColorShade" ) 10189 { 10190 rGrabBagElement.second >>= str; 10191 AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeShade ), 10192 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); 10193 } 10194 else if ( rGrabBagElement.first == "CharThemeColorTint" ) 10195 { 10196 rGrabBagElement.second >>= str; 10197 AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeTint ), 10198 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); 10199 } 10200 else if( rGrabBagElement.first == "CharThemeFontNameCs" || 10201 rGrabBagElement.first == "CharThemeFontNameAscii" || 10202 rGrabBagElement.first == "CharThemeFontNameEastAsia" || 10203 rGrabBagElement.first == "CharThemeOriginalColor" ) 10204 { 10205 // just skip these, they were processed before 10206 } 10207 else if(rGrabBagElement.first == "CharGlowTextEffect" || 10208 rGrabBagElement.first == "CharShadowTextEffect" || 10209 rGrabBagElement.first == "CharReflectionTextEffect" || 10210 rGrabBagElement.first == "CharTextOutlineTextEffect" || 10211 rGrabBagElement.first == "CharTextFillTextEffect" || 10212 rGrabBagElement.first == "CharScene3DTextEffect" || 10213 rGrabBagElement.first == "CharProps3DTextEffect" || 10214 rGrabBagElement.first == "CharLigaturesTextEffect" || 10215 rGrabBagElement.first == "CharNumFormTextEffect" || 10216 rGrabBagElement.first == "CharNumSpacingTextEffect" || 10217 rGrabBagElement.first == "CharStylisticSetsTextEffect" || 10218 rGrabBagElement.first == "CharCntxtAltsTextEffect") 10219 { 10220 beans::PropertyValue aPropertyValue; 10221 rGrabBagElement.second >>= aPropertyValue; 10222 m_aTextEffectsGrabBag.push_back(aPropertyValue); 10223 } 10224 else if (rGrabBagElement.first == "SdtEndBefore") 10225 { 10226 if (m_aRunSdt.m_bStartedSdt) 10227 m_bEndCharSdt = true; 10228 } 10229 else if (rGrabBagElement.first == "SdtPr" && FLY_NOT_PROCESSED != m_nStateOfFlyFrame ) 10230 { 10231 const uno::Sequence<beans::PropertyValue> aGrabBagSdt = 10232 rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); 10233 m_aRunSdt.GetSdtParamsFromGrabBag(aGrabBagSdt); 10234 } 10235 else 10236 SAL_INFO("sw.ww8", "DocxAttributeOutput::CharGrabBag: unhandled grab bag property " << rGrabBagElement.first); 10237 } 10238 } 10239 10240 DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, const FSHelperPtr& pSerializer, oox::drawingml::DrawingML* pDrawingML ) 10241 : AttributeOutputBase(rExport.GetFilter().getFileUrl()), 10242 m_rExport( rExport ), 10243 m_pSerializer( pSerializer ), 10244 m_rDrawingML( *pDrawingML ), 10245 m_bEndCharSdt(false), 10246 m_endPageRef( false ), 10247 m_pFootnotesList( new ::docx::FootnotesList() ), 10248 m_pEndnotesList( new ::docx::FootnotesList() ), 10249 m_footnoteEndnoteRefTag( 0 ), 10250 m_pRedlineData( nullptr ), 10251 m_nRedlineId( 0 ), 10252 m_bOpenedSectPr( false ), 10253 m_bHadSectPr(false), 10254 m_bRunTextIsOn( false ), 10255 m_bWritingHeaderFooter( false ), 10256 m_bAnchorLinkedToNode(false), 10257 m_bWritingField( false ), 10258 m_bPreventDoubleFieldsHandling( false ), 10259 m_nNextBookmarkId( 0 ), 10260 m_nNextAnnotationMarkId( 0 ), 10261 m_nEmbedFlyLevel(0), 10262 m_pMoveRedlineData(nullptr), 10263 m_pCurrentFrame( nullptr ), 10264 m_bParagraphOpened( false ), 10265 m_bParagraphFrameOpen( false ), 10266 m_bIsFirstParagraph( true ), 10267 m_bAlternateContentChoiceOpen( false ), 10268 m_bPostponedProcessingFly( false ), 10269 m_nColBreakStatus( COLBRK_NONE ), 10270 m_bPostponedPageBreak( false ), 10271 m_nTextFrameLevel( 0 ), 10272 m_closeHyperlinkInThisRun( false ), 10273 m_closeHyperlinkInPreviousRun( false ), 10274 m_startedHyperlink( false ), 10275 m_nHyperLinkCount(0), 10276 m_nFieldsInHyperlink( 0 ), 10277 m_bExportingOutline(false), 10278 m_nChartCount(0), 10279 pendingPlaceholder( nullptr ), 10280 m_postitFieldsMaxId( 0 ), 10281 m_anchorId( 1 ), 10282 m_nextFontId( 1 ), 10283 m_tableReference(new TableReference()), 10284 m_bIgnoreNextFill(false), 10285 m_pTableStyleExport(std::make_shared<DocxTableStyleExport>(rExport.m_rDoc, pSerializer)), 10286 m_bParaBeforeAutoSpacing(false), 10287 m_bParaAfterAutoSpacing(false), 10288 m_nParaBeforeSpacing(0), 10289 m_nParaAfterSpacing(0) 10290 , m_nStateOfFlyFrame( FLY_NOT_PROCESSED ) 10291 { 10292 // Push initial items to the RelId cache. In case the document contains no 10293 // special streams (headers, footers, etc.) then these items are used 10294 // during the full export. 10295 PushRelIdCache(); 10296 } 10297 10298 DocxAttributeOutput::~DocxAttributeOutput() 10299 { 10300 } 10301 10302 DocxExport& DocxAttributeOutput::GetExport() 10303 { 10304 return m_rExport; 10305 } 10306 10307 void DocxAttributeOutput::SetSerializer( ::sax_fastparser::FSHelperPtr const & pSerializer ) 10308 { 10309 m_pSerializer = pSerializer; 10310 m_pTableStyleExport->SetSerializer(pSerializer); 10311 } 10312 10313 bool DocxAttributeOutput::HasFootnotes() const 10314 { 10315 return !m_pFootnotesList->isEmpty(); 10316 } 10317 10318 bool DocxAttributeOutput::HasEndnotes() const 10319 { 10320 return !m_pEndnotesList->isEmpty(); 10321 } 10322 10323 bool DocxAttributeOutput::HasPostitFields() const 10324 { 10325 return !m_postitFields.empty(); 10326 } 10327 10328 void DocxAttributeOutput::BulletDefinition(int nId, const Graphic& rGraphic, Size aSize) 10329 { 10330 m_pSerializer->startElementNS(XML_w, XML_numPicBullet, 10331 FSNS(XML_w, XML_numPicBulletId), OString::number(nId)); 10332 10333 OStringBuffer aStyle; 10334 // Size is in twips, we need it in points. 10335 aStyle.append("width:" + OString::number(double(aSize.Width()) / 20)); 10336 aStyle.append("pt;height:" + OString::number(double(aSize.Height()) / 20) + "pt"); 10337 m_pSerializer->startElementNS(XML_w, XML_pict); 10338 m_pSerializer->startElementNS( XML_v, XML_shape, 10339 XML_style, aStyle.getStr(), 10340 FSNS(XML_o, XML_bullet), "t"); 10341 10342 OUString aRelId = m_rDrawingML.WriteImage(rGraphic); 10343 m_pSerializer->singleElementNS( XML_v, XML_imagedata, 10344 FSNS(XML_r, XML_id), OUStringToOString(aRelId, RTL_TEXTENCODING_UTF8), 10345 FSNS(XML_o, XML_title), ""); 10346 10347 m_pSerializer->endElementNS(XML_v, XML_shape); 10348 m_pSerializer->endElementNS(XML_w, XML_pict); 10349 10350 m_pSerializer->endElementNS(XML_w, XML_numPicBullet); 10351 } 10352 10353 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 10354
