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