LibreOffice Module sw (master) 1
docxattributeoutput.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <memory>
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
44#include <comphelper/random.hxx>
45#include <comphelper/string.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>
55#include <oox/ole/olehelper.hxx>
56
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>
69#include <editeng/cmapitem.hxx>
70#include <editeng/udlnitem.hxx>
71#include <editeng/langitem.hxx>
72#include <editeng/lspcitem.hxx>
74#include <editeng/fhgtitem.hxx>
75#include <editeng/colritem.hxx>
77#include <editeng/ulspitem.hxx>
79#include <editeng/shdditem.hxx>
86#include <editeng/pgrditem.hxx>
88#include <editeng/blinkitem.hxx>
90#include <editeng/editobj.hxx>
91#include <editeng/keepitem.hxx>
94#include <svx/xdef.hxx>
95#include <svx/xfillit0.hxx>
96#include <svx/xflclit.hxx>
97#include <svx/xflgrit.hxx>
98#include <svx/svdouno.hxx>
100#include <svx/svdogrp.hxx>
101#include <svl/grabbagitem.hxx>
102#include <sfx2/sfxbasemodel.hxx>
103#include <tools/date.hxx>
104#include <tools/datetime.hxx>
106#include <svl/whiter.hxx>
107#include <rtl/tencinfo.h>
108#include <sal/log.hxx>
109#include <sot/exchange.hxx>
110
111#include <docufld.hxx>
112#include <authfld.hxx>
113#include <flddropdown.hxx>
114#include <fmtclds.hxx>
115#include <fmtinfmt.hxx>
116#include <fmtrowsplt.hxx>
117#include <fmtline.hxx>
118#include <ftninfo.hxx>
119#include <htmltbl.hxx>
120#include <lineinfo.hxx>
121#include <ndgrf.hxx>
122#include <ndole.hxx>
123#include <ndtxt.hxx>
124#include <pagedesc.hxx>
125#include <paratr.hxx>
126#include <swmodule.hxx>
127#include <swtable.hxx>
128#include <txtftn.hxx>
129#include <fmtautofmt.hxx>
130#include <docsh.hxx>
131#include <docary.hxx>
132#include <fmtclbl.hxx>
135#include <grfatr.hxx>
136#include <frmatr.hxx>
137#include <txtatr.hxx>
138#include <frameformats.hxx>
139#include <textcontentcontrol.hxx>
140
141#include <o3tl/string_view.hxx>
143#include <osl/file.hxx>
144#include <utility>
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#include <com/sun/star/text/ControlCharacter.hpp>
155
156#include <algorithm>
157#include <cstddef>
158#include <stdarg.h>
159#include <string_view>
160
162#include <unicode/regex.h>
163
164using ::editeng::SvxBorderLine;
165
166using namespace oox;
167using namespace docx;
168using namespace sax_fastparser;
169using namespace nsSwDocInfoSubType;
170using namespace sw::util;
171using namespace ::com::sun::star;
172using namespace ::com::sun::star::drawing;
173
174const sal_Int32 Tag_StartParagraph_1 = 1;
175const sal_Int32 Tag_StartParagraph_2 = 2;
176const sal_Int32 Tag_WriteSdtBlock = 3;
179const sal_Int32 Tag_StartRun_1 = 6;
180const sal_Int32 Tag_StartRun_2 = 7;
181const sal_Int32 Tag_StartRun_3 = 8;
182const sal_Int32 Tag_EndRun_1 = 9;
183const sal_Int32 Tag_EndRun_2 = 10;
184const sal_Int32 Tag_StartRunProperties = 11;
186const sal_Int32 Tag_Redline_1 = 13;
187const sal_Int32 Tag_Redline_2 = 14;
188const sal_Int32 Tag_TableDefinition = 15;
189const sal_Int32 Tag_OutputFlyFrame = 16;
190const sal_Int32 Tag_StartSection = 17;
191
192namespace {
193
194class FFDataWriterHelper
195{
196 ::sax_fastparser::FSHelperPtr m_pSerializer;
197 void writeCommonStart( const OUString& rName,
198 const OUString& rEntryMacro,
199 const OUString& rExitMacro,
200 const OUString& rHelp,
201 const OUString& rHint )
202 {
203 m_pSerializer->startElementNS(XML_w, XML_ffData);
204 m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName);
205 m_pSerializer->singleElementNS(XML_w, XML_enabled);
206 m_pSerializer->singleElementNS(XML_w, XML_calcOnExit, FSNS(XML_w, XML_val), "0");
207
208 if ( !rEntryMacro.isEmpty() )
209 m_pSerializer->singleElementNS( XML_w, XML_entryMacro,
210 FSNS(XML_w, XML_val), rEntryMacro );
211
212 if ( !rExitMacro.isEmpty() )
213 m_pSerializer->singleElementNS(XML_w, XML_exitMacro, FSNS(XML_w, XML_val), rExitMacro);
214
215 if ( !rHelp.isEmpty() )
216 m_pSerializer->singleElementNS( XML_w, XML_helpText,
217 FSNS(XML_w, XML_type), "text",
218 FSNS(XML_w, XML_val), rHelp );
219
220 if ( !rHint.isEmpty() )
221 m_pSerializer->singleElementNS( XML_w, XML_statusText,
222 FSNS(XML_w, XML_type), "text",
223 FSNS(XML_w, XML_val), rHint );
224
225 }
226 void writeFinish()
227 {
228 m_pSerializer->endElementNS( XML_w, XML_ffData );
229 }
230public:
231 explicit FFDataWriterHelper( ::sax_fastparser::FSHelperPtr rSerializer ) : m_pSerializer(std::move( rSerializer )){}
232 void WriteFormCheckbox( const OUString& rName,
233 const OUString& rEntryMacro,
234 const OUString& rExitMacro,
235 const OUString& rHelp,
236 const OUString& rHint,
237 bool bChecked )
238 {
239 writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint );
240 // Checkbox specific bits
241 m_pSerializer->startElementNS(XML_w, XML_checkBox);
242 // currently hardcoding autosize
243 // #TODO check if this defaulted
244 m_pSerializer->startElementNS(XML_w, XML_sizeAuto);
245 m_pSerializer->endElementNS( XML_w, XML_sizeAuto );
246 if ( bChecked )
247 m_pSerializer->singleElementNS(XML_w, XML_checked);
248 m_pSerializer->endElementNS( XML_w, XML_checkBox );
249 writeFinish();
250 }
251
252 void WriteFormText( const OUString& rName,
253 const OUString& rEntryMacro,
254 const OUString& rExitMacro,
255 const OUString& rHelp,
256 const OUString& rHint,
257 const OUString& rType,
258 const OUString& rDefaultText,
259 sal_uInt16 nMaxLength,
260 const OUString& rFormat )
261 {
262 writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint );
263
264 m_pSerializer->startElementNS(XML_w, XML_textInput);
265 if ( !rType.isEmpty() )
266 m_pSerializer->singleElementNS(XML_w, XML_type, FSNS(XML_w, XML_val), rType);
267 if ( !rDefaultText.isEmpty() )
268 m_pSerializer->singleElementNS(XML_w, XML_default, FSNS(XML_w, XML_val), rDefaultText);
269 if ( nMaxLength )
270 m_pSerializer->singleElementNS( XML_w, XML_maxLength,
271 FSNS(XML_w, XML_val), OString::number(nMaxLength) );
272 if ( !rFormat.isEmpty() )
273 m_pSerializer->singleElementNS(XML_w, XML_format, FSNS(XML_w, XML_val), rFormat);
274 m_pSerializer->endElementNS( XML_w, XML_textInput );
275
276 writeFinish();
277 }
278};
279
280class FieldMarkParamsHelper
281{
282 const sw::mark::IFieldmark& mrFieldmark;
283 public:
284 explicit FieldMarkParamsHelper( const sw::mark::IFieldmark& rFieldmark ) : mrFieldmark( rFieldmark ) {}
285 OUString const & getName() const { return mrFieldmark.GetName(); }
286 template < typename T >
287 bool extractParam( const OUString& rKey, T& rResult )
288 {
289 bool bResult = false;
290 if ( mrFieldmark.GetParameters() )
291 {
292 sw::mark::IFieldmark::parameter_map_t::const_iterator it = mrFieldmark.GetParameters()->find( rKey );
293 if ( it != mrFieldmark.GetParameters()->end() )
294 bResult = ( it->second >>= rResult );
295 }
296 return bResult;
297 }
298};
299
300// [ISO/IEC29500-1:2016] 17.18.50 ST_LongHexNumber (Eight Digit Hexadecimal Value)
301OUString NumberToHexBinary(sal_Int32 n)
302{
303 OUStringBuffer aBuf;
305 return aBuf.makeStringAndClear();
306}
307
308}
309
310void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ )
311{
312 if (bIsRTL)
313 m_pSerializer->singleElementNS(XML_w, XML_rtl, FSNS(XML_w, XML_val), "true");
314}
315
317static bool lcl_isOnelinerSdt(std::u16string_view rName)
318{
319 return rName == u"Title" || rName == u"Subtitle" || rName == u"Company";
320}
321
322static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ...)
323{
324 if (!pAttrList.is())
325 pAttrList = FastSerializerHelper::createAttrList();
326
327 va_list args;
328 va_start(args, nAttrs);
329 for (sal_Int32 i = 0; i < nAttrs; i++)
330 {
331 sal_Int32 nName = va_arg(args, sal_Int32);
332 const char* pValue = va_arg(args, const char*);
333 if (pValue)
334 pAttrList->add(nName, pValue);
335 }
336 va_end(args);
337}
338
339static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const char* sAttrValue)
340{
341 AddToAttrList(pAttrList, 1, nAttrName, sAttrValue);
342}
343
344// write a floating table directly to docx without the surrounding frame
346{
347 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
349 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
350
351 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
352 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
353
354 //Save data here and restore when out of scope
355 ExportDataSaveRestore aDataGuard(GetExport(), nStt, nEnd, pParentFrame);
356
357 // set a floatingTableFrame AND unset parent frame,
358 // otherwise exporter thinks we are still in a frame
359 m_rExport.SetFloatingTableFrame(pParentFrame);
360 m_rExport.m_pParentFrame = nullptr;
361
363
365}
366
367static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutput)
368{
369 const auto& rExport = rDocxAttributeOutput.GetExport();
370 // iterate though all SpzFrameFormats and check whether they are anchored to the current text node
371 for( sal_uInt16 nCnt = rExport.m_rDoc.GetSpzFrameFormats()->size(); nCnt; )
372 {
373 const SwFrameFormat* pFrameFormat = (*rExport.m_rDoc.GetSpzFrameFormats())[ --nCnt ];
374 const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
375 const SwPosition* pPosition = rAnchor.GetContentAnchor();
376
377 if (!pPosition || ! rExport.m_pCurPam->GetPointNode().GetTextNode())
378 continue;
379
380 if (pPosition->GetNode() != *rExport.m_pCurPam->GetPointNode().GetTextNode())
381 continue;
382
383 const SwNodeIndex* pStartNode = pFrameFormat->GetContent().GetContentIdx();
384 if (!pStartNode)
385 continue;
386
387 SwNodeIndex aStartNode = *pStartNode;
388
389 // go to the next node (actual content)
390 ++aStartNode;
391
392 // this has to be a table
393 if (!aStartNode.GetNode().IsTableNode())
394 continue;
395
396 // go to the end of the table
397 SwNodeOffset aEndIndex = aStartNode.GetNode().EndOfSectionIndex();
398 // go one deeper
399 aEndIndex++;
400 // this has to be the end of the content
401 if (aEndIndex != pFrameFormat->GetContent().GetContentIdx()->GetNode().EndOfSectionIndex())
402 continue;
403
404 // check for a grabBag and "TablePosition" attribute -> then we can export the table directly
405 SwTableNode* pTableNode = aStartNode.GetNode().GetTableNode();
406 SwTable& rTable = pTableNode->GetTable();
407 SwFrameFormat* pTableFormat = rTable.GetFrameFormat();
408 const SfxGrabBagItem* pTableGrabBag = pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG);
409 std::map<OUString, css::uno::Any> aTableGrabBag = pTableGrabBag->GetGrabBag();
410 // no grabbag?
411 if (aTableGrabBag.find("TablePosition") == aTableGrabBag.end())
412 continue;
413
414 // write table to docx
415 ww8::Frame aFrame(*pFrameFormat,*pPosition);
416 rDocxAttributeOutput.WriteFloatingTable(&aFrame);
417 }
418}
419
421 bool bGenerateParaId)
422{
423 // Paragraphs (in headers/footers/comments/frames etc) can start before another finishes.
424 // So a stack is needed to keep track of each paragraph's status separately.
425 // Complication: Word can't handle nested text boxes, so those need to be collected together.
426 if ( !m_aFramesOfParagraph.size() || !m_nTextFrameLevel )
427 m_aFramesOfParagraph.push(std::vector<ww8::Frame>());
428
429 // look ahead for floating tables that were put into a frame during import
430 // floating tables in shapes are not supported: exclude this case
431 if (!pTextNodeInfo && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
432 {
434 }
435
438
439 // Output table/table row/table cell starts if needed
440 if ( pTextNodeInfo )
441 {
442 // New cell/row?
443 if ( m_tableReference->m_nTableDepth > 0 && !m_tableReference->m_bTableCellOpen )
444 {
445 ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner( pTextNodeInfo->getInnerForDepth( m_tableReference->m_nTableDepth ) );
446 if ( pDeepInner->getCell() == 0 )
447 StartTableRow( pDeepInner );
448
449 const sal_uInt32 nCell = pDeepInner->getCell();
450 const sal_uInt32 nRow = pDeepInner->getRow();
451
452 SyncNodelessCells(pDeepInner, nCell, nRow);
453 StartTableCell(pDeepInner, nCell, nRow);
454 }
455
456 sal_uInt32 nRow = pTextNodeInfo->getRow();
457 sal_uInt32 nCell = pTextNodeInfo->getCell();
458 if (nCell == 0)
459 {
460 // Do we have to start the table?
461 // [If we are at the right depth already, it means that we
462 // continue the table cell]
463 sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
464
465 if ( nCurrentDepth > m_tableReference->m_nTableDepth )
466 {
467 // Start all the tables that begin here
468 for ( sal_uInt32 nDepth = m_tableReference->m_nTableDepth + 1; nDepth <= nCurrentDepth; ++nDepth )
469 {
470 ww8::WW8TableNodeInfoInner::Pointer_t pInner( pTextNodeInfo->getInnerForDepth( nDepth ) );
471
472 StartTable( pInner );
473 StartTableRow( pInner );
474
475 StartTableCell(pInner, 0, nDepth == nCurrentDepth ? nRow : 0);
476 }
477
478 m_tableReference->m_nTableDepth = nCurrentDepth;
479 }
480 }
481 }
482
483 // Look up the "sdt end before this paragraph" property early, when it
484 // would normally arrive, it would be too late (would be after the
485 // paragraph start has been written).
486 bool bEndParaSdt = false;
488 {
489 SwTextNode* pTextNode = m_rExport.m_pCurPam->GetPointNode().GetTextNode();
490 if (pTextNode && pTextNode->GetpSwAttrSet())
491 {
492 const SfxItemSet* pSet = pTextNode->GetpSwAttrSet();
493 if (const SfxPoolItem* pItem = pSet->GetItem(RES_PARATR_GRABBAG))
494 {
495 const SfxGrabBagItem& rParaGrabBag = static_cast<const SfxGrabBagItem&>(*pItem);
496 const std::map<OUString, css::uno::Any>& rMap = rParaGrabBag.GetGrabBag();
497 bEndParaSdt = m_aParagraphSdt.m_bStartedSdt && rMap.find("ParaSdtEndBefore") != rMap.end();
498 }
499 }
500 }
501 // TODO also avoid multiline paragraphs in those SDT types for shape text
503 if (bEndParaSdt || (m_aParagraphSdt.m_bStartedSdt && m_bHadSectPr) || bOneliner)
504 {
505 // This is the common case: "close sdt before the current paragraph" was requested by the next paragraph.
508 }
509 m_bHadSectPr = false;
510
511 // this mark is used to be able to enclose the paragraph inside a sdr tag.
512 // We will only know if we have to do that later.
514
515 std::optional<OUString> aParaId;
516 sal_Int32 nParaId = 0;
517 if (bGenerateParaId)
518 {
519 nParaId = m_nNextParaId++;
520 aParaId = NumberToHexBinary(nParaId);
521 }
522 m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId);
523
524 // postpone the output of the run (we get it before the paragraph
525 // properties, but must write it after them)
527
528 // no section break in this paragraph yet; can be set in SectionBreak()
529 m_pSectionInfo.reset();
530
531 m_bParagraphOpened = true;
532 m_bIsFirstParagraph = false;
533
534 return nParaId;
535}
536
537static OString convertToOOXMLVertOrient(sal_Int16 nOrient)
538{
539 switch( nOrient )
540 {
541 case text::VertOrientation::CENTER:
542 case text::VertOrientation::LINE_CENTER:
543 return "center";
544 case text::VertOrientation::BOTTOM:
545 return "bottom";
546 case text::VertOrientation::LINE_BOTTOM:
547 return "outside";
548 case text::VertOrientation::TOP:
549 return "top";
550 case text::VertOrientation::LINE_TOP:
551 return "inside";
552 default:
553 return OString();
554 }
555}
556
557static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle)
558{
559 switch( nOrient )
560 {
561 case text::HoriOrientation::LEFT:
562 return bIsPosToggle ? "inside" : "left";
563 case text::HoriOrientation::INSIDE:
564 return "inside";
565 case text::HoriOrientation::RIGHT:
566 return bIsPosToggle ? "outside" : "right";
567 case text::HoriOrientation::OUTSIDE:
568 return "outside";
569 case text::HoriOrientation::CENTER:
570 case text::HoriOrientation::FULL:
571 return "center";
572 default:
573 return OString();
574 }
575}
576
577static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)
578{
579 switch (nOrientRel)
580 {
581 case text::RelOrientation::PAGE_PRINT_AREA:
582 return "margin";
583 case text::RelOrientation::PAGE_FRAME:
584 return "page";
585 case text::RelOrientation::FRAME:
586 case text::RelOrientation::TEXT_LINE:
587 default:
588 return "text";
589 }
590}
591
592static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel)
593{
594 switch (nOrientRel)
595 {
596 case text::RelOrientation::PAGE_PRINT_AREA:
597 return "margin";
598 case text::RelOrientation::PAGE_FRAME:
599 return "page";
600 case text::RelOrientation::CHAR:
601 case text::RelOrientation::PAGE_RIGHT:
602 case text::RelOrientation::FRAME:
603 default:
604 return "text";
605 }
606}
607
609{
610 if (m_pTokenChildren.is() )
611 m_pTokenChildren.clear();
612 if (m_pDataBindingAttrs.is() )
613 m_pDataBindingAttrs.clear();
614 if (m_pTextAttrs.is())
615 m_pTextAttrs.clear();
616 if (!m_aAlias.isEmpty())
617 m_aAlias.clear();
618 if (!m_aPlaceHolderDocPart.isEmpty())
619 m_aPlaceHolderDocPart.clear();
620 if (!m_aColor.isEmpty())
621 m_aColor.clear();
622 m_bHasId = false;
623}
624
625void SdtBlockHelper::WriteSdtBlock(const ::sax_fastparser::FSHelperPtr& pSerializer, bool bRunTextIsOn, bool bParagraphHasDrawing)
626{
627 if (m_nSdtPrToken <= 0 && !m_pDataBindingAttrs.is() && !m_bHasId)
628 return;
629
630 // sdt start mark
631 pSerializer->mark(Tag_WriteSdtBlock);
632
633 pSerializer->startElementNS(XML_w, XML_sdt);
634
635 // output sdt properties
636 pSerializer->startElementNS(XML_w, XML_sdtPr);
637
638 if (m_nSdtPrToken > 0 && m_pTokenChildren.is())
639 {
640 if (!m_pTokenAttributes.is())
641 pSerializer->startElement(m_nSdtPrToken);
642 else
643 {
645 pSerializer->startElement(m_nSdtPrToken, xAttrList);
646 }
647
648 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)) {
649 const uno::Sequence<xml::FastAttribute> aChildren = m_pTokenChildren->getFastAttributes();
650 for (const auto& rChild : aChildren)
651 pSerializer->singleElement(rChild.Token, FSNS(XML_w, XML_val), rChild.Value);
652 }
653
654 pSerializer->endElement(m_nSdtPrToken);
655 }
656 else if ((m_nSdtPrToken > 0) && m_nSdtPrToken != FSNS(XML_w, XML_id) && !(bRunTextIsOn && bParagraphHasDrawing))
657 {
658 if (!m_pTokenAttributes.is())
659 pSerializer->singleElement(m_nSdtPrToken);
660 else
661 {
663 pSerializer->singleElement(m_nSdtPrToken, xAttrList);
664 }
665 }
666
667 WriteExtraParams(pSerializer);
668
669 pSerializer->endElementNS(XML_w, XML_sdtPr);
670
671 // sdt contents start tag
672 pSerializer->startElementNS(XML_w, XML_sdtContent);
673
674 // prepend the tags since the sdt start mark before the paragraph
675 pSerializer->mergeTopMarks(Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND);
676
677 // write the ending tags after the paragraph
678 m_bStartedSdt = true;
679
680 // clear sdt status
681 m_nSdtPrToken = 0;
682 m_pTokenChildren.clear();
683 m_pDataBindingAttrs.clear();
684 m_pTextAttrs.clear();
685 m_aAlias.clear();
686 m_bHasId = false;
687}
688
689void SdtBlockHelper::WriteExtraParams(const ::sax_fastparser::FSHelperPtr& pSerializer)
690{
691 if (m_nSdtPrToken == FSNS(XML_w, XML_id) || m_bHasId)
692 //Word won't open a document with an empty id tag, we fill it with a random number
693 pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val),
694 OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())));
695
696 if (m_pDataBindingAttrs.is())
697 {
699 pSerializer->singleElementNS(XML_w, XML_dataBinding, xAttrList);
700 }
701
702 if (m_pTextAttrs.is())
703 {
704 rtl::Reference<FastAttributeList> xAttrList = std::move(m_pTextAttrs);
705 pSerializer->singleElementNS(XML_w, XML_text, xAttrList);
706 }
707
708 if (!m_aPlaceHolderDocPart.isEmpty())
709 {
710 pSerializer->startElementNS(XML_w, XML_placeholder);
711 pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, XML_val), m_aPlaceHolderDocPart);
712 pSerializer->endElementNS(XML_w, XML_placeholder);
713 }
714 if (!m_aColor.isEmpty())
715 {
716 pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, XML_val), m_aColor);
717 }
718
719 if (!m_aAlias.isEmpty())
720 pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), m_aAlias);
721}
722
723void SdtBlockHelper::EndSdtBlock(const ::sax_fastparser::FSHelperPtr& pSerializer)
724{
725 pSerializer->endElementNS(XML_w, XML_sdtContent);
726 pSerializer->endElementNS(XML_w, XML_sdt);
727 m_bStartedSdt = false;
728}
729
730void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::PropertyValue>& aGrabBagSdt)
731{
732 for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt)
733 {
734 if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox")
735 {
736 m_nSdtPrToken = FSNS(XML_w14, XML_checkbox);
737 uno::Sequence<beans::PropertyValue> aGrabBag;
738 aPropertyValue.Value >>= aGrabBag;
739 for (const auto& rProp : std::as_const(aGrabBag))
740 {
741 OUString sValue = rProp.Value.get<OUString>();
742 if (rProp.Name == "ooxml:CT_SdtCheckbox_checked")
744 FSNS(XML_w14, XML_checked),
745 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
746 else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState")
748 FSNS(XML_w14, XML_checkedState),
749 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
750 else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState")
752 FSNS(XML_w14, XML_uncheckedState),
753 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
754 }
755 }
756 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pDataBindingAttrs.is())
757 {
758 uno::Sequence<beans::PropertyValue> aGrabBag;
759 aPropertyValue.Value >>= aGrabBag;
760 for (const auto& rProp : std::as_const(aGrabBag))
761 {
762 OUString sValue = rProp.Value.get<OUString>();
763 if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings")
765 FSNS( XML_w, XML_prefixMappings ),
766 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
767 else if (rProp.Name == "ooxml:CT_DataBinding_xpath")
769 FSNS( XML_w, XML_xpath ),
770 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
771 else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID")
773 FSNS( XML_w, XML_storeItemID ),
774 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
775 }
776 }
777 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text")
778 {
779 uno::Sequence<beans::PropertyValue> aGrabBag;
780 aPropertyValue.Value >>= aGrabBag;
781 if (aGrabBag.hasElements())
782 {
783 for (const auto& rProp : std::as_const(aGrabBag))
784 {
785 OUString sValue = rProp.Value.get<OUString>();
786 if (rProp.Name == "ooxml:CT_SdtText_multiLine")
788 FSNS(XML_w, XML_multiLine),
789 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
790 }
791 }
792 else
793 {
794 // We still have w:text, but no attrs
795 m_nSdtPrToken = FSNS(XML_w, XML_text);
796 }
797 }
798 else if (aPropertyValue.Name == "ooxml:CT_SdtPlaceholder_docPart")
799 {
800 uno::Sequence<beans::PropertyValue> aGrabBag;
801 aPropertyValue.Value >>= aGrabBag;
802 for (const auto& rProp : std::as_const(aGrabBag))
803 {
804 OUString sValue = rProp.Value.get<OUString>();
805 if (rProp.Name == "ooxml:CT_SdtPlaceholder_docPart_val")
806 m_aPlaceHolderDocPart = sValue;
807 }
808 }
809 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_color")
810 {
811 uno::Sequence<beans::PropertyValue> aGrabBag;
812 aPropertyValue.Value >>= aGrabBag;
813 for (const auto& rProp : std::as_const(aGrabBag))
814 {
815 OUString sValue = rProp.Value.get<OUString>();
816 if (rProp.Name == "ooxml:CT_SdtColor_val")
817 m_aColor = sValue;
818 }
819 }
820 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aAlias.isEmpty())
821 {
822 if (!(aPropertyValue.Value >>= m_aAlias))
823 SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt alias value");
824 }
825 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id")
826 m_bHasId = true;
827 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation")
828 m_nSdtPrToken = FSNS(XML_w, XML_citation);
829 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" ||
830 aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
831 {
832 if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj")
833 m_nSdtPrToken = FSNS(XML_w, XML_docPartObj);
834 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
835 m_nSdtPrToken = FSNS(XML_w, XML_docPartList);
836
837 uno::Sequence<beans::PropertyValue> aGrabBag;
838 aPropertyValue.Value >>= aGrabBag;
839 for (const auto& rProp : std::as_const(aGrabBag))
840 {
841 OUString sValue = rProp.Value.get<OUString>();
842 if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery")
844 FSNS(XML_w, XML_docPartGallery),
845 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
846 else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory")
848 FSNS(XML_w, XML_docPartCategory),
849 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
850 else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique")
851 {
852 if (sValue.isEmpty())
853 sValue = "true";
854 AddToAttrList(m_pTokenChildren, FSNS(XML_w, XML_docPartUnique),
855 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
856 }
857 }
858 }
859 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation")
860 m_nSdtPrToken = FSNS(XML_w, XML_equation);
861 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture")
862 m_nSdtPrToken = FSNS(XML_w, XML_picture);
863 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group")
864 m_nSdtPrToken = FSNS(XML_w, XML_group);
865 else
866 SAL_WARN("sw.ww8", "GetSdtParamsFromGrabBag unhandled SdtPr grab bag property " << aPropertyValue.Name);
867 }
868}
869
871{
872 rtl::Reference<sax_fastparser::FastAttributeList> attrList = FastSerializerHelper::createAttrList();
873
874 awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(), pFrameFormat->GetVertOrient().GetPos());
875
876 attrList->add( FSNS( XML_w, XML_w), OString::number(rSize.Width()));
877 attrList->add( FSNS( XML_w, XML_h), OString::number(rSize.Height()));
878
879 attrList->add( FSNS( XML_w, XML_x), OString::number(aPos.X));
880 attrList->add( FSNS( XML_w, XML_y), OString::number(aPos.Y));
881
882 sal_Int16 nLeft = pFrameFormat->GetLRSpace().GetLeft();
883 sal_Int16 nRight = pFrameFormat->GetLRSpace().GetRight();
884 sal_Int16 nUpper = pFrameFormat->GetULSpace().GetUpper();
885 sal_Int16 nLower = pFrameFormat->GetULSpace().GetLower();
886
887 attrList->add(FSNS(XML_w, XML_hSpace), OString::number((nLeft + nRight) / 2));
888 attrList->add(FSNS(XML_w, XML_vSpace), OString::number((nUpper + nLower) / 2));
889
890 OString relativeFromH = convertToOOXMLHoriOrientRel( pFrameFormat->GetHoriOrient().GetRelationOrient() );
891 OString relativeFromV = convertToOOXMLVertOrientRel( pFrameFormat->GetVertOrient().GetRelationOrient() );
892
893 switch (pFrameFormat->GetSurround().GetValue())
894 {
895 case css::text::WrapTextMode_NONE:
896 attrList->add( FSNS( XML_w, XML_wrap), "notBeside");
897 break;
898 case css::text::WrapTextMode_DYNAMIC:
899 attrList->add(FSNS(XML_w, XML_wrap), "auto");
900 break;
901 case css::text::WrapTextMode_PARALLEL:
902 default:
903 attrList->add(FSNS(XML_w, XML_wrap), "around");
904 break;
905 }
906 attrList->add( FSNS( XML_w, XML_vAnchor), relativeFromV );
907 attrList->add( FSNS( XML_w, XML_hAnchor), relativeFromH );
908 attrList->add( FSNS( XML_w, XML_hRule), "exact");
909
910 m_pSerializer->singleElementNS( XML_w, XML_framePr, attrList );
911}
912
914{
915 uno::Reference< drawing::XShape > xShape;
916 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
917 if (pSdrObj)
918 xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
919 uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY);
920 uno::Reference< beans::XPropertySetInfo > xPropSetInfo;
921 if (xPropertySet.is())
922 xPropSetInfo = xPropertySet->getPropertySetInfo();
923 uno::Any aFrameProperties ;
924 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
925 {
926 uno::Sequence< beans::PropertyValue > propList;
927 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
928 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
929 [](const beans::PropertyValue& rProp) { return rProp.Name == "ParaFrameProperties"; });
930 if (pProp != std::cend(propList))
931 aFrameProperties = pProp->Value;
932 }
933 bool bFrameProperties = false;
934 aFrameProperties >>= bFrameProperties;
935 return bFrameProperties;
936}
937
939{
940 // write the paragraph properties + the run, already in the correct order
941 m_pSerializer->mergeTopMarks(Tag_StartParagraph_2);
942 std::vector< std::shared_ptr <ww8::Frame> > aFramePrTextbox;
943 // Write the anchored frame if any
944 // Word can't handle nested text boxes, so write them on the same level.
947 {
949
951 m_oPostponedCustomShape.emplace();
952
953 // The for loop can change the size of m_aFramesOfParagraph, so the max size cannot be set in stone before the loop.
954 size_t nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
955 for (size_t nIndex = 0; nIndex < nFrames; ++nIndex)
956 {
958 ww8::Frame aFrame = m_aFramesOfParagraph.top()[nIndex];
959 const SwFrameFormat& rFrameFormat = aFrame.GetFrameFormat();
960
961 if (!TextBoxIsFramePr(rFrameFormat) || m_bWritingHeaderFooter)
962 {
964 {
965 // Run-level SDT still open? Close it before AlternateContent.
967 }
968 m_pSerializer->startElementNS(XML_w, XML_r);
969 m_pSerializer->startElementNS(XML_mc, XML_AlternateContent);
970 m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "wps");
983 //Reset the table infos after saving.
984 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
985
991 {
992 DocxTableExportContext aDMLTableExportContext(*this);
994 }
995 m_pSerializer->endElementNS(XML_mc, XML_Choice);
997
998 // Reset table infos, otherwise the depth of the cells will be incorrect,
999 // in case the text frame had table(s) and we try to export the
1000 // same table second time.
1001 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
1002 //reset the tableReference.
1003
1004 m_pSerializer->startElementNS(XML_mc, XML_Fallback);
1005 {
1006 DocxTableExportContext aVMLTableExportContext(*this);
1008 }
1009 m_rExport.m_pTableInfo = pOldTableInfo;
1010
1011 m_pSerializer->endElementNS(XML_mc, XML_Fallback);
1012 m_pSerializer->endElementNS(XML_mc, XML_AlternateContent);
1013 m_pSerializer->endElementNS( XML_w, XML_r );
1014 m_bParagraphFrameOpen = false;
1015 }
1016 else
1017 {
1018 std::shared_ptr<ww8::Frame> pFramePr = std::make_shared<ww8::Frame>(aFrame);
1019 aFramePrTextbox.push_back(pFramePr);
1020 }
1021
1022 nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
1023 }
1024 if (!m_oPostponedCustomShape->empty())
1025 {
1026 m_pSerializer->startElementNS(XML_w, XML_r);
1028 m_pSerializer->endElementNS( XML_w, XML_r );
1029 }
1031
1032 if ( m_aFramesOfParagraph.size() )
1033 m_aFramesOfParagraph.top().clear();
1034
1035 if (!pTextNodeInfoInner)
1036 {
1037 // Ending a non-table paragraph, clear floating tables before paragraph.
1039 }
1040 }
1041
1043 if ( m_aFramesOfParagraph.size() && !m_nTextFrameLevel )
1045
1046 /* If m_nHyperLinkCount > 0 that means hyperlink tag is not yet closed.
1047 * This is due to nested hyperlink tags. So close it before end of paragraph.
1048 */
1049 if(m_nHyperLinkCount > 0)
1050 {
1051 for(sal_Int32 nHyperLinkToClose = 0; nHyperLinkToClose < m_nHyperLinkCount; ++nHyperLinkToClose)
1052 m_pSerializer->endElementNS( XML_w, XML_hyperlink );
1054 }
1055
1057 {
1058 // Run-level SDT still open? Close it now.
1060 }
1061
1063 {
1064 // tdf#128889 Trailing page break
1066 m_bPageBreakAfter = false;
1067 }
1068
1069 m_pSerializer->endElementNS( XML_w, XML_p );
1070 // on export sdt blocks are never nested ATM
1072 {
1074
1076 {
1077 if (m_tableReference->m_bTableCellOpen)
1078 m_tableReference->m_bTableCellParaSdtOpen = true;
1081 }
1082 }
1083 else
1084 {
1085 //These should be written out to the actual Node and not to the anchor.
1086 //Clear them as they will be repopulated when the node is processed.
1088 m_aParagraphSdt.m_bHasId = false;
1090 }
1091
1093
1094 // Write framePr
1095 for ( const auto & pFrame : aFramePrTextbox )
1096 {
1097 DocxTableExportContext aTableExportContext(*this);
1098 m_pCurrentFrame = pFrame.get();
1100 m_pCurrentFrame = nullptr;
1101 }
1102
1104
1105 //sdtcontent is written so Set m_bParagraphHasDrawing to false
1107 m_bRunTextIsOn = false;
1108 m_pSerializer->mergeTopMarks(Tag_StartParagraph_1);
1109
1110 aFramePrTextbox.clear();
1111 // Check for end of cell, rows, tables here
1112 FinishTableRowCell( pTextNodeInfoInner );
1113
1115 m_bParagraphOpened = false;
1116
1117 // Clear bookmarks of the current paragraph
1120}
1121
1122#define MAX_CELL_IN_WORD 62
1123
1124void DocxAttributeOutput::SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow)
1125{
1126 sal_Int32 nOpenCell = m_LastOpenCell.back();
1127 if (nOpenCell != -1 && nOpenCell != nCell && nOpenCell < MAX_CELL_IN_WORD)
1128 EndTableCell(nOpenCell);
1129
1130 sal_Int32 nClosedCell = m_LastClosedCell.back();
1131 for (sal_Int32 i = nClosedCell+1; i < nCell; ++i)
1132 {
1133 if (i >= MAX_CELL_IN_WORD)
1134 break;
1135
1136 if (i == 0)
1137 StartTableRow(pInner);
1138
1139 StartTableCell(pInner, i, nRow);
1140 m_pSerializer->singleElementNS(XML_w, XML_p);
1141 EndTableCell(i);
1142 }
1143}
1144
1146{
1147 if ( !pInner )
1148 return;
1149
1150 // Where are we in the table
1151 sal_uInt32 nRow = pInner->getRow();
1152 sal_Int32 nCell = pInner->getCell();
1153
1154 InitTableHelper( pInner );
1155
1156 // HACK
1157 // msoffice seems to have an internal limitation of 63 columns for tables
1158 // and refuses to load .docx with more, even though the spec seems to allow that;
1159 // so simply if there are more columns, don't close the last one msoffice will handle
1160 // and merge the contents of the remaining ones into it (since we don't close the cell
1161 // here, following ones will not be opened)
1162 const bool limitWorkaround = (nCell >= MAX_CELL_IN_WORD && !pInner->isEndOfLine());
1163 const bool bEndCell = pInner->isEndOfCell() && !limitWorkaround;
1164 const bool bEndRow = pInner->isEndOfLine();
1165
1166 if (bEndCell)
1167 {
1168 while (pInner->getDepth() < m_tableReference->m_nTableDepth)
1169 {
1170 //we expect that the higher depth row was closed, and
1171 //we are just missing the table close
1172 assert(m_LastOpenCell.back() == -1 && m_LastClosedCell.back() == -1);
1173 EndTable();
1174 }
1175
1176 SyncNodelessCells(pInner, nCell, nRow);
1177
1178 sal_Int32 nClosedCell = m_LastClosedCell.back();
1179 if (nCell == nClosedCell)
1180 {
1181 //Start missing trailing cell(s)
1182 ++nCell;
1183 StartTableCell(pInner, nCell, nRow);
1184
1185 //Continue on missing next trailing cell(s)
1186 ww8::RowSpansPtr xRowSpans = pInner->getRowSpansOfRow();
1187 sal_Int32 nRemainingCells = xRowSpans->size() - nCell;
1188 for (sal_Int32 i = 1; i < nRemainingCells; ++i)
1189 {
1190 if (bForceEmptyParagraph)
1191 {
1192 m_pSerializer->singleElementNS(XML_w, XML_p);
1193 }
1194
1195 EndTableCell(nCell);
1196
1197 StartTableCell(pInner, nCell, nRow);
1198 }
1199 }
1200
1201 if (bForceEmptyParagraph)
1202 {
1203 m_pSerializer->singleElementNS(XML_w, XML_p);
1204 }
1205
1206 EndTableCell(nCell);
1207 }
1208
1209 // This is a line end
1210 if (bEndRow)
1211 EndTableRow();
1212
1213 // This is the end of the table
1214 if (pInner->isFinalEndOfLine())
1215 EndTable();
1216}
1217
1219{
1220 m_pSerializer->singleElementNS(XML_w, XML_p);
1221}
1222
1224{
1225 // output page/section breaks
1226 // Writer can have them at the beginning of a paragraph, or at the end, but
1227 // in docx, we have to output them in the paragraph properties of the last
1228 // paragraph in a section. To get it right, we have to switch to the next
1229 // paragraph, and detect the section breaks there.
1230 SwNodeIndex aNextIndex( rNode, 1 );
1231
1232 if (rNode.IsTextNode() || rNode.IsSectionNode())
1233 {
1234 if (aNextIndex.GetNode().IsTextNode())
1235 {
1236 const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
1237 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen);
1238 }
1239 else if (aNextIndex.GetNode().IsTableNode())
1240 {
1241 const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
1242 const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
1243 m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
1244 }
1245 }
1246 else if (rNode.IsEndNode())
1247 {
1248 if (aNextIndex.GetNode().IsTextNode())
1249 {
1250 // Handle section break between a table and a text node following it.
1251 // Also handle section endings
1252 const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
1254 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen);
1255 }
1256 else if (aNextIndex.GetNode().IsTableNode())
1257 {
1258 // Handle section break between tables.
1259 const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
1260 const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
1261 m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
1262 }
1263 }
1264}
1265
1267{
1269
1270 m_pSerializer->startElementNS(XML_w, XML_pPr);
1271
1272 // and output the section break now (if it appeared)
1274 {
1276 m_pSectionInfo.reset();
1277 }
1278
1280}
1281
1283{
1285
1286 // Write the elements in the spec order
1287 static const sal_Int32 aOrder[] =
1288 {
1289 FSNS( XML_w, XML_pStyle ),
1290 FSNS( XML_w, XML_keepNext ),
1291 FSNS( XML_w, XML_keepLines ),
1292 FSNS( XML_w, XML_pageBreakBefore ),
1293 FSNS( XML_w, XML_framePr ),
1294 FSNS( XML_w, XML_widowControl ),
1295 FSNS( XML_w, XML_numPr ),
1296 FSNS( XML_w, XML_suppressLineNumbers ),
1297 FSNS( XML_w, XML_pBdr ),
1298 FSNS( XML_w, XML_shd ),
1299 FSNS( XML_w, XML_tabs ),
1300 FSNS( XML_w, XML_suppressAutoHyphens ),
1301 FSNS( XML_w, XML_kinsoku ),
1302 FSNS( XML_w, XML_wordWrap ),
1303 FSNS( XML_w, XML_overflowPunct ),
1304 FSNS( XML_w, XML_topLinePunct ),
1305 FSNS( XML_w, XML_autoSpaceDE ),
1306 FSNS( XML_w, XML_autoSpaceDN ),
1307 FSNS( XML_w, XML_bidi ),
1308 FSNS( XML_w, XML_adjustRightInd ),
1309 FSNS( XML_w, XML_snapToGrid ),
1310 FSNS( XML_w, XML_spacing ),
1311 FSNS( XML_w, XML_ind ),
1312 FSNS( XML_w, XML_contextualSpacing ),
1313 FSNS( XML_w, XML_mirrorIndents ),
1314 FSNS( XML_w, XML_suppressOverlap ),
1315 FSNS( XML_w, XML_jc ),
1316 FSNS( XML_w, XML_textDirection ),
1317 FSNS( XML_w, XML_textAlignment ),
1318 FSNS( XML_w, XML_textboxTightWrap ),
1319 FSNS( XML_w, XML_outlineLvl ),
1320 FSNS( XML_w, XML_divId ),
1321 FSNS( XML_w, XML_cnfStyle ),
1322 FSNS( XML_w, XML_rPr ),
1323 FSNS( XML_w, XML_sectPr ),
1324 FSNS( XML_w, XML_pPrChange )
1325 };
1326
1327 // postpone the output so that we can later [in EndParagraphProperties()]
1328 // prepend the properties before the run
1329 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
1331}
1332
1334{
1335 if ( m_rExport.SdrExporter().getFlyAttrList().is() )
1336 {
1339
1340 m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList );
1341 }
1342
1343 if ( m_pParagraphSpacingAttrList.is() )
1344 {
1346 m_pSerializer->singleElementNS( XML_w, XML_spacing, xAttrList );
1347 }
1348
1349 if ( m_pBackgroundAttrList.is() )
1350 {
1352 m_pSerializer->singleElementNS( XML_w, XML_shd, xAttrList );
1353 }
1354}
1355
1356namespace
1357{
1358
1360void lcl_writeParagraphMarkerProperties(DocxAttributeOutput& rAttributeOutput, const SfxItemSet& rParagraphMarkerProperties)
1361{
1362 const SfxItemSet* pOldI = rAttributeOutput.GetExport().GetCurItemSet();
1363 rAttributeOutput.GetExport().SetCurItemSet(&rParagraphMarkerProperties);
1364
1365 SfxWhichIter aIter(rParagraphMarkerProperties);
1366 sal_uInt16 nWhichId = aIter.FirstWhich();
1367 const SfxPoolItem* pItem = nullptr;
1368 // Did we already produce a <w:sz> element?
1369 bool bFontSizeWritten = false;
1370 while (nWhichId)
1371 {
1372 if (aIter.GetItemState(true, &pItem) == SfxItemState::SET)
1373 {
1374 if (isCHRATR(nWhichId) || nWhichId == RES_TXTATR_CHARFMT)
1375 {
1376 // Will this item produce a <w:sz> element?
1377 bool bFontSizeItem = nWhichId == RES_CHRATR_FONTSIZE || nWhichId == RES_CHRATR_CJK_FONTSIZE;
1378 if (!bFontSizeWritten || !bFontSizeItem)
1379 rAttributeOutput.OutputItem(*pItem);
1380 if (bFontSizeItem)
1381 bFontSizeWritten = true;
1382 }
1383 else if (nWhichId == RES_TXTATR_AUTOFMT)
1384 {
1385 const SwFormatAutoFormat* pAutoFormat = static_cast<const SwFormatAutoFormat*>(pItem);
1386 lcl_writeParagraphMarkerProperties(rAttributeOutput, *pAutoFormat->GetStyleHandle());
1387 }
1388 }
1389 nWhichId = aIter.NextWhich();
1390 }
1391 rAttributeOutput.GetExport().SetCurItemSet(pOldI);
1392}
1393
1394const char *RubyAlignValues[] =
1395{
1396 "center",
1397 "distributeLetter",
1398 "distributeSpace",
1399 "left",
1400 "right",
1401 "rightVertical"
1402};
1403
1404
1405const char *lclConvertWW8JCToOOXMLRubyAlign(sal_Int32 nJC)
1406{
1407 const sal_Int32 nElements = SAL_N_ELEMENTS(RubyAlignValues);
1408 if ( nJC >=0 && nJC < nElements )
1409 return RubyAlignValues[nJC];
1410 return RubyAlignValues[0];
1411}
1412
1413}
1414
1415void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted)
1416{
1417 // Call the 'Redline' function. This will add redline (change-tracking) information that regards to paragraph properties.
1418 // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
1419
1420 // If there is RedlineData present, call WriteCollectedParagraphProperties() for writing pPr before calling Redline().
1421 // As there will be another pPr for redline and LO might mix both.
1422 if(pRedlineData)
1424 Redline( pRedlineData );
1425
1427
1428 // Merge the marks for the ordered elements
1430
1431 // Write 'Paragraph Mark' properties
1432 m_pSerializer->startElementNS(XML_w, XML_rPr);
1433 // mark() before paragraph mark properties child elements.
1435
1436 // The 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' are used to hold information
1437 // that should be collected by different properties in the core, and are all flushed together
1438 // to the DOCX when the function 'WriteCollectedRunProperties' gets called.
1439 // So we need to store the current status of these lists, so that we can revert back to them when
1440 // we are done exporting the redline attributes.
1442 m_pFontsAttrList.clear();
1446 m_pCharLangAttrList.clear();
1447
1448 lcl_writeParagraphMarkerProperties(*this, rParagraphMarkerProperties);
1449
1450 // Write the collected run properties that are stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
1452
1453 // Revert back the original values that were stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
1454 m_pFontsAttrList = pFontsAttrList_Original.get();
1455 m_pEastAsianLayoutAttrList = pEastAsianLayoutAttrList_Original.get();
1456 m_pCharLangAttrList = pCharLangAttrList_Original.get();
1457
1458 if ( pRedlineParagraphMarkerDeleted )
1459 {
1460 StartRedline( pRedlineParagraphMarkerDeleted );
1461 EndRedline( pRedlineParagraphMarkerDeleted );
1462 }
1463 if ( pRedlineParagraphMarkerInserted )
1464 {
1465 StartRedline( pRedlineParagraphMarkerInserted );
1466 EndRedline( pRedlineParagraphMarkerInserted );
1467 }
1468
1469 // mergeTopMarks() after paragraph mark properties child elements.
1471 m_pSerializer->endElementNS( XML_w, XML_rPr );
1472
1474 {
1475 const SwFrameFormat& rFrameFormat = m_pCurrentFrame->GetFrameFormat();
1476 const SvxBoxItem& rBox = rFrameFormat.GetBox();
1477 if (TextBoxIsFramePr(rFrameFormat))
1478 {
1479 const Size aSize = m_pCurrentFrame->GetSize();
1480 PopulateFrameProperties(&rFrameFormat, aSize);
1481 FormatBox(rBox);
1482 }
1483 }
1484
1485 m_pSerializer->endElementNS( XML_w, XML_pPr );
1486
1487 // RDF metadata for this text node.
1488 SwTextNode* pTextNode = m_rExport.m_pCurPam->GetPointNode().GetTextNode();
1489 std::map<OUString, OUString> aStatements;
1490 if (pTextNode)
1491 aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", *pTextNode);
1492 if (!aStatements.empty())
1493 {
1494 m_pSerializer->startElementNS(XML_w, XML_smartTag,
1495 FSNS(XML_w, XML_uri), "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
1496 FSNS(XML_w, XML_element), "RDF");
1497 m_pSerializer->startElementNS(XML_w, XML_smartTagPr);
1498 for (const auto& rStatement : aStatements)
1499 m_pSerializer->singleElementNS(XML_w, XML_attr,
1500 FSNS(XML_w, XML_name), rStatement.first,
1501 FSNS(XML_w, XML_val), rStatement.second);
1502 m_pSerializer->endElementNS(XML_w, XML_smartTagPr);
1503 m_pSerializer->endElementNS(XML_w, XML_smartTag);
1504 }
1505
1507 {
1508 m_pSerializer->startElementNS(XML_w, XML_r);
1509 m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "column");
1510 m_pSerializer->endElementNS( XML_w, XML_r );
1511
1514 else
1516 }
1517
1519 {
1520 m_pSerializer->startElementNS(XML_w, XML_r);
1521 m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
1522 m_pSerializer->endElementNS( XML_w, XML_r );
1523
1524 m_bPostponedPageBreak = false;
1525 }
1526
1527 // merge the properties _before_ the run (strictly speaking, just
1528 // after the start of the paragraph)
1530}
1531
1533{
1534 m_nStateOfFlyFrame = nStateOfFlyFrame;
1535}
1536
1537void DocxAttributeOutput::SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode )
1538{
1539 m_bAnchorLinkedToNode = bAnchorLinkedToNode ;
1540}
1541
1543{
1545}
1546
1548{
1550}
1551
1552void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/, bool /*bSingleEmptyRun*/ )
1553{
1554 // Don't start redline data here, possibly there is a hyperlink later, and
1555 // that has to be started first.
1556 m_pRedlineData = pRedlineData;
1557
1558 // this mark is used to be able to enclose the run inside a sdr tag.
1560
1561 // postpone the output of the start of a run (there are elements that need
1562 // to be written before the start of the run, but we learn which they are
1563 // _inside_ of the run)
1564 m_pSerializer->mark(Tag_StartRun_2); // let's call it "postponed run start"
1565
1566 // postpone the output of the text (we get it before the run properties,
1567 // but must write it after them)
1568 m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text"
1569}
1570
1571void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_Int32 nLen, bool /*bLastRun*/)
1572{
1573 int nFieldsInPrevHyperlink = m_nFieldsInHyperlink;
1574 // Reset m_nFieldsInHyperlink if a new hyperlink is about to start
1575 if ( m_pHyperlinkAttrList.is() )
1576 {
1578 }
1579
1580 // Write field starts
1581 for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin() + nFieldsInPrevHyperlink; pIt != m_Fields.end(); )
1582 {
1583 // Add the fields starts for all but hyperlinks and TOCs
1584 if (pIt->bOpen && pIt->pField && pIt->eType != ww::eFORMDROPDOWN &&
1585 // it is not an input field with extra grabbag params (sdt field)
1586 (!(pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements()))
1587 )
1588 {
1589 StartField_Impl( pNode, nPos, *pIt );
1590
1591 // Remove the field from the stack if only the start has to be written
1592 // Unknown fields should be removed too
1593 if ( !pIt->bClose || ( pIt->eType == ww::eUNKNOWN ) )
1594 {
1595 pIt = m_Fields.erase( pIt );
1596 continue;
1597 }
1598
1600 {
1602 }
1603 }
1604 ++pIt;
1605 }
1606
1607 // write the run properties + the text, already in the correct order
1608 m_pSerializer->mergeTopMarks(Tag_StartRun_3); // merges with "postponed text", see above
1609
1610 // level down, to be able to prepend the actual run start attribute (just
1611 // before "postponed run start")
1612 m_pSerializer->mark(Tag_EndRun_1); // let's call it "actual run start"
1613 bool bCloseEarlierSDT = false;
1614
1615 if (m_bEndCharSdt)
1616 {
1617 // This is the common case: "close sdt before the current run" was requested by the next run.
1618
1619 // if another sdt starts in this run, then wait
1620 // as closing the sdt now, might cause nesting of sdts
1621 if (m_aRunSdt.m_nSdtPrToken > 0)
1622 bCloseEarlierSDT = true;
1623 else
1625 m_bEndCharSdt = false;
1626 }
1627
1629 {
1630 if ( m_startedHyperlink )
1631 {
1632 for ( int i = 0; i < nFieldsInPrevHyperlink; i++ )
1633 {
1634 // If fields begin before hyperlink then
1635 // it should end before hyperlink close
1636 EndField_Impl( pNode, nPos, m_Fields.back( ) );
1637 m_Fields.pop_back();
1638 }
1639 m_pSerializer->endElementNS( XML_w, XML_hyperlink );
1640 m_startedHyperlink = false;
1641 m_endPageRef = false;
1643 }
1645 }
1646
1647 // Write the hyperlink and toc fields starts
1648 for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); )
1649 {
1650 // Add the fields starts for hyperlinks, TOCs and index marks
1651 if (pIt->bOpen && (!pIt->pField || pIt->eType == ww::eFORMDROPDOWN ||
1652 // InputField with extra grabbag params - it is sdt field
1653 (pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements())))
1654 {
1656 StartField_Impl( pNode, nPos, *pIt, true );
1658
1661
1662 // Remove the field if no end needs to be written
1663 if (!pIt->bSep)
1664 {
1665 pIt = m_Fields.erase( pIt );
1666 continue;
1667 }
1668 }
1669 if (pIt->bSep && !pIt->pField)
1670 {
1671 // for TOXMark:
1672 // Word ignores bookmarks in field result that is empty;
1673 // work around this by writing bookmark into field command.
1674 if (!m_sFieldBkm.isEmpty())
1675 {
1679 m_sFieldBkm.clear();
1680 }
1681 CmdEndField_Impl(pNode, nPos, true);
1682 // Remove the field if no end needs to be written
1683 if (!pIt->bClose)
1684 {
1685 pIt = m_Fields.erase( pIt );
1686 continue;
1687 }
1688 }
1689 ++pIt;
1690 }
1691
1692 // Start the hyperlink after the fields separators or we would generate invalid file
1693 bool newStartedHyperlink(false);
1694 if ( m_pHyperlinkAttrList.is() )
1695 {
1696 // if we are ending a hyperlink and there's another one starting here,
1697 // don't do this, so that the fields are closed further down when
1698 // the end hyperlink is handled, which is more likely to put the end in
1699 // the right place, as far as i can tell (not very far in this muck)
1701 {
1702 // end ToX fields that want to end _before_ starting the hyperlink
1703 for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); )
1704 {
1705 if (it->bClose && !it->pField)
1706 {
1707 EndField_Impl( pNode, nPos, *it );
1708 it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1));
1709 }
1710 else
1711 {
1712 ++it;
1713 }
1714 }
1715 }
1716 newStartedHyperlink = true;
1717
1719
1720 m_pSerializer->startElementNS( XML_w, XML_hyperlink, xAttrList );
1721 m_startedHyperlink = true;
1723 }
1724
1725 // if there is some redlining in the document, output it
1727
1728 // XML_r node should be surrounded with bookmark-begin and bookmark-end nodes if it has bookmarks.
1729 // The same is applied for permission ranges.
1730 // But due to unit test "testFdo85542" let's output bookmark-begin with bookmark-end.
1735
1736 if( m_closeHyperlinkInThisRun && m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc"))
1737 {
1738 OUString sToken;
1739 m_pSerializer->startElementNS(XML_w, XML_r);
1740 m_pSerializer->startElementNS(XML_w, XML_rPr);
1741 m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1742 m_pSerializer->endElementNS( XML_w, XML_rPr );
1743 m_pSerializer->startElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "begin");
1744 m_pSerializer->endElementNS( XML_w, XML_fldChar );
1745 m_pSerializer->endElementNS( XML_w, XML_r );
1746
1747
1748 m_pSerializer->startElementNS(XML_w, XML_r);
1749 m_pSerializer->startElementNS(XML_w, XML_rPr);
1750 m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1751 m_pSerializer->endElementNS( XML_w, XML_rPr );
1752 sToken = "PAGEREF " + m_hyperLinkAnchor + " \\h"; // '\h' Creates a hyperlink to the bookmarked paragraph.
1753 DoWriteCmd( sToken );
1754 m_pSerializer->endElementNS( XML_w, XML_r );
1755
1756 // Write the Field separator
1757 m_pSerializer->startElementNS(XML_w, XML_r);
1758 m_pSerializer->startElementNS(XML_w, XML_rPr);
1759 m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1760 m_pSerializer->endElementNS( XML_w, XML_rPr );
1761 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1762 FSNS( XML_w, XML_fldCharType ), "separate" );
1763 m_pSerializer->endElementNS( XML_w, XML_r );
1764 // At start of every "PAGEREF" field m_endPageRef value should be true.
1765 m_endPageRef = true;
1766 }
1767
1769
1770 if (nLen != -1)
1771 {
1773 if (pAttr && pAttr->GetStart() == nPos)
1774 {
1775 auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
1776 m_pContentControl = pTextContentControl->GetContentControl().GetContentControl();
1778 }
1779 }
1780
1781 m_pSerializer->startElementNS(XML_w, XML_r);
1782 if(GetExport().m_bTabInTOC && m_pHyperlinkAttrList.is())
1783 {
1784 RunText("\t") ;
1785 }
1786 m_pSerializer->mergeTopMarks(Tag_EndRun_1, sax_fastparser::MergeMarks::PREPEND); // merges with "postponed run start", see above
1787
1788 if ( !m_sRawText.isEmpty() )
1789 {
1791 m_sRawText.clear();
1792 }
1793
1794 // write the run start + the run content
1795 m_pSerializer->mergeTopMarks(Tag_StartRun_2); // merges the "actual run start"
1796 // append the actual run end
1797 m_pSerializer->endElementNS( XML_w, XML_r );
1798
1799 if (nLen != -1)
1800 {
1801 sal_Int32 nEnd = nPos + nLen;
1803 if (pAttr && *pAttr->GetEnd() == nEnd)
1804 {
1806 }
1807 }
1808
1809 // if there is some redlining in the document, output it
1810 // (except in the case of fields with multiple runs)
1812
1813 // enclose in a sdt block, if necessary: if one is already started, then don't do it for now
1814 // (so on export sdt blocks are never nested ATM)
1816 {
1818 }
1819 else
1820 {
1821 //These should be written out to the actual Node and not to the anchor.
1822 //Clear them as they will be repopulated when the node is processed.
1825 }
1826
1827 if (bCloseEarlierSDT)
1828 {
1832 }
1833
1834 m_pSerializer->mergeTopMarks(Tag_StartRun_1);
1835
1836 // XML_r node should be surrounded with permission-begin and permission-end nodes if it has permission.
1838
1839 for (const auto& rpMath : m_aPostponedMaths)
1840 WritePostponedMath(rpMath.pMathObject, rpMath.nMathObjAlignment);
1841 m_aPostponedMaths.clear();
1842
1843 for (const auto& rpControl : m_aPostponedFormControls)
1844 WritePostponedFormControl(rpControl);
1846
1848
1850
1851 if ( !m_bWritingField )
1852 {
1853 m_pRedlineData = nullptr;
1854 }
1855
1857 {
1858 if ( m_startedHyperlink )
1859 {
1860 if( m_endPageRef )
1861 {
1862 // Hyperlink is started and fldchar "end" needs to be written for PAGEREF
1863 m_pSerializer->startElementNS(XML_w, XML_r);
1864 m_pSerializer->startElementNS(XML_w, XML_rPr);
1865 m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1866 m_pSerializer->endElementNS( XML_w, XML_rPr );
1867 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1868 FSNS( XML_w, XML_fldCharType ), "end" );
1869 m_pSerializer->endElementNS( XML_w, XML_r );
1870 m_endPageRef = false;
1871 m_hyperLinkAnchor.clear();
1872 }
1873 for ( int i = 0; i < m_nFieldsInHyperlink; i++ )
1874 {
1875 // If fields begin after hyperlink start then
1876 // it should end before hyperlink close
1877 EndField_Impl( pNode, nPos, m_Fields.back( ) );
1878 m_Fields.pop_back();
1879 }
1881
1882 m_pSerializer->endElementNS( XML_w, XML_hyperlink );
1883 m_startedHyperlink = false;
1885 }
1887 }
1888
1889 if (!newStartedHyperlink)
1890 {
1891 while ( m_Fields.begin() != m_Fields.end() )
1892 {
1893 EndField_Impl( pNode, nPos, m_Fields.front( ) );
1894 m_Fields.erase( m_Fields.begin( ) );
1895 }
1897 }
1898
1899 // end ToX fields
1900 for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); )
1901 {
1902 if (it->bClose && !it->pField)
1903 {
1904 EndField_Impl( pNode, nPos, *it );
1905 it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1));
1906 }
1907 else
1908 {
1909 ++it;
1910 }
1911 }
1912
1913 if ( m_pRedlineData )
1914 {
1916 m_pRedlineData = nullptr;
1917 }
1918
1922}
1923
1924void DocxAttributeOutput::DoWriteBookmarkTagStart(std::u16string_view bookmarkName)
1925{
1926 m_pSerializer->singleElementNS(XML_w, XML_bookmarkStart,
1927 FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId),
1928 FSNS(XML_w, XML_name), BookmarkToWord(bookmarkName));
1929}
1930
1932{
1933 m_pSerializer->singleElementNS(XML_w, XML_bookmarkEnd,
1934 FSNS(XML_w, XML_id), OString::number(nId));
1935}
1936
1937void DocxAttributeOutput::DoWriteMoveRangeTagStart(const OString & bookmarkName,
1938 bool bFrom, const SwRedlineData* pRedlineData)
1939{
1940 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
1942
1943 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
1944 const DateTime aDateTime = pRedlineData->GetTimeStamp();
1945 bool bNoDate = bRemovePersonalInfo ||
1946 ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
1947
1950
1951 pAttributeList->add(FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId));
1952 pAttributeList->add(FSNS(XML_w, XML_author ), bRemovePersonalInfo
1953 ? "Author" + OString::number( GetExport().GetInfoID(rAuthor) )
1954 : OUStringToOString(rAuthor, RTL_TEXTENCODING_UTF8));
1955 if (!bNoDate)
1956 pAttributeList->add(FSNS(XML_w, XML_date ), DateTimeToOString( aDateTime ));
1957 pAttributeList->add(FSNS(XML_w, XML_name), bookmarkName);
1958 m_pSerializer->singleElementNS( XML_w, bFrom ? XML_moveFromRangeStart : XML_moveToRangeStart, pAttributeList );
1959
1960 // tdf#150166 avoid of unpaired moveRangeEnd at moved ToC
1962}
1963
1964void DocxAttributeOutput::DoWriteMoveRangeTagEnd(sal_Int32 const nId, bool bFrom)
1965{
1966 if ( m_rSavedBookmarksIds.count(nId) )
1967 {
1968 m_pSerializer->singleElementNS(XML_w, bFrom
1969 ? XML_moveFromRangeEnd
1970 : XML_moveToRangeEnd,
1971 FSNS(XML_w, XML_id), OString::number(nId));
1972
1974 }
1975}
1976
1978{
1979 auto aRange = m_aBookmarksOfParagraphStart.equal_range(nRunPos);
1980 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1981 {
1982 DoWriteBookmarkTagStart(aIter->second);
1984 m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(aIter->second), RTL_TEXTENCODING_UTF8);
1986 }
1987}
1988
1990{
1991 auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nRunPos);
1992 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1993 {
1994 // Get the id of the bookmark
1995 auto pPos = m_rOpenedBookmarksIds.find(aIter->second);
1996 if (pPos != m_rOpenedBookmarksIds.end())
1997 {
1998 // Output the bookmark
1999 DoWriteBookmarkTagEnd(pPos->second);
2000 m_rOpenedBookmarksIds.erase(aIter->second);
2001 }
2002 }
2003}
2004
2006void DocxAttributeOutput::DoWriteBookmarksStart(std::vector<OUString>& rStarts, const SwRedlineData* pRedlineData)
2007{
2008 for (const OUString & bookmarkName : rStarts)
2009 {
2010 // Output the bookmark (including MoveBookmark of the tracked moving)
2011 bool bMove = false;
2012 bool bFrom = false;
2013 OString sBookmarkName = OUStringToOString(
2014 BookmarkToWord(bookmarkName, &bMove, &bFrom), RTL_TEXTENCODING_UTF8);
2015 if ( bMove )
2016 {
2017 // TODO: redline data of MoveBookmark is restored from the first redline of the bookmark
2018 // range. But a later deletion within a tracked moving is still imported as plain
2019 // deletion, so check IsMoved() and skip the export of the tracked moving to avoid
2020 // export with bad author or date
2021 if ( pRedlineData && pRedlineData->IsMoved() )
2022 DoWriteMoveRangeTagStart(sBookmarkName, bFrom, pRedlineData);
2023 }
2024 else
2025 DoWriteBookmarkTagStart(bookmarkName);
2026
2028 m_sLastOpenedBookmark = sBookmarkName;
2030 }
2031 rStarts.clear();
2032}
2033
2035void DocxAttributeOutput::DoWriteBookmarksEnd(std::vector<OUString>& rEnds)
2036{
2037 for (const OUString & bookmarkName : rEnds)
2038 {
2039 // Get the id of the bookmark
2040 auto pPos = m_rOpenedBookmarksIds.find(bookmarkName);
2041
2042 if (pPos != m_rOpenedBookmarksIds.end())
2043 {
2044 bool bMove = false;
2045 bool bFrom = false;
2046 BookmarkToWord(bookmarkName, &bMove, &bFrom);
2047 // Output the bookmark (including MoveBookmark of the tracked moving)
2048 if ( bMove )
2049 DoWriteMoveRangeTagEnd(pPos->second, bFrom);
2050 else
2051 DoWriteBookmarkTagEnd(pPos->second);
2052
2053 m_rOpenedBookmarksIds.erase(bookmarkName);
2054 }
2055 }
2056 rEnds.clear();
2057}
2058
2059// For construction of the special bookmark name template for permissions:
2060// see, PermInsertPosition::createBookmarkName()
2061//
2062// Syntax:
2063// - "permission-for-user:<permission-id>:<permission-user-name>"
2064// - "permission-for-group:<permission-id>:<permission-group-name>"
2065//
2066void DocxAttributeOutput::DoWritePermissionTagStart(std::u16string_view permission)
2067{
2068 std::u16string_view permissionIdAndName;
2069
2070 if (o3tl::starts_with(permission, u"permission-for-group:", &permissionIdAndName))
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 const std::u16string_view permissionName = permissionIdAndName.substr(separatorIndex + 1);
2076
2077 m_pSerializer->singleElementNS(XML_w, XML_permStart,
2078 FSNS(XML_w, XML_id), BookmarkToWord(permissionId),
2079 FSNS(XML_w, XML_edGrp), BookmarkToWord(permissionName));
2080 }
2081 else
2082 {
2083 auto const ok = o3tl::starts_with(
2084 permission, u"permission-for-user:", &permissionIdAndName);
2085 assert(ok); (void)ok;
2086 const std::size_t separatorIndex = permissionIdAndName.find(u':');
2087 assert(separatorIndex != std::u16string_view::npos);
2088 const std::u16string_view permissionId = permissionIdAndName.substr(0, separatorIndex);
2089 const std::u16string_view permissionName = permissionIdAndName.substr(separatorIndex + 1);
2090
2091 m_pSerializer->singleElementNS(XML_w, XML_permStart,
2092 FSNS(XML_w, XML_id), BookmarkToWord(permissionId),
2093 FSNS(XML_w, XML_ed), BookmarkToWord(permissionName));
2094 }
2095}
2096
2097
2098// For construction of the special bookmark name template for permissions:
2099// see, PermInsertPosition::createBookmarkName()
2100//
2101// Syntax:
2102// - "permission-for-user:<permission-id>:<permission-user-name>"
2103// - "permission-for-group:<permission-id>:<permission-group-name>"
2104//
2105void DocxAttributeOutput::DoWritePermissionTagEnd(std::u16string_view permission)
2106{
2107 std::u16string_view permissionIdAndName;
2108
2109 auto const ok = o3tl::starts_with(permission, u"permission-for-group:", &permissionIdAndName) ||
2110 o3tl::starts_with(permission, u"permission-for-user:", &permissionIdAndName);
2111 assert(ok); (void)ok;
2112
2113 const std::size_t separatorIndex = permissionIdAndName.find(u':');
2114 assert(separatorIndex != std::u16string_view::npos);
2115 const std::u16string_view permissionId = permissionIdAndName.substr(0, separatorIndex);
2116
2117 m_pSerializer->singleElementNS(XML_w, XML_permEnd,
2118 FSNS(XML_w, XML_id), BookmarkToWord(permissionId));
2119}
2120
2123{
2124 for (const OUString & permission : m_rPermissionsStart)
2125 {
2126 DoWritePermissionTagStart(permission);
2127 }
2128 m_rPermissionsStart.clear();
2129}
2130
2133{
2134 for (const OUString & permission : m_rPermissionsEnd)
2135 {
2136 DoWritePermissionTagEnd(permission);
2137 }
2138 m_rPermissionsEnd.clear();
2139}
2140
2142{
2143 // Write the start annotation marks
2144 for ( const auto & rName : m_rAnnotationMarksStart )
2145 {
2146 // Output the annotation mark
2147 /* Ensure that the existing Annotation Marks are not overwritten
2148 as it causes discrepancy when DocxAttributeOutput::PostitField
2149 refers to this map & while mapping comment id's in document.xml &
2150 comment.xml.
2151 */
2152 if ( m_rOpenedAnnotationMarksIds.end() == m_rOpenedAnnotationMarksIds.find( rName ) )
2153 {
2154 const sal_Int32 nId = m_nNextAnnotationMarkId++;
2156 m_pSerializer->singleElementNS( XML_w, XML_commentRangeStart,
2157 FSNS( XML_w, XML_id ), OString::number(nId) );
2159 }
2160 }
2162
2163 // export the end annotation marks
2164 for ( const auto & rName : m_rAnnotationMarksEnd )
2165 {
2166 // Get the id of the annotation mark
2167 std::map< OString, sal_Int32 >::iterator pPos = m_rOpenedAnnotationMarksIds.find( rName );
2168 if ( pPos != m_rOpenedAnnotationMarksIds.end( ) )
2169 {
2170 const sal_Int32 nId = ( *pPos ).second;
2171 m_pSerializer->singleElementNS( XML_w, XML_commentRangeEnd,
2172 FSNS( XML_w, XML_id ), OString::number(nId) );
2173 m_rOpenedAnnotationMarksIds.erase( rName );
2174
2175 m_pSerializer->startElementNS(XML_w, XML_r);
2176 m_pSerializer->singleElementNS( XML_w, XML_commentReference, FSNS( XML_w, XML_id ),
2177 OString::number(nId) );
2178 m_pSerializer->endElementNS(XML_w, XML_r);
2179 }
2180 }
2181 m_rAnnotationMarksEnd.clear();
2182}
2183
2185{
2186 const ::sw::mark::IFieldmark& rFieldmark = *rInfos.pFieldmark;
2187 FieldMarkParamsHelper params( rFieldmark );
2188
2189 OUString sEntryMacro;
2190 params.extractParam("EntryMacro", sEntryMacro);
2191 OUString sExitMacro;
2192 params.extractParam("ExitMacro", sExitMacro);
2193 OUString sHelp;
2194 params.extractParam("Help", sHelp);
2195 OUString sHint;
2196 params.extractParam("Hint", sHint); // .docx StatusText
2197 if ( sHint.isEmpty() )
2198 params.extractParam("Description", sHint); // .doc StatusText
2199
2200 if ( rInfos.eType == ww::eFORMDROPDOWN )
2201 {
2202 uno::Sequence< OUString> vListEntries;
2203 OUString sName, sSelected;
2204
2205 params.extractParam( ODF_FORMDROPDOWN_LISTENTRY, vListEntries );
2206 if (vListEntries.getLength() > ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT)
2207 vListEntries = uno::Sequence< OUString>(vListEntries.getArray(), ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT);
2208
2209 sName = params.getName();
2210 sal_Int32 nSelectedIndex = 0;
2211
2212 if ( params.extractParam( ODF_FORMDROPDOWN_RESULT, nSelectedIndex ) )
2213 {
2214 if (nSelectedIndex < vListEntries.getLength() )
2215 sSelected = vListEntries[ nSelectedIndex ];
2216 }
2217
2218 GetExport().DoComboBox( sName, OUString(), OUString(), sSelected, vListEntries );
2219 }
2220 else if ( rInfos.eType == ww::eFORMCHECKBOX )
2221 {
2222 OUString sName;
2223 bool bChecked = false;
2224
2225 params.extractParam( ODF_FORMCHECKBOX_NAME, sName );
2226
2227 const sw::mark::ICheckboxFieldmark* pCheckboxFm = dynamic_cast<const sw::mark::ICheckboxFieldmark*>(&rFieldmark);
2228 if ( pCheckboxFm && pCheckboxFm->IsChecked() )
2229 bChecked = true;
2230
2231 FFDataWriterHelper ffdataOut( m_pSerializer );
2232 ffdataOut.WriteFormCheckbox( sName, sEntryMacro, sExitMacro, sHelp, sHint, bChecked );
2233 }
2234 else if ( rInfos.eType == ww::eFORMTEXT )
2235 {
2236 OUString sType;
2237 params.extractParam("Type", sType);
2238 OUString sDefaultText;
2239 params.extractParam("Content", sDefaultText);
2240 sal_uInt16 nMaxLength = 0;
2241 params.extractParam("MaxLength", nMaxLength);
2242 OUString sFormat;
2243 params.extractParam("Format", sFormat);
2244 FFDataWriterHelper ffdataOut( m_pSerializer );
2245 ffdataOut.WriteFormText( params.getName(), sEntryMacro, sExitMacro, sHelp, sHint,
2246 sType, sDefaultText, nMaxLength, sFormat );
2247 }
2248}
2249
2250void DocxAttributeOutput::WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt)
2251{
2252 m_pSerializer->startElementNS(XML_w, XML_sdt);
2253 m_pSerializer->startElementNS(XML_w, XML_sdtPr);
2254
2255 if(!sFullDate.isEmpty())
2256 m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sFullDate);
2257 else
2258 m_pSerializer->startElementNS(XML_w, XML_date);
2259
2260 // Replace quotation mark used for marking static strings in date format
2261 OUString sDateFormat1 = sDateFormat.replaceAll("\"", "'");
2262 m_pSerializer->singleElementNS(XML_w, XML_dateFormat,
2263 FSNS(XML_w, XML_val), sDateFormat1);
2264 m_pSerializer->singleElementNS(XML_w, XML_lid,
2265 FSNS(XML_w, XML_val), sLang);
2266 m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs,
2267 FSNS(XML_w, XML_val), "dateTime");
2268 m_pSerializer->singleElementNS(XML_w, XML_calendar,
2269 FSNS(XML_w, XML_val), "gregorian");
2270 m_pSerializer->endElementNS(XML_w, XML_date);
2271
2272 if (aGrabBagSdt.hasElements())
2273 {
2274 // There are some extra sdt parameters came from grab bag
2275 SdtBlockHelper aSdtBlock;
2276 aSdtBlock.GetSdtParamsFromGrabBag(aGrabBagSdt);
2278 }
2279
2280 m_pSerializer->endElementNS(XML_w, XML_sdtPr);
2281
2282 m_pSerializer->startElementNS(XML_w, XML_sdtContent);
2283}
2284
2285void DocxAttributeOutput::WriteSdtPlainText(const OUString & sValue, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt)
2286{
2287 m_pSerializer->startElementNS(XML_w, XML_sdt);
2288 m_pSerializer->startElementNS(XML_w, XML_sdtPr);
2289
2290 if (aGrabBagSdt.hasElements())
2291 {
2292 // There are some extra sdt parameters came from grab bag
2293 SdtBlockHelper aSdtBlock;
2294 aSdtBlock.GetSdtParamsFromGrabBag(aGrabBagSdt);
2296
2297 if (aSdtBlock.m_nSdtPrToken && aSdtBlock.m_nSdtPrToken != FSNS(XML_w, XML_id))
2298 {
2299 // Write <w:text/> or whatsoever from grabbag
2300 m_pSerializer->singleElement(aSdtBlock.m_nSdtPrToken);
2301 }
2302
2303 // Store databindings data for later writing to corresponding XMLs
2304 OUString sPrefixMapping, sXpath;
2305 for (const auto& rProp : std::as_const(aGrabBagSdt))
2306 {
2307 if (rProp.Name == "ooxml:CT_SdtPr_dataBinding")
2308 {
2309 uno::Sequence<beans::PropertyValue> aDataBindingProps;
2310 rProp.Value >>= aDataBindingProps;
2311 for (const auto& rDBProp : std::as_const(aDataBindingProps))
2312 {
2313 if (rDBProp.Name == "ooxml:CT_DataBinding_prefixMappings")
2314 sPrefixMapping = rDBProp.Value.get<OUString>();
2315 else if (rDBProp.Name == "ooxml:CT_DataBinding_xpath")
2316 sXpath = rDBProp.Value.get<OUString>();
2317 }
2318 }
2319 }
2320
2321 if (sXpath.getLength())
2322 {
2323 // Given xpath is sufficient
2324 m_rExport.AddSdtData(sPrefixMapping, sXpath, sValue);
2325 }
2326 }
2327
2328 m_pSerializer->endElementNS(XML_w, XML_sdtPr);
2329
2330 m_pSerializer->startElementNS(XML_w, XML_sdtContent);
2331}
2332
2334{
2335 if (!m_pContentControl)
2336 {
2337 return;
2338 }
2339
2340 m_pSerializer->startElementNS(XML_w, XML_sdt);
2341 m_pSerializer->startElementNS(XML_w, XML_sdtPr);
2342 if (!m_pContentControl->GetPlaceholderDocPart().isEmpty())
2343 {
2344 m_pSerializer->startElementNS(XML_w, XML_placeholder);
2345 m_pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, XML_val),
2346 m_pContentControl->GetPlaceholderDocPart());
2347 m_pSerializer->endElementNS(XML_w, XML_placeholder);
2348 }
2349
2350 if (!m_pContentControl->GetDataBindingPrefixMappings().isEmpty() || !m_pContentControl->GetDataBindingXpath().isEmpty() || !m_pContentControl->GetDataBindingStoreItemID().isEmpty())
2351 {
2352 m_pSerializer->singleElementNS( XML_w, XML_dataBinding,
2353 FSNS(XML_w, XML_prefixMappings), m_pContentControl->GetDataBindingPrefixMappings(),
2354 FSNS(XML_w, XML_xpath), m_pContentControl->GetDataBindingXpath(),
2355 FSNS(XML_w, XML_storeItemID), m_pContentControl->GetDataBindingStoreItemID());
2356 }
2357
2358 if (!m_pContentControl->GetColor().isEmpty())
2359 {
2360 m_pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, XML_val),
2361 m_pContentControl->GetColor());
2362 }
2363
2364 if (m_pContentControl->GetShowingPlaceHolder())
2365 {
2366 m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr);
2367 }
2368
2369 if (m_pContentControl->GetPicture())
2370 {
2371 m_pSerializer->singleElementNS(XML_w, XML_picture);
2372 }
2373
2374 if (m_pContentControl->GetCheckbox())
2375 {
2376 m_pSerializer->startElementNS(XML_w14, XML_checkbox);
2377 m_pSerializer->singleElementNS(XML_w14, XML_checked, FSNS(XML_w14, XML_val),
2378 OString::number(int(m_pContentControl->GetChecked())));
2379 OUString aCheckedState = m_pContentControl->GetCheckedState();
2380 if (!aCheckedState.isEmpty())
2381 {
2382 m_pSerializer->singleElementNS(XML_w14, XML_checkedState, FSNS(XML_w14, XML_val),
2383 OString::number(aCheckedState[0], /*radix=*/16));
2384 }
2385 OUString aUncheckedState = m_pContentControl->GetUncheckedState();
2386 if (!aUncheckedState.isEmpty())
2387 {
2388 m_pSerializer->singleElementNS(XML_w14, XML_uncheckedState, FSNS(XML_w14, XML_val),
2389 OString::number(aUncheckedState[0], /*radix=*/16));
2390 }
2391 m_pSerializer->endElementNS(XML_w14, XML_checkbox);
2392 }
2393
2394 if (m_pContentControl->HasListItems())
2395 {
2396 m_pSerializer->startElementNS(XML_w, XML_dropDownList);
2397 for (const auto& rItem : m_pContentControl->GetListItems())
2398 {
2399 m_pSerializer->singleElementNS(XML_w, XML_listItem,
2400 FSNS(XML_w, XML_displayText), rItem.m_aDisplayText,
2401 FSNS(XML_w, XML_value), rItem.m_aValue);
2402 }
2403 m_pSerializer->endElementNS(XML_w, XML_dropDownList);
2404 }
2405
2406 if (m_pContentControl->GetDate())
2407 {
2408 OUString aCurrentDate = m_pContentControl->GetCurrentDate();
2409 if (aCurrentDate.isEmpty())
2410 {
2411 m_pSerializer->startElementNS(XML_w, XML_date);
2412 }
2413 else
2414 {
2415 m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), aCurrentDate);
2416 }
2417 OUString aDateFormat = m_pContentControl->GetDateFormat().replaceAll("\"", "'");
2418 if (!aDateFormat.isEmpty())
2419 {
2420 m_pSerializer->singleElementNS(XML_w, XML_dateFormat, FSNS(XML_w, XML_val),
2421 aDateFormat);
2422 }
2423 OUString aDateLanguage = m_pContentControl->GetDateLanguage();
2424 if (!aDateLanguage.isEmpty())
2425 {
2426 m_pSerializer->singleElementNS(XML_w, XML_lid, FSNS(XML_w, XML_val),
2427 aDateLanguage);
2428 }
2429 m_pSerializer->endElementNS(XML_w, XML_date);
2430 }
2431
2432 if (m_pContentControl->GetPlainText())
2433 {
2434 m_pSerializer->singleElementNS(XML_w, XML_text);
2435 }
2436
2437 m_pSerializer->endElementNS(XML_w, XML_sdtPr);
2438 m_pSerializer->startElementNS(XML_w, XML_sdtContent);
2439
2440 const OUString& rPrefixMapping = m_pContentControl->GetDataBindingPrefixMappings();
2441 const OUString& rXpath = m_pContentControl->GetDataBindingXpath();
2442 if (!rXpath.isEmpty())
2443 {
2444 // This content control has a data binding, update the data source.
2445 SwTextContentControl* pTextAttr = m_pContentControl->GetTextAttr();
2446 SwTextNode* pTextNode = m_pContentControl->GetTextNode();
2447 SwPosition aPoint(*pTextNode, pTextAttr->GetStart());
2448 SwPosition aMark(*pTextNode, *pTextAttr->GetEnd());
2449 SwPaM aPam(aMark, aPoint);
2450 OUString aSnippet = aPam.GetText();
2451 static sal_Unicode const aForbidden[] = {
2453 0
2454 };
2455 aSnippet = comphelper::string::removeAny(aSnippet, aForbidden);
2456 m_rExport.AddSdtData(rPrefixMapping, rXpath, aSnippet);
2457 }
2458
2459 m_pContentControl = nullptr;
2460}
2461
2463{
2464 m_pSerializer->endElementNS(XML_w, XML_sdtContent);
2465 m_pSerializer->endElementNS(XML_w, XML_sdt);
2466}
2467
2469 std::u16string_view rName,
2470 OUString const& rSelected,
2471 uno::Sequence<OUString> const& rListItems)
2472{
2473 m_pSerializer->startElementNS(XML_w, XML_sdt);
2474 m_pSerializer->startElementNS(XML_w, XML_sdtPr);
2475
2476 m_pSerializer->singleElementNS(XML_w, XML_alias,
2477 FSNS(XML_w, XML_val), OUStringToOString(rName, RTL_TEXTENCODING_UTF8));
2478
2479 sal_Int32 nId = comphelper::findValue(rListItems, rSelected);
2480 if (nId == -1)
2481 {
2482 nId = 0;
2483 }
2484
2485 m_pSerializer->startElementNS(XML_w, XML_dropDownList,
2486 FSNS(XML_w, XML_lastValue), OString::number(nId));
2487
2488 for (auto const& rItem : rListItems)
2489 {
2490 auto const item(OUStringToOString(rItem, RTL_TEXTENCODING_UTF8));
2491 m_pSerializer->singleElementNS(XML_w, XML_listItem,
2492 FSNS(XML_w, XML_value), item,
2493 FSNS(XML_w, XML_displayText), item);
2494 }
2495
2496 m_pSerializer->endElementNS(XML_w, XML_dropDownList);
2497 m_pSerializer->endElementNS(XML_w, XML_sdtPr);
2498
2499 m_pSerializer->startElementNS(XML_w, XML_sdtContent);
2500}
2501
2502void DocxAttributeOutput::WriteSdtDropDownEnd(OUString const& rSelected,
2503 uno::Sequence<OUString> const& rListItems)
2504{
2505 // note: rSelected might be empty?
2506 sal_Int32 nId = comphelper::findValue(rListItems, rSelected);
2507 if (nId == -1)
2508 {
2509 nId = 0;
2510 }
2511
2512 // the lastValue only identifies the entry in the list, also export
2513 // currently selected item's displayText as run content (if one exists)
2514 if (rListItems.size())
2515 {
2516 m_pSerializer->startElementNS(XML_w, XML_r);
2517 m_pSerializer->startElementNS(XML_w, XML_t);
2518 m_pSerializer->writeEscaped(rListItems[nId]);
2519 m_pSerializer->endElementNS(XML_w, XML_t);
2520 m_pSerializer->endElementNS(XML_w, XML_r);
2521 }
2522
2524}
2525
2526void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun )
2527{
2528 if ( rInfos.pField && rInfos.eType == ww::eUNKNOWN )
2529 {
2530 // Expand unsupported fields
2531 RunText(rInfos.pField->ExpandField(/*bCached=*/true, nullptr));
2532 return;
2533 }
2534 else if ( rInfos.eType == ww::eFORMDATE )
2535 {
2536 const sw::mark::IDateFieldmark& rFieldmark = dynamic_cast<const sw::mark::IDateFieldmark&>(*rInfos.pFieldmark);
2537 FieldMarkParamsHelper params(rFieldmark);
2538
2539 OUString sFullDate;
2540 OUString sCurrentDate;
2541 params.extractParam( ODF_FORMDATE_CURRENTDATE, sCurrentDate );
2542 if(!sCurrentDate.isEmpty())
2543 {
2544 sFullDate = sCurrentDate + "T00:00:00Z";
2545 }
2546 else
2547 {
2548 std::pair<bool, double> aResult = rFieldmark.GetCurrentDate();
2549 if(aResult.first)
2550 {
2551 sFullDate = rFieldmark.GetDateInStandardDateFormat(aResult.second) + "T00:00:00Z";
2552 }
2553 }
2554
2555 OUString sDateFormat;
2556 params.extractParam( ODF_FORMDATE_DATEFORMAT, sDateFormat );
2557 OUString sLang;
2558 params.extractParam( ODF_FORMDATE_DATEFORMAT_LANGUAGE, sLang );
2559
2560 uno::Sequence<beans::PropertyValue> aSdtParams;
2561 params.extractParam(UNO_NAME_MISC_OBJ_INTEROPGRABBAG, aSdtParams);
2562
2563 WriteFormDateStart( sFullDate, sDateFormat, sLang, aSdtParams);
2564 return;
2565 }
2566 else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField)
2567 {
2568 assert(!rInfos.pFieldmark);
2569 SwDropDownField const& rField2(*static_cast<SwDropDownField const*>(rInfos.pField.get()));
2571 rField2.GetSelectedItem(),
2572 rField2.GetItemSequence());
2573 return;
2574 }
2575 else if (rInfos.eType == ww::eFILLIN)
2576 {
2577 SwInputField const& rField(*static_cast<SwInputField const*>(rInfos.pField.get()));
2578 if (rField.getGrabBagParams().hasElements())
2579 {
2580 WriteSdtPlainText(rField.GetPar1(), rField.getGrabBagParams());
2581 m_sRawText = rField.GetPar1(); // Write field content also as a fallback
2582 return;
2583 }
2584 }
2585
2586 if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands
2587 {
2588 if ( bWriteRun )
2589 m_pSerializer->startElementNS(XML_w, XML_r);
2590
2591 if ( rInfos.eType == ww::eFORMDROPDOWN )
2592 {
2593 m_pSerializer->startElementNS( XML_w, XML_fldChar,
2594 FSNS( XML_w, XML_fldCharType ), "begin" );
2595 assert( rInfos.pFieldmark && !rInfos.pField );
2596 WriteFFData(rInfos);
2597 m_pSerializer->endElementNS( XML_w, XML_fldChar );
2598
2599 if ( bWriteRun )
2600 m_pSerializer->endElementNS( XML_w, XML_r );
2601
2602 CmdField_Impl( pNode, nPos, rInfos, bWriteRun );
2603 }
2604 else
2605 {
2606 // Write the field start
2607 if ( rInfos.pField && (rInfos.pField->Which() == SwFieldIds::DateTime) && rInfos.pField->GetSubType() & FIXEDFLD )
2608 {
2609 m_pSerializer->startElementNS( XML_w, XML_fldChar,
2610 FSNS( XML_w, XML_fldCharType ), "begin",
2611 FSNS( XML_w, XML_fldLock ), "true" );
2612 }
2613 else
2614 {
2615 m_pSerializer->startElementNS( XML_w, XML_fldChar,
2616 FSNS( XML_w, XML_fldCharType ), "begin" );
2617 }
2618
2619 if ( rInfos.pFieldmark )
2620 WriteFFData( rInfos );
2621
2622 m_pSerializer->endElementNS( XML_w, XML_fldChar );
2623
2624 if ( bWriteRun )
2625 m_pSerializer->endElementNS( XML_w, XML_r );
2626
2627 // The hyperlinks fields can't be expanded: the value is
2628 // normally in the text run
2629 if ( !rInfos.pField )
2630 CmdField_Impl( pNode, nPos, rInfos, bWriteRun );
2631 else
2632 m_bWritingField = true;
2633 }
2634 }
2635}
2636
2637void DocxAttributeOutput::DoWriteCmd( std::u16string_view rCmd )
2638{
2639 std::u16string_view sCmd = o3tl::trim(rCmd);
2640 if (o3tl::starts_with(sCmd, u"SEQ"))
2641 {
2642 OUString sSeqName( o3tl::trim(msfilter::util::findQuotedText(sCmd, "SEQ ", '\\')) );
2643 m_aSeqBookmarksNames[sSeqName].push_back(m_sLastOpenedBookmark);
2644 }
2645 // Write the Field command
2646 sal_Int32 nTextToken = XML_instrText;
2647 if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete )
2648 nTextToken = XML_delInstrText;
2649
2650 m_pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve");
2651 m_pSerializer->writeEscaped( rCmd );
2652 m_pSerializer->endElementNS( XML_w, nTextToken );
2653
2654}
2655
2656void DocxAttributeOutput::CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun )
2657{
2658 // Write the Field instruction
2659 if ( bWriteRun )
2660 {
2661 bool bWriteCombChars(false);
2662 m_pSerializer->startElementNS(XML_w, XML_r);
2663
2664 if (rInfos.eType == ww::eEQ)
2665 bWriteCombChars = true;
2666
2667 DoWriteFieldRunProperties( pNode, nPos, bWriteCombChars );
2668 }
2669
2670 sal_Int32 nIdx { rInfos.sCmd.isEmpty() ? -1 : 0 };
2671 while ( nIdx >= 0 )
2672 {
2673 OUString sToken = rInfos.sCmd.getToken( 0, '\t', nIdx );
2674 if ( rInfos.eType == ww::eCREATEDATE
2675 || rInfos.eType == ww::eSAVEDATE
2676 || rInfos.eType == ww::ePRINTDATE
2677 || rInfos.eType == ww::eDATE
2678 || rInfos.eType == ww::eTIME )
2679 {
2680 sToken = sToken.replaceAll("NNNN", "dddd");
2681 sToken = sToken.replaceAll("NN", "ddd");
2682 }
2683 else if ( rInfos.eType == ww::eEquals )
2684 {
2685 // Use original OOXML formula, if it exists and its conversion hasn't been changed
2686 bool bIsChanged = true;
2687 if ( pNode->GetTableBox() )
2688 {
2690 {
2691 OUString sActualFormula = sToken.trim();
2692 const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
2693 std::map<OUString, uno::Any>::const_iterator aStoredFormula = rGrabBag.find("CellFormulaConverted");
2694 if ( aStoredFormula != rGrabBag.end() && sActualFormula.indexOf('=') == 0 &&
2695 o3tl::trim(sActualFormula.subView(1)) == o3tl::trim(aStoredFormula->second.get<OUString>()) )
2696 {
2697 aStoredFormula = rGrabBag.find("CellFormula");
2698 if ( aStoredFormula != rGrabBag.end() )
2699 {
2700 sToken = " =" + aStoredFormula->second.get<OUString>();
2701 bIsChanged = false;
2702 }
2703 }
2704 }
2705 }
2706
2707 if ( bIsChanged )
2708 {
2709 UErrorCode nErr(U_ZERO_ERROR);
2710 icu::UnicodeString sInput(sToken.getStr());
2711 // remove < and > around cell references, e.g. <A1> to A1, <A1:B2> to A1:B2
2712 icu::RegexMatcher aMatcher("<([A-Z]{1,3}[0-9]+(:[A-Z]{1,3}[0-9]+)?)>", sInput, 0, nErr);
2713 sInput = aMatcher.replaceAll(icu::UnicodeString("$1"), nErr);
2714 // convert MEAN to AVERAGE
2715 icu::RegexMatcher aMatcher2("\\bMEAN\\b", sInput, UREGEX_CASE_INSENSITIVE, nErr);
2716 sToken = aMatcher2.replaceAll(icu::UnicodeString("AVERAGE"), nErr).getTerminatedBuffer();
2717 }
2718 }
2719
2720 // Write the Field command
2721 DoWriteCmd( sToken );
2722
2723 // Replace tabs by </instrText><tab/><instrText>
2724 if ( nIdx > 0 ) // Is another token expected?
2725 RunText( "\t" );
2726 }
2727
2728 if ( bWriteRun )
2729 {
2730 m_pSerializer->endElementNS( XML_w, XML_r );
2731 }
2732}
2733
2735 sal_Int32 const nPos, bool const bWriteRun)
2736{
2737 // Write the Field separator
2738 if ( bWriteRun )
2739 {
2740 m_pSerializer->startElementNS(XML_w, XML_r);
2742 }
2743
2744 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
2745 FSNS( XML_w, XML_fldCharType ), "separate" );
2746
2747 if ( bWriteRun )
2748 {
2749 m_pSerializer->endElementNS( XML_w, XML_r );
2750 }
2751}
2752
2786void DocxAttributeOutput::DoWriteFieldRunProperties( const SwTextNode * pNode, sal_Int32 nPos, bool bWriteCombChars)
2787{
2788 if (! pNode)
2789 {
2790 // nothing to do
2791 return;
2792 }
2793
2795
2796 {
2797 m_pSerializer->startElementNS(XML_w, XML_rPr);
2798
2799 // 1. output webHidden flag
2800 if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() )
2801 {
2802 m_pSerializer->singleElementNS(XML_w, XML_webHidden);
2803 }
2804
2805 // 2. find all active character properties
2806 SwWW8AttrIter aAttrIt( m_rExport, *pNode );
2807 aAttrIt.OutAttr( nPos, bWriteCombChars );
2808
2809 // 3. write the character properties
2811
2812 m_pSerializer->endElementNS( XML_w, XML_rPr );
2813 }
2814
2816}
2817
2818void DocxAttributeOutput::EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos )
2819{
2820 if (rInfos.eType == ww::eFORMDATE)
2821 {
2823 return;
2824 }
2825 else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField)
2826 {
2827 // write selected item from End not Start to ensure that any bookmarks
2828 // precede it
2829 SwDropDownField const& rField(*static_cast<SwDropDownField const*>(rInfos.pField.get()));
2831 return;
2832 }
2833 else if (rInfos.eType == ww::eFILLIN && rInfos.pField)
2834 {
2835 SwInputField const& rField(*static_cast<SwInputField const*>(rInfos.pField.get()));
2836 if (rField.getGrabBagParams().hasElements())
2837 {
2839 return;
2840 }
2841 }
2842 // The command has to be written before for the hyperlinks
2843 if ( rInfos.pField )
2844 {
2845 CmdField_Impl( pNode, nPos, rInfos, true );
2846 CmdEndField_Impl( pNode, nPos, true );
2847 }
2848
2849 // Write the bookmark start if any
2850 if ( !m_sFieldBkm.isEmpty() )
2851 {
2853 }
2854
2855 if (rInfos.pField ) // For hyperlinks and TOX
2856 {
2857 // Write the Field latest value
2858 m_pSerializer->startElementNS(XML_w, XML_r);
2860
2861 OUString sExpand;
2862 if(rInfos.eType == ww::eCITATION)
2863 {
2864 sExpand = static_cast<SwAuthorityField const*>(rInfos.pField.get())
2865 ->ExpandCitation(AUTH_FIELD_TITLE, nullptr);
2866 }
2867 else if(rInfos.eType != ww::eFORMDROPDOWN)
2868 {
2869 sExpand = rInfos.pField->ExpandField(true, nullptr);
2870 }
2871 // newlines embedded in fields are 0x0B in MSO and 0x0A for us
2872 RunText(sExpand.replace(0x0A, 0x0B));
2873
2874 m_pSerializer->endElementNS( XML_w, XML_r );
2875 }
2876
2877 // Write the bookmark end if any
2878 if ( !m_sFieldBkm.isEmpty() )
2879 {
2881
2883 }
2884
2885 // Write the Field end
2886 if ( rInfos.bClose )
2887 {
2888 m_bWritingField = false;
2889 m_pSerializer->startElementNS(XML_w, XML_r);
2891 m_pSerializer->singleElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "end");
2892 m_pSerializer->endElementNS( XML_w, XML_r );
2893 }
2894 // Write the ref field if a bookmark had to be set and the field
2895 // should be visible
2896 if ( !rInfos.pField )
2897 {
2898 m_sFieldBkm.clear();
2899 return;
2900 }
2901
2902 sal_uInt16 nSubType = rInfos.pField->GetSubType( );
2903 bool bIsSetField = rInfos.pField->GetTyp( )->Which( ) == SwFieldIds::SetExp;
2904 bool bShowRef = bIsSetField && ( nSubType & nsSwExtendedSubType::SUB_INVISIBLE ) == 0;
2905
2906 if (!bShowRef)
2907 {
2908 m_sFieldBkm.clear();
2909 }
2910
2911 if (m_sFieldBkm.isEmpty())
2912 return;
2913
2914 // Write the field beginning
2915 m_pSerializer->startElementNS(XML_w, XML_r);
2916 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
2917 FSNS( XML_w, XML_fldCharType ), "begin" );
2918 m_pSerializer->endElementNS( XML_w, XML_r );
2919
2920 rInfos.sCmd = FieldString( ww::eREF );
2921 rInfos.sCmd += "\"";
2922 rInfos.sCmd += m_sFieldBkm;
2923 rInfos.sCmd += "\" ";
2924
2925 // Clean the field bookmark data to avoid infinite loop
2926 m_sFieldBkm = OUString( );
2927
2928 // Write the end of the field
2929 EndField_Impl( pNode, nPos, rInfos );
2930}
2931
2933{
2934 // postpone the output so that we can later [in EndRunProperties()]
2935 // prepend the properties before the text
2937
2938 m_pSerializer->startElementNS(XML_w, XML_rPr);
2939
2940 if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() )
2941 {
2942 m_pSerializer->singleElementNS(XML_w, XML_webHidden);
2943 }
2945
2946 assert( !m_oPostponedGraphic );
2947 m_oPostponedGraphic.emplace();
2948
2949 assert( !m_oPostponedDiagrams );
2950 m_oPostponedDiagrams.emplace();
2951
2952 assert(!m_oPostponedDMLDrawings);
2953 m_oPostponedDMLDrawings.emplace();
2954
2955 assert( !m_oPostponedOLEs );
2956 m_oPostponedOLEs.emplace();
2957}
2958
2960{
2961 m_pFontsAttrList = nullptr;
2963 m_pCharLangAttrList = nullptr;
2964
2965 // Write the elements in the spec order
2966 static const sal_Int32 aOrder[] =
2967 {
2968 FSNS( XML_w, XML_rStyle ),
2969 FSNS( XML_w, XML_rFonts ),
2970 FSNS( XML_w, XML_b ),
2971 FSNS( XML_w, XML_bCs ),
2972 FSNS( XML_w, XML_i ),
2973 FSNS( XML_w, XML_iCs ),
2974 FSNS( XML_w, XML_caps ),
2975 FSNS( XML_w, XML_smallCaps ),
2976 FSNS( XML_w, XML_strike ),
2977 FSNS( XML_w, XML_dstrike ),
2978 FSNS( XML_w, XML_outline ),
2979 FSNS( XML_w, XML_shadow ),
2980 FSNS( XML_w, XML_emboss ),
2981 FSNS( XML_w, XML_imprint ),
2982 FSNS( XML_w, XML_noProof ),
2983 FSNS( XML_w, XML_snapToGrid ),
2984 FSNS( XML_w, XML_vanish ),
2985 FSNS( XML_w, XML_webHidden ),
2986 FSNS( XML_w, XML_color ),
2987 FSNS( XML_w, XML_spacing ),
2988 FSNS( XML_w, XML_w ),
2989 FSNS( XML_w, XML_kern ),
2990 FSNS( XML_w, XML_position ),
2991 FSNS( XML_w, XML_sz ),
2992 FSNS( XML_w, XML_szCs ),
2993 FSNS( XML_w, XML_highlight ),
2994 FSNS( XML_w, XML_u ),
2995 FSNS( XML_w, XML_effect ),
2996 FSNS( XML_w, XML_bdr ),
2997 FSNS( XML_w, XML_shd ),
2998 FSNS( XML_w, XML_fitText ),
2999 FSNS( XML_w, XML_vertAlign ),
3000 FSNS( XML_w, XML_rtl ),
3001 FSNS( XML_w, XML_cs ),
3002 FSNS( XML_w, XML_em ),
3003 FSNS( XML_w, XML_lang ),
3004 FSNS( XML_w, XML_eastAsianLayout ),
3005 FSNS( XML_w, XML_specVanish ),
3006 FSNS( XML_w, XML_oMath ),
3007 FSNS( XML_w, XML_rPrChange ),
3008 FSNS( XML_w, XML_del ),
3009 FSNS( XML_w, XML_ins ),
3010 FSNS( XML_w, XML_moveFrom ),
3011 FSNS( XML_w, XML_moveTo ),
3012 FSNS( XML_w14, XML_glow ),
3013 FSNS( XML_w14, XML_shadow ),
3014 FSNS( XML_w14, XML_reflection ),
3015 FSNS( XML_w14, XML_textOutline ),
3016 FSNS( XML_w14, XML_textFill ),
3017 FSNS( XML_w14, XML_scene3d ),
3018 FSNS( XML_w14, XML_props3d ),
3019 FSNS( XML_w14, XML_ligatures ),
3020 FSNS( XML_w14, XML_numForm ),
3021 FSNS( XML_w14, XML_numSpacing ),
3022 FSNS( XML_w14, XML_stylisticSets ),
3023 FSNS( XML_w14, XML_cntxtAlts ),
3024 };
3025
3026 // postpone the output so that we can later [in EndParagraphProperties()]
3027 // prepend the properties before the run
3028 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
3030}
3031
3032namespace
3033{
3034
3035struct NameToId
3036{
3037 OUString maName;
3038 sal_Int32 maId;
3039};
3040
3041const NameToId constNameToIdMapping[] =
3042{
3043 { OUString("glow"), FSNS( XML_w14, XML_glow ) },
3044 { OUString("shadow"), FSNS( XML_w14, XML_shadow ) },
3045 { OUString("reflection"), FSNS( XML_w14, XML_reflection ) },
3046 { OUString("textOutline"), FSNS( XML_w14, XML_textOutline ) },
3047 { OUString("textFill"), FSNS( XML_w14, XML_textFill ) },
3048 { OUString("scene3d"), FSNS( XML_w14, XML_scene3d ) },
3049 { OUString("props3d"), FSNS( XML_w14, XML_props3d ) },
3050 { OUString("ligatures"), FSNS( XML_w14, XML_ligatures ) },
3051 { OUString("numForm"), FSNS( XML_w14, XML_numForm ) },
3052 { OUString("numSpacing"), FSNS( XML_w14, XML_numSpacing ) },
3053 { OUString("stylisticSets"),FSNS( XML_w14, XML_stylisticSets ) },
3054 { OUString("cntxtAlts"), FSNS( XML_w14, XML_cntxtAlts ) },
3055
3056 { OUString("val"), FSNS( XML_w14, XML_val ) },
3057 { OUString("rad"), FSNS( XML_w14, XML_rad ) },
3058 { OUString("blurRad"), FSNS( XML_w14, XML_blurRad ) },
3059 { OUString("stA"), FSNS( XML_w14, XML_stA ) },
3060 { OUString("stPos"), FSNS( XML_w14, XML_stPos ) },
3061 { OUString("endA"), FSNS( XML_w14, XML_endA ) },
3062 { OUString("endPos"), FSNS( XML_w14, XML_endPos ) },
3063 { OUString("dist"), FSNS( XML_w14, XML_dist ) },
3064 { OUString("dir"), FSNS( XML_w14, XML_dir ) },
3065 { OUString("fadeDir"), FSNS( XML_w14, XML_fadeDir ) },
3066 { OUString("sx"), FSNS( XML_w14, XML_sx ) },
3067 { OUString("sy"), FSNS( XML_w14, XML_sy ) },
3068 { OUString("kx"), FSNS( XML_w14, XML_kx ) },
3069 { OUString("ky"), FSNS( XML_w14, XML_ky ) },
3070 { OUString("algn"), FSNS( XML_w14, XML_algn ) },
3071 { OUString("w"), FSNS( XML_w14, XML_w ) },
3072 { OUString("cap"), FSNS( XML_w14, XML_cap ) },
3073 { OUString("cmpd"), FSNS( XML_w14, XML_cmpd ) },
3074 { OUString("pos"), FSNS( XML_w14, XML_pos ) },
3075 { OUString("ang"), FSNS( XML_w14, XML_ang ) },
3076 { OUString("scaled"), FSNS( XML_w14, XML_scaled ) },
3077 { OUString("path"), FSNS( XML_w14, XML_path ) },
3078 { OUString("l"), FSNS( XML_w14, XML_l ) },
3079 { OUString("t"), FSNS( XML_w14, XML_t ) },
3080 { OUString("r"), FSNS( XML_w14, XML_r ) },
3081 { OUString("b"), FSNS( XML_w14, XML_b ) },
3082 { OUString("lim"), FSNS( XML_w14, XML_lim ) },
3083 { OUString("prst"), FSNS( XML_w14, XML_prst ) },
3084 { OUString("rig"), FSNS( XML_w14, XML_rig ) },
3085 { OUString("lat"), FSNS( XML_w14, XML_lat ) },
3086 { OUString("lon"), FSNS( XML_w14, XML_lon ) },
3087 { OUString("rev"), FSNS( XML_w14, XML_rev ) },
3088 { OUString("h"), FSNS( XML_w14, XML_h ) },
3089 { OUString("extrusionH"), FSNS( XML_w14, XML_extrusionH ) },
3090 { OUString("contourW"), FSNS( XML_w14, XML_contourW ) },
3091 { OUString("prstMaterial"), FSNS( XML_w14, XML_prstMaterial ) },
3092 { OUString("id"), FSNS( XML_w14, XML_id ) },
3093
3094 { OUString("schemeClr"), FSNS( XML_w14, XML_schemeClr ) },
3095 { OUString("srgbClr"), FSNS( XML_w14, XML_srgbClr ) },
3096 { OUString("tint"), FSNS( XML_w14, XML_tint ) },
3097 { OUString("shade"), FSNS( XML_w14, XML_shade ) },
3098 { OUString("alpha"), FSNS( XML_w14, XML_alpha ) },
3099 { OUString("hueMod"), FSNS( XML_w14, XML_hueMod ) },
3100 { OUString("sat"), FSNS( XML_w14, XML_sat ) },
3101 { OUString("satOff"), FSNS( XML_w14, XML_satOff ) },
3102 { OUString("satMod"), FSNS( XML_w14, XML_satMod ) },
3103 { OUString("lum"), FSNS( XML_w14, XML_lum ) },
3104 { OUString("lumOff"), FSNS( XML_w14, XML_lumOff ) },
3105 { OUString("lumMod"), FSNS( XML_w14, XML_lumMod ) },
3106 { OUString("noFill"), FSNS( XML_w14, XML_noFill ) },
3107 { OUString("solidFill"), FSNS( XML_w14, XML_solidFill ) },
3108 { OUString("gradFill"), FSNS( XML_w14, XML_gradFill ) },
3109 { OUString("gsLst"), FSNS( XML_w14, XML_gsLst ) },
3110 { OUString("gs"), FSNS( XML_w14, XML_gs ) },
3111 { OUString("pos"), FSNS( XML_w14, XML_pos ) },
3112 { OUString("lin"), FSNS( XML_w14, XML_lin ) },
3113 { OUString("path"), FSNS( XML_w14, XML_path ) },
3114 { OUString("fillToRect"), FSNS( XML_w14, XML_fillToRect ) },
3115 { OUString("prstDash"), FSNS( XML_w14, XML_prstDash ) },
3116 { OUString("round"), FSNS( XML_w14, XML_round ) },
3117 { OUString("bevel"), FSNS( XML_w14, XML_bevel ) },
3118 { OUString("miter"), FSNS( XML_w14, XML_miter ) },
3119 { OUString("camera"), FSNS( XML_w14, XML_camera ) },
3120 { OUString("lightRig"), FSNS( XML_w14, XML_lightRig ) },
3121 { OUString("rot"), FSNS( XML_w14, XML_rot ) },
3122 { OUString("bevelT"), FSNS( XML_w14, XML_bevelT ) },
3123 { OUString("bevelB"), FSNS( XML_w14, XML_bevelB ) },
3124 { OUString("extrusionClr"), FSNS( XML_w14, XML_extrusionClr ) },
3125 { OUString("contourClr"), FSNS( XML_w14, XML_contourClr ) },
3126 { OUString("styleSet"), FSNS( XML_w14, XML_styleSet ) },
3127};
3128
3129std::optional<sal_Int32> lclGetElementIdForName(std::u16string_view rName)
3130{
3131 for (auto const & i : constNameToIdMapping)
3132 {
3133 if (rName == i.maName)
3134 {
3135 return i.maId;
3136 }
3137 }
3138 return std::optional<sal_Int32>();
3139}
3140
3141void lclProcessRecursiveGrabBag(sal_Int32 aElementId, const css::uno::Sequence<css::beans::PropertyValue>& rElements, sax_fastparser::FSHelperPtr const & pSerializer)
3142{
3143 css::uno::Sequence<css::beans::PropertyValue> aAttributes;
3144 rtl::Reference<FastAttributeList> pAttributes = FastSerializerHelper::createAttrList();
3145
3146 for (const auto& rElement : rElements)
3147 {
3148 if (rElement.Name == "attributes")
3149 {
3150 rElement.Value >>= aAttributes;
3151 }
3152 }
3153
3154 for (const auto& rAttribute : std::as_const(aAttributes))
3155 {
3156 uno::Any aAny = rAttribute.Value;
3157 OString aValue;
3158
3159 if(aAny.getValueType() == cppu::UnoType<sal_Int32>::get())
3160 {
3161 aValue = OString::number(aAny.get<sal_Int32>());
3162 }
3163 else if(aAny.getValueType() == cppu::UnoType<OUString>::get())
3164 {
3165 aValue = OUStringToOString(aAny.get<OUString>(), RTL_TEXTENCODING_ASCII_US);
3166 }
3167
3168 std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rAttribute.Name);
3169 if(aSubElementId)
3170 pAttributes->add(*aSubElementId, aValue);
3171 }
3172
3173 pSerializer->startElement(aElementId, pAttributes);
3174
3175 for (const auto& rElement : rElements)
3176 {
3177 css::uno::Sequence<css::beans::PropertyValue> aSumElements;
3178
3179 std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rElement.Name);
3180 if(aSubElementId)
3181 {
3182 rElement.Value >>= aSumElements;
3183 lclProcessRecursiveGrabBag(*aSubElementId, aSumElements, pSerializer);
3184 }
3185 }
3186
3187 pSerializer->endElement(aElementId);
3188}
3189
3190}
3191
3193{
3194 // Write all differed properties
3195 if ( m_pFontsAttrList.is() )
3196 {
3197 rtl::Reference<FastAttributeList> xAttrList = std::move( m_pFontsAttrList );
3198 m_pSerializer->singleElementNS( XML_w, XML_rFonts, xAttrList );
3199 }
3200
3201 if ( m_pColorAttrList.is() )
3202 {
3204
3205 m_pSerializer->singleElementNS( XML_w, XML_color, xAttrList );
3206 }
3207
3208 if ( m_pEastAsianLayoutAttrList.is() )
3209 {
3211 m_pSerializer->singleElementNS( XML_w, XML_eastAsianLayout, xAttrList );
3212 }
3213
3214 if ( m_pCharLangAttrList.is() )
3215 {
3217 m_pSerializer->singleElementNS( XML_w, XML_lang, xAttrList );
3218 }
3219
3221 {
3222 std::string_view pVal;
3223 m_pColorAttrList->getAsView(FSNS(XML_w, XML_val), pVal);
3224 if (!pVal.empty() && pVal != "auto")
3225 {
3226 m_pSerializer->startElementNS(XML_w14, XML_textFill);
3227 m_pSerializer->startElementNS(XML_w14, XML_solidFill);
3228 m_pSerializer->startElementNS(XML_w14, XML_srgbClr, FSNS(XML_w14, XML_val), pVal.data());
3229 sal_Int32 nTransparence = m_nCharTransparence * oox::drawingml::MAX_PERCENT / 255.0;
3230 m_pSerializer->singleElementNS(XML_w14, XML_alpha, FSNS(XML_w14, XML_val), OString::number(nTransparence));
3231 m_pSerializer->endElementNS(XML_w14, XML_srgbClr);
3232 m_pSerializer->endElementNS(XML_w14, XML_solidFill);
3233 m_pSerializer->endElementNS(XML_w14, XML_textFill);
3235 }
3236 }
3237 m_pColorAttrList.clear();
3238 for (const beans::PropertyValue & i : m_aTextEffectsGrabBag)
3239 {
3240 std::optional<sal_Int32> aElementId = lclGetElementIdForName(i.Name);
3241 if(aElementId)
3242 {
3243 uno::Sequence<beans::PropertyValue> aGrabBagSeq;
3244 i.Value >>= aGrabBagSeq;
3245 lclProcessRecursiveGrabBag(*aElementId, aGrabBagSeq, m_pSerializer);
3246 }
3247 }
3248 m_aTextEffectsGrabBag.clear();
3249}
3250
3252{
3253 // Call the 'Redline' function. This will add redline (change-tracking) information that regards to run properties.
3254 // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
3255
3256 // If there is RedlineData present, call WriteCollectedRunProperties() for writing rPr before calling Redline().
3257 // As there will be another rPr for redline and LO might mix both.
3258 if(pRedlineData)
3260 Redline( pRedlineData );
3261
3263
3264 // Merge the marks for the ordered elements
3266
3267 m_pSerializer->endElementNS( XML_w, XML_rPr );
3268
3269 // write footnotes/endnotes if we have any
3271
3273
3274 // merge the properties _before_ the run text (strictly speaking, just
3275 // after the start of the run)
3277
3279
3281 //We need to write w:drawing tag after the w:rPr.
3283
3284 //We need to write w:pict tag after the w:rPr.
3286
3288
3290}
3291
3293{
3294 if (!pSdrObj)
3295 return;
3296
3297 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObj)->getUnoShape());
3298 uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
3299 if( !xPropSet.is() )
3300 return;
3301
3302 uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
3303 uno::Sequence< beans::PropertyValue > aGrabBag;
3304 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
3305 {
3306 xPropSet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
3307 }
3308 else if(xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag"))
3309 {
3310 xPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
3311 }
3312
3313 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
3314 [this](const beans::PropertyValue& rProp) {
3315 return "SdtEndBefore" == rProp.Name && m_aRunSdt.m_bStartedSdt && !m_bEndCharSdt; });
3316 if (pProp != std::cend(aGrabBag))
3317 pProp->Value >>= m_bEndCharSdt;
3318}
3319
3321{
3322 for (const auto & rPostponedDiagram : *m_oPostponedGraphic)
3323 FlyFrameGraphic(rPostponedDiagram.grfNode, rPostponedDiagram.size,
3324 nullptr, nullptr,
3325 rPostponedDiagram.pSdrObj);
3326 m_oPostponedGraphic.reset();
3327}
3328
3330{
3331 for( const auto & rPostponedDiagram : *m_oPostponedDiagrams )
3332 m_rExport.SdrExporter().writeDiagram(rPostponedDiagram.object,
3333 *rPostponedDiagram.frame, m_anchorId++);
3334 m_oPostponedDiagrams.reset();
3335}
3336
3338{
3339 if( m_footnoteEndnoteRefTag == 0 )
3340 return false;
3341
3342 // output the character style for MS Word's benefit
3343 const SwEndNoteInfo& rInfo = m_footnoteEndnoteRefTag == XML_footnoteRef ?
3345 const SwCharFormat* pCharFormat = rInfo.GetCharFormat( m_rExport.m_rDoc );
3346 if ( pCharFormat )
3347 {
3348 const OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat)));
3349 m_pSerializer->startElementNS(XML_w, XML_rPr);
3350 m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
3351 m_pSerializer->endElementNS( XML_w, XML_rPr );
3352 }
3353
3354 if (m_footnoteCustomLabel.isEmpty())
3355 m_pSerializer->singleElementNS(XML_w, m_footnoteEndnoteRefTag);
3356 else
3359 return true;
3360}
3361
3368static bool impl_WriteRunText( FSHelperPtr const & pSerializer, sal_Int32 nTextToken,
3369 const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true )
3370{
3371 const sal_Unicode *pBegin = rBegin;
3372
3373 // skip one character after the end
3374 if ( bMove )
3375 rBegin = pEnd + 1;
3376
3377 if ( pBegin >= pEnd )
3378 return false; // we want to write at least one character
3379
3380 // we have to add 'preserve' when starting/ending with space
3381 if ( *pBegin == ' ' || *( pEnd - 1 ) == ' ' )
3382 {
3383 pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve");
3384 }
3385 else
3386 pSerializer->startElementNS(XML_w, nTextToken);
3387
3388 pSerializer->writeEscaped( std::u16string_view( pBegin, pEnd - pBegin ) );
3389
3390 pSerializer->endElementNS( XML_w, nTextToken );
3391
3392 return true;
3393}
3394
3395void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCharSet*/ )
3396{
3398 {
3400 }
3401 m_bRunTextIsOn = true;
3402 // one text can be split into more <w:t>blah</w:t>'s by line breaks etc.
3403 const sal_Unicode *pBegin = rText.getStr();
3404 const sal_Unicode *pEnd = pBegin + rText.getLength();
3405
3406 // the text run is usually XML_t, with the exception of the deleted (and not moved) text
3407 sal_Int32 nTextToken = XML_t;
3408
3409 bool bMoved = m_pRedlineData && m_pRedlineData->IsMoved() &&
3410 // tdf#150166 save tracked moving around TOC as w:ins, w:del
3411 SwDoc::GetCurTOX(*m_rExport.m_pCurPam->GetPoint()) == nullptr;
3412
3413 if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete && !bMoved )
3414 {
3415 nTextToken = XML_delText;
3416 }
3417
3418 sal_Unicode prevUnicode = *pBegin;
3419
3420 for ( const sal_Unicode *pIt = pBegin; pIt < pEnd; ++pIt )
3421 {
3422 switch ( *pIt )
3423 {
3424 case 0x09: // tab
3425 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
3426 m_pSerializer->singleElementNS(XML_w, XML_tab);
3427 prevUnicode = *pIt;
3428 break;
3429 case 0x0b: // line break
3430 case static_cast<sal_Unicode>(text::ControlCharacter::LINE_BREAK):
3431 {
3432 if (impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ) || prevUnicode < 0x0020)
3433 {
3434 m_pSerializer->singleElementNS(XML_w, XML_br);
3435 prevUnicode = *pIt;
3436 }
3437 }
3438 break;
3439 case 0x1E: //non-breaking hyphen
3440 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
3441 m_pSerializer->singleElementNS(XML_w, XML_noBreakHyphen);
3442 prevUnicode = *pIt;
3443 break;
3444 case 0x1F: //soft (on demand) hyphen
3445 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
3446 m_pSerializer->singleElementNS(XML_w, XML_softHyphen);
3447 prevUnicode = *pIt;
3448 break;
3449 default:
3450 if ( *pIt < 0x0020 ) // filter out the control codes
3451 {
3452 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
3453 SAL_INFO("sw.ww8", "Ignored control code in a text run: " << unsigned(*pIt) );
3454 }
3455 prevUnicode = *pIt;
3456 break;
3457 }
3458 }
3459
3460 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false );
3461}
3462
3463void DocxAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding /*eCharSet*/)
3464{
3465 m_sRawText = rText;
3466}
3467
3468void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby )
3469{
3470 WW8Ruby aWW8Ruby( rNode, rRuby, GetExport() );
3471 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRuby( const SwTextNode& rNode, const SwFormatRuby& rRuby )" );
3472 EndRun( &rNode, nPos, -1 ); // end run before starting ruby to avoid nested runs, and overlap
3473 assert(!m_closeHyperlinkInThisRun); // check that no hyperlink overlaps ruby
3475 m_pSerializer->startElementNS(XML_w, XML_r);
3476 m_pSerializer->startElementNS(XML_w, XML_ruby);
3477 m_pSerializer->startElementNS(XML_w, XML_rubyPr);
3478
3479 m_pSerializer->singleElementNS( XML_w, XML_rubyAlign,
3480 FSNS( XML_w, XML_val ), lclConvertWW8JCToOOXMLRubyAlign(aWW8Ruby.GetJC()) );
3481 sal_uInt32 nHps = (aWW8Ruby.GetRubyHeight() + 5) / 10;
3482 sal_uInt32 nHpsBaseText = (aWW8Ruby.GetBaseHeight() + 5) / 10;
3483 m_pSerializer->singleElementNS(XML_w, XML_hps, FSNS(XML_w, XML_val), OString::number(nHps));
3484
3485 m_pSerializer->singleElementNS( XML_w, XML_hpsRaise,
3486 FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) );
3487
3488 m_pSerializer->singleElementNS( XML_w, XML_hpsBaseText,
3489 FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) );
3490
3491 lang::Locale aLocale( SwBreakIt::Get()->GetLocale(
3492 rNode.GetLang( nPos ) ) );
3493 OUString sLang( LanguageTag::convertToBcp47( aLocale) );
3494 m_pSerializer->singleElementNS(XML_w, XML_lid, FSNS(XML_w, XML_val), sLang);
3495
3496 m_pSerializer->endElementNS( XML_w, XML_rubyPr );
3497
3498 m_pSerializer->startElementNS(XML_w, XML_rt);
3499 StartRun( nullptr, nPos );
3501
3502 if (rRuby.GetTextRuby() && rRuby.GetTextRuby()->GetCharFormat())
3503 {
3504 const SwCharFormat* pFormat = rRuby.GetTextRuby()->GetCharFormat();
3505 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(rRuby.GetText(), 0);
3506 TypedWhichId<SvxFontItem> nWhichFont = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONT : RES_CHRATR_CJK_FONT;
3507 TypedWhichId<SvxFontHeightItem> nWhichFontSize = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONTSIZE : RES_CHRATR_CJK_FONTSIZE;
3508
3509 CharFont(pFormat->GetFormatAttr(nWhichFont));
3510 CharFontSize(pFormat->GetFormatAttr(nWhichFontSize));
3512 }
3513
3514 EndRunProperties( nullptr );
3515 RunText( rRuby.GetText( ) );
3516 EndRun( &rNode, nPos, -1 );
3517 m_pSerializer->endElementNS( XML_w, XML_rt );
3518
3519 m_pSerializer->startElementNS(XML_w, XML_rubyBase);
3520 StartRun( nullptr, nPos );
3521}
3522
3523void DocxAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos)
3524{
3525 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRuby()" );
3526 EndRun( &rNode, nPos, -1 );
3527 m_pSerializer->endElementNS( XML_w, XML_rubyBase );
3528 m_pSerializer->endElementNS( XML_w, XML_ruby );
3529 m_pSerializer->endElementNS( XML_w, XML_r );
3530 StartRun(nullptr, nPos); // open Run again so OutputTextNode loop can close it
3531}
3532
3533bool DocxAttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark )
3534{
3535 bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark );
3536
3537 if ( !pMark->isEmpty() )
3538 {
3539 OUString sURL = *pLinkURL;
3540
3541 if ( bBookMarkOnly )
3542 sURL = FieldString( ww::eHYPERLINK );
3543 else
3544 sURL = FieldString( ww::eHYPERLINK ) + "\"" + sURL + "\"";
3545
3546 sURL += " \\l \"" + *pMark + "\"";
3547
3548 if ( !rTarget.isEmpty() )
3549 sURL += " \\n " + rTarget;
3550
3551 *pLinkURL = sURL;
3552 }
3553
3554 return bBookMarkOnly;
3555}
3556
3557void DocxAttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos )
3558{
3559 m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName));
3560 m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName));
3561}
3562
3563bool DocxAttributeOutput::StartURL( const OUString& rUrl, const OUString& rTarget )
3564{
3565 OUString sMark;
3566 OUString sUrl;
3567
3568 bool bBookmarkOnly = AnalyzeURL( rUrl, rTarget, &sUrl, &sMark );
3569
3570 m_hyperLinkAnchor = sMark;
3571
3572 if ( !sMark.isEmpty() && !bBookmarkOnly )
3573 {
3574 m_rExport.OutputField( nullptr, ww::eHYPERLINK, sUrl );
3575 }
3576 else
3577 {
3578 // Output a hyperlink XML element
3579 m_pHyperlinkAttrList = FastSerializerHelper::createAttrList();
3580
3581 if ( !bBookmarkOnly )
3582 {
3583 OString sId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
3584 oox::getRelationship(Relationship::HYPERLINK),
3585 sUrl, true ), RTL_TEXTENCODING_UTF8 );
3586
3587 m_pHyperlinkAttrList->add(FSNS(XML_r, XML_id), sId);
3588 }
3589 else
3590 {
3591 // Is this a link to a sequence? Then try to replace that with a
3592 // normal bookmark, as Word won't understand our special
3593 // <seqname>!<index>|sequence syntax.
3594 if (sMark.endsWith("|sequence"))
3595 {
3596 sal_Int32 nPos = sMark.indexOf('!');
3597 if (nPos != -1)
3598 {
3599 // Extract <seqname>, the field instruction text has the name quoted.
3600 OUString aSequenceName = sMark.copy(0, nPos);
3601 // Extract <index>.
3602 sal_uInt32 nIndex = o3tl::toUInt32(sMark.subView(nPos + 1, sMark.getLength() - nPos - sizeof("|sequence")));
3603 std::map<OUString, std::vector<OString> >::iterator it = m_aSeqBookmarksNames.find(aSequenceName);
3604 if (it != m_aSeqBookmarksNames.end())
3605 {
3606 std::vector<OString>& rNames = it->second;
3607 if (rNames.size() > nIndex)
3608 // We know the bookmark name for this sequence and this index, do the replacement.
3609 sMark = OStringToOUString(rNames[nIndex], RTL_TEXTENCODING_UTF8);
3610 }
3611 }
3612 }
3613 else if (sMark.endsWith("|toxmark"))
3614 {
3615 if (auto const it = GetExport().m_TOXMarkBookmarksByURL.find(sMark);
3616 it != GetExport().m_TOXMarkBookmarksByURL.end())
3617 {
3618 sMark = it->second;
3619 }
3620 }
3621 // Spaces are prohibited in bookmark name.
3622 sMark = sMark.replace(' ', '_');
3623 m_pHyperlinkAttrList->add( FSNS( XML_w, XML_anchor ),
3624 OUStringToOString( sMark, RTL_TEXTENCODING_UTF8 ) );
3625 }
3626
3627 if ( !rTarget.isEmpty() )
3628 {
3629 OString soTarget = OUStringToOString( rTarget, RTL_TEXTENCODING_UTF8 );
3630 m_pHyperlinkAttrList->add(FSNS(XML_w, XML_tgtFrame), soTarget);
3631 }
3632 }
3633
3634 return true;
3635}
3636
3638{
3640 if(m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc"))
3641 {
3642 m_endPageRef = true;
3643 }
3644 return true;
3645}
3646
3647void DocxAttributeOutput::FieldVanish(const OUString& rText,
3648 ww::eField const eType, OUString const*const pBookmarkName)
3649{
3650 WriteField_Impl(nullptr, eType, rText, FieldFlags::All, pBookmarkName);
3651}
3652
3653// The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
3654// 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
3655// 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
3657{
3658 if ( !pRedlineData )
3659 return;
3660
3661 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
3663
3664 OString aId( OString::number( pRedlineData->GetSeqNo() ) );
3665 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
3666 const DateTime aDateTime = pRedlineData->GetTimeStamp();
3667 bool bNoDate = bRemovePersonalInfo ||
3668 ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
3669
3670 switch( pRedlineData->GetType() )
3671 {
3672 case RedlineType::Insert:
3673 break;
3674
3675 case RedlineType::Delete:
3676 break;
3677
3678 case RedlineType::Format:
3679 {
3682
3683 pAttributeList->add(FSNS( XML_w, XML_id ), aId);
3684 pAttributeList->add(FSNS( XML_w, XML_author ), bRemovePersonalInfo
3685 ? "Author" + OString::number( GetExport().GetInfoID(rAuthor) )
3686 : rAuthor.toUtf8());
3687 if (!bNoDate)
3688 pAttributeList->add(FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ));
3689 m_pSerializer->startElementNS( XML_w, XML_rPrChange, pAttributeList );
3690
3691 // Check if there is any extra data stored in the redline object
3692 if (pRedlineData->GetExtraData())
3693 {
3694 const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData();
3695 const SwRedlineExtraData_FormatColl* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData);
3696
3697 // Check if the extra data is of type 'formatting changes'
3698 if (pFormattingChanges)
3699 {
3700 // Get the item set that holds all the changes properties
3701 const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
3702 if (pChangesSet)
3703 {
3705
3706 m_pSerializer->startElementNS(XML_w, XML_rPr);
3707
3708 // Output the redline item set
3709 if (pChangesSet)
3710 m_rExport.OutputItemSet( *pChangesSet, false, true, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF );
3711
3712 m_pSerializer->endElementNS( XML_w, XML_rPr );
3713
3715 }
3716 }
3717 }
3718
3719 m_pSerializer->endElementNS( XML_w, XML_rPrChange );
3720 break;
3721 }
3722 case RedlineType::ParagraphFormat:
3723 {
3726
3727 pAttributeList->add(FSNS( XML_w, XML_id ), aId);
3728 pAttributeList->add(FSNS( XML_w, XML_author ), bRemovePersonalInfo
3729 ? "Author" + OString::number( GetExport().GetInfoID(rAuthor) )
3730 : rAuthor.toUtf8());
3731 if (!bNoDate)
3732 pAttributeList->add(FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ));
3733 m_pSerializer->startElementNS( XML_w, XML_pPrChange, pAttributeList );
3734
3735 // Check if there is any extra data stored in the redline object
3736 if (pRedlineData->GetExtraData())
3737 {
3738 const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData();
3739 const SwRedlineExtraData_FormatColl* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData);
3740
3741 // Check if the extra data is of type 'formatting changes'
3742 if (pFormattingChanges)
3743 {
3744 // Get the item set that holds all the changes properties
3745 const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
3746 const OUString & sParaStyleName = pFormattingChanges->GetFormatName();
3747 if (pChangesSet || !sParaStyleName.isEmpty())
3748 {
3750
3751 m_pSerializer->startElementNS(XML_w, XML_pPr);
3752
3753 OString sStyleName = MSWordStyles::CreateStyleId( sParaStyleName );
3754 if ( !sStyleName.isEmpty() )
3755 m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), sStyleName);
3756
3757 // The 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' are used to hold information
3758 // that should be collected by different properties in the core, and are all flushed together
3759 // to the DOCX when the function 'WriteCollectedParagraphProperties' gets called.
3760 // So we need to store the current status of these lists, so that we can revert back to them when
3761 // we are done exporting the redline attributes.
3766
3767 // Output the redline item set
3768 if (pChangesSet)
3769 m_rExport.OutputItemSet( *pChangesSet, true, false, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF );
3770
3771 // Write the collected paragraph properties that are stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
3773
3774 // Revert back the original values that were stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
3775 m_rExport.SdrExporter().getFlyAttrList() = pFlyAttrList_Original;
3776 m_pParagraphSpacingAttrList = pParagraphSpacingAttrList_Original;
3777
3778 m_pSerializer->endElementNS( XML_w, XML_pPr );
3779
3781 }
3782 }
3783 }
3784 m_pSerializer->endElementNS( XML_w, XML_pPrChange );
3785 break;
3786 }
3787 default:
3788 SAL_WARN("sw.ww8", "Unhandled redline type for export " << SwRedlineTypeToOUString(pRedlineData->GetType()));
3789 break;
3790 }
3791}
3792
3793// The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
3794// 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
3795// 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
3797{
3798 if ( !pRedlineData )
3799 return;
3800
3801 // write out stack of this redline recursively (first the oldest)
3802 StartRedline( pRedlineData->Next() );
3803
3804 OString aId( OString::number( m_nRedlineId++ ) );
3805
3806 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
3808
3809 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
3810 OString aAuthor( OUStringToOString( bRemovePersonalInfo
3811 ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
3812 : rAuthor, RTL_TEXTENCODING_UTF8 ) );
3813
3814 const DateTime aDateTime = pRedlineData->GetTimeStamp();
3815 bool bNoDate = bRemovePersonalInfo ||
3816 ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
3817 bool bMoved = pRedlineData->IsMoved() &&
3818 // tdf#150166 save tracked moving around TOC as w:ins, w:del
3819 SwDoc::GetCurTOX(*m_rExport.m_pCurPam->GetPoint()) == nullptr;
3820 switch ( pRedlineData->GetType() )
3821 {
3822 case RedlineType::Insert:
3823 case RedlineType::Delete:
3824 {
3825 sal_Int32 eElement = RedlineType::Insert == pRedlineData->GetType()
3826 ? ( bMoved ? XML_moveTo : XML_ins )
3827 : ( bMoved ? XML_moveFrom : XML_del );
3828 if ( bNoDate )
3829 m_pSerializer->startElementNS( XML_w, eElement,
3830 FSNS( XML_w, XML_id ), aId,
3831 FSNS( XML_w, XML_author ), aAuthor );
3832 else
3833 m_pSerializer->startElementNS( XML_w, eElement,
3834 FSNS( XML_w, XML_id ), aId,
3835 FSNS( XML_w, XML_author ), aAuthor,
3836 FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) );
3837 break;
3838 }
3839 case RedlineType::Format:
3840 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRedline()" );
3841 break;
3842 default:
3843 break;
3844 }
3845}
3846
3848{
3849 if ( !pRedlineData || m_bWritingField )
3850 return;
3851
3852 bool bMoved = pRedlineData->IsMoved() &&
3853 // tdf#150166 save tracked moving around TOC as w:ins, w:del
3854 SwDoc::GetCurTOX(*m_rExport.m_pCurPam->GetPoint()) == nullptr;
3855 switch ( pRedlineData->GetType() )
3856 {
3857 case RedlineType::Insert:
3858 m_pSerializer->endElementNS( XML_w, bMoved ? XML_moveTo : XML_ins );
3859 break;
3860
3861 case RedlineType::Delete:
3862 m_pSerializer->endElementNS( XML_w, bMoved ? XML_moveFrom : XML_del );
3863 break;
3864
3865 case RedlineType::Format:
3866 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRedline()" );
3867 break;
3868 default:
3869 break;
3870 }
3871
3872 // write out stack of this redline recursively (first the newest)
3873 EndRedline( pRedlineData->Next() );
3874}
3875
3876void DocxAttributeOutput::FormatDrop( const SwTextNode& /*rNode*/, const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t )
3877{
3878 SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle )" );
3879}
3880
3882{
3883 OString aStyleId(m_rExport.m_pStyles->GetStyleId(nStyle));
3884
3885 m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), aStyleId);
3886}
3887
3888static void impl_borderLine( FSHelperPtr const & pSerializer, sal_Int32 elementToken, const SvxBorderLine* pBorderLine, sal_uInt16 nDist,
3889 bool bWriteShadow, const table::BorderLine2* rStyleProps = nullptr )
3890{
3891 // Compute val attribute value
3892 // Can be one of:
3893 // single, double,
3894 // basicWideOutline, basicWideInline
3895 // OOXml also supports those types of borders, but we'll try to play with the first ones.
3896 // thickThinMediumGap, thickThinLargeGap, thickThinSmallGap
3897 // thinThickLargeGap, thinThickMediumGap, thinThickSmallGap
3898 const char* pVal = "nil";
3899 if ( pBorderLine && !pBorderLine->isEmpty( ) )
3900 {
3901 switch (pBorderLine->GetBorderLineStyle())
3902 {
3903 case SvxBorderLineStyle::SOLID:
3904 pVal = "single";
3905 break;
3906 case SvxBorderLineStyle::DOTTED:
3907 pVal = "dotted";
3908 break;
3909 case SvxBorderLineStyle::DASHED:
3910 pVal = "dashed";
3911 break;
3912 case SvxBorderLineStyle::DOUBLE:
3913 case SvxBorderLineStyle::DOUBLE_THIN:
3914 pVal = "double";
3915 break;
3916 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
3917 pVal = "thinThickSmallGap";
3918 break;
3919 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
3920 pVal = "thinThickMediumGap";
3921 break;
3922 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
3923 pVal = "thinThickLargeGap";
3924 break;
3925 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
3926 pVal = "thickThinSmallGap";
3927 break;
3928 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
3929 pVal = "thickThinMediumGap";
3930 break;
3931 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
3932 pVal = "thickThinLargeGap";
3933 break;
3934 case SvxBorderLineStyle::EMBOSSED:
3935 pVal = "threeDEmboss";
3936 break;
3937 case SvxBorderLineStyle::ENGRAVED:
3938 pVal = "threeDEngrave";
3939 break;
3940 case SvxBorderLineStyle::OUTSET:
3941 pVal = "outset";
3942 break;
3943 case SvxBorderLineStyle::INSET:
3944 pVal = "inset";
3945 break;
3946 case SvxBorderLineStyle::FINE_DASHED:
3947 pVal = "dashSmallGap";
3948 break;
3949 case SvxBorderLineStyle::DASH_DOT:
3950 pVal = "dotDash";
3951 break;
3952 case SvxBorderLineStyle::DASH_DOT_DOT:
3953 pVal = "dotDotDash";
3954 break;
3955 case SvxBorderLineStyle::NONE:
3956 default:
3957 break;
3958 }
3959 }
3960 else if ( !rStyleProps || !rStyleProps->LineWidth )
3961 // no line, and no line set by the style either:
3962 // there is no need to write the property
3963 return;
3964
3965 // compare the properties with the theme properties before writing them:
3966 // if they are equal, it means that they were style-defined and there is
3967 // no need to write them.
3968 if( rStyleProps != nullptr && pBorderLine && !pBorderLine->isEmpty() &&
3969 pBorderLine->GetBorderLineStyle() == static_cast<SvxBorderLineStyle>(rStyleProps->LineStyle) &&
3970 pBorderLine->GetColor() == Color(ColorTransparency, rStyleProps->Color) &&
3971 pBorderLine->GetWidth() == o3tl::toTwips(rStyleProps->LineWidth, o3tl::Length::mm100) )
3972 return;
3973
3974 rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
3975 pAttr->add( FSNS( XML_w, XML_val ), OString( pVal ) );
3976
3977 if ( pBorderLine && !pBorderLine->isEmpty() )
3978 {
3979 // Compute the sz attribute
3980
3981 double const fConverted( ::editeng::ConvertBorderWidthToWord(
3982 pBorderLine->GetBorderLineStyle(), pBorderLine->GetWidth()));
3983 // The unit is the 8th of point
3984 sal_Int32 nWidth = sal_Int32( fConverted / 2.5 );
3985 const sal_Int32 nMinWidth = 2;
3986 const sal_Int32 nMaxWidth = 96;
3987
3988 if ( nWidth > nMaxWidth )
3989 nWidth = nMaxWidth;
3990 else if ( nWidth < nMinWidth )
3991 nWidth = nMinWidth;
3992
3993 pAttr->add( FSNS( XML_w, XML_sz ), OString::number( nWidth ) );
3994
3995 // Get the distance (in pt)
3996 pAttr->add(FSNS(XML_w, XML_space), OString::number(rtl::math::round(nDist / 20.0)));
3997
3998 // Get the color code as an RRGGBB hex value
3999 OString sColor( msfilter::util::ConvertColor( pBorderLine->GetColor( ) ) );
4000 pAttr->add( FSNS( XML_w, XML_color ), sColor );
4001 }
4002
4003 if (bWriteShadow)
4004 {
4005 // Set the shadow value
4006 pAttr->add( FSNS( XML_w, XML_shadow ), "1" );
4007 }
4008
4009 pSerializer->singleElementNS( XML_w, elementToken, pAttr );
4010}
4011
4013{
4014 OutputBorderOptions rOptions;
4015
4016 rOptions.tag = XML_tcBorders;
4017 rOptions.bUseStartEnd = !bEcma;
4018 rOptions.bWriteTag = true;
4019 rOptions.bWriteDistance = false;
4020
4021 return rOptions;
4022}
4023
4025{
4026 OutputBorderOptions rOptions;
4027
4028 rOptions.tag = XML_pBdr;
4029 rOptions.bUseStartEnd = false;
4030 rOptions.bWriteTag = false;
4031 rOptions.bWriteDistance = true;
4032
4033 return rOptions;
4034}
4035
4036static void impl_borders( FSHelperPtr const & pSerializer,
4037 const SvxBoxItem& rBox,
4038 const OutputBorderOptions& rOptions,
4039 std::map<SvxBoxItemLine,
4040 css::table::BorderLine2> &rTableStyleConf )
4041{
4042 static const SvxBoxItemLine aBorders[] =
4043 {
4044 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4045 };
4046
4047 const sal_Int32 aXmlElements[] =
4048 {
4049 XML_top,
4050 rOptions.bUseStartEnd ? XML_start : XML_left,
4051 XML_bottom,
4052 rOptions.bUseStartEnd ? XML_end : XML_right
4053 };
4054 bool tagWritten = false;
4055 const SvxBoxItemLine* pBrd = aBorders;
4056
4057 for( int i = 0; i < 4; ++i, ++pBrd )
4058 {
4059 const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
4060 const table::BorderLine2 *aStyleProps = nullptr;
4061 if( rTableStyleConf.find( *pBrd ) != rTableStyleConf.end() )
4062 aStyleProps = &rTableStyleConf[ *pBrd ];
4063
4064 if (!tagWritten && rOptions.bWriteTag)
4065 {
4066 pSerializer->startElementNS(XML_w, rOptions.tag);
4067 tagWritten = true;
4068 }
4069
4070 bool bWriteShadow = false;
4071 if (rOptions.aShadowLocation == SvxShadowLocation::NONE)
4072 {
4073 // The border has no shadow
4074 }
4075 else if (rOptions.aShadowLocation == SvxShadowLocation::BottomRight)
4076 {
4077 // Special case of 'Bottom-Right' shadow:
4078 // If the shadow location is 'Bottom-Right' - then turn on the shadow
4079 // for ALL the sides. This is because in Word - if you select a shadow
4080 // for a border - it turn on the shadow for ALL the sides (but shows only
4081 // the bottom-right one).
4082 // This is so that no information will be lost if passed through LibreOffice
4083 bWriteShadow = true;
4084 }
4085 else
4086 {
4087 // If there is a shadow, and it's not the regular 'Bottom-Right',
4088 // then write only the 'shadowed' sides of the border
4089 if (
4090 ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::TOP ) ||
4091 ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::BottomLeft) && *pBrd == SvxBoxItemLine::LEFT ) ||
4092 ((rOptions.aShadowLocation == SvxShadowLocation::BottomLeft ) && *pBrd == SvxBoxItemLine::BOTTOM) ||
4093 ((rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::RIGHT )
4094 )
4095 {
4096 bWriteShadow = true;
4097 }
4098 }
4099
4100 sal_uInt16 nDist = 0;
4101 if (rOptions.bWriteDistance)
4102 {
4103 if (rOptions.pDistances)
4104 {
4105 if ( *pBrd == SvxBoxItemLine::TOP)
4106 nDist = rOptions.pDistances->nTop;
4107 else if ( *pBrd == SvxBoxItemLine::LEFT)
4108 nDist = rOptions.pDistances->nLeft;
4109 else if ( *pBrd == SvxBoxItemLine::BOTTOM)
4110 nDist = rOptions.pDistances->nBottom;
4111 else if ( *pBrd == SvxBoxItemLine::RIGHT)
4112 nDist = rOptions.pDistances->nRight;
4113 }
4114 else
4115 {
4116 nDist = rBox.GetDistance(*pBrd);
4117 }
4118 }
4119
4120 impl_borderLine( pSerializer, aXmlElements[i], pLn, nDist, bWriteShadow, aStyleProps );
4121 }
4122 if (tagWritten && rOptions.bWriteTag) {
4123 pSerializer->endElementNS( XML_w, rOptions.tag );
4124 }
4125}
4126
4127static void impl_cellMargins( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins = nullptr)
4128{
4129 static const SvxBoxItemLine aBorders[] =
4130 {
4131 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4132 };
4133
4134 const sal_Int32 aXmlElements[] =
4135 {
4136 XML_top,
4137 bUseStartEnd ? XML_start : XML_left,
4138 XML_bottom,
4139 bUseStartEnd ? XML_end : XML_right
4140 };
4141 bool tagWritten = false;
4142 const SvxBoxItemLine* pBrd = aBorders;
4143 for( int i = 0; i < 4; ++i, ++pBrd )
4144 {
4145 sal_Int32 nDist = sal_Int32( rBox.GetDistance( *pBrd ) );
4146
4147 if (pDefaultMargins)
4148 {
4149 // Skip output if cell margin == table default margin
4150 if (sal_Int32( pDefaultMargins->GetDistance( *pBrd ) ) == nDist)
4151 continue;
4152 }
4153
4154 if (!tagWritten) {
4155 pSerializer->startElementNS(XML_w, tag);
4156 tagWritten = true;
4157 }
4158 pSerializer->singleElementNS( XML_w, aXmlElements[i],
4159 FSNS( XML_w, XML_w ), OString::number(nDist),
4160 FSNS( XML_w, XML_type ), "dxa" );
4161 }
4162 if (tagWritten) {
4163 pSerializer->endElementNS( XML_w, tag );
4164 }
4165}
4166
4167void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
4168{
4169 m_pSerializer->startElementNS(XML_w, XML_tcPr);
4170
4171 const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
4172
4174
4175 // Output any table cell redlines if there are any attached to this specific cell
4176 TableCellRedline( pTableTextNodeInfoInner );
4177
4178 // Cell preferred width
4179 SwTwips nWidth = GetGridCols( pTableTextNodeInfoInner )->at( nCell );
4180 if ( nCell )
4181 nWidth = nWidth - GetGridCols( pTableTextNodeInfoInner )->at( nCell - 1 );
4182 m_pSerializer->singleElementNS( XML_w, XML_tcW,
4183 FSNS( XML_w, XML_w ), OString::number(nWidth),
4184 FSNS( XML_w, XML_type ), "dxa" );
4185
4186 // Horizontal spans
4187 const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
4188 SwWriteTableRow *pRow = rRows[ nRow ].get();
4189 const SwWriteTableCells& rTableCells = pRow->GetCells();
4190 if (nCell < rTableCells.size() )
4191 {
4192 const SwWriteTableCell& rCell = *rTableCells[nCell];
4193 const sal_uInt16 nColSpan = rCell.GetColSpan();
4194 if ( nColSpan > 1 )
4195 m_pSerializer->singleElementNS( XML_w, XML_gridSpan,
4196 FSNS( XML_w, XML_val ), OString::number(nColSpan) );
4197 }
4198
4199 // Vertical merges
4200 ww8::RowSpansPtr xRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow();
4201 sal_Int32 vSpan = (*xRowSpans)[nCell];
4202 if ( vSpan > 1 )
4203 {
4204 m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "restart");
4205 }
4206 else if ( vSpan < 0 )
4207 {
4208 m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "continue");
4209 }
4210
4212 {
4213 const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
4214 std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("CellCnfStyle");
4215 if (it != rGrabBag.end())
4216 {
4217 uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
4218 m_pTableStyleExport->CnfStyle(aAttributes);
4219 }
4220 }
4221
4222
4223 const SvxBoxItem& rBox = pTableBox->GetFrameFormat( )->GetBox( );
4224 const SvxBoxItem& rDefaultBox = (*m_TableFirstCells.rbegin())->getTableBox( )->GetFrameFormat( )->GetBox( );
4225 {
4226 // The cell borders
4228 m_aTableStyleConfs.back());
4229 }
4230
4231 TableBackgrounds( pTableTextNodeInfoInner );
4232
4233 {
4234 // Cell margins
4235 impl_cellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox );
4236 }
4237
4238 TableVerticalCell( pTableTextNodeInfoInner );
4239
4240 m_pSerializer->endElementNS( XML_w, XML_tcPr );
4241}
4242
4244{
4245 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
4246 if (m_xTableWrt && pTable == m_xTableWrt->GetTable())
4247 return;
4248
4249 tools::Long nPageSize = 0;
4250 bool bRelBoxSize = false;
4251
4252 // Create the SwWriteTable instance to use col spans (and maybe other infos)
4253 GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
4254
4255 const SwFrameFormat *pFormat = pTable->GetFrameFormat( );
4256 const sal_uInt32 nTableSz = static_cast<sal_uInt32>(pFormat->GetFrameSize( ).GetWidth( ));
4257
4258 const SwHTMLTableLayout *pLayout = pTable->GetHTMLTableLayout();
4259 if( pLayout && pLayout->IsExportable() )
4260 m_xTableWrt.reset(new SwWriteTable(pTable, pLayout));
4261 else
4262 m_xTableWrt.reset(new SwWriteTable(pTable, pTable->GetTabLines(), nPageSize, nTableSz, false));
4263}
4264
4266{
4267 m_aTableStyleConfs.push_back({});
4268
4269 // In case any paragraph SDT's are open, close them here.
4271
4272 m_pSerializer->startElementNS(XML_w, XML_tbl);
4273
4274 m_TableFirstCells.push_back(pTableTextNodeInfoInner);
4275 m_LastOpenCell.push_back(-1);
4276 m_LastClosedCell.push_back(-1);
4277
4278 InitTableHelper( pTableTextNodeInfoInner );
4279 TableDefinition( pTableTextNodeInfoInner );
4280}
4281
4283{
4284 m_pSerializer->endElementNS( XML_w, XML_tbl );
4285
4286 if ( m_tableReference->m_nTableDepth > 0 )
4287 --m_tableReference->m_nTableDepth;
4288
4289 m_LastClosedCell.pop_back();
4290 m_LastOpenCell.pop_back();
4291 m_TableFirstCells.pop_back();
4292
4293 // We closed the table; if it is a nested table, the cell that contains it
4294 // still continues
4295 // set to true only if we were in a nested table, not otherwise.
4296 if( !m_TableFirstCells.empty() )
4297 m_tableReference->m_bTableCellOpen = true;
4298
4299 // Cleans the table helper
4300 m_xTableWrt.reset();
4301
4302 m_aTableStyleConfs.pop_back();
4303}
4304
4306{
4307 m_pSerializer->startElementNS(XML_w, XML_tr);
4308
4309 // Output the row properties
4310 m_pSerializer->startElementNS(XML_w, XML_trPr);
4311
4312 // Header row: tblHeader
4313 const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
4314 if ( pTable->GetRowsToRepeat( ) > pTableTextNodeInfoInner->getRow( ) )
4315 m_pSerializer->singleElementNS(XML_w, XML_tblHeader, FSNS(XML_w, XML_val), "true"); // TODO to overwrite table style may need explicit false
4316
4317 TableRowRedline( pTableTextNodeInfoInner );
4318 TableHeight( pTableTextNodeInfoInner );
4319 TableCanSplit( pTableTextNodeInfoInner );
4320
4321 const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox();
4322 const SwTableLine* pTableLine = pTableBox->GetUpper();
4324 {
4325 const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
4326 std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("RowCnfStyle");
4327 if (it != rGrabBag.end())
4328 {
4329 uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
4330 m_pTableStyleExport->CnfStyle(aAttributes);
4331 }
4332 }
4333
4334
4335 m_pSerializer->endElementNS( XML_w, XML_trPr );
4336}
4337
4339{
4340 m_pSerializer->endElementNS( XML_w, XML_tr );
4341 m_LastOpenCell.back() = -1;
4342 m_LastClosedCell.back() = -1;
4343}
4344
4345void DocxAttributeOutput::StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
4346{
4347 m_LastOpenCell.back() = nCell;
4348
4349 InitTableHelper( pTableTextNodeInfoInner );
4350
4351 m_pSerializer->startElementNS(XML_w, XML_tc);
4352
4353 // Write the cell properties here
4354 TableCellProperties( pTableTextNodeInfoInner, nCell, nRow );
4355
4356 m_tableReference->m_bTableCellOpen = true;
4357}
4358
4360{
4361 m_LastClosedCell.back() = nCell;
4362 m_LastOpenCell.back() = -1;
4363
4364 if (m_tableReference->m_bTableCellParaSdtOpen)
4366
4367 m_pSerializer->endElementNS( XML_w, XML_tc );
4368
4369 m_tableReference->m_bTableCellOpen = false;
4370 m_tableReference->m_bTableCellParaSdtOpen = false;
4371}
4372
4374{
4375}
4376
4378{
4379}
4380
4381namespace
4382{
4383
4385OString lcl_padStartToLength(OString const & aString, sal_Int32 nLen, char cFill)
4386{
4387 if (nLen > aString.getLength())
4388 {
4389 sal_Int32 nDiff = nLen - aString.getLength();
4390 OStringBuffer aBuffer;
4391 comphelper::string::padToLength(aBuffer, nDiff, cFill);
4392 aBuffer.append(aString);
4393 return aBuffer.makeStringAndClear();
4394 }
4395 else
4396 return aString;
4397}
4398
4399//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
4400//Since this is not import code, "-1" needs to be handled as the mode that LO will save as.
4401//To identify how your code should handle a "-1", look in DocxExport::WriteSettings().
4402sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& rDocExport)
4403{
4404 sal_Int32 nWordCompatibilityMode = rDocExport.getWordCompatibilityModeFromGrabBag();
4405
4406 // TODO: this is duplicated, better store it in DocxExport member?
4408 {
4409 if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
4410 {
4411 nWordCompatibilityMode = 14;
4412 }
4413 }
4414
4415 return nWordCompatibilityMode;
4416}
4417
4418}
4419
4421{
4423
4424 // Write the table properties
4425 m_pSerializer->startElementNS(XML_w, XML_tblPr);
4426
4427 static const sal_Int32 aOrder[] =
4428 {
4429 FSNS( XML_w, XML_tblStyle ),
4430 FSNS( XML_w, XML_tblpPr ),
4431 FSNS( XML_w, XML_tblOverlap ),
4432 FSNS( XML_w, XML_bidiVisual ),
4433 FSNS( XML_w, XML_tblStyleRowBandSize ),
4434 FSNS( XML_w, XML_tblStyleColBandSize ),
4435 FSNS( XML_w, XML_tblW ),
4436 FSNS( XML_w, XML_jc ),
4437 FSNS( XML_w, XML_tblCellSpacing ),
4438 FSNS( XML_w, XML_tblInd ),
4439 FSNS( XML_w, XML_tblBorders ),
4440 FSNS( XML_w, XML_shd ),
4441 FSNS( XML_w, XML_tblLayout ),
4442 FSNS( XML_w, XML_tblCellMar ),
4443 FSNS( XML_w, XML_tblLook ),
4444 FSNS( XML_w, XML_tblPrChange )
4445 };
4446
4447 // postpone the output so that we can later []
4448 // prepend the properties before the run
4449 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
4451
4452 tools::Long nPageSize = 0;
4453 const char* widthType = "dxa";
4454
4455 // If actual width of table is relative it should export is as "pct".`
4456 const SwTable *pTable = pTableTextNodeInfoInner->getTable();
4457 SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
4458 const SwFormatFrameSize &rSize = pTableFormat->GetFrameSize();
4459 int nWidthPercent = rSize.GetWidthPercent();
4460 // If we export a floating table: we use the widthPercent of the surrounding frame
4461 const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame();
4462 if (pFloatingTableFrame)
4463 {
4464 const SwFormatFrameSize &rFrameSize = pFloatingTableFrame->GetFrameFormat().GetFrameSize();
4465 nWidthPercent = rFrameSize.GetWidthPercent();
4466 }
4467
4468 uno::Reference<beans::XPropertySet> xPropertySet(SwXTextTables::GetObject(*pTable->GetFrameFormat( )),uno::UNO_QUERY);
4469 bool isWidthRelative = false;
4470 xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative;
4471 if (!isWidthRelative && !nWidthPercent)
4472 {
4473 // The best fit for "automatic" table placement is relative 100%
4474 short nHoriOrient = -1;
4475 xPropertySet->getPropertyValue("HoriOrient") >>= nHoriOrient;
4476 isWidthRelative = nHoriOrient == text::HoriOrientation::FULL;
4477 if (isWidthRelative)
4478 nWidthPercent = 100;
4479 }
4480
4481 if(isWidthRelative)
4482 {
4494 nPageSize = nWidthPercent * 50 ;
4495 widthType = "pct" ;
4496 }
4497 else
4498 {
4499 bool bRelBoxSize = false;
4500 // Create the SwWriteTable instance to use col spans (and maybe other infos)
4501 GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
4502 if(nPageSize == 0)
4503 widthType = "auto";
4504 }
4505
4506 // Output the table preferred width
4507 m_pSerializer->singleElementNS( XML_w, XML_tblW,
4508 FSNS( XML_w, XML_w ), OString::number(nPageSize),
4509 FSNS( XML_w, XML_type ), widthType );
4510
4511 // Disable layout autofit, as it does not exist in LibreOffice yet
4512 m_pSerializer->singleElementNS( XML_w, XML_tblLayout,
4513 FSNS( XML_w, XML_type ), "fixed" );
4514
4515 // Look for the table style property in the table grab bag
4516 std::map<OUString, css::uno::Any> aGrabBag =
4517 pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
4518
4519 // We should clear the TableStyle map. In case of Table inside multiple tables it contains the
4520 // table border style of the previous table.
4521 std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = m_aTableStyleConfs.back();
4522 rTableStyleConf.clear();
4523
4524 // Extract properties from grab bag
4525 for( const auto & rGrabBagElement : aGrabBag )
4526 {
4527 if( rGrabBagElement.first == "TableStyleName")
4528 {
4529 OString sStyleName = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
4530 m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, XML_val), sStyleName);
4531 }
4532 else if( rGrabBagElement.first == "TableStyleTopBorder" )
4533 rTableStyleConf[SvxBoxItemLine::TOP] = rGrabBagElement.second.get<table::BorderLine2>();
4534 else if( rGrabBagElement.first == "TableStyleBottomBorder" )
4535 rTableStyleConf[SvxBoxItemLine::BOTTOM]
4536 = rGrabBagElement.second.get<table::BorderLine2>();
4537 else if( rGrabBagElement.first == "TableStyleLeftBorder" )
4538 rTableStyleConf[SvxBoxItemLine::LEFT]
4539 = rGrabBagElement.second.get<table::BorderLine2>();
4540 else if( rGrabBagElement.first == "TableStyleRightBorder" )
4541 rTableStyleConf[SvxBoxItemLine::RIGHT]
4542 = rGrabBagElement.second.get<table::BorderLine2>();
4543 else if (rGrabBagElement.first == "TableStyleLook")
4544 {
4545 rtl::Reference<FastAttributeList> pAttributeList = FastSerializerHelper::createAttrList();
4546 const uno::Sequence<beans::PropertyValue> aAttributeList = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
4547
4548 for (const auto& rAttribute : aAttributeList)
4549 {
4550 if (rAttribute.Name == "val")
4551 pAttributeList->add(FSNS(XML_w, XML_val), lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16), 4, '0'));
4552 else
4553 {
4554 static DocxStringTokenMap const aTokens[] =
4555 {
4556 {"firstRow", XML_firstRow},
4557 {"lastRow", XML_lastRow},
4558 {"firstColumn", XML_firstColumn},
4559 {"lastColumn", XML_lastColumn},
4560 {"noHBand", XML_noHBand},
4561 {"noVBand", XML_noVBand},
4562 {nullptr, 0}
4563 };
4564
4565 if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name))
4566 pAttributeList->add(FSNS(XML_w, nToken), (rAttribute.Value.get<sal_Int32>() ? "1" : "0"));
4567 }
4568 }
4569
4570 m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList);
4571 }
4572 else if (rGrabBagElement.first == "TablePosition" &&
4573 // skip empty table position (tables in footnotes converted to
4574 // floating tables temporarily, don't export this)
4575 rGrabBagElement.second != uno::Any() )
4576 {
4577 rtl::Reference<FastAttributeList> attrListTablePos = FastSerializerHelper::createAttrList( );
4578 const uno::Sequence<beans::PropertyValue> aTablePosition = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue> >();
4579 // look for a surrounding frame and take it's position values
4580 const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame();
4581 if( pFrame )
4582 {
4583 // we export the values of the surrounding Frame
4584 OString sOrientation;
4585 sal_Int32 nValue;
4586
4587 // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY!
4588 OString sTblpXSpec = convertToOOXMLHoriOrient( pFrame->GetFrameFormat().GetHoriOrient().GetHoriOrient(), pFrame->GetFrameFormat().GetHoriOrient().IsPosToggle() );
4589 OString sTblpYSpec = convertToOOXMLVertOrient( pFrame->GetFrameFormat().GetVertOrient().GetVertOrient() );
4590
4591 sOrientation = convertToOOXMLVertOrientRel( pFrame->GetFrameFormat().GetVertOrient().GetRelationOrient() );
4592 attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
4593
4594 if( !sTblpYSpec.isEmpty() )
4595 attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), sTblpYSpec);
4596
4597 sOrientation = convertToOOXMLHoriOrientRel( pFrame->GetFrameFormat().GetHoriOrient().GetRelationOrient() );
4598 attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
4599
4600 if( !sTblpXSpec.isEmpty() )
4601 attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), sTblpXSpec);
4602
4603 nValue = pFrame->GetFrameFormat().GetULSpace().GetLower();
4604 if( nValue != 0 )
4605 attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) );
4606
4607 nValue = pFrame->GetFrameFormat().GetLRSpace().GetLeft();
4608 if( nValue != 0 )
4609 attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) );
4610
4611 nValue = pFrame->GetFrameFormat().GetLRSpace().GetRight();
4612 if( nValue != 0 )
4613 attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) );
4614
4615 nValue = pFrame->GetFrameFormat().GetULSpace().GetUpper();
4616 if( nValue != 0 )
4617 attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) );
4618
4619 if( sTblpXSpec.isEmpty() ) // do not write tblpX if tblpXSpec is present
4620 {
4621 nValue = pFrame->GetFrameFormat().GetHoriOrient().GetPos();
4622 // we need to revert the additional shift introduced by
4623 // lcl_DecrementHoriOrientPosition() in writerfilter
4624 // 1st: left distance of the table
4625 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4626 const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
4627 const SvxBoxItem& rBox = pFrameFormat->GetBox( );
4628 sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT);
4629 nValue += nLeftDistance;
4630
4631 // 2nd: if a left border is given, revert the shift by half the width
4632 // from lcl_DecrementHoriOrientPosition() in writerfilter
4633 if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft())
4634 {
4635 tools::Long nWidth = pLeftBorder->GetWidth();
4636 nValue += (nWidth / 2);
4637 }
4638
4639 attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) );
4640 }
4641
4642 if( sTblpYSpec.isEmpty() ) // do not write tblpY if tblpYSpec is present
4643 {
4644 nValue = pFrame->GetFrameFormat().GetVertOrient().GetPos();
4645 attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) );
4646 }
4647 }
4648 else // ( pFrame = 0 )
4649 {
4650 // we export the values from the grabBag
4651 for (const auto& rProp : aTablePosition)
4652 {
4653 if (rProp.Name == "vertAnchor" && !rProp.Value.get<OUString>().isEmpty())
4654 {
4655 OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4656 attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
4657 }
4658 else if (rProp.Name == "tblpYSpec" && !rProp.Value.get<OUString>().isEmpty())
4659 {
4660 OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4661 attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), sOrientation);
4662 }
4663 else if (rProp.Name == "horzAnchor" && !rProp.Value.get<OUString>().isEmpty())
4664 {
4665 OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4666 attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
4667 }
4668 else if (rProp.Name == "tblpXSpec" && !rProp.Value.get<OUString>().isEmpty())
4669 {
4670 OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4671 attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), sOrientation);
4672 }
4673 else if (rProp.Name == "bottomFromText")
4674 {
4675 sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4676 attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) );
4677 }
4678 else if (rProp.Name == "leftFromText")
4679 {
4680 sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4681 attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) );
4682 }
4683 else if (rProp.Name == "rightFromText")
4684 {
4685 sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4686 attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) );
4687 }
4688 else if (rProp.Name == "topFromText")
4689 {
4690 sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4691 attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) );
4692 }
4693 else if (rProp.Name == "tblpX")
4694 {
4695 sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4696 attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) );
4697 }
4698 else if (rProp.Name == "tblpY")
4699 {
4700 sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4701 attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) );
4702 }
4703 }
4704 }
4705
4706 m_pSerializer->singleElementNS( XML_w, XML_tblpPr, attrListTablePos);
4707 attrListTablePos = nullptr;
4708 }
4709 else
4710 SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: unhandled property: " << rGrabBagElement.first);
4711 }
4712
4713 // Output the table alignment
4714 const char* pJcVal;
4715 sal_Int32 nIndent = 0;
4716 switch ( pTableFormat->GetHoriOrient( ).GetHoriOrient( ) )
4717 {
4718 case text::HoriOrientation::CENTER:
4719 pJcVal = "center";
4720 break;
4721 case text::HoriOrientation::RIGHT:
4722 if ( bEcma )
4723 pJcVal = "right";
4724 else
4725 pJcVal = "end";
4726 break;
4727 default:
4729 case text::HoriOrientation::LEFT_AND_WIDTH:
4730 {
4731 if ( bEcma )
4732 pJcVal = "left";
4733 else
4734 pJcVal = "start";
4735 nIndent = sal_Int32( pTableFormat->GetLRSpace().GetLeft() );
4736
4737 // Table indentation has different meaning in Word, depending if the table is nested or not.
4738 // If nested, tblInd is added to parent table's left spacing and defines left edge position
4739 // If not nested, text position of left-most cell must be at absolute X = tblInd
4740 // so, table_spacing + table_spacing_to_content = tblInd
4741
4742 // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
4743 // if no compatibilityMode is defined (which now should only happen on a new export to .docx),
4744 // LO uses a higher compatibility than 2010's 14.
4745 sal_Int32 nMode = lcl_getWordCompatibilityMode(m_rExport);
4746
4747 const SwFrameFormat* pFrameFormat = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();
4748 if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth == 0)
4749 nIndent += pFrameFormat->GetBox().GetDistance( SvxBoxItemLine::LEFT );
4750 else
4751 {
4752 // adjust for SW considering table to start mid-border instead of nested/2013's left-side-of-border.
4753 nIndent -= pFrameFormat->GetBox().CalcLineWidth( SvxBoxItemLine::LEFT ) / 2;
4754 }
4755
4756 break;
4757 }
4758 }
4759 m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pJcVal);
4760
4761 // Output the table background color (although cell value still needs to be specified)
4762 const SvxBrushItem *pColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4763 Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
4764 if ( aColor != COL_AUTO )
4765 {
4766 OString sColor = msfilter::util::ConvertColor( aColor );
4767 m_pSerializer->singleElementNS( XML_w, XML_shd,
4768 FSNS( XML_w, XML_fill ), sColor,
4769 FSNS( XML_w, XML_val ), "clear" );
4770 }
4771
4772 // Output the table borders
4773 TableDefaultBorders( pTableTextNodeInfoInner );
4774
4775 // Output the default cell margins
4776 TableDefaultCellMargins( pTableTextNodeInfoInner );
4777
4778 TableBidi( pTableTextNodeInfoInner );
4779
4780 // Table indent (need to get written even if == 0)
4781 m_pSerializer->singleElementNS( XML_w, XML_tblInd,
4782 FSNS( XML_w, XML_w ), OString::number(nIndent),
4783 FSNS( XML_w, XML_type ), "dxa" );
4784
4785 // Merge the marks for the ordered elements
4786 m_pSerializer->mergeTopMarks(Tag_TableDefinition);
4787
4788 m_pSerializer->endElementNS( XML_w, XML_tblPr );
4789
4790 // Write the table grid infos
4791 m_pSerializer->startElementNS(XML_w, XML_tblGrid);
4792 sal_Int32 nPrv = 0;
4793 ww8::WidthsPtr pColumnWidths = GetColumnWidths( pTableTextNodeInfoInner );
4794 for ( auto aColumnWidth : *pColumnWidths )
4795 {
4796 sal_Int32 nWidth = sal_Int32( aColumnWidth ) - nPrv;
4797 m_pSerializer->singleElementNS( XML_w, XML_gridCol,
4798 FSNS( XML_w, XML_w ), OString::number(nWidth) );
4799 nPrv = sal_Int32( aColumnWidth );
4800 }
4801
4802 m_pSerializer->endElementNS( XML_w, XML_tblGrid );
4803}
4804
4806{
4807 // Table defaults should only be created IF m_aTableStyleConf contents haven't come from a table style.
4808 // Previously this function wrote out Cell A1 as the table default, causing problems with no benefit.
4809}
4810
4812{
4813 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4814 const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
4815 const SvxBoxItem& rBox = pFrameFormat->GetBox( );
4816 const bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
4817
4818 impl_cellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma);
4819}
4820
4822{
4823 const SwTable *pTable = pTableTextNodeInfoInner->getTable();
4824 const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
4825 const SwTableLine *pTableRow = pTableBox->GetUpper();
4826 const SwFrameFormat *pFormat = pTableBox->GetFrameFormat( );
4827
4828 const SvxBrushItem *pColorProp = pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4829 Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
4830
4831 const SwFrameFormat *pRowFormat = pTableRow->GetFrameFormat( );
4832 const SvxBrushItem *pRowColorProp = pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4833 if ( pRowColorProp && aColor == COL_AUTO)
4834 aColor = pRowColorProp->GetColor();
4835
4836 const SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
4837 const SvxBrushItem *pTableColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4838 if ( pTableColorProp && aColor == COL_AUTO )
4839 aColor = pTableColorProp->GetColor();
4840
4841 const OString sColor = msfilter::util::ConvertColor( aColor );
4842
4843 std::map<OUString, css::uno::Any> aGrabBag =
4844 pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
4845
4846 OString sOriginalColor;
4847 std::map<OUString, css::uno::Any>::iterator aGrabBagElement = aGrabBag.find("originalColor");
4848 if( aGrabBagElement != aGrabBag.end() )
4849 sOriginalColor = OUStringToOString( aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
4850
4851 if ( sOriginalColor != sColor )
4852 {
4853 // color changed by the user, or no grab bag: write sColor
4854 if ( sColor != "auto" )
4855 {
4856 m_pSerializer->singleElementNS( XML_w, XML_shd,
4857 FSNS( XML_w, XML_fill ), sColor,
4858 FSNS( XML_w, XML_val ), "clear" );
4859 }
4860 }
4861 else
4862 {
4864
4865 for( const auto & rGrabBagElement : aGrabBag )
4866 {
4867 if (!rGrabBagElement.second.has<OUString>())
4868 continue;
4869
4870 OString sValue = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
4871 if( rGrabBagElement.first == "themeFill")
4872 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFill ), sValue.getStr() );
4873 else if( rGrabBagElement.first == "themeFillTint")
4874 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillTint ), sValue.getStr() );
4875 else if( rGrabBagElement.first == "themeFillShade")
4876 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillShade ), sValue.getStr() );
4877 else if( rGrabBagElement.first == "fill" )
4878 AddToAttrList( pAttrList, FSNS( XML_w, XML_fill ), sValue.getStr() );
4879 else if( rGrabBagElement.first == "themeColor")
4880 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeColor ), sValue.getStr() );
4881 else if( rGrabBagElement.first == "themeTint")
4882 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeTint ), sValue.getStr() );
4883 else if( rGrabBagElement.first == "themeShade")
4884 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeShade ), sValue.getStr() );
4885 else if( rGrabBagElement.first == "color")
4886 AddToAttrList( pAttrList, FSNS( XML_w, XML_color ), sValue.getStr() );
4887 else if( rGrabBagElement.first == "val")
4888 AddToAttrList( pAttrList, FSNS( XML_w, XML_val ), sValue.getStr() );
4889 }
4890 m_pSerializer->singleElementNS( XML_w, XML_shd, pAttrList.get() );
4891 }
4892}
4893
4895{
4896 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4897 const SwTableLine * pTabLine = pTabBox->GetUpper();
4898
4899 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
4901
4902 // check table row property "HasTextChangesOnly"
4905 if ( nChange != SwRedlineTable::npos )
4906 {
4908 const SwRangeRedline* pRedline = aRedlineTable[ nChange ];
4909 SwTableRowRedline* pTableRowRedline = nullptr;
4910 bool bIsInExtra = false;
4911
4912 // use the original DOCX redline data stored in ExtraRedlineTable,
4913 // if it exists and its type wasn't changed
4915 for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
4916 {
4917 SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
4918 pTableRowRedline = dynamic_cast<SwTableRowRedline*>(pExtraRedline);
4919 if (pTableRowRedline && &pTableRowRedline->GetTableLine() == pTabLine)
4920 {
4921 bIsInExtra = true;
4922 break;
4923 }
4924 }
4925
4926 const SwRedlineData& aRedlineData = bIsInExtra &&
4927 // still the same type (an inserted row could become a tracked deleted one)
4928 pTableRowRedline->GetRedlineData().GetType() == pRedline->GetRedlineData().GetType()
4929 ? pTableRowRedline->GetRedlineData()
4930 : pRedline->GetRedlineData();
4931
4932 // Note: all redline ranges and table row redline (with the same author and timestamp)
4933 // use the same redline id in OOXML exported by MSO, but it seems, the recent solution
4934 // (different IDs for different ranges, also row changes) is also portable.
4935 OString aId( OString::number( m_nRedlineId++ ) );
4936 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
4937 OString aAuthor( OUStringToOString( bRemovePersonalInfo
4938 ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
4939 : rAuthor, RTL_TEXTENCODING_UTF8 ) );
4940
4941 const DateTime aDateTime = aRedlineData.GetTimeStamp();
4942 bool bNoDate = bRemovePersonalInfo ||
4943 ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
4944
4945 if ( bNoDate )
4946 m_pSerializer->singleElementNS( XML_w,
4947 RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins,
4948 FSNS( XML_w, XML_id ), aId,
4949 FSNS( XML_w, XML_author ), aAuthor );
4950 else
4951 m_pSerializer->singleElementNS( XML_w,
4952 RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins,
4953 FSNS( XML_w, XML_id ), aId,
4954 FSNS( XML_w, XML_author ), aAuthor,
4955 FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) );
4956 return;
4957 }
4958}
4959
4961{
4962 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4963
4964 bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
4966
4967 // search next Redline
4969 for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
4970 {
4971 SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
4972 const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
4973 if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox)
4974 {
4975 // Redline for this table cell
4976 const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
4977 RedlineType nRedlineType = aRedlineData.GetType();
4978 switch (nRedlineType)
4979 {
4980 case RedlineType::TableCellInsert:
4981 case RedlineType::TableCellDelete:
4982 {
4983 OString aId( OString::number( m_nRedlineId++ ) );
4984 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
4985 OString aAuthor( OUStringToOString( bRemovePersonalInfo
4986 ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
4987 : rAuthor, RTL_TEXTENCODING_UTF8 ) );
4988
4989 sal_Int32 nElement = nRedlineType == RedlineType::TableCellInsert
4990 ? XML_cellIns
4991 : XML_cellDel;
4992 const DateTime aDateTime = aRedlineData.GetTimeStamp();
4993 bool bNoDate = bRemovePersonalInfo || ( aDateTime.GetYear() == 1970 &&
4994 aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
4995 if ( bNoDate )
4996 m_pSerializer->singleElementNS( XML_w, nElement,
4997 FSNS( XML_w, XML_id ), aId,
4998 FSNS( XML_w, XML_author ), aAuthor );
4999 else
5000 m_pSerializer->singleElementNS( XML_w, nElement,
5001 FSNS( XML_w, XML_id ), aId,
5002 FSNS( XML_w, XML_author ), aAuthor,
5003 FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) );
5004 }
5005 break;
5006 default: break;
5007 }
5008 }
5009 }
5010}
5011
5013{
5014 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
5015 const SwTableLine * pTabLine = pTabBox->GetUpper();
5016 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
5017
5018 const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
5019 if ( !(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()) )
5020 return;
5021
5022 sal_Int32 nHeight = rLSz.GetHeight();
5023 const char *pRule = nullptr;
5024
5025 switch ( rLSz.GetHeightSizeType() )
5026 {
5027 case SwFrameSize::Fixed: pRule = "exact"; break;
5028 case SwFrameSize::Minimum: pRule = "atLeast"; break;
5029 default: break;
5030 }
5031
5032 if ( pRule )
5033 m_pSerializer->singleElementNS( XML_w, XML_trHeight,
5034 FSNS( XML_w, XML_val ), OString::number(nHeight),
5035 FSNS( XML_w, XML_hRule ), pRule );
5036}
5037
5039{
5040 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
5041 const SwTableLine * pTabLine = pTabBox->GetUpper();
5042 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
5043
5044 const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit( );
5045 // if rSplittable is true then no need to write <w:cantSplit w:val="false"/>
5046 // as default row prop is allow row to break across page.
5047 if( !rSplittable.GetValue( ) )
5048 m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w, XML_val), "true");
5049}
5050
5052{
5053 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
5054 const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
5055
5056 if ( m_rExport.TrueFrameDirection( *pFrameFormat ) == SvxFrameDirection::Horizontal_RL_TB )
5057 {
5058 m_pSerializer->singleElementNS(XML_w, XML_bidiVisual, FSNS(XML_w, XML_val), "true");
5059 }
5060}
5061
5063{
5064 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
5065 const SwFrameFormat *pFrameFormat = pTabBox->GetFrameFormat( );
5066
5067 if ( SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection( *pFrameFormat ) )
5068 m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "tbRl");
5069 else if ( SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection( *pFrameFormat ) )
5070 {
5071 m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "btLr");
5072 }
5073
5074 const