LibreOffice Module sw (master)  1
ascatr.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 <hintids.hxx>
21 #include <tools/stream.hxx>
22 #include <comphelper/string.hxx>
23 #include <pam.hxx>
24 #include <doc.hxx>
25 #include <ndtxt.hxx>
27 #include <redline.hxx>
28 #include "wrtasc.hxx"
29 #include <txatbase.hxx>
30 #include <txtfld.hxx>
31 #include <fmtftn.hxx>
32 #include <fmtfld.hxx>
33 #include <fldbas.hxx>
34 #include <ftninfo.hxx>
35 #include <numrule.hxx>
36 
37 #include <algorithm>
38 
39 /*
40  * This file contains all output functions of the ASCII-Writer;
41  * For all nodes, attributes, formats and chars.
42  */
43 
44 namespace {
45 
46 class SwASC_AttrIter
47 {
48  SwASCWriter& m_rWrt;
49  const SwTextNode& m_rNd;
50  sal_Int32 m_nCurrentSwPos;
51 
52  sal_Int32 SearchNext( sal_Int32 nStartPos );
53 
54 public:
55  SwASC_AttrIter( SwASCWriter& rWrt, const SwTextNode& rNd, sal_Int32 nStt );
56 
57  void NextPos() { m_nCurrentSwPos = SearchNext(m_nCurrentSwPos + 1); }
58 
59  sal_Int32 WhereNext() const { return m_nCurrentSwPos; }
60 
61  bool OutAttr( sal_Int32 nSwPos );
62 };
63 
64 }
65 
66 SwASC_AttrIter::SwASC_AttrIter(SwASCWriter& rWr, const SwTextNode& rTextNd, sal_Int32 nStt)
67  : m_rWrt(rWr)
68  , m_rNd(rTextNd)
69  , m_nCurrentSwPos(0)
70 {
71  m_nCurrentSwPos = SearchNext(nStt + 1);
72 }
73 
74 sal_Int32 SwASC_AttrIter::SearchNext( sal_Int32 nStartPos )
75 {
76  sal_Int32 nMinPos = SAL_MAX_INT32;
77  const SwpHints* pTextAttrs = m_rNd.GetpSwpHints();
78  if( pTextAttrs )
79  {
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 )
83  {
84  const SwTextAttr* pHt = pTextAttrs->Get(i);
85  if ( pHt->HasDummyChar() )
86  {
87  sal_Int32 nPos = pHt->GetStart();
88 
89  if( nPos >= nStartPos && nPos <= nMinPos )
90  nMinPos = nPos;
91 
92  if( ( ++nPos ) >= nStartPos && nPos < nMinPos )
93  nMinPos = nPos;
94  }
95  else if ( pHt->HasContent() )
96  {
97  const sal_Int32 nHintStart = pHt->GetStart();
98  if ( nHintStart >= nStartPos && nHintStart <= nMinPos )
99  {
100  nMinPos = nHintStart;
101  }
102 
103  const sal_Int32 nHintEnd = pHt->End() ? *pHt->End() : COMPLETE_STRING;
104  if ( nHintEnd >= nStartPos && nHintEnd < nMinPos )
105  {
106  nMinPos = nHintEnd;
107  }
108  }
109  }
110  }
111  return nMinPos;
112 }
113 
114 bool SwASC_AttrIter::OutAttr( sal_Int32 nSwPos )
115 {
116  bool bRet = false;
117  const SwpHints* pTextAttrs = m_rNd.GetpSwpHints();
118  if( pTextAttrs )
119  {
120  for( size_t i = 0; i < pTextAttrs->Count(); ++i )
121  {
122  const SwTextAttr* pHt = pTextAttrs->Get(i);
123  if ( ( pHt->HasDummyChar()
124  || pHt->HasContent() )
125  && nSwPos == pHt->GetStart() )
126  {
127  bRet = true;
128  OUString sOut;
129  switch( pHt->Which() )
130  {
131  case RES_TXTATR_FIELD:
134  sOut = static_txtattr_cast<SwTextField const*>(pHt)
135  ->GetFormatField().GetField()->ExpandField(true, nullptr);
136  break;
137 
138  case RES_TXTATR_FTN:
139  {
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());
146  else
147  sOut = m_rWrt.m_pDoc->GetFootnoteInfo().m_aFormat.GetNumStr(
148  rFootnote.GetNumber());
149  }
150  break;
152  {
153  // Downgrade the clearing break to a simple linebreak.
154  sOut = OUStringChar(GetCharOfTextAttr(*pHt));
155  break;
156  }
157  }
158  if( !sOut.isEmpty() )
159  m_rWrt.Strm().WriteUnicodeOrByteText(sOut);
160  }
161  else if( nSwPos < pHt->GetStart() )
162  break;
163  }
164  }
165  return bRet;
166 }
167 
168 namespace {
169 
170 class SwASC_RedlineIter
171 {
172 private:
173  SwTextNode const& m_rNode;
174  IDocumentRedlineAccess const& m_rIDRA;
175  SwRedlineTable::size_type m_nextRedline;
176 
177 public:
178  SwASC_RedlineIter(SwASCWriter const& rWriter, SwTextNode const& rNode)
179  : m_rNode(rNode)
180  , m_rIDRA(rNode.GetDoc().getIDocumentRedlineAccess())
181  , m_nextRedline(rWriter.m_bHideDeleteRedlines
182  ? m_rIDRA.GetRedlinePos(m_rNode, RedlineType::Delete)
183  : SwRedlineTable::npos)
184  {
185  }
186 
187  bool CheckNodeDeleted()
188  {
189  if (m_nextRedline == SwRedlineTable::npos)
190  {
191  return false;
192  }
193  SwRangeRedline const*const pRedline(m_rIDRA.GetRedlineTable()[m_nextRedline]);
194  return pRedline->Start()->nNode.GetIndex() < m_rNode.GetIndex()
195  && m_rNode.GetIndex() < pRedline->End()->nNode.GetIndex();
196  }
197 
198  std::pair<sal_Int32, sal_Int32> GetNextRedlineSkip()
199  {
200  sal_Int32 nRedlineStart(COMPLETE_STRING);
201  sal_Int32 nRedlineEnd(COMPLETE_STRING);
202  for ( ; m_nextRedline < m_rIDRA.GetRedlineTable().size(); ++m_nextRedline)
203  {
204  SwRangeRedline const*const pRedline(m_rIDRA.GetRedlineTable()[m_nextRedline]);
205  if (pRedline->GetType() != RedlineType::Delete)
206  {
207  continue;
208  }
209  SwPosition const*const pStart(pRedline->Start());
210  SwPosition const*const pEnd(pRedline->End());
211  if (m_rNode.GetIndex() < pStart->nNode.GetIndex())
212  {
213  m_nextRedline = SwRedlineTable::npos;
214  break; // done
215  }
216  if (nRedlineStart == COMPLETE_STRING)
217  {
218  nRedlineStart = pStart->nNode.GetIndex() == m_rNode.GetIndex()
219  ? pStart->nContent.GetIndex()
220  : 0;
221  }
222  else
223  {
224  if (pStart->nContent.GetIndex() != nRedlineEnd)
225  {
226  assert(nRedlineEnd < pStart->nContent.GetIndex());
227  break; // no increment, revisit it next call
228  }
229  }
230  nRedlineEnd = pEnd->nNode.GetIndex() == m_rNode.GetIndex()
231  ? pEnd->nContent.GetIndex()
232  : COMPLETE_STRING;
233  }
234  return std::make_pair(nRedlineStart, nRedlineEnd);
235  }
236 };
237 
238 }
239 
240 // Output of the node
241 
243 {
244  const SwTextNode& rNd = static_cast<SwTextNode&>(rNode);
245 
246  sal_Int32 nStrPos = rWrt.m_pCurrentPam->GetPoint()->nContent.GetIndex();
247  const sal_Int32 nNodeEnd = rNd.Len();
248  sal_Int32 nEnd = nNodeEnd;
249  bool bLastNd = rWrt.m_pCurrentPam->GetPoint()->nNode == rWrt.m_pCurrentPam->GetMark()->nNode;
250  if( bLastNd )
251  nEnd = rWrt.m_pCurrentPam->GetMark()->nContent.GetIndex();
252 
253  bool bIsOneParagraph = rWrt.m_pOrigPam->Start()->nNode == rWrt.m_pOrigPam->End()->nNode && !getenv("SW_ASCII_COPY_NUMBERING");
254 
255  SwASC_AttrIter aAttrIter( static_cast<SwASCWriter&>(rWrt), rNd, nStrPos );
256  SwASC_RedlineIter redlineIter(static_cast<SwASCWriter&>(rWrt), rNd);
257 
258  if (redlineIter.CheckNodeDeleted())
259  {
260  return rWrt;
261  }
262 
263  const SwNumRule* pNumRule = rNd.GetNumRule();
264  if (pNumRule && !nStrPos && rWrt.m_bExportParagraphNumbering && !bIsOneParagraph)
265  {
266  bool bIsOutlineNumRule = pNumRule == rNd.GetDoc().GetOutlineNumRule();
267 
268  // indent each numbering level by 4 spaces
269  OUString level;
270  if (!bIsOutlineNumRule)
271  {
272  for (int i = 0; i <= rNd.GetActualListLevel(); ++i)
273  level += " ";
274  }
275 
276  // set up bullets or numbering
277  OUString numString(rNd.GetNumString());
278  if (numString.isEmpty() && !bIsOutlineNumRule)
279  {
280  if (rNd.HasBullet() && !rNd.HasVisibleNumberingOrBullet())
281  numString = " ";
282  else if (rNd.HasBullet())
283  numString = OUString(numfunc::GetBulletChar(rNd.GetActualListLevel()));
284  else if (!rNd.HasBullet() && !rNd.HasVisibleNumberingOrBullet())
285  numString = " ";
286  }
287 
288  if (!level.isEmpty() || !numString.isEmpty())
289  rWrt.Strm().WriteUnicodeOrByteText(OUStringConcatenation(level + numString + " "));
290  }
291 
292  OUString aStr( rNd.GetText() );
293  if( rWrt.m_bASCII_ParaAsBlank )
294  aStr = aStr.replace(0x0A, ' ');
295 
296  const bool bExportSoftHyphens = RTL_TEXTENCODING_UCS2 == rWrt.GetAsciiOptions().GetCharSet() ||
297  RTL_TEXTENCODING_UTF8 == rWrt.GetAsciiOptions().GetCharSet();
298 
299  std::pair<sal_Int32, sal_Int32> curRedline(redlineIter.GetNextRedlineSkip());
300  for (;;) {
301  const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
302 
303  bool isOutAttr(false);
304  if (nStrPos < curRedline.first || curRedline.second <= nStrPos)
305  {
306  isOutAttr = aAttrIter.OutAttr(nStrPos);
307  }
308 
309  if (!isOutAttr)
310  {
311  OUStringBuffer buf;
312  while (true)
313  {
314  if (nNextAttr <= curRedline.first)
315  {
316  buf.append(aStr.subView(nStrPos, nNextAttr - nStrPos));
317  break;
318  }
319  else if (nStrPos < curRedline.second)
320  {
321  if (nStrPos < curRedline.first)
322  {
323  buf.append(aStr.subView(nStrPos, curRedline.first - nStrPos));
324  }
325  if (curRedline.second <= nNextAttr)
326  {
327  nStrPos = curRedline.second;
328  curRedline = redlineIter.GetNextRedlineSkip();
329  }
330  else
331  {
332  nStrPos = nNextAttr;
333  break;
334  }
335  }
336  else
337  {
338  curRedline = redlineIter.GetNextRedlineSkip();
339  }
340  }
341  OUString aOutStr(buf.makeStringAndClear());
342  if ( !bExportSoftHyphens )
343  aOutStr = aOutStr.replaceAll(OUStringChar(CHAR_SOFTHYPHEN), "");
344 
345  // all INWORD should be already removed by OutAttr
346  // but the field-marks are not attributes so filter those
347  static sal_Unicode const forbidden [] = {
355  0
356  };
357  aOutStr = comphelper::string::removeAny(aOutStr, forbidden);
358 
359  rWrt.Strm().WriteUnicodeOrByteText( aOutStr );
360  }
361  nStrPos = nNextAttr;
362  if (nStrPos >= nEnd)
363  {
364  break;
365  }
366  aAttrIter.NextPos();
367  }
368 
369  if( !bLastNd ||
370  ( ( !rWrt.m_bWriteClipboardDoc && !rWrt.m_bASCII_NoLastLineEnd )
371  && !nStrPos && nEnd == nNodeEnd ) )
372  rWrt.Strm().WriteUnicodeOrByteText( static_cast<SwASCWriter&>(rWrt).GetLineEnd());
373 
374  return rWrt;
375 }
376 
377 /*
378  * Create the table for the ASCII function pointers to the output
379  * function.
380  * There are local structures that only need to be known to the ASCII DLL.
381  */
382 
384 /* RES_TXTNODE */ OutASC_SwTextNode,
385 /* RES_GRFNODE */ nullptr,
386 /* RES_OLENODE */ nullptr
387 };
388 
389 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SwNodeOffset min(const SwNodeOffset &a, const SwNodeOffset &b)
Definition: nodeoffset.hxx:35
rtl_TextEncoding GetCharSet() const
Definition: shellio.hxx:72
bool HasBullet() const
Returns if this text node has a bullet.
Definition: ndtxt.cxx:3142
Marks a position in the document model.
Definition: pam.hxx:36
const OUString & GetText() const
Definition: ndtxt.hxx:218
SwNodeIndex nNode
Definition: pam.hxx:38
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:277
FnNodeOut SwNodeFnTab[RES_NODE_END-RES_NODE_BEGIN]
Definition: wrt_fn.hxx:50
bool HasDummyChar() const
Definition: txatbase.hxx:107
bool IsEndNote() const
Definition: fmtftn.hxx:71
sal_uInt16 Which() const
Definition: txatbase.hxx:116
#define CH_TXT_ATR_FORMELEMENT
Definition: hintids.hxx:180
int GetActualListLevel() const
Returns the actual list level of this text node, when it is a list item.
Definition: ndtxt.cxx:4139
#define CHAR_SOFTHYPHEN
Definition: swtypes.hxx:163
size_type size() const
Definition: docary.hxx:267
#define CH_TXTATR_BREAKWORD
Definition: hintids.hxx:173
sal_uInt16 sal_Unicode
constexpr TypedWhichId< SwFormatFootnote > RES_TXTATR_FTN(59)
OUString removeAny(std::u16string_view rIn, sal_Unicode const *const pChars)
A wrapper around SfxPoolItem to store the start position of (usually) a text portion, with an optional end.
Definition: txatbase.hxx:43
bool m_bASCII_ParaAsBlank
Definition: shellio.hxx:416
sal_Unicode GetBulletChar(sal_uInt8 nLevel)
retrieve unicode of character used for the default bullet list for the given list level ...
Definition: number.cxx:1396
#define CH_TXT_ATR_INPUTFIELDSTART
Definition: hintids.hxx:177
sal_Int32 GetStart() const
Definition: txatbase.hxx:88
const OUString & GetNumStr() const
Definition: fmtftn.hxx:68
SwNodeOffset GetIndex() const
Definition: ndindex.hxx:152
sal_Unicode GetCharOfTextAttr(const SwTextAttr &rAttr)
Definition: thints.cxx:3498
bool m_bWriteClipboardDoc
Definition: shellio.hxx:413
bool m_bASCII_NoLastLineEnd
Definition: shellio.hxx:417
SwPaM * m_pOrigPam
Definition: shellio.hxx:409
constexpr TypedWhichId< SwFormatField > RES_TXTATR_FIELD(RES_TXTATR_NOEND_BEGIN)
size_t Count() const
Definition: ndhints.hxx:142
#define SAL_MAX_INT32
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
int i
SwDoc & GetDoc()
Definition: node.hxx:213
static Writer & OutASC_SwTextNode(Writer &rWrt, SwContentNode &rNode)
Definition: ascatr.cxx:242
bool HasContent() const
Definition: txatbase.hxx:112
SwNodeOffset GetIndex() const
Definition: node.hxx:292
vector_type::size_type size_type
Definition: docary.hxx:222
constexpr TypedWhichId< SwFormatLineBreak > RES_TXTATR_LINEBREAK(61)
#define CH_TXT_ATR_FIELDSTART
Definition: hintids.hxx:182
SwNumRule * GetNumRule(bool bInParent=true) const
Returns numbering rule of this text node.
Definition: ndtxt.cxx:2833
T static_txtattr_cast(S *s)
Definition: txatbase.hxx:257
const SwFormatFootnote & GetFootnote() const
Definition: txatbase.hxx:208
const SwPosition * Start() const
Definition: pam.hxx:213
constexpr TypedWhichId< SwFormatField > RES_TXTATR_ANNOTATION(60)
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
SwNodeFnTab aASCNodeFnTab
Definition: ascatr.cxx:383
#define CH_TXT_ATR_INPUTFIELDEND
Definition: hintids.hxx:178
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
bool m_bExportParagraphNumbering
Definition: shellio.hxx:419
const SwPosition * End() const
Definition: pam.hxx:218
const sal_Int32 * End() const
Definition: txatbase.hxx:156
constexpr TypedWhichId< SwFormatField > RES_TXTATR_INPUTFIELD(55)
#define CH_TXT_ATR_FIELDEND
Definition: hintids.hxx:184
std::shared_ptr< SwUnoCursor > m_pCurrentPam
Definition: shellio.hxx:410
constexpr sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:57
OUString GetNumString(const bool _bInclPrefixAndSuffixStrings=true, const unsigned int _nRestrictToThisLevel=MAXLEVEL, SwRootFrame const *pLayout=nullptr) const
Returns outline of numbering string.
Definition: ndtxt.cxx:3160
SvStream & Strm()
Definition: writer.cxx:215
RedlineType
const SwAsciiOptions & GetAsciiOptions() const
Definition: shellio.hxx:441
bool HasVisibleNumberingOrBullet() const
Returns if the paragraph has a visible numbering or bullet.
Definition: ndtxt.cxx:4173
virtual const SwRedlineTable & GetRedlineTable() const =0
#define CH_TXT_ATR_FIELDSEP
Definition: hintids.hxx:183
static constexpr size_type npos
Definition: docary.hxx:223
bool WriteUnicodeOrByteText(std::u16string_view rStr, rtl_TextEncoding eDestCharSet)
sal_uInt16 GetNumber() const
Definition: fmtftn.hxx:69
aStr
sal_uInt16 nPos
SwNumRule * GetOutlineNumRule() const
Definition: doc.hxx:1023