1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <hintids.hxx>
21 #include <tools/stream.hxx>
22 #include <comphelper/string.hxx>
26 #include <IDocumentRedlineAccess.hxx>
27 #include <redline.hxx>
29 #include <txatbase.hxx>
34 #include <ftninfo.hxx>
35 #include <numrule.hxx>
40 * This file contains all output functions of the ASCII-Writer;
41 * For all nodes, attributes, formats and chars.
49 const SwTextNode
& m_rNd
;
50 sal_Int32 m_nCurrentSwPos
;
52 sal_Int32
SearchNext( sal_Int32 nStartPos
);
55 SwASC_AttrIter( SwASCWriter
& rWrt
, const SwTextNode
& rNd
, sal_Int32 nStt
);
57 void NextPos() { m_nCurrentSwPos
= SearchNext(m_nCurrentSwPos
+ 1); }
59 sal_Int32
WhereNext() const { return m_nCurrentSwPos
; }
61 bool OutAttr( sal_Int32 nSwPos
);
66 SwASC_AttrIter::SwASC_AttrIter(SwASCWriter
& rWr
, const SwTextNode
& rTextNd
, sal_Int32 nStt
)
71 m_nCurrentSwPos
= SearchNext(nStt
+ 1);
74 sal_Int32
SwASC_AttrIter::SearchNext( sal_Int32 nStartPos
)
76 sal_Int32 nMinPos
= SAL_MAX_INT32
;
77 const SwpHints
* pTextAttrs
= m_rNd
.GetpSwpHints();
80 // TODO: This can be optimized, if we make use of the fact that the TextAttrs
81 // are sorted by starting position. We would need to remember two indices, however.
82 for ( size_t i
= 0; i
< pTextAttrs
->Count(); ++i
)
84 const SwTextAttr
* pHt
= pTextAttrs
->Get(i
);
85 if ( pHt
->HasDummyChar() )
87 sal_Int32 nPos
= pHt
->GetStart();
89 if( nPos
>= nStartPos
&& nPos
<= nMinPos
)
92 if( ( ++nPos
) >= nStartPos
&& nPos
< nMinPos
)
95 else if ( pHt
->HasContent() )
97 const sal_Int32 nHintStart
= pHt
->GetStart();
98 if ( nHintStart
>= nStartPos
&& nHintStart
<= nMinPos
)
100 nMinPos
= nHintStart
;
103 const sal_Int32 nHintEnd
= pHt
->End() ? *pHt
->End() : COMPLETE_STRING
;
104 if ( nHintEnd
>= nStartPos
&& nHintEnd
< nMinPos
)
114 bool SwASC_AttrIter::OutAttr( sal_Int32 nSwPos
)
117 const SwpHints
* pTextAttrs
= m_rNd
.GetpSwpHints();
120 for( size_t i
= 0; i
< pTextAttrs
->Count(); ++i
)
122 const SwTextAttr
* pHt
= pTextAttrs
->Get(i
);
123 if ( ( pHt
->HasDummyChar()
124 || pHt
->HasContent() )
125 && nSwPos
== pHt
->GetStart() )
129 switch( pHt
->Which() )
131 case RES_TXTATR_FIELD
:
132 case RES_TXTATR_ANNOTATION
:
133 case RES_TXTATR_INPUTFIELD
:
134 sOut
= static_txtattr_cast
<SwTextField
const*>(pHt
)
135 ->GetFormatField().GetField()->ExpandField(true, nullptr);
140 const SwFormatFootnote
& rFootnote
= pHt
->GetFootnote();
141 if( !rFootnote
.GetNumStr().isEmpty() )
142 sOut
= rFootnote
.GetNumStr();
143 else if( rFootnote
.IsEndNote() )
144 sOut
= m_rWrt
.m_pDoc
->GetEndNoteInfo().m_aFormat
.GetNumStr(
145 rFootnote
.GetNumber());
147 sOut
= m_rWrt
.m_pDoc
->GetFootnoteInfo().m_aFormat
.GetNumStr(
148 rFootnote
.GetNumber());
151 case RES_TXTATR_LINEBREAK
:
153 // Downgrade the clearing break to a simple linebreak.
154 sOut
= OUStringChar(GetCharOfTextAttr(*pHt
));
158 if( !sOut
.isEmpty() )
159 m_rWrt
.Strm().WriteUnicodeOrByteText(sOut
);
161 else if( nSwPos
< pHt
->GetStart() )
170 class SwASC_RedlineIter
173 SwTextNode
const& m_rNode
;
174 IDocumentRedlineAccess
const& m_rIDRA
;
175 SwRedlineTable::size_type m_nextRedline
;
178 SwASC_RedlineIter(SwASCWriter
const& rWriter
, SwTextNode
const& rNode
)
180 , m_rIDRA(rNode
.GetDoc().getIDocumentRedlineAccess())
181 , m_nextRedline(rWriter
.m_bHideDeleteRedlines
182 ? m_rIDRA
.GetRedlinePos(m_rNode
, RedlineType::Delete
)
183 : SwRedlineTable::npos
)
187 bool CheckNodeDeleted()
189 if (m_nextRedline
== SwRedlineTable::npos
)
193 SwRangeRedline
const*const pRedline(m_rIDRA
.GetRedlineTable()[m_nextRedline
]);
194 return pRedline
->Start()->GetNodeIndex() < m_rNode
.GetIndex()
195 && m_rNode
.GetIndex() < pRedline
->End()->GetNodeIndex();
198 std::pair
<sal_Int32
, sal_Int32
> GetNextRedlineSkip()
200 sal_Int32
nRedlineStart(COMPLETE_STRING
);
201 sal_Int32
nRedlineEnd(COMPLETE_STRING
);
202 for ( ; m_nextRedline
< m_rIDRA
.GetRedlineTable().size(); ++m_nextRedline
)
204 SwRangeRedline
const*const pRedline(m_rIDRA
.GetRedlineTable()[m_nextRedline
]);
205 if (pRedline
->GetType() != RedlineType::Delete
)
209 auto [pStart
, pEnd
] = pRedline
->StartEnd(); // SwPosition*
210 if (m_rNode
.GetIndex() < pStart
->GetNodeIndex())
212 m_nextRedline
= SwRedlineTable::npos
;
215 if (nRedlineStart
== COMPLETE_STRING
)
217 nRedlineStart
= pStart
->GetNodeIndex() == m_rNode
.GetIndex()
218 ? pStart
->GetContentIndex()
223 if (pStart
->GetContentIndex() != nRedlineEnd
)
225 assert(nRedlineEnd
< pStart
->GetContentIndex());
226 break; // no increment, revisit it next call
229 nRedlineEnd
= pEnd
->GetNodeIndex() == m_rNode
.GetIndex()
230 ? pEnd
->GetContentIndex()
233 return std::make_pair(nRedlineStart
, nRedlineEnd
);
239 // Output of the node
241 static Writer
& OutASC_SwTextNode( Writer
& rWrt
, SwContentNode
& rNode
)
243 const SwTextNode
& rNd
= static_cast<SwTextNode
&>(rNode
);
245 sal_Int32 nStrPos
= rWrt
.m_pCurrentPam
->GetPoint()->GetContentIndex();
246 const sal_Int32 nNodeEnd
= rNd
.Len();
247 sal_Int32 nEnd
= nNodeEnd
;
248 bool bLastNd
= rWrt
.m_pCurrentPam
->GetPoint()->GetNode() == rWrt
.m_pCurrentPam
->GetMark()->GetNode();
250 nEnd
= rWrt
.m_pCurrentPam
->GetMark()->GetContentIndex();
252 bool bIsOneParagraph
= rWrt
.m_pOrigPam
->Start()->GetNode() == rWrt
.m_pOrigPam
->End()->GetNode() && !getenv("SW_ASCII_COPY_NUMBERING");
254 SwASC_AttrIter
aAttrIter( static_cast<SwASCWriter
&>(rWrt
), rNd
, nStrPos
);
255 SwASC_RedlineIter
redlineIter(static_cast<SwASCWriter
&>(rWrt
), rNd
);
257 if (redlineIter
.CheckNodeDeleted())
262 const SwNumRule
* pNumRule
= rNd
.GetNumRule();
263 if (pNumRule
&& !nStrPos
&& rWrt
.m_bExportParagraphNumbering
&& !bIsOneParagraph
)
265 bool bIsOutlineNumRule
= pNumRule
== rNd
.GetDoc().GetOutlineNumRule();
267 // indent each numbering level by 4 spaces
269 if (!bIsOutlineNumRule
)
271 for (int i
= 0; i
<= rNd
.GetActualListLevel(); ++i
)
275 // set up bullets or numbering
276 OUString
numString(rNd
.GetNumString());
277 if (numString
.isEmpty() && !bIsOutlineNumRule
)
279 if (rNd
.HasBullet() && !rNd
.HasVisibleNumberingOrBullet())
281 else if (rNd
.HasBullet())
282 numString
= OUString(numfunc::GetBulletChar(rNd
.GetActualListLevel()));
283 else if (!rNd
.HasBullet() && !rNd
.HasVisibleNumberingOrBullet())
287 if (!level
.isEmpty() || !numString
.isEmpty())
288 rWrt
.Strm().WriteUnicodeOrByteText(Concat2View(level
+ numString
+ " "));
291 OUString
aStr( rNd
.GetText() );
292 if( rWrt
.m_bASCII_ParaAsBlank
)
293 aStr
= aStr
.replace(0x0A, ' ');
295 const bool bExportSoftHyphens
= RTL_TEXTENCODING_UCS2
== rWrt
.GetAsciiOptions().GetCharSet() ||
296 RTL_TEXTENCODING_UTF8
== rWrt
.GetAsciiOptions().GetCharSet();
298 std::pair
<sal_Int32
, sal_Int32
> curRedline(redlineIter
.GetNextRedlineSkip());
300 const sal_Int32 nNextAttr
= std::min(aAttrIter
.WhereNext(), nEnd
);
302 bool isOutAttr(false);
303 if (nStrPos
< curRedline
.first
|| curRedline
.second
<= nStrPos
)
305 isOutAttr
= aAttrIter
.OutAttr(nStrPos
);
313 if (nNextAttr
<= curRedline
.first
)
315 buf
.append(aStr
.subView(nStrPos
, nNextAttr
- nStrPos
));
318 else if (nStrPos
< curRedline
.second
)
320 if (nStrPos
< curRedline
.first
)
322 buf
.append(aStr
.subView(nStrPos
, curRedline
.first
- nStrPos
));
324 if (curRedline
.second
<= nNextAttr
)
326 nStrPos
= curRedline
.second
;
327 curRedline
= redlineIter
.GetNextRedlineSkip();
337 curRedline
= redlineIter
.GetNextRedlineSkip();
340 OUString
aOutStr(buf
.makeStringAndClear());
341 if ( !bExportSoftHyphens
)
342 aOutStr
= aOutStr
.replaceAll(OUStringChar(CHAR_SOFTHYPHEN
), "");
344 // all INWORD should be already removed by OutAttr
345 // but the field-marks are not attributes so filter those
346 static sal_Unicode
const forbidden
[] = {
347 CH_TXT_ATR_INPUTFIELDSTART
,
348 CH_TXT_ATR_INPUTFIELDEND
,
349 CH_TXT_ATR_FORMELEMENT
,
350 CH_TXT_ATR_FIELDSTART
,
356 aOutStr
= comphelper::string::removeAny(aOutStr
, forbidden
);
358 rWrt
.Strm().WriteUnicodeOrByteText( aOutStr
);
369 ( ( !rWrt
.m_bWriteClipboardDoc
&& !rWrt
.m_bASCII_NoLastLineEnd
)
370 && !nStrPos
&& nEnd
== nNodeEnd
) )
371 rWrt
.Strm().WriteUnicodeOrByteText( static_cast<SwASCWriter
&>(rWrt
).GetLineEnd());
377 * Create the table for the ASCII function pointers to the output
379 * There are local structures that only need to be known to the ASCII DLL.
382 SwNodeFnTab aASCNodeFnTab
= {
383 /* RES_TXTNODE */ OutASC_SwTextNode
,
384 /* RES_GRFNODE */ nullptr,
385 /* RES_OLENODE */ nullptr
388 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */