/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <algorithm>
#include <memory>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <hintids.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/fontitem.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdotext.hxx>
#include <svx/svdouno.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <rtl/character.hxx>
#include <unotools/securityoptions.hxx>
#include <doc.hxx>
#include "wrtww8.hxx"
#include <docary.hxx>
#include <poolfmt.hxx>
#include <fmtpdsc.hxx>
#include <pagedesc.hxx>
#include <ndtxt.hxx>
#include <ftninfo.hxx>
#include <fmthdft.hxx>
#include <section.hxx>
#include <fmtcntnt.hxx>
#include <fmtftn.hxx>
#include <ndindex.hxx>
#include <txtftn.hxx>
#include <charfmt.hxx>
#include <docufld.hxx>
#include <dcontact.hxx>
#include <fmtcnct.hxx>
#include <ftnidx.hxx>
#include <fmtclds.hxx>
#include <lineinfo.hxx>
#include <fmtline.hxx>
#include <swtable.hxx>
#include <redline.hxx>
#include <msfilter.hxx>
#include <swmodule.hxx>
#include <charatr.hxx>
#include "sprmids.hxx"
#include "writerhelper.hxx"
#include "writerwordglue.hxx"
#include <wwstyles.hxx>
#include "ww8par.hxx"
#include "ww8attributeoutput.hxx"
#include "docxattributeoutput.hxx"
#include "rtfattributeoutput.hxx"
#include <unordered_set>
using namespace css;
using namespace sw::util;
using namespace nsHdFtFlags;
/// For the output of sections.
struct WW8_PdAttrDesc
{
std::unique_ptr<sal_uInt8[]> m_pData;
sal_uInt16 m_nLen;
WW8_FC m_nSepxFcPos;
WW8_PdAttrDesc() : m_nLen(0), m_nSepxFcPos(0xffffffff) /*default: none*/
{ }
};
namespace {
struct WW8_SED
{
SVBT16 aBits1; // orientation change + internal, Default: 6
SVBT32 fcSepx; // FC file offset to beginning of SEPX for section.
// 0xFFFFFFFF for no Sprms
SVBT16 fnMpr; // used internally by Windows Word, Default: 0
SVBT32 fcMpr; // FC, points to offset in FC space for MacWord
// Default: 0xffffffff ( nothing )
// cbSED is 12 (decimal)), C (hex).
};
}
// class WW8_WrPlc0 is only used for header and footer positioning
// ie there is no content support structure
class WW8_WrPlc0
{
private:
std::vector<sal_uLong> m_aPos; // PTRARR of CPs / FCs
sal_uLong m_nOfs;
WW8_WrPlc0(WW8_WrPlc0 const&) = delete;
WW8_WrPlc0& operator=(WW8_WrPlc0 const&) = delete;
public:
explicit WW8_WrPlc0( sal_uLong nOffset );
sal_uInt16 Count() const { return m_aPos.size(); }
void Append( sal_uLong nStartCpOrFc );
void Write( SvStream& rStrm );
};
// Styles
// According to [MS-DOC] v20221115 2.9.271 STSH,
// "The beginning of the rglpstd array is reserved for specific "fixed-index" application-defined
// styles. A particular fixed-index, application-defined style has the same istd value in every
// stylesheet. The rglpstd MUST contain an LPStd for each of these fixed-index styles and the order
// MUST match the order in the following table.
//
// istd sti of application-defined style (see sti in StdfBase)
// 0 0
// 1 1
// 2 2
// 3 3
// 4 4
// 5 5
// 6 6
// 7 7
// 8 8
// 9 9
// 10 65
// 11 105
// 12 107
// 13 Reserved for future use
// 14 Reserved for future use"
//
// And [MS-OE376] v20220816 2.1.236 Part 4 Section 2.7.3.9, name (Primary Style Name)
// specifies the following mapping:
//
// sti Style name Style type
// 0 Normal paragraph
// 1 heading 1 paragraph
// 2 heading 2 paragraph
// 3 heading 3 paragraph
// 4 heading 4 paragraph
// 5 heading 5 paragraph
// 6 heading 6 paragraph
// 7 heading 7 paragraph
// 8 heading 8 paragraph
// 9 heading 9 paragraph
// 65 Default Paragraph Font character
// 105 Normal Table table
// 107 No List numbering
#define WW8_RESERVED_SLOTS 15
// GetId( SwCharFormat ) for use in text -> zero is not allowed,
// use "Default Char Style" instead
sal_uInt16 MSWordExportBase::GetId( const SwCharFormat* pFormat ) const
{
sal_uInt16 nRet = m_pStyles->GetSlot( pFormat );
return ( nRet != 0x0fff ) ? nRet : 10; // Default Char Style
}
// GetId( SwTextFormatColl ) for use in TextNodes -> zero is not allowed,
// "Standard" instead
sal_uInt16 MSWordExportBase::GetId( const SwTextFormatColl& rColl ) const
{
sal_uInt16 nRet = m_pStyles->GetSlot( &rColl );
return ( nRet != 0xfff ) ? nRet : 0; // Default TextFormatColl
}
//typedef pFormatT
MSWordStyles::MSWordStyles( MSWordExportBase& rExport, bool bListStyles )
: m_rExport( rExport ),
m_bListStyles(bListStyles)
{
// if exist any Foot-/End-Notes then get from the EndNoteInfo struct
// the CharFormats. They will create it!
if ( !m_rExport.m_rDoc.GetFootnoteIdxs().empty() )
{
m_rExport.m_rDoc.GetEndNoteInfo().GetAnchorCharFormat( m_rExport.m_rDoc );
m_rExport.m_rDoc.GetEndNoteInfo().GetCharFormat( m_rExport.m_rDoc );
m_rExport.m_rDoc.GetFootnoteInfo().GetAnchorCharFormat( m_rExport.m_rDoc );
m_rExport.m_rDoc.GetFootnoteInfo().GetCharFormat( m_rExport.m_rDoc );
}
memset( m_aHeadingParagraphStyles, -1 , MAXLEVEL * sizeof( sal_uInt16));
BuildStylesTable();
BuildWwNames();
BuildStyleIds();
}
MSWordStyles::~MSWordStyles()
{
}
// Sty_SetWWSlot() dependencies for the styles -> zero is allowed
sal_uInt16 MSWordStyles::GetSlot( const SwFormat* pFormat ) const
{
for (size_t slot = 0; slot < m_aStyles.size(); ++slot)
if (m_aStyles[slot].format == pFormat)
return slot;
return 0xfff; // 0xfff: WW: zero
}
/// Get reserved slot number during building the style table.
static sal_uInt16 BuildGetSlot(const SwFormat& rFormat)
{
switch (sal_uInt16 nId = rFormat.GetPoolFormatId())
{
case RES_POOLCOLL_STANDARD:
return 0;
case RES_POOLCOLL_HEADLINE1:
case RES_POOLCOLL_HEADLINE2:
case RES_POOLCOLL_HEADLINE3:
case RES_POOLCOLL_HEADLINE4:
case RES_POOLCOLL_HEADLINE5:
case RES_POOLCOLL_HEADLINE6:
case RES_POOLCOLL_HEADLINE7:
case RES_POOLCOLL_HEADLINE8:
case RES_POOLCOLL_HEADLINE9:
{
sal_uInt16 nRet = nId - (RES_POOLCOLL_HEADLINE1 - 1);
assert(nRet < WW8_RESERVED_SLOTS);
return nRet;
}
}
return 0xfff;
}
// Keep in sync with StyleSheetTable::ConvertStyleName
sal_uInt16 MSWordStyles::GetWWId( const SwFormat& rFormat )
{
sal_uInt16 nRet = ww::stiUser; // user style as default
sal_uInt16 nPoolId = rFormat.GetPoolFormatId();
if( nPoolId == RES_POOLCOLL_STANDARD )
nRet = ww::stiNormal;
else if( nPoolId >= RES_POOLCOLL_HEADLINE1 &&
nPoolId <= RES_POOLCOLL_HEADLINE9 )
nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiLevFirst - RES_POOLCOLL_HEADLINE1);
else if( nPoolId >= RES_POOLCOLL_TOX_IDX1 &&
nPoolId <= RES_POOLCOLL_TOX_IDX3 )
nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiIndexFirst - RES_POOLCOLL_TOX_IDX1);
else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT1 &&
nPoolId <= RES_POOLCOLL_TOX_CNTNT5 )
nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiToc1 - RES_POOLCOLL_TOX_CNTNT1);
else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT6 &&
nPoolId <= RES_POOLCOLL_TOX_CNTNT9 )
nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiToc6 - RES_POOLCOLL_TOX_CNTNT6);
else
switch( nPoolId )
{
case RES_POOLCOLL_FOOTNOTE: nRet = ww::stiFootnoteText; break;
case RES_POOLCOLL_MARGINAL: nRet = ww::stiAtnText; break;
case RES_POOLCOLL_HEADER: nRet = ww::stiHeader; break;
case RES_POOLCOLL_FOOTER: nRet = ww::stiFooter; break;
case RES_POOLCOLL_TOX_IDXH: nRet = ww::stiIndexHeading; break;
case RES_POOLCOLL_LABEL: nRet = ww::stiCaption; break;
case RES_POOLCOLL_TOX_ILLUS1: nRet = ww::stiToCaption; break;
case RES_POOLCOLL_ENVELOPE_ADDRESS: nRet = ww::stiEnvAddr; break;
case RES_POOLCOLL_SEND_ADDRESS: nRet = ww::stiEnvRet; break;
case RES_POOLCHR_FOOTNOTE_ANCHOR: nRet = ww::stiFootnoteRef; break;
case RES_POOLCHR_LINENUM: nRet = ww::stiLnn; break;
case RES_POOLCHR_PAGENO: nRet = ww::stiPgn; break;
case RES_POOLCHR_ENDNOTE_ANCHOR: nRet = ww::stiEdnRef; break;
case RES_POOLCOLL_ENDNOTE: nRet = ww::stiEdnText; break;
case RES_POOLCOLL_TOX_AUTHORITIESH: nRet = ww::stiToa; break;
case RES_POOLCOLL_TOX_CNTNTH: nRet = ww::stiToaHeading; break;
case RES_POOLCOLL_LISTS_BEGIN: nRet = ww::stiList; break;
case RES_POOLCOLL_BULLET_LEVEL1: nRet = ww::stiListBullet; break;
case RES_POOLCOLL_NUM_LEVEL1: nRet = ww::stiListNumber; break;
case RES_POOLCOLL_BULLET_LEVEL2: nRet = ww::stiListBullet2; break;
case RES_POOLCOLL_BULLET_LEVEL3: nRet = ww::stiListBullet3; break;
case RES_POOLCOLL_BULLET_LEVEL4: nRet = ww::stiListBullet4; break;
case RES_POOLCOLL_BULLET_LEVEL5: nRet = ww::stiListBullet5; break;
case RES_POOLCOLL_NUM_LEVEL2: nRet = ww::stiListNumber2; break;
case RES_POOLCOLL_NUM_LEVEL3: nRet = ww::stiListNumber3; break;
case RES_POOLCOLL_NUM_LEVEL4: nRet = ww::stiListNumber4; break;
case RES_POOLCOLL_NUM_LEVEL5: nRet = ww::stiListNumber5; break;
case RES_POOLCOLL_DOC_TITLE: nRet = ww::stiTitle; break;
case RES_POOLCOLL_DOC_APPENDIX: nRet = ww::stiClosing; break;
case RES_POOLCOLL_SIGNATURE: nRet = ww::stiSignature; break;
case RES_POOLCOLL_TEXT: nRet = ww::stiBodyText; break;
case RES_POOLCOLL_TEXT_MOVE: nRet = ww::stiBodyTextInd1; break;
case RES_POOLCOLL_BULLET_NONUM1: nRet = ww::stiListCont; break;
case RES_POOLCOLL_BULLET_NONUM2: nRet = ww::stiListCont2; break;
case RES_POOLCOLL_BULLET_NONUM3: nRet = ww::stiListCont3; break;
case RES_POOLCOLL_BULLET_NONUM4: nRet = ww::stiListCont4; break;
case RES_POOLCOLL_BULLET_NONUM5: nRet = ww::stiListCont5; break;
case RES_POOLCOLL_DOC_SUBTITLE: nRet = ww::stiSubtitle; break;
case RES_POOLCOLL_GREETING: nRet = ww::stiSalutation; break;
case RES_POOLCOLL_TEXT_IDENT: nRet = ww::stiBodyText1I; break;
case RES_POOLCHR_INET_NORMAL: nRet = ww::stiHyperlink; break;
case RES_POOLCHR_INET_VISIT: nRet = ww::stiHyperlinkFollowed; break;
case RES_POOLCHR_HTML_STRONG: nRet = ww::stiStrong; break;
case RES_POOLCHR_HTML_EMPHASIS: nRet = ww::stiEmphasis; break;
}
return nRet;
}
void MSWordStyles::BuildStylesTable()
{
assert(m_aStyles.empty());
// Put reserved slots first, then character styles, then paragraph styles
m_aStyles.resize(WW8_RESERVED_SLOTS);
const SwCharFormats& rArr = *m_rExport.m_rDoc.GetCharFormats(); // first CharFormat
// the default character style ( 0 ) will not be outputted !
for (size_t n = 1; n < rArr.size() && m_aStyles.size() < MSWORD_MAX_STYLES_LIMIT; ++n)
{
if (m_rExport.GetExportFormat() == MSWordExportBase::DOCX
&& rArr[n]->GetName().startsWith("ListLabel"))
{
// tdf#92335 don't export redundant DOCX import style "ListLabel"
continue;
}
m_aStyles.emplace_back(rArr[n]);
}
if (m_aStyles.size() == MSWORD_MAX_STYLES_LIMIT)
{
SAL_WARN("sw.ww8", "MSWordStyles::BuildStylesTable: too many styles, have "
<< rArr.size() << " char styles");
}
const SwTextFormatColls& rArr2 = *m_rExport.m_rDoc.GetTextFormatColls(); // then TextFormatColls
// the default paragraph style ( 0 ) will not be outputted !
for (size_t n = 1; n < rArr2.size(); ++n)
{
SwTextFormatColl* pFormat = rArr2[n];
sal_uInt16 nSlot = BuildGetSlot(*pFormat);
if (nSlot != 0xfff)
{
m_aStyles[nSlot] = { pFormat };
}
else
{
if (m_aStyles.size() >= MSWORD_MAX_STYLES_LIMIT)
continue;
m_aStyles.emplace_back(pFormat);
nSlot = m_aStyles.size() - 1;
}
if ( pFormat->IsAssignedToListLevelOfOutlineStyle() )
{
int nLvl = pFormat->GetAssignedOutlineStyleLevel() ;
if (nLvl >= 0 && nLvl < MAXLEVEL)
m_aHeadingParagraphStyles[nLvl] = nSlot;
}
}
if (!m_bListStyles)
return;
const SwNumRuleTable& rNumRuleTable = m_rExport.m_rDoc.GetNumRuleTable();
for (size_t i = 0; i < rNumRuleTable.size() && m_aStyles.size() < MSWORD_MAX_STYLES_LIMIT; ++i)
{
const SwNumRule* pNumRule = rNumRuleTable[i];
if (pNumRule->IsAutoRule() || pNumRule->GetName().startsWith("WWNum"))
continue;
m_aStyles.emplace_back(pNumRule);
}
}
// StyleSheetTable::ConvertStyleName appends the suffix do disambiguate conflicting style names
static OUString StripWWSuffix(const OUString& s)
{
OUString ret = s;
(void)ret.endsWith(" (WW)", &ret);
return ret;
}
void MSWordStyles::BuildWwNames()
{
std::unordered_set<OUString> aUsed;
auto makeUniqueName = [&aUsed](OUString& name) {
// toAsciiLowerCase rules out e.g. user's "normal"; no problem if there are non-ASCII chars
OUString lower(name.toAsciiLowerCase());
if (!aUsed.insert(lower).second)
{
int nFree = 1;
while (!aUsed.insert(lower + OUString::number(nFree)).second)
++nFree;
name += OUString::number(nFree);
}
};
// We want to map LO's default style to Word's "Normal" style.
// Word looks for this specific style name when reading docx files.
// (It must be the English word regardless of languages and locales)
assert(!m_aStyles.empty());
assert(!m_aStyles[0].format || m_aStyles[0].ww_id == ww::stiNormal);
m_aStyles[0].ww_name = "Normal";
aUsed.insert(u"normal"_ustr);
// 1. Handle styles having special wwIds, and thus pre-defined names
for (auto& entry : m_aStyles)
{
if (!entry.ww_name.isEmpty())
continue; // "Normal" is already added
if (entry.ww_id >= ww::stiMax)
continue; // Not a format with special name
assert(entry.format);
entry.ww_name = OUString::createFromAscii(ww::GetEnglishNameFromSti(static_cast<ww::sti>(entry.ww_id)));
makeUniqueName(entry.ww_name);
}
// 2. Now handle other styles
for (auto& entry : m_aStyles)
{
if (!entry.ww_name.isEmpty())
continue;
if (entry.format)
entry.ww_name = StripWWSuffix(entry.format->GetName());
else if (entry.num_rule)
entry.ww_name = StripWWSuffix(entry.num_rule->GetName());
else
continue;
makeUniqueName(entry.ww_name);
}
}
OString MSWordStyles::CreateStyleId(std::u16string_view aName)
{
return OUStringToOString(msfilter::util::CreateDOCXStyleId(aName), RTL_TEXTENCODING_UTF8);
}
void MSWordStyles::BuildStyleIds()
{
std::unordered_set<OString> aUsed;
for (auto& entry : m_aStyles)
{
OString aStyleId = CreateStyleId(entry.ww_name);
if (aStyleId.isEmpty())
aStyleId = "Style"_ostr;
OString aLower(aStyleId.toAsciiLowerCase());
// check for uniqueness & construct something unique if we have to
if (!aUsed.insert(aLower).second)
{
int nFree = 1;
while (!aUsed.insert(aLower + OString::number(nFree)).second)
++nFree;
aStyleId += OString::number(nFree);
}
entry.style_id = aStyleId;
}
}
OString const & MSWordStyles::GetStyleId(sal_uInt16 nSlot) const
{
assert(!m_aStyles[nSlot].style_id.isEmpty());
return m_aStyles[nSlot].style_id;
}
const OUString & MSWordStyles::GetStyleWWName(SwFormat const*const pFormat) const
{
if (auto slot = m_rExport.m_pStyles->GetSlot(pFormat); slot != 0xfff)
{
assert(!m_aStyles[slot].ww_name.isEmpty());
return m_aStyles[slot].ww_name;
}
return EMPTY_OUSTRING;
}
/// For WW8 only - extend pO so that the size of pTableStrm is even.
static void impl_SkipOdd(std::unique_ptr<ww::bytes> const& pO, std::size_t nTableStrmTell)
{
if ( ( nTableStrmTell + pO->size() ) & 1 ) // start on even
pO->push_back( sal_uInt8(0) ); // Address
}
void WW8AttributeOutput::EndStyle()
{
impl_SkipOdd( m_rWW8Export.m_pO, m_rWW8Export.m_pTableStrm->Tell() );
short nLen = m_rWW8Export.m_pO->size() - 2; // length of the style
sal_uInt8* p = m_rWW8Export.m_pO->data() + m_nPOPosStdLen1;
ShortToSVBT16( nLen, p ); // add
p = m_rWW8Export.m_pO->data() + m_nPOPosStdLen2;
ShortToSVBT16( nLen, p ); // also
m_rWW8Export.m_pTableStrm->WriteBytes(m_rWW8Export.m_pO->data(), m_rWW8Export.m_pO->size());
m_rWW8Export.m_pO->clear();
}
void WW8AttributeOutput::StartStyle( const OUString& rName, StyleType eType, sal_uInt16 nWwBase,
sal_uInt16 nWwNext, sal_uInt16 /*nWwLink*/, sal_uInt16 nWwId, sal_uInt16 /*nSlot*/, bool bAutoUpdate )
{
sal_uInt8 aWW8_STD[ sizeof( WW8_STD ) ] = {};
sal_uInt8* pData = aWW8_STD;
sal_uInt16 nBit16 = 0x1000; // fInvalHeight
nBit16 |= (ww::stiNil & nWwId);
Set_UInt16( pData, nBit16 );
nBit16 = nWwBase << 4; // istdBase
nBit16 |= (eType == STYLE_TYPE_PARA ? 1 : 2); // sgc
Set_UInt16( pData, nBit16 );
nBit16 = nWwNext << 4; // istdNext
nBit16 |= (eType == STYLE_TYPE_PARA ? 2 : 1); // cupx
Set_UInt16( pData, nBit16 );
pData += sizeof( sal_uInt16 ); // bchUpe
nBit16 = bAutoUpdate ? 1 : 0; // fAutoRedef : 1
Set_UInt16( pData, nBit16 );
// now new:
// from Ver8 there are two fields more:
// sal_uInt16 fHidden : 1; /* hidden from UI?
// sal_uInt16 : 14; /* unused bits
sal_uInt16 nLen = static_cast< sal_uInt16 >( ( pData - aWW8_STD ) + 1 +
(2 * (rName.getLength() + 1)) ); // temporary
m_nPOPosStdLen1 = m_rWW8Export.m_pO->size(); // Adr1 for adding the length
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nLen );
m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aWW8_STD, pData );
m_nPOPosStdLen2 = m_nPOPosStdLen1 + 8; // Adr2 for adding of "end of upx"
// write names
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, rName.getLength() ); // length
SwWW8Writer::InsAsString16( *m_rWW8Export.m_pO, rName );
m_rWW8Export.m_pO->push_back( sal_uInt8(0) ); // Despite P-String 0 at the end!
}
void MSWordStyles::SetStyleDefaults( const SwFormat& rFormat, bool bPap )
{
const sw::BroadcastingModify* pOldMod = m_rExport.m_pOutFormatNode;
m_rExport.m_pOutFormatNode = &rFormat;
bool aFlags[ RES_FRMATR_END - RES_CHRATR_BEGIN ];
sal_uInt16 nStt, nEnd, n;
if( bPap )
{
nStt = RES_PARATR_BEGIN;
nEnd = RES_FRMATR_END;
}
else
{
nStt = RES_CHRATR_BEGIN;
nEnd = RES_TXTATR_END;
}
// dynamic defaults
const SfxItemPool& rPool = *rFormat.GetAttrSet().GetPool();
for( n = nStt; n < nEnd; ++n )
aFlags[ n - RES_CHRATR_BEGIN ] = nullptr != rPool.GetUserDefaultItem( n )
|| SfxItemState::SET == m_rExport.m_rDoc.GetDfltTextFormatColl()->GetItemState( n, false );
// static defaults, that differs between WinWord and SO
if( bPap )
{
aFlags[ static_cast< sal_uInt16 >(RES_PARATR_WIDOWS) - RES_CHRATR_BEGIN ] = true;
aFlags[ static_cast< sal_uInt16 >(RES_PARATR_HYPHENZONE) - RES_CHRATR_BEGIN ] = true;
aFlags[ static_cast< sal_uInt16 >(RES_FRAMEDIR) - RES_CHRATR_BEGIN ] = true;
}
else
{
aFlags[ RES_CHRATR_FONTSIZE - RES_CHRATR_BEGIN ] = true;
aFlags[ RES_CHRATR_LANGUAGE - RES_CHRATR_BEGIN ] = true;
}
const SfxItemSet* pOldI = m_rExport.GetCurItemSet();
m_rExport.SetCurItemSet( &rFormat.GetAttrSet() );
const bool* pFlags = aFlags + ( nStt - RES_CHRATR_BEGIN );
for ( n = nStt; n < nEnd; ++n, ++pFlags )
{
if ( *pFlags && !m_rExport.ignoreAttributeForStyleDefaults( n )
&& SfxItemState::SET != rFormat.GetItemState(n, false))
{
//If we are a character property then see if it is one of the
//western/asian ones that must be collapsed together for export to
//word. If so default to the western variant.
if ( bPap || m_rExport.CollapseScriptsforWordOk(
i18n::ScriptType::LATIN, n) )
{
m_rExport.AttrOutput().OutputItem( rFormat.GetFormatAttr( n ) );
}
}
}
m_rExport.SetCurItemSet( pOldI );
m_rExport.m_pOutFormatNode = pOldMod;
}
void WW8AttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 nStyle )
{
impl_SkipOdd( m_rWW8Export.m_pO, m_rWW8Export.m_pTableStrm->Tell() );
sal_uInt16 nLen = bParProp ? 2 : 0; // default length
m_nStyleLenPos = m_rWW8Export.m_pO->size(); // adding length
// Don't save pointer, because it
// changes by _grow!
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nLen ); // Style-Len
m_nStyleStartSize = m_rWW8Export.m_pO->size();
if ( bParProp )
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nStyle ); // Style-Number
}
void MSWordStyles::WriteProperties( const SwFormat* pFormat, bool bParProp, sal_uInt16 nPos,
bool bInsDefCharSiz )
{
m_rExport.AttrOutput().StartStyleProperties( bParProp, nPos );
OSL_ENSURE( m_rExport.m_pCurrentStyle == nullptr, "Current style not NULL" ); // set current style before calling out
m_rExport.m_pCurrentStyle = pFormat;
m_rExport.OutputFormat( *pFormat, bParProp, !bParProp );
OSL_ENSURE( m_rExport.m_pCurrentStyle == pFormat, "current style was changed" );
// reset current style...
m_rExport.m_pCurrentStyle = nullptr;
if ( bInsDefCharSiz ) // not derived from other Style
SetStyleDefaults( *pFormat, bParProp );
m_rExport.AttrOutput().EndStyleProperties( bParProp );
}
void WW8AttributeOutput::EndStyleProperties( bool /*bParProp*/ )
{
sal_uInt16 nLen = m_rWW8Export.m_pO->size() - m_nStyleStartSize;
sal_uInt8* pUpxLen = m_rWW8Export.m_pO->data() + m_nStyleLenPos; // adding length
ShortToSVBT16( nLen, pUpxLen ); // add default length
}
void MSWordStyles::GetStyleData( const SwFormat* pFormat, bool& bFormatColl, sal_uInt16& nBase, sal_uInt16& nNext, sal_uInt16& nLink )
{
bFormatColl = pFormat->Which() == RES_TXTFMTCOLL || pFormat->Which() == RES_CONDTXTFMTCOLL;
// Default: none
nBase = 0xfff;
// Derived from?
if ( !pFormat->IsDefault() )
nBase = GetSlot( pFormat->DerivedFrom() );
const SwFormat* pNext;
const SwFormat* pLink = nullptr;
if ( bFormatColl )
{
auto pFormatColl = static_cast<const SwTextFormatColl*>(pFormat);
pNext = &pFormatColl->GetNextTextFormatColl();
pLink = pFormatColl->GetLinkedCharFormat();
}
else
{
pNext = pFormat; // CharFormat: next CharFormat == self
auto pCharFormat = static_cast<const SwCharFormat*>(pFormat);
pLink = pCharFormat->GetLinkedParaFormat();
}
nNext = GetSlot( pNext );
if (pLink)
{
nLink = GetSlot(pLink);
}
}
void WW8AttributeOutput::DefaultStyle()
{
m_rWW8Export.m_pTableStrm->WriteUInt16(0); // empty Style
}
void MSWordStyles::OutputStyle(sal_uInt16 nSlot)
{
const auto& entry = m_aStyles[nSlot];
if (entry.num_rule)
{
m_rExport.AttrOutput().StartStyle( entry.ww_name, STYLE_TYPE_LIST,
/*nBase =*/ 0, /*nWwNext =*/ 0, /*nWwLink =*/ 0, /*nWWId =*/ 0, nSlot,
/*bAutoUpdateFormat =*/ false );
m_rExport.AttrOutput().EndStyle();
}
else if (!entry.format)
{
m_rExport.AttrOutput().DefaultStyle();
}
else
{
bool bFormatColl;
sal_uInt16 nBase, nWwNext;
sal_uInt16 nWwLink = 0x0FFF;
GetStyleData(entry.format, bFormatColl, nBase, nWwNext, nWwLink);
m_rExport.AttrOutput().StartStyle(entry.ww_name, (bFormatColl ? STYLE_TYPE_PARA : STYLE_TYPE_CHAR),
nBase, nWwNext, nWwLink, m_aStyles[nSlot].ww_id, nSlot,
entry.format->IsAutoUpdateOnDirectFormat() );
if ( bFormatColl )
WriteProperties( entry.format, true, nSlot, nBase==0xfff ); // UPX.papx
WriteProperties( entry.format, false, nSlot, bFormatColl && nBase==0xfff ); // UPX.chpx
m_rExport.AttrOutput().EndStyle();
}
}
void WW8AttributeOutput::StartStyles()
{
WW8Fib& rFib = *m_rWW8Export.m_pFib;
sal_uInt64 nCurPos = m_rWW8Export.m_pTableStrm->Tell();
if ( nCurPos & 1 ) // start on even
{
m_rWW8Export.m_pTableStrm->WriteChar( char(0) ); // Address
++nCurPos;
}
rFib.m_fcStshfOrig = rFib.m_fcStshf = nCurPos;
m_nStyleCountPos = nCurPos + 2; // count is added later
static sal_uInt8 aStShi[] = {
0x12, 0x00,
0x0F, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x5B, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00 };
m_rWW8Export.m_pTableStrm->WriteBytes(&aStShi, sizeof(aStShi));
}
void WW8AttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles )
{
WW8Fib& rFib = *m_rWW8Export.m_pFib;
rFib.m_lcbStshfOrig = rFib.m_lcbStshf = m_rWW8Export.m_pTableStrm->Tell() - rFib.m_fcStshf;
SwWW8Writer::WriteShort( *m_rWW8Export.m_pTableStrm, m_nStyleCountPos, nNumberOfStyles );
}
void MSWordStyles::OutputStylesTable()
{
m_rExport.m_bStyDef = true;
m_rExport.AttrOutput().StartStyles();
// HACK
// Ms Office seems to have an internal limitation of 4091 styles
// and refuses to load .docx with more, even though the spec seems to allow that;
// so simply if there are more styles, don't export those
// Implementing check for all exports DOCX, DOC, RTF
assert(m_aStyles.size() <= MSWORD_MAX_STYLES_LIMIT);
for (size_t slot = 0; slot < m_aStyles.size(); ++slot)
OutputStyle(slot);
m_rExport.AttrOutput().EndStyles(m_aStyles.size());
m_rExport.m_bStyDef = false;
}
// Fonts
wwFont::wwFont(std::u16string_view rFamilyName, FontPitch ePitch, FontFamily eFamily,
rtl_TextEncoding eChrSet)
: mbAlt(false), mePitch(ePitch), meFamily(eFamily), meChrSet(eChrSet)
{
FontMapExport aResult(rFamilyName);
msFamilyNm = aResult.msPrimary;
msAltNm = aResult.msSecondary;
if (!msAltNm.isEmpty() && msAltNm != msFamilyNm &&
(msFamilyNm.getLength() + msAltNm.getLength() + 2 <= 65) )
{
//max size of szFfn in 65 chars
mbAlt = true;
}
maWW8_FFN[0] = static_cast<sal_uInt8>( 6 - 1 + 0x22 + ( 2 * ( 1 + msFamilyNm.getLength() ) ));
if (mbAlt)
maWW8_FFN[0] = static_cast< sal_uInt8 >(maWW8_FFN[0] + 2 * ( 1 + msAltNm.getLength()));
sal_uInt8 aB = 0;
switch(ePitch)
{
case PITCH_VARIABLE:
aB |= 2; // aF.prg = 2
break;
case PITCH_FIXED:
aB |= 1;
break;
default: // aF.prg = 0 : DEFAULT_PITCH (windows.h)
break;
}
aB |= 1 << 2; // aF.fTrueType = 1; don't know any better;
switch(eFamily)
{
case FAMILY_ROMAN:
aB |= 1 << 4; // aF.ff = 1;
break;
case FAMILY_SWISS:
aB |= 2 << 4; // aF.ff = 2;
break;
case FAMILY_MODERN:
aB |= 3 << 4; // aF.ff = 3;
break;
case FAMILY_SCRIPT:
aB |= 4 << 4; // aF.ff = 4;
break;
case FAMILY_DECORATIVE:
aB |= 5 << 4; // aF.ff = 5;
break;
default: // aF.ff = 0; FF_DONTCARE (windows.h)
break;
}
maWW8_FFN[1] = aB;
ShortToSVBT16( 400, &maWW8_FFN[2] ); // don't know any better
// 400 == FW_NORMAL (windows.h)
//#i61927# For unicode fonts like Arial Unicode, Word 97+ sets the chs
//to SHIFTJIS presumably to capture that it's a multi-byte encoding font
//but Word95 doesn't do this, and sets it to 0 (ANSI), so we should do the
//same
maWW8_FFN[4] = sw::ms::rtl_TextEncodingToWinCharset(eChrSet);
if (mbAlt)
maWW8_FFN[5] = static_cast< sal_uInt8 >(msFamilyNm.getLength() + 1);
}
void wwFont::Write(SvStream *pTableStrm) const
{
pTableStrm->WriteBytes(maWW8_FFN, sizeof(maWW8_FFN)); // fixed part
// from Ver8 following two fields intersected,
// we ignore them.
//char panose[ 10 ]; // 0x6 PANOSE
//char fs[ 24 ]; // 0x10 FONTSIGNATURE
SwWW8Writer::FillCount(*pTableStrm, 0x22);
SwWW8Writer::WriteString16(*pTableStrm, msFamilyNm, true);
if (mbAlt)
SwWW8Writer::WriteString16(*pTableStrm, msAltNm, true);
}
void wwFont::WriteDocx( DocxAttributeOutput* rAttrOutput ) const
{
// no font embedding, panose id, subsetting, ... implemented
if (msFamilyNm.isEmpty())
return;
rAttrOutput->StartFont( msFamilyNm );
if ( mbAlt )
rAttrOutput->FontAlternateName( msAltNm );
rAttrOutput->FontCharset( sw::ms::rtl_TextEncodingToWinCharset( meChrSet ), meChrSet );
rAttrOutput->FontFamilyType( meFamily );
rAttrOutput->FontPitchType( mePitch );
rAttrOutput->EmbedFont( msFamilyNm, meFamily, mePitch );
rAttrOutput->EndFont();
}
void wwFont::WriteRtf( const RtfAttributeOutput* rAttrOutput ) const
{
rAttrOutput->FontFamilyType( meFamily, *this );
rAttrOutput->FontPitchType( mePitch );
rAttrOutput->FontCharset(
sw::ms::rtl_TextEncodingToWinCharsetRTF(msFamilyNm, msAltNm, meChrSet));
rAttrOutput->StartFont( msFamilyNm );
if ( mbAlt )
rAttrOutput->FontAlternateName( msAltNm );
rAttrOutput->EndFont();
}
bool operator<(const wwFont &r1, const wwFont &r2)
{
int nRet = memcmp(r1.maWW8_FFN, r2.maWW8_FFN, sizeof(r1.maWW8_FFN));
if (nRet == 0)
{
nRet = r1.msFamilyNm.compareTo(r2.msFamilyNm);
if (nRet == 0)
nRet = r1.msAltNm.compareTo(r2.msAltNm);
}
return nRet < 0;
}
sal_uInt16 wwFontHelper::GetId(const wwFont &rFont)
{
sal_uInt16 nRet;
std::map<wwFont, sal_uInt16>::const_iterator aIter = maFonts.find(rFont);
if (aIter != maFonts.end())
nRet = aIter->second;
else
{
nRet = static_cast< sal_uInt16 >(maFonts.size());
maFonts[rFont] = nRet;
}
return nRet;
}
void wwFontHelper::InitFontTable(const SwDoc& rDoc)
{
GetId(wwFont(u"Times New Roman", PITCH_VARIABLE,
FAMILY_ROMAN, RTL_TEXTENCODING_MS_1252));
GetId(wwFont(u"Symbol", PITCH_VARIABLE, FAMILY_ROMAN,
RTL_TEXTENCODING_SYMBOL));
GetId(wwFont(u"Arial", PITCH_VARIABLE, FAMILY_SWISS,
RTL_TEXTENCODING_MS_1252));
const SvxFontItem* pFont = GetDfltAttr(RES_CHRATR_FONT);
GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
pFont->GetFamily(), pFont->GetCharSet()));
const SfxItemPool& rPool = rDoc.GetAttrPool();
pFont = rPool.GetUserDefaultItem(RES_CHRATR_FONT);
if (nullptr != pFont)
{
GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
pFont->GetFamily(), pFont->GetCharSet()));
}
if (!m_bLoadAllFonts)
return;
const TypedWhichId<SvxFontItem> aTypes[] { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT };
for (const TypedWhichId<SvxFontItem> & pId : aTypes)
{
const_cast<SwDoc&>(rDoc).ForEachCharacterFontItem(pId, /*bIgnoreAutoStyles*/false,
[this] (const SvxFontItem& rFontItem) -> bool
{
GetId(wwFont(rFontItem.GetFamilyName(), rFontItem.GetPitch(),
rFontItem.GetFamily(), rFontItem.GetCharSet()));
return true;
});
}
}
sal_uInt16 wwFontHelper::GetId(const SvxFontItem& rFont)
{
wwFont aFont(rFont.GetFamilyName(), rFont.GetPitch(), rFont.GetFamily(),
rFont.GetCharSet());
return GetId(aFont);
}
std::vector< const wwFont* > wwFontHelper::AsVector() const
{
std::vector<const wwFont *> aFontList( maFonts.size() );
for ( const auto& aFont : maFonts )
aFontList[aFont.second] = &aFont.first;
return aFontList;
}
void wwFontHelper::WriteFontTable(SvStream *pTableStream, WW8Fib& rFib)
{
rFib.m_fcSttbfffn = pTableStream->Tell();
/*
* Reserve some space to fill in the len after we know how big it is
*/
SwWW8Writer::WriteLong(*pTableStream, 0);
/*
* Convert from fast insertion map to linear vector in the order that we
* want to write.
*/
std::vector<const wwFont *> aFontList( AsVector() );
/*
* Write them all to pTableStream
*/
for ( auto aFont : aFontList )
aFont->Write(pTableStream);
/*
* Write the position and len in the FIB
*/
rFib.m_lcbSttbfffn = pTableStream->Tell() - rFib.m_fcSttbfffn;
SwWW8Writer::WriteLong( *pTableStream, rFib.m_fcSttbfffn, maFonts.size());
}
void wwFontHelper::WriteFontTable( DocxAttributeOutput& rAttrOutput )
{
std::vector<const wwFont *> aFontList( AsVector() );
for ( auto aFont : aFontList )
aFont->WriteDocx(&rAttrOutput);
}
void wwFontHelper::WriteFontTable( const RtfAttributeOutput& rAttrOutput )
{
std::vector<const wwFont *> aFontList( AsVector() );
for ( auto aFont : aFontList )
aFont->WriteRtf(&rAttrOutput);
}
WW8_WrPlc0::WW8_WrPlc0( sal_uLong nOffset )
: m_nOfs( nOffset )
{
}
void WW8_WrPlc0::Append( sal_uLong nStartCpOrFc )
{
m_aPos.push_back( nStartCpOrFc - m_nOfs );
}
void WW8_WrPlc0::Write( SvStream& rStrm )
{
for( const auto& rPos : m_aPos )
{
rStrm.WriteUInt32(rPos);
}
}
// class MSWordSections : translate PageDescs into Sections
// also deals with header and footer
MSWordSections::MSWordSections( MSWordExportBase& rExport )
: mbDocumentIsProtected( false )
{
const SwSectionFormat *pFormat = nullptr;
rExport.m_pCurrentPageDesc = &rExport.m_rDoc.GetPageDesc( 0 );
const SwNode* pNd = rExport.m_pCurPam->GetPointContentNode();
const SfxItemSet* pSet = pNd ? &static_cast<const SwContentNode*>(pNd)->GetSwAttrSet() : nullptr;
sal_uLong nRstLnNum = pSet ? pSet->Get( RES_LINENUMBER ).GetStartValue() : 0;
const SwTableNode* pTableNd = rExport.m_pCurPam->GetPointNode().FindTableNode();
const SwSectionNode* pSectNd = nullptr;
if ( pTableNd )
{
pSet = &pTableNd->GetTable().GetFrameFormat()->GetAttrSet();
pNd = pTableNd;
}
else if (pNd && nullptr != ( pSectNd = pNd->FindSectionNode() ))
{
if ( SectionType::ToxHeader == pSectNd->GetSection().GetType() &&
pSectNd->StartOfSectionNode()->IsSectionNode() )
{
pSectNd = pSectNd->StartOfSectionNode()->GetSectionNode();
}
if ( SectionType::ToxContent == pSectNd->GetSection().GetType() )
{
pNd = pSectNd;
rExport.m_pCurPam->GetPoint()->Assign(*pNd);
}
if ( SectionType::Content == pSectNd->GetSection().GetType() )
pFormat = pSectNd->GetSection().GetFormat();
}
// tdf#118393: FILESAVE: DOCX Export loses header/footer
rExport.m_bFirstTOCNodeWithSection = pSectNd &&
( SectionType::ToxHeader == pSectNd->GetSection().GetType() ||
SectionType::ToxContent == pSectNd->GetSection().GetType() );
// Try to get page descriptor of the first node
const SwFormatPageDesc* pDescItem;
if ( pSet &&
(pDescItem = pSet->GetItemIfSet( RES_PAGEDESC )) &&
pDescItem->GetPageDesc() )
{
AppendSection( *pDescItem, *pNd, pFormat, nRstLnNum );
}
else
AppendSection( rExport.m_pCurrentPageDesc, pFormat, nRstLnNum, /*bIsFirstParagraph=*/true );
}
WW8_WrPlcSepx::WW8_WrPlcSepx( MSWordExportBase& rExport )
: MSWordSections( rExport )
, m_bHeaderFooterWritten( false )
{
// to be in sync with the AppendSection() call in the MSWordSections
// constructor
m_aCps.push_back( 0 );
}
MSWordSections::~MSWordSections()
{
}
WW8_WrPlcSepx::~WW8_WrPlcSepx()
{
}
bool MSWordSections::HeaderFooterWritten()
{
return false; // only relevant for WW8
}
bool WW8_WrPlcSepx::HeaderFooterWritten()
{
return m_bHeaderFooterWritten;
}
sal_uInt16 MSWordSections::CurrentNumberOfColumns( const SwDoc &rDoc ) const
{
OSL_ENSURE( !m_aSects.empty(), "no segment inserted yet" );
if ( m_aSects.empty() )
return 1;
return GetFormatCol(rDoc, m_aSects.back()).GetNumCols();
}
const SwFormatCol& MSWordSections::GetFormatCol(const SwDoc &rDoc, const WW8_SepInfo& rInfo)
{
const SwPageDesc* pPd = rInfo.pPageDesc;
if ( !pPd )
pPd = &rDoc.GetPageDesc( 0 );
const SfxItemSet &rSet = pPd->GetMaster().GetAttrSet();
SfxItemSetFixed<RES_COL, RES_COL> aSet( *rSet.GetPool() );
aSet.SetParent( &rSet );
//0xffffffff, what the hell is going on with that!, fixme most terribly
if ( rInfo.pSectionFormat && reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != rInfo.pSectionFormat )
aSet.Put( rInfo.pSectionFormat->GetFormatAttr( RES_COL ) );
return aSet.Get(RES_COL);
}
const WW8_SepInfo* MSWordSections::CurrentSectionInfo()
{
if ( !m_aSects.empty() )
return &m_aSects.back();
return nullptr;
}
void MSWordSections::AppendSection( const SwPageDesc* pPd,
const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo, bool bIsFirstParagraph )
{
if (HeaderFooterWritten()) {
return; // #i117955# prevent new sections in endnotes
}
m_aSects.emplace_back( pPd, pSectionFormat, nLnNumRestartNo, std::nullopt, nullptr, bIsFirstParagraph );
NeedsDocumentProtected( m_aSects.back() );
}
void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwPageDesc* pPd,
const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
{
if (HeaderFooterWritten()) {
return; // #i117955# prevent new sections in endnotes
}
m_aCps.push_back( nStartCp );
AppendSection( pPd, pSectionFormat, nLnNumRestartNo );
}
void MSWordSections::AppendSection( const SwFormatPageDesc& rPD,
const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
{
if (HeaderFooterWritten()) {
return; // #i117955# prevent new sections in endnotes
}
WW8_SepInfo aI( rPD.GetPageDesc(), pSectionFormat, nLnNumRestartNo, rPD.GetNumOffset(), &rNd );
m_aSects.push_back( aI );
NeedsDocumentProtected( aI );
}
void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwFormatPageDesc& rPD,
const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
{
if (HeaderFooterWritten()) {
return; // #i117955# prevent new sections in endnotes
}
m_aCps.push_back( nStartCp );
AppendSection( rPD, rNd, pSectionFormat, nLnNumRestartNo );
}
void WW8_WrPlcSepx::WriteFootnoteEndText( WW8Export& rWrt, sal_uLong nCpStt )
{
sal_uInt8 nInfoFlags = 0;
const SwFootnoteInfo& rInfo = rWrt.m_rDoc.GetFootnoteInfo();
if( !rInfo.m_aErgoSum.isEmpty() ) nInfoFlags |= 0x02;
if( !rInfo.m_aQuoVadis.isEmpty() ) nInfoFlags |= 0x04;
sal_uInt8 nEmptyStt = 0;
if( nInfoFlags )
{
m_pTextPos->Append( nCpStt ); // empty footnote separator
if( 0x02 & nInfoFlags ) // Footnote continuation separator
{
m_pTextPos->Append( nCpStt );
rWrt.WriteStringAsPara( rInfo.m_aErgoSum );
rWrt.WriteStringAsPara( OUString() );
nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() );
}
else
m_pTextPos->Append( nCpStt );
if( 0x04 & nInfoFlags ) // Footnote continuation notice
{
m_pTextPos->Append( nCpStt );
rWrt.WriteStringAsPara( rInfo.m_aQuoVadis );
rWrt.WriteStringAsPara( OUString() );
nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() );
}
else
m_pTextPos->Append( nCpStt );
nEmptyStt = 3;
}
while( 6 > nEmptyStt++ )
m_pTextPos->Append( nCpStt );
// set the flags at the Dop right away
WW8Dop& rDop = *rWrt.m_pDop;
// Footnote Info
switch( rInfo.m_eNum )
{
case FTNNUM_PAGE: rDop.rncFootnote = 2; break;
case FTNNUM_CHAPTER: rDop.rncFootnote = 1; break;
default: rDop.rncFootnote = 0; break;
} // rncFootnote
rDop.nfcFootnoteRef = WW8Export::GetNumId( rInfo.m_aFormat.GetNumberingType() );
rDop.nFootnote = rInfo.m_nFootnoteOffset + 1;
rDop.fpc = rWrt.m_bFootnoteAtTextEnd ? 2 : 1;
// Endnote Info
rDop.rncEdn = 0; // rncEdn: Don't Restart
const SwEndNoteInfo& rEndInfo = rWrt.m_rDoc.GetEndNoteInfo();
rDop.nfcEdnRef = WW8Export::GetNumId( rEndInfo.m_aFormat.GetNumberingType() );
rDop.nEdn = rEndInfo.m_nFootnoteOffset + 1;
rDop.epc = rWrt.m_bEndAtTextEnd ? 3 : 0;
}
void MSWordSections::SetHeaderFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
sal_uInt8 nFlag )
{
const SwFormatHeader* pItem = rFormat.GetItemIfSet(RES_HEADER);
if( pItem && pItem->IsActive() && pItem->GetHeaderFormat() )
rHeadFootFlags |= nFlag;
}
void MSWordSections::SetFooterFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
sal_uInt8 nFlag )
{
const SwFormatFooter* pItem = rFormat.GetItemIfSet(RES_FOOTER);
if( pItem && pItem->IsActive() && pItem->GetFooterFormat() )
rHeadFootFlags |= nFlag;
}
void WW8_WrPlcSepx::OutHeaderFooter( WW8Export& rWrt, bool bHeader,
const SwFormat& rFormat, sal_uLong& rCpPos, sal_uInt8 nHFFlags,
sal_uInt8 nFlag, sal_uInt8 nBreakCode)
{
if ( nFlag & nHFFlags )
{
m_pTextPos->Append( rCpPos );
rWrt.WriteHeaderFooterText( rFormat, bHeader);
rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains )
rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() );
}
else
{
m_pTextPos->Append( rCpPos );
if ((bHeader? rWrt.m_bHasHdr : rWrt.m_bHasFtr) && nBreakCode!=0)
{
rWrt.WriteStringAsPara( OUString() ); // Empty paragraph for empty header/footer
rWrt.WriteStringAsPara( OUString() ); // a CR that WW8 needs for end of the stream
rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() );
}
}
}
void MSWordSections::NeedsDocumentProtected(const WW8_SepInfo &rInfo)
{
if (rInfo.IsProtected())
mbDocumentIsProtected = true;
}
bool WW8_SepInfo::IsProtected() const
{
bool bRet = false;
if (
pSectionFormat &&
(reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != pSectionFormat)
)
{
const SwSection *pSection = pSectionFormat->GetSection();
if (pSection && pSection->IsProtect())
{
bRet = true;
}
}
return bRet;
}
void MSWordSections::CheckForFacinPg( const WW8Export& rWrt ) const
{
// 2 values getting set
// Dop.fFacingPages == Header and Footer different
// Dop.fSwapBordersFacingPgs == mirrored borders
sal_uInt16 nEnd = 0;
for( const WW8_SepInfo& rSepInfo : m_aSects )
{
if( !rSepInfo.pSectionFormat )
{
const SwPageDesc* pPd = rSepInfo.pPageDesc;
if( pPd->GetFollow() && pPd != pPd->GetFollow() &&
pPd->GetFollow()->GetFollow() == pPd->GetFollow() &&
rSepInfo.pPDNd &&
pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) )
// so this is first page and subsequent, so only respect follow
pPd = pPd->GetFollow();
// left-/right chain of pagedescs ?
else if( !( 1 & nEnd ) &&
pPd->GetFollow() && pPd != pPd->GetFollow() &&
pPd->GetFollow()->GetFollow() == pPd &&
(( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) &&
UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ||
( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) &&
UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ))
{
rWrt.m_pDop->fFacingPages = rWrt.m_pDop->fMirrorMargins = true;
nEnd |= 1;
}
if( !( 1 & nEnd ) &&
( !pPd->IsHeaderShared() || !pPd->IsFooterShared() ))
{
rWrt.m_pDop->fFacingPages = true;
nEnd |= 1;
}
if( !( 2 & nEnd ) &&
UseOnPage::Mirror == ( UseOnPage::Mirror & pPd->ReadUseOn() ))
{
rWrt.m_pDop->fSwapBordersFacingPgs =
rWrt.m_pDop->fMirrorMargins = true;
nEnd |= 2;
}
if( 3 == nEnd )
break; // We do not need to go any further
}
}
}
bool MSWordSections::HasBorderItem( const SwFormat& rFormat )
{
const SvxBoxItem* pItem = rFormat.GetItemIfSet(RES_BOX);
return pItem &&
( pItem->GetTop() ||
pItem->GetBottom() ||
pItem->GetLeft() ||
pItem->GetRight() );
}
void WW8AttributeOutput::StartSection()
{
m_rWW8Export.m_pO->clear();
}
void WW8AttributeOutput::SectFootnoteEndnotePr()
{
const SwFootnoteInfo& rInfo = m_rWW8Export.m_rDoc.GetFootnoteInfo();
const SwEndNoteInfo& rEndNoteInfo = m_rWW8Export.m_rDoc.GetEndNoteInfo();
m_rWW8Export.InsUInt16( NS_sprm::SRncFtn::val );
switch( rInfo.m_eNum )
{
case FTNNUM_PAGE: m_rWW8Export.m_pO->push_back( sal_uInt8/*rncRstPage*/ (2) ); break;
case FTNNUM_CHAPTER: m_rWW8Export.m_pO->push_back( sal_uInt8/*rncRstSect*/ (1) ); break;
default: m_rWW8Export.m_pO->push_back( sal_uInt8/*rncCont*/ (0) ); break;
}
m_rWW8Export.InsUInt16(NS_sprm::SNfcFtnRef::val);
sal_uInt8 nId = WW8Export::GetNumId(rInfo.m_aFormat.GetNumberingType());
SwWW8Writer::InsUInt16(*m_rWW8Export.m_pO, nId);
m_rWW8Export.InsUInt16(NS_sprm::SNfcEdnRef::val);
nId = WW8Export::GetNumId(rEndNoteInfo.m_aFormat.GetNumberingType());
SwWW8Writer::InsUInt16(*m_rWW8Export.m_pO, nId);
}
void WW8AttributeOutput::SectionFormProtection( bool bProtected )
{
//If the document is to be exported as protected, then if a segment
//is not protected, set the unlocked flag
if ( m_rWW8Export.m_pSepx->DocumentIsProtected() && !bProtected )
{
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFProtected::val );
m_rWW8Export.m_pO->push_back( 1 );
}
}
void WW8AttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo )
{
// sprmSNLnnMod - activate Line Numbering and define Modulo
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SNLnnMod::val );
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, rLnNumInfo.GetCountBy() );
// sprmSDxaLnn - xPosition of Line Number
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SDxaLnn::val );
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, rLnNumInfo.GetPosFromLeft() );
// sprmSLnc - restart number: 0 per page, 1 per section, 2 never restart
if ( nRestartNo || !rLnNumInfo.IsRestartEachPage() )
{
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SLnc::val );
m_rWW8Export.m_pO->push_back( nRestartNo ? 1 : 2 );
}
// sprmSLnnMin - Restart the Line Number with given value
if ( nRestartNo )
{
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SLnnMin::val );
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, o3tl::narrowing<sal_uInt16>(nRestartNo) - 1 );
}
}
void WW8AttributeOutput::SectionTitlePage()
{
// sprmSFTitlePage
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFTitlePage::val );
m_rWW8Export.m_pO->push_back( 1 );
}
void WW8AttributeOutput::SectionPageBorders( const SwFrameFormat* pPdFormat, const SwFrameFormat* pPdFirstPgFormat )
{
// write border of page
sal_uInt16 nPgBorder = MSWordSections::HasBorderItem( *pPdFormat ) ? 0 : USHRT_MAX;
if ( pPdFormat != pPdFirstPgFormat )
{
if ( MSWordSections::HasBorderItem( *pPdFirstPgFormat ) )
{
if ( USHRT_MAX == nPgBorder )
{
nPgBorder = 1;
// only the first page outlined -> Get the BoxItem from the correct format
m_rWW8Export.m_pISet = &pPdFirstPgFormat->GetAttrSet();
OutputItem( pPdFirstPgFormat->GetFormatAttr( RES_BOX ) );
}
}
else if ( !nPgBorder )
nPgBorder = 2;
}
// [MS-DOC] 2.9.255 SPgbPropOperand; 2.9.185 PgbOffsetFrom
if (m_bFromEdge)
nPgBorder |= (1<<5);
if ( USHRT_MAX != nPgBorder )
{
// write the Flag and Border Attribute
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SPgbProp::val );
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nPgBorder );
}
}
void WW8AttributeOutput::SectionBiDi( bool bBiDi )
{
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFBiDi::val );
m_rWW8Export.m_pO->push_back( bBiDi? 1: 0 );
}
void WW8AttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber )
{
// sprmSNfcPgn
sal_uInt8 nb = WW8Export::GetNumId( nNumType );
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SNfcPgn::val );
m_rWW8Export.m_pO->push_back( nb );
if ( oPageRestartNumber )
{
// sprmSFPgnRestart
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFPgnRestart::val );
m_rWW8Export.m_pO->push_back( 1 );
// sprmSPgnStart
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SPgnStart97::val );
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, *oPageRestartNumber );
}
}
void WW8AttributeOutput::SectionType( sal_uInt8 nBreakCode )
{
if ( 2 != nBreakCode ) // new page is the default
{
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SBkc::val );
m_rWW8Export.m_pO->push_back( nBreakCode );
}
}
void WW8Export::SetupSectionPositions( WW8_PdAttrDesc* pA )
{
if ( !pA )
return;
if ( !m_pO->empty() ) // are there attributes ?
{
pA->m_nLen = m_pO->size();
pA->m_pData.reset(new sal_uInt8 [m_pO->size()]);
// store for later
memcpy( pA->m_pData.get(), m_pO->data(), m_pO->size() );
m_pO->clear(); // clear HdFt-Text
}
else // no attributes there
{
pA->m_pData.reset();
pA->m_nLen = 0;
}
}
void WW8AttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA )
{
if ( drawing::TextVerticalAdjust_TOP == nVA ) // top alignment is the default
return;
sal_uInt8 nMSVA = 0;
switch( nVA )
{
case drawing::TextVerticalAdjust_CENTER:
nMSVA = 1;
break;
case drawing::TextVerticalAdjust_BOTTOM: //Writer = 2, Word = 3
nMSVA = 3;
break;
case drawing::TextVerticalAdjust_BLOCK: //Writer = 3, Word = 2
nMSVA = 2;
break;
default:
break;
}
SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SVjc::val );
m_rWW8Export.m_pO->push_back( nMSVA );
}
void WW8Export::WriteHeadersFooters( sal_uInt8 nHeadFootFlags,
const SwFrameFormat& rFormat, const SwFrameFormat& rLeftHeaderFormat, const SwFrameFormat& rLeftFooterFormat, const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode, bool /*bEvenAndOddHeaders*/ )
{
sal_uLong nCpPos = Fc2Cp( Strm().Tell() );
IncrementHdFtIndex();
if ( !(nHeadFootFlags & WW8_HEADER_EVEN) && m_pDop->fFacingPages )
m_pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode );
else
m_pSepx->OutHeaderFooter( *this, true, rLeftHeaderFormat, nCpPos, nHeadFootFlags, WW8_HEADER_EVEN, nBreakCode );
IncrementHdFtIndex();
m_pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode );
IncrementHdFtIndex();
if ( !(nHeadFootFlags & WW8_FOOTER_EVEN) && m_pDop->fFacingPages )
m_pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode );
else
m_pSepx->OutHeaderFooter( *this, false, rLeftFooterFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_EVEN, nBreakCode );
IncrementHdFtIndex();
m_pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode );
//#i24344# Drawing objects cannot be directly shared between main hd/ft
//and title hd/ft so we need to differentiate them
IncrementHdFtIndex();
m_pSepx->OutHeaderFooter( *this, true, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_HEADER_FIRST, nBreakCode );
m_pSepx->OutHeaderFooter( *this, false, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_FIRST, nBreakCode );
}
namespace
{
/**
* Determines if the continuous section break we start should use page style properties (header,
* footer, margins) from the next page style of the previous section.
*/
bool UsePrevSectionNextStyle(sal_uInt8 nBreakCode, const SwPageDesc* pPd,
const WW8_SepInfo& rSepInfo)
{
if (nBreakCode != 0)
{
// Not a continuous section break.
return false;
}
if (!pPd->GetFollow())
{
// Page style has no follow style.
return false;
}
// We start a continuous section break without headers/footers. Possibly the importer had
// headers/footers for this section break and put them to the closest page break's page style's
// next page style. See "find a node in the section that has a page break" in writerfilter/.
// Try the last-in-practice paragraph of the previous section.
const SwSectionFormat* pSection = rSepInfo.pSectionFormat;
if (pSection == reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)))
{
return false;
}
const SwNodeIndex* pSectionStart = pSection->GetContent().GetContentIdx();
if (!pSectionStart)
{
return false;
}
SwPaM aPaM(*pSectionStart);
aPaM.Move(fnMoveBackward);
if (!aPaM.GetPointNode().IsTextNode())
{
return false;
}
SwTextNode* pTextNode = aPaM.GetPointNode().GetTextNode();
const SwAttrSet* pParaProps = &pTextNode->GetSwAttrSet();
sal_uInt32 nCharHeight = pParaProps->GetSize().GetHeight();
if (nCharHeight > 20)
{
return false;
}
aPaM.Move(fnMoveBackward);
if (!aPaM.GetPointNode().IsTextNode())
{
return false;
}
pTextNode = aPaM.GetPointNode().GetTextNode();
pParaProps = &pTextNode->GetSwAttrSet();
return pParaProps->HasItem(RES_PAGEDESC);
}
}
void MSWordExportBase::SectionProperties( const WW8_SepInfo& rSepInfo, WW8_PdAttrDesc* pA )
{
const SwPageDesc* pPd = rSepInfo.pPageDesc;
if ( rSepInfo.pSectionFormat && !pPd )
pPd = &m_rDoc.GetPageDesc( 0 );
m_pCurrentPageDesc = pPd;
if ( !pPd )
return;
bool bOldPg = m_bOutPageDescs;
m_bOutPageDescs = true;
const SwPageDesc* pSavedPageDesc = pPd;
AttrOutput().StartSection();
AttrOutput().SectFootnoteEndnotePr();
// forms
AttrOutput().SectionFormProtection( rSepInfo.IsProtected() );
// line numbers
const SwLineNumberInfo& rLnNumInfo = m_rDoc.GetLineNumberInfo();
if ( rLnNumInfo.IsPaintLineNumbers() )
AttrOutput().SectionLineNumbering( rSepInfo.nLnNumRestartNo, rLnNumInfo );
/* sprmSBkc, break code: 0 No break, 1 New column
2 New page, 3 Even page, 4 Odd page
*/
sal_uInt8 nBreakCode = 2; // default start new page
bool bOutPgDscSet = true, bLeftRightPgChain = false, bOutputStyleItemSet = false;
const SwFrameFormat* pPdFormat = &pPd->GetMaster();
bool bUsePrevSectionNextStyle = false;
if ( rSepInfo.pSectionFormat )
{
// if pSectionFormat is set, then there is a SectionNode
// valid pointer -> start Section ,
// 0xfff -> Section terminated
nBreakCode = 0; // consecutive section
if (rSepInfo.pPDNd && (rSepInfo.pPDNd->IsContentNode() || rSepInfo.pPDNd->IsTableNode()))
{
const SfxItemSet* pSet
= rSepInfo.pPDNd->IsContentNode()
? &rSepInfo.pPDNd->GetContentNode()->GetSwAttrSet()
: &rSepInfo.pPDNd->GetTableNode()->GetTable().GetFrameFormat()->GetAttrSet();
if (!NoPageBreakSection(pSet))
nBreakCode = 2;
}
if (reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != rSepInfo.pSectionFormat)
{
if ( nBreakCode == 0 )
bOutPgDscSet = false;
// produce Itemset, which inherits PgDesk-Attr-Set:
// as child also the parent is searched if 'deep'-OutputItemSet
const SfxItemSet* pPdSet = &pPdFormat->GetAttrSet();
bUsePrevSectionNextStyle = GetExportFormat() == ExportFormat::DOCX
&& UsePrevSectionNextStyle(nBreakCode, pPd, rSepInfo);
if (bUsePrevSectionNextStyle)
{
// Take page margins from the previous section's next style.
pPdSet = &pPd->GetFollow()->GetMaster().GetAttrSet();
}
SfxItemSet aSet( *pPdSet->GetPool(), pPdSet->GetRanges() );
aSet.SetParent( pPdSet );
// at the child ONLY change column structure according to Sect-Attr.
const SvxLRSpaceItem &rSectionLR =
rSepInfo.pSectionFormat->GetFormatAttr( RES_LR_SPACE );
const SvxLRSpaceItem &rPageLR =
pPdFormat->GetFormatAttr( RES_LR_SPACE );
SvxLRSpaceItem aResultLR( rPageLR.GetLeft() +
rSectionLR.GetLeft(), rPageLR.GetRight() +
rSectionLR.GetRight(), 0, RES_LR_SPACE );
//i120133: The Section width should consider section indent value.
if (rSectionLR.GetLeft()+rSectionLR.GetRight()!=0)
{
const SwFormatCol& rCol = rSepInfo.pSectionFormat->GetFormatAttr(RES_COL);
SwFormatCol aCol(rCol);
aCol.SetAdjustValue(rSectionLR.GetLeft()+rSectionLR.GetRight());
aSet.Put(aCol);
}
else
aSet.Put(rSepInfo.pSectionFormat->GetFormatAttr(RES_COL));
aSet.Put( aResultLR );
// and write into the WW-File
const SfxItemSet* pOldI = m_pISet;
m_pISet = &aSet;
// Switch off test on default item values, if page description
// set (value of <bOutPgDscSet>) isn't written.
AttrOutput().OutputStyleItemSet( aSet, bOutPgDscSet );
bOutputStyleItemSet = true;
//Cannot export as normal page framedir, as continuous sections
//cannot contain any grid settings like proper sections
AttrOutput().SectionBiDi( SvxFrameDirection::Horizontal_RL_TB == TrueFrameDirection( *rSepInfo.pSectionFormat ) );
m_pISet = pOldI;
}
}
// Libreoffice 4.0 introduces support for page styles (SwPageDesc) with
// a different header/footer for the first page. The same effect can be
// achieved by chaining two page styles together (SwPageDesc::GetFollow)
// which are identical except for header/footer.
// The latter method was previously used by the doc/docx import filter.
// In both of these cases, we emit a single Word section with different
// first page header/footer.
const SwFrameFormat* pPdFirstPgFormat = &pPd->GetFirstMaster();
bool titlePage = !pPd->IsFirstShared();
if ( bOutPgDscSet )
{
// if a Follow is set and it does not point to itself,
// then there is a page chain.
// If this emulates a "first page", we can detect it here and write
// it as title page.
// With Left/Right changes it's different - we have to detect where
// the change of pages is, but here it's too late for that!
if ( pPd->GetFollow() && pPd != pPd->GetFollow() &&
pPd->GetFollow()->GetFollow() == pPd->GetFollow() &&
pPd->IsHeaderShared() && pPd->IsFooterShared() &&
( !rSepInfo.pPDNd || pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) ) )
{
const SwPageDesc *pFollow = pPd->GetFollow();
const SwFrameFormat& rFollowFormat = pFollow->GetMaster();
if (sw::util::IsPlausableSingleWordSection(*pPdFirstPgFormat, rFollowFormat))
{
if (titlePage)
{
// Do nothing. First format is already set.
}
else if (rSepInfo.pPDNd)
pPdFirstPgFormat = pPd->GetPageFormatOfNode( *rSepInfo.pPDNd );
else
pPdFirstPgFormat = &pPd->GetMaster();
m_pCurrentPageDesc = pPd = pFollow;
pPdFormat = &rFollowFormat;
// has different headers/footers for the title page
titlePage = true;
}
}
else if (nBreakCode == 2 && pPd == m_pPreviousSectionPageDesc && pPd->GetFollow() == pPd)
{
// The first title page has already been displayed in the previous section. Drop it.
titlePage = false;
}
const SfxItemSet* pOldI = m_pISet;
const SfxPoolItem* pItem;
if ( titlePage && SfxItemState::SET ==
pPdFirstPgFormat->GetItemState( RES_PAPER_BIN, true, &pItem ) )
{
m_pISet = &pPdFirstPgFormat->GetAttrSet();
m_bOutFirstPage = true;
AttrOutput().OutputItem( *pItem );
m_bOutFirstPage = false;
}
// left-/right chain of pagedescs ?
if ( pPd->GetFollow() && pPd != pPd->GetFollow() &&
pPd->GetFollow()->GetFollow() == pPd &&
(( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) &&
UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ||
( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) &&
UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ))
{
bLeftRightPgChain = true;
// which is the reference point? (left or right?)
// assume it is on the right side!
if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) )
{
nBreakCode = 3;
pPdFormat = &pPd->GetMaster(); //use the current page for settings (margins/width etc)
pPd = pPd->GetFollow(); //switch to the right page for the right/odd header/footer
}
else
nBreakCode = 4;
}
m_pISet = &pPdFormat->GetAttrSet();
if (!bOutputStyleItemSet)
{
if (titlePage)
{
m_pFirstPageFormat = pPdFirstPgFormat;
}
AttrOutput().OutputStyleItemSet( pPdFormat->GetAttrSet(), false );
if (titlePage)
{
m_pFirstPageFormat = nullptr;
}
}
AttrOutput().SectionPageBorders( pPdFormat, pPdFirstPgFormat );
m_pISet = pOldI;
// then the rest of the settings from PageDesc
AttrOutput().SectionPageNumbering( pPd->GetNumType().GetNumberingType(), rSepInfo.oPgRestartNo );
// will it be only left or only right pages?
if ( 2 == nBreakCode )
{
if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) )
nBreakCode = 3;
else if ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) )
nBreakCode = 4;
}
}
if (titlePage)
AttrOutput().SectionTitlePage();
AttrOutput().SectionType( nBreakCode );
if( rSepInfo.pPageDesc ) {
AttrOutput().TextVerticalAdjustment( rSepInfo.pPageDesc->GetVerticalAdjustment() );
}
// Header or Footer
sal_uInt8 nHeadFootFlags = 0;
// Should we output a w:evenAndOddHeaders tag or not?
// N.B.: despite its name this tag affects _both_ headers and footers!
bool bEvenAndOddHeaders = true;
bool bEvenAndOddFooters = true;
const SwFrameFormat* pPdLeftHeaderFormat = nullptr;
const SwFrameFormat* pPdLeftFooterFormat = nullptr;
if (bLeftRightPgChain)
{
const SwFrameFormat* pHeaderFormat = pPd->GetStashedFrameFormat(true, true, true);
const SwFrameFormat* pFooterFormat = pPd->GetStashedFrameFormat(false, true, true);
if (pHeaderFormat)
{
pPdLeftHeaderFormat = pHeaderFormat;
bEvenAndOddHeaders = false;
}
else
{
pPdLeftHeaderFormat = &pPd->GetFollow()->GetFirstLeft();
}
if (pFooterFormat)
{
pPdLeftFooterFormat = pFooterFormat;
bEvenAndOddFooters = false;
}
else
{
pPdLeftFooterFormat = &pPd->GetFollow()->GetFirstLeft();
}
}
else
{
const SwFrameFormat* pHeaderFormat = pPd->GetStashedFrameFormat(true, true, false);
const SwFrameFormat* pFooterFormat = pPd->GetStashedFrameFormat(false, true, false);
if (pHeaderFormat)
{
pPdLeftHeaderFormat = pHeaderFormat;
bEvenAndOddHeaders = false;
}
else
{
pPdLeftHeaderFormat = &pPd->GetLeft();
}
if (pFooterFormat)
{
pPdLeftFooterFormat = pFooterFormat;
bEvenAndOddFooters = false;
}
else
{
pPdLeftFooterFormat = &pPd->GetLeft();
}
}
// Ensure that headers are written if section is first paragraph
if (nBreakCode != 0 || (rSepInfo.pSectionFormat && rSepInfo.bIsFirstParagraph))
{
if ( titlePage )
{
// there is a First Page:
MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_HEADER_FIRST );
MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_FOOTER_FIRST );
}
else
{
if ( pPd->GetStashedFrameFormat(true, true, true) && pPdLeftHeaderFormat && pPdLeftHeaderFormat->GetHeader().GetHeaderFormat() )
{
MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_FIRST );
}
if ( pPd->GetStashedFrameFormat(false, true, true) && pPdLeftFooterFormat && pPdLeftFooterFormat->GetFooter().GetFooterFormat() )
{
MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_FIRST );
}
}
MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD );
MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD );
if ( !pPd->IsHeaderShared() || bLeftRightPgChain )
{
MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_EVEN );
}
else if ( pPd->IsHeaderShared() && pPd->GetStashedFrameFormat(true, true, false) && pPdLeftHeaderFormat && pPdLeftHeaderFormat->GetHeader().GetHeaderFormat() )
{
MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_EVEN );
bEvenAndOddHeaders = false;
}
if ( !pPd->IsFooterShared() || bLeftRightPgChain )
{
MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_EVEN );
}
else if ( pPd->IsFooterShared() && pPd->GetStashedFrameFormat(false, true, false) && pPdLeftFooterFormat && pPdLeftFooterFormat->GetFooter().GetFooterFormat() )
{
MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_EVEN );
bEvenAndOddFooters = false;
}
}
// binary filters only
SetupSectionPositions( pA );
/*
!!!!!!!!!!!
// borders at header and footer texts would be done like this:
// This should use something like pOut,
// which is repeated with every special text line.
const SwFrameFormat* pFFormat = rFt.GetFooterFormat();
const SvxBoxItem& rBox = pFFormat->GetBox(false);
OutWW8_SwFormatBox1( m_rWW8Export.pOut, rBox, false);
!!!!!!!!!!!
You can turn this into paragraph attributes, which are then observed in each paragraph.
Applies to background / border.
!!!!!!!!!!!
*/
const SwTextNode *pOldPageRoot = GetHdFtPageRoot();
SetHdFtPageRoot( rSepInfo.pPDNd ? rSepInfo.pPDNd->GetTextNode() : nullptr );
if (bUsePrevSectionNextStyle && nHeadFootFlags == 0)
{
// Take headers/footers from the previous section's next style.
pPdFormat = &pPd->GetFollow()->GetMaster();
MSWordSections::SetHeaderFlag(nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD);
MSWordSections::SetFooterFlag(nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD);
}
WriteHeadersFooters( nHeadFootFlags, *pPdFormat, *pPdLeftHeaderFormat, *pPdLeftFooterFormat, *pPdFirstPgFormat, nBreakCode, bEvenAndOddHeaders && bEvenAndOddFooters );
SetHdFtPageRoot( pOldPageRoot );
AttrOutput().EndSection();
// outside of the section properties again
m_bOutPageDescs = bOldPg;
m_pPreviousSectionPageDesc = pSavedPageDesc;
}
bool WW8_WrPlcSepx::WriteKFText( WW8Export& rWrt )
{
sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() );
OSL_ENSURE( !m_pTextPos, "who set the pointer?" );
m_pTextPos.reset( new WW8_WrPlc0( nCpStart ) );
WriteFootnoteEndText( rWrt, nCpStart );
CheckForFacinPg( rWrt );
unsigned int nOldIndex = rWrt.GetHdFtIndex();
rWrt.SetHdFtIndex( 0 );
for (const WW8_SepInfo & rSepInfo : m_aSects)
{
auto pAttrDesc = std::make_shared<WW8_PdAttrDesc>();
m_SectionAttributes.push_back(pAttrDesc);
rWrt.SectionProperties( rSepInfo, pAttrDesc.get() );
// FIXME: this writes the section properties, but not of all sections;
// it's possible that later in the document (e.g. in endnotes) sections
// are added, but they won't have their properties written here!
m_bHeaderFooterWritten = true;
}
rWrt.SetHdFtIndex( nOldIndex ); //0
if ( m_pTextPos->Count() )
{
// HdFt available?
sal_uLong nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() );
m_pTextPos->Append( nCpEnd ); // End of last Header/Footer for PlcfHdd
if ( nCpEnd > nCpStart )
{
++nCpEnd;
m_pTextPos->Append( nCpEnd + 1 ); // End of last Header/Footer for PlcfHdd
rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains )
}
rWrt.m_pFieldHdFt->Finish( nCpEnd, rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpFootnote );
rWrt.m_pFib->m_ccpHdr = nCpEnd - nCpStart;
}
else
{
m_pTextPos.reset();
}
return rWrt.m_pFib->m_ccpHdr != 0;
}
void WW8_WrPlcSepx::WriteSepx( SvStream& rStrm ) const
{
OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(m_aSects.size())
, "WriteSepx(): arrays out of sync!");
for (const auto & rSectionAttribute : m_SectionAttributes) // all sections
{
WW8_PdAttrDesc *const pA = rSectionAttribute.get();
if (pA->m_nLen && pA->m_pData != nullptr)
{
pA->m_nSepxFcPos = rStrm.Tell();
rStrm.WriteUInt16(pA->m_nLen);
rStrm.WriteBytes(pA->m_pData.get(), pA->m_nLen);
}
}
}
void WW8_WrPlcSepx::WritePlcSed( WW8Export& rWrt ) const
{
OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(m_aSects.size())
, "WritePlcSed(): arrays out of sync!");
OSL_ENSURE( m_aCps.size() == m_aSects.size() + 1, "WrPlcSepx: DeSync" );
sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
for( decltype(m_aSects)::size_type i = 0; i <= m_aSects.size(); i++ )
{
sal_uInt32 nP = m_aCps[i];
rWrt.m_pTableStrm->WriteUInt32(nP);
}
static WW8_SED aSed = {{4, 0},{0, 0, 0, 0},{0, 0},{0xff, 0xff, 0xff, 0xff}};
for (const auto & rSectionAttribute : m_SectionAttributes)
{
// Sepx-Pos
UInt32ToSVBT32( rSectionAttribute->m_nSepxFcPos, aSed.fcSepx );
rWrt.m_pTableStrm->WriteBytes(&aSed, sizeof(aSed));
}
rWrt.m_pFib->m_fcPlcfsed = nFcStart;
rWrt.m_pFib->m_lcbPlcfsed = rWrt.m_pTableStrm->Tell() - nFcStart;
}
void WW8_WrPlcSepx::WritePlcHdd( WW8Export& rWrt ) const
{
// Don't write out the PlcfHdd if ccpHdd is 0: it's a validation failure case.
if( rWrt.m_pFib->m_ccpHdr != 0 && m_pTextPos && m_pTextPos->Count() )
{
rWrt.m_pFib->m_fcPlcfhdd = rWrt.m_pTableStrm->Tell();
m_pTextPos->Write( *rWrt.m_pTableStrm ); // Plc0
rWrt.m_pFib->m_lcbPlcfhdd = rWrt.m_pTableStrm->Tell() -
rWrt.m_pFib->m_fcPlcfhdd;
}
}
void MSWordExportBase::WriteHeaderFooterText( const SwFormat& rFormat, bool bHeader )
{
const SwFormatContent *pContent;
if ( bHeader )
{
m_bHasHdr = true;
const SwFormatHeader& rHd = rFormat.GetHeader();
OSL_ENSURE( rHd.GetHeaderFormat(), "Header text is not here" );
if ( !rHd.GetHeaderFormat() )
return;
pContent = &rHd.GetHeaderFormat()->GetContent();
}
else
{
m_bHasFtr = true;
const SwFormatFooter& rFt = rFormat.GetFooter();
OSL_ENSURE( rFt.GetFooterFormat(), "Footer text is not here" );
if ( !rFt.GetFooterFormat() )
return;
pContent = &rFt.GetFooterFormat()->GetContent();
}
const SwNodeIndex* pSttIdx = pContent->GetContentIdx();
if ( pSttIdx )
{
SwNodeIndex aIdx( *pSttIdx, 1 ),
aEnd( *pSttIdx->GetNode().EndOfSectionNode() );
SwNodeOffset nStart = aIdx.GetIndex();
SwNodeOffset nEnd = aEnd.GetIndex();
// range, i.e. valid node
if ( nStart < nEnd )
{
bool bOldKF = m_bOutKF;
m_bOutKF = true;
WriteSpecialText( nStart, nEnd, TXT_HDFT );
m_bOutKF = bOldKF;
}
else
pSttIdx = nullptr;
}
if ( !pSttIdx )
{
// there is no Header/Footer, but a CR is still necessary
OSL_ENSURE( pSttIdx, "Header/Footer text is not really present" );
AttrOutput().EmptyParagraph();
}
}
// class WW8_WrPlcFootnoteEdn : Collect the Footnotes and Endnotes and output their text
// and Plcs at the end of the document.
// WW8_WrPlcFootnoteEdn is the class for Footnotes and Endnotes
WW8_WrPlcSubDoc::WW8_WrPlcSubDoc()
{
}
WW8_WrPlcSubDoc::~WW8_WrPlcSubDoc()
{
}
void WW8_WrPlcFootnoteEdn::Append( WW8_CP nCp, const SwFormatFootnote& rFootnote )
{
m_aCps.push_back( nCp );
m_aContent.push_back( &rFootnote );
}
WW8_Annotation::WW8_Annotation(const SwPostItField* pPostIt, WW8_CP nRangeStart, WW8_CP nRangeEnd)
:
m_nRangeStart(nRangeStart),
m_nRangeEnd(nRangeEnd),
mpAuthorIDs(new SvtSecurityMapPersonalInfo)
{
mpRichText = pPostIt->GetTextObject();
if (!mpRichText)
msSimpleText = pPostIt->GetText();
initPersonalInfo(pPostIt->GetPar1(), pPostIt->GetInitials(),
DateTime(pPostIt->GetDate(), pPostIt->GetTime()));
}
WW8_Annotation::WW8_Annotation(const SwRedlineData* pRedline)
:
mpRichText(nullptr),
msSimpleText(pRedline->GetComment()),
m_nRangeStart(0),
m_nRangeEnd(0),
mpAuthorIDs(new SvtSecurityMapPersonalInfo)
{
initPersonalInfo(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor()), u""_ustr,
pRedline->GetTimeStamp());
}
void WW8_Annotation::initPersonalInfo(const OUString& sAuthor, const OUString& sInitials,
DateTime aDateTime)
{
bool bRemovePersonalInfo
= SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo)
&& !SvtSecurityOptions::IsOptionSet(
SvtSecurityOptions::EOption::DocWarnKeepNoteAuthorDateInfo);
msOwner = bRemovePersonalInfo ? "Author" + OUString::number(mpAuthorIDs->GetInfoID(sAuthor))
: sAuthor;
m_sInitials = bRemovePersonalInfo ? "A" + OUString::number(mpAuthorIDs->GetInfoID(sAuthor))
: sInitials;
maDateTime = bRemovePersonalInfo ? DateTime(DateTime::EMPTY) : aDateTime;
}
bool WW8_Annotation::HasRange() const
{
if (m_nRangeStart != m_nRangeEnd)
{
return true;
}
return !m_bIgnoreEmpty;
}
void WW8_WrPlcAnnotations::AddRangeStartPosition(const OUString& rName, WW8_CP nStartCp,
bool bIgnoreEmpty)
{
m_aRangeStartPositions[rName] = std::make_pair(nStartCp, bIgnoreEmpty);
}
void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwPostItField *pPostIt )
{
m_aCps.push_back( nCp );
WW8_Annotation* p;
if( m_aRangeStartPositions.find(pPostIt->GetName()) != m_aRangeStartPositions.end() )
{
auto [nStartCp, bIgnoreEmpty] = m_aRangeStartPositions[pPostIt->GetName()];
p = new WW8_Annotation(pPostIt, nStartCp, nCp);
p->m_bIgnoreEmpty = bIgnoreEmpty;
m_aRangeStartPositions.erase(pPostIt->GetName());
}
else
{
p = new WW8_Annotation(pPostIt, nCp, nCp);
}
m_aContent.push_back( p );
}
void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwRedlineData *pRedline )
{
maProcessedRedlines.insert(pRedline);
m_aCps.push_back( nCp );
WW8_Annotation* p = new WW8_Annotation(pRedline);
m_aContent.push_back( p );
}
bool WW8_WrPlcAnnotations::IsNewRedlineComment( const SwRedlineData *pRedline )
{
return maProcessedRedlines.find(pRedline) == maProcessedRedlines.end();
}
WW8_WrPlcAnnotations::~WW8_WrPlcAnnotations()
{
for(const void * p : m_aContent)
delete static_cast<WW8_Annotation const *>(p);
}
bool WW8_WrPlcSubDoc::WriteGenericText( WW8Export& rWrt, sal_uInt8 nTTyp,
WW8_CP& rCount )
{
sal_uInt16 nLen = m_aContent.size();
if ( !nLen )
return false;
sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() );
m_pTextPos.reset( new WW8_WrPlc0( nCpStart ) );
sal_uInt16 i;
switch ( nTTyp )
{
case TXT_ATN:
for ( i = 0; i < nLen; i++ )
{
// beginning for PlcfAtnText
m_pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
rWrt.WritePostItBegin();
const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
if (rAtn.mpRichText)
rWrt.WriteOutliner(*rAtn.mpRichText, nTTyp);
else
{
OUString sText(rAtn.msSimpleText);
rWrt.WriteStringAsPara(sText.replace(0x0A, 0x0B));
}
}
break;
case TXT_TXTBOX:
case TXT_HFTXTBOX:
for ( i = 0; i < nLen; i++ )
{
// textbox content
WW8_CP nCP = rWrt.Fc2Cp( rWrt.Strm().Tell() );
m_aCps.insert( m_aCps.begin()+i, nCP );
m_pTextPos->Append( nCP );
if( m_aContent[ i ] != nullptr )
{
// is it a writer or sdr - textbox?
const SdrObject& rObj = *static_cast<SdrObject const *>(m_aContent[ i ]);
if (rObj.GetObjInventor() == SdrInventor::FmForm)
{
sal_uInt8 nOldTyp = rWrt.m_nTextTyp;
rWrt.m_nTextTyp = nTTyp;
rWrt.GetOCXExp().ExportControl(rWrt, dynamic_cast<const SdrUnoObj&>(rObj));
rWrt.m_nTextTyp = nOldTyp;
}
else if( auto pText = DynCastSdrTextObj(&rObj) )
rWrt.WriteSdrTextObj(*pText, nTTyp);
else
{
const SwFrameFormat* pFormat = ::FindFrameFormat( &rObj );
assert(pFormat && "where is the format?");
const SwNodeIndex* pNdIdx = pFormat->GetContent().GetContentIdx();
assert(pNdIdx && "where is the StartNode of the Textbox?");
rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1,
pNdIdx->GetNode().EndOfSectionIndex(),
nTTyp );
{
SwNodeIndex aContentIdx = *pNdIdx;
++aContentIdx;
if ( aContentIdx.GetNode().IsTableNode() )
{
bool bContainsOnlyTables = true;
do {
aContentIdx = *(aContentIdx.GetNode().EndOfSectionNode());
++aContentIdx;
if ( !aContentIdx.GetNode().IsTableNode() &&
aContentIdx.GetIndex() != pNdIdx->GetNode().EndOfSectionIndex() )
{
bContainsOnlyTables = false;
}
} while ( aContentIdx.GetNode().IsTableNode() );
if ( bContainsOnlyTables )
{
// Additional paragraph containing a space to
// assure that by WW created RTF from written WW8
// does not crash WW.
rWrt.WriteStringAsPara( u" "_ustr );
}
}
}
}
}
else if (i < m_aSpareFormats.size() && m_aSpareFormats[i])
{
const SwFrameFormat& rFormat = *m_aSpareFormats[i];
const SwNodeIndex* pNdIdx = rFormat.GetContent().GetContentIdx();
rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1,
pNdIdx->GetNode().EndOfSectionIndex(), nTTyp );
}
// CR at end of one textbox text ( otherwise WW gpft :-( )
rWrt.WriteStringAsPara( OUString() );
}
break;
case TXT_EDN:
case TXT_FTN:
for ( i = 0; i < nLen; i++ )
{
// beginning for PlcfFootnoteText/PlcfEdnText
m_pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
// Note content
const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(m_aContent[ i ]);
rWrt.WriteFootnoteBegin( *pFootnote );
const SwNodeIndex* pIdx = pFootnote->GetTextFootnote()->GetStartNode();
assert(pIdx && "Where is the start node of Foot-/Endnote?");
rWrt.WriteSpecialText( pIdx->GetIndex() + 1,
pIdx->GetNode().EndOfSectionIndex(),
nTTyp );
}
break;
default:
OSL_ENSURE( false, "What kind of SubDocType is that?" );
}
m_pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
// CR to the end ( otherwise WW complains )
rWrt.WriteStringAsPara( OUString() );
WW8_CP nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() );
m_pTextPos->Append( nCpEnd );
rCount = nCpEnd - nCpStart;
return ( rCount != 0 );
}
static bool lcl_AuthorComp( const std::pair<OUString,OUString>& aFirst, const std::pair<OUString,OUString>& aSecond)
{
return aFirst.first < aSecond.first;
}
static bool lcl_PosComp( const std::pair<WW8_CP, int>& aFirst, const std::pair<WW8_CP, int>& aSecond)
{
return aFirst.first < aSecond.first;
}
void WW8_WrPlcSubDoc::WriteGenericPlc( WW8Export& rWrt, sal_uInt8 nTTyp,
WW8_FC& rTextStart, sal_Int32& rTextCount, WW8_FC& rRefStart, sal_Int32& rRefCount ) const
{
sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
sal_uInt16 nLen = m_aCps.size();
if ( !nLen )
return;
OSL_ENSURE( m_aCps.size() + 2 == m_pTextPos->Count(), "WritePlc: DeSync" );
std::vector<std::pair<OUString,OUString> > aStrArr;
WW8Fib& rFib = *rWrt.m_pFib; // n+1-th CP-Pos according to the manual
bool bWriteCP = true;
switch ( nTTyp )
{
case TXT_ATN:
{
std::vector< std::pair<WW8_CP, int> > aRangeStartPos; // The second of the pair is the original index before sorting.
std::vector< std::pair<WW8_CP, int> > aRangeEndPos; // Same, so we can map between the indexes before/after sorting.
std::map<int, int> aAtnStartMap; // Maps from annotation index to start index.
std::map<int, int> aStartAtnMap; // Maps from start index to annotation index.
std::map<int, int> aStartEndMap; // Maps from start index to end index.
// then write first the GrpXstAtnOwners
int nIdx = 0;
for ( sal_uInt16 i = 0; i < nLen; ++i )
{
const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
aStrArr.emplace_back(rAtn.msOwner,rAtn.m_sInitials);
// record start and end positions for ranges
if (rAtn.HasRange())
{
aRangeStartPos.emplace_back(rAtn.m_nRangeStart, nIdx);
aRangeEndPos.emplace_back(rAtn.m_nRangeEnd, nIdx);
++nIdx;
}
}
//sort and remove duplicates
std::sort(aStrArr.begin(), aStrArr.end(),&lcl_AuthorComp);
auto aIter = std::unique(aStrArr.begin(), aStrArr.end());
aStrArr.erase(aIter, aStrArr.end());
// Also sort the start and end positions. We need to reference
// the start index in the annotation table and also need to
// reference the end index in the start table, so build a map
// that knows what index to reference, after sorting.
std::sort(aRangeStartPos.begin(), aRangeStartPos.end(), &lcl_PosComp);
for (decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i)
{
aAtnStartMap[aRangeStartPos[i].second] = i;
aStartAtnMap[i] = aRangeStartPos[i].second;
}
std::sort(aRangeEndPos.begin(), aRangeEndPos.end(), &lcl_PosComp);
for (decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i)
aStartEndMap[aAtnStartMap[ aRangeEndPos[i].second ]] = i;
for ( decltype(aStrArr)::size_type i = 0; i < aStrArr.size(); ++i )
{
const OUString& sAuthor = aStrArr[i].first;
SwWW8Writer::WriteShort(*rWrt.m_pTableStrm, sAuthor.getLength());
SwWW8Writer::WriteString16(*rWrt.m_pTableStrm, sAuthor,
false);
}
rFib.m_fcGrpStAtnOwners = nFcStart;
nFcStart = rWrt.m_pTableStrm->Tell();
rFib.m_lcbGrpStAtnOwners = nFcStart - rFib.m_fcGrpStAtnOwners;
// Commented text ranges
if( !aRangeStartPos.empty() )
{
// Commented text ranges starting positions (Plcfbkf.aCP)
rFib.m_fcPlcfAtnbkf = nFcStart;
for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
{
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, aRangeStartPos[i].first );
}
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, rFib.m_ccpText + 1);
// Commented text ranges additional information (Plcfbkf.aFBKF)
for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
{
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, aStartEndMap[i] ); // FBKF.ibkl
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 ); // FBKF.bkc
}
nFcStart = rWrt.m_pTableStrm->Tell();
rFib.m_lcbPlcfAtnbkf = nFcStart - rFib.m_fcPlcfAtnbkf;
// Commented text ranges ending positions (PlcfBkl.aCP)
rFib.m_fcPlcfAtnbkl = nFcStart;
for ( decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i )
{
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, aRangeEndPos[i].first );
}
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, rFib.m_ccpText + 1);
nFcStart = rWrt.m_pTableStrm->Tell();
rFib.m_lcbPlcfAtnbkl = nFcStart - rFib.m_fcPlcfAtnbkl;
// Commented text ranges as bookmarks (SttbfAtnBkmk)
rFib.m_fcSttbfAtnbkmk = nFcStart;
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, sal_Int16(sal_uInt16(0xFFFF)) ); // SttbfAtnBkmk.fExtend
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, aRangeStartPos.size() ); // SttbfAtnBkmk.cData
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0xA ); // SttbfAtnBkmk.cbExtra
for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
{
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 ); // SttbfAtnBkmk.cchData
// One ATNBE structure for all text ranges
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0x0100 ); // ATNBE.bmc
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, aStartAtnMap[i] ); // ATNBE.lTag
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, -1 ); // ATNBE.lTagOld
}
nFcStart = rWrt.m_pTableStrm->Tell();
rFib.m_lcbSttbfAtnbkmk = nFcStart - rFib.m_fcSttbfAtnbkmk;
}
// Write the extended >= Word XP ATRD records
for( sal_uInt16 i = 0; i < nLen; ++i )
{
const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
sal_uInt32 nDTTM = sw::ms::DateTime2DTTM(rAtn.maDateTime);
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nDTTM );
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
}
rFib.m_fcAtrdExtra = nFcStart;
nFcStart = rWrt.m_pTableStrm->Tell();
rFib.m_lcbAtrdExtra = nFcStart - rFib.m_fcAtrdExtra;
rFib.m_fcHplxsdr = 0x01010002; //WTF, but apparently necessary
rFib.m_lcbHplxsdr = 0;
}
break;
case TXT_TXTBOX:
case TXT_HFTXTBOX:
{
m_pTextPos->Write( *rWrt.m_pTableStrm );
const std::vector<sal_uInt32>* pShapeIds = GetShapeIdArr();
OSL_ENSURE( pShapeIds, "Where are the ShapeIds?" );
for ( sal_uInt16 i = 0; i < nLen; ++i )
{
// write textbox story - FTXBXS
// is it a writer or sdr - textbox?
const SdrObject* pObj = static_cast<SdrObject const *>(m_aContent[ i ]);
sal_Int32 nCnt = 1;
if (DynCastSdrTextObj( pObj ))
{
// find the "highest" SdrObject of this
const SwFrameFormat& rFormat = *::FindFrameFormat( pObj );
const SwFormatChain* pChn = &rFormat.GetChain();
while ( pChn->GetNext() )
{
// has a chain?
// then calc the cur pos in the chain
++nCnt;
pChn = &pChn->GetNext()->GetChain();
}
}
if( nullptr == pObj )
{
if (i < m_aSpareFormats.size() && m_aSpareFormats[i])
{
const SwFrameFormat& rFormat = *m_aSpareFormats[i];
const SwFormatChain* pChn = &rFormat.GetChain();
while( pChn->GetNext() )
{
// has a chain?
// then calc the cur pos in the chain
++nCnt;
pChn = &pChn->GetNext()->GetChain();
}
}
}
// long cTxbx / iNextReuse
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nCnt );
// long cReusable
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
// short fReusable
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
// long reserved
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, -1 );
// long lid
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm,
(*pShapeIds)[i]);
// long txidUndo
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
}
SwWW8Writer::FillCount( *rWrt.m_pTableStrm, 22 );
bWriteCP = false;
}
break;
}
if ( bWriteCP )
{
// write CP Positions
for ( sal_uInt16 i = 0; i < nLen; i++ )
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, m_aCps[ i ] );
// n+1-th CP-Pos according to the manual
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm,
rFib.m_ccpText + rFib.m_ccpFootnote + rFib.m_ccpHdr + rFib.m_ccpEdn +
rFib.m_ccpTxbx + rFib.m_ccpHdrTxbx + 1 );
if ( TXT_ATN == nTTyp )
{
sal_uInt16 nlTag = 0;
for ( sal_uInt16 i = 0; i < nLen; ++i )
{
const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
//aStrArr is sorted
auto aIter = std::lower_bound(aStrArr.begin(),
aStrArr.end(), std::pair<OUString,OUString>(rAtn.msOwner,OUString()),
&lcl_AuthorComp);
OSL_ENSURE(aIter != aStrArr.end() && aIter->first == rAtn.msOwner,
"Impossible");
sal_uInt16 nFndPos = static_cast< sal_uInt16 >(aIter - aStrArr.begin());
OUString sInitials( aIter->second );
sal_uInt8 nInitialsLen = static_cast<sal_uInt8>(sInitials.getLength());
if ( nInitialsLen > 9 )
{
sInitials = sInitials.copy( 0, 9 );
nInitialsLen = 9;
}
// xstUsrInitl[ 10 ] pascal-style String holding initials
// of annotation author
SwWW8Writer::WriteShort(*rWrt.m_pTableStrm, nInitialsLen);
SwWW8Writer::WriteString16(*rWrt.m_pTableStrm, sInitials,
false);
SwWW8Writer::FillCount( *rWrt.m_pTableStrm,
(9 - nInitialsLen) * 2 );
// documents layout of WriteShort's below:
// SVBT16 ibst; // index into GrpXstAtnOwners
// SVBT16 ak; // not used
// SVBT16 grfbmc; // not used
// SVBT32 ITagBkmk; // when not -1, this tag identifies the ATNBE
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, nFndPos );
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
if (rAtn.HasRange())
{
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nlTag );
++nlTag;
}
else
SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, -1 );
}
}
else
{
sal_uInt16 nNo = 0;
for ( sal_uInt16 i = 0; i < nLen; ++i ) // write Flags
{
const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(m_aContent[ i ]);
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm,
!pFootnote->GetNumStr().isEmpty() ? 0 : ++nNo );
}
}
}
rRefStart = nFcStart;
nFcStart = rWrt.m_pTableStrm->Tell();
rRefCount = nFcStart - rRefStart;
m_pTextPos->Write( *rWrt.m_pTableStrm );
switch ( nTTyp )
{
case TXT_TXTBOX:
case TXT_HFTXTBOX:
for ( sal_uInt16 i = 0; i < nLen; ++i )
{
// write break descriptor (BKD)
// short itxbxs
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, i );
// short dcpDepend
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
// short flags : icol/fTableBreak/fColumnBreak/fMarked/
// fUnk/fTextOverflow
SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0x800 );
}
SwWW8Writer::FillCount( *rWrt.m_pTableStrm, 6 );
break;
}
rTextStart = nFcStart;
rTextCount = rWrt.m_pTableStrm->Tell() - nFcStart;
}
const std::vector<sal_uInt32>* WW8_WrPlcSubDoc::GetShapeIdArr() const
{
return nullptr;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1053 Calling the 'HeaderFooterWritten' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'wrtw8sty.cxx:1090', 'wrtw8sty.cxx:1180', 'wrtww8.hxx:213'.
↑ V519 The 'm_pISet' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1819, 1847.
↑ V560 A part of conditional expression is always true: pPdLeftHeaderFormat.
↑ V560 A part of conditional expression is always true: pPdLeftFooterFormat.
↑ V560 A part of conditional expression is always true: pPdLeftHeaderFormat.
↑ V560 A part of conditional expression is always true: pPdLeftFooterFormat.
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.