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