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