LibreOffice Module sw (master)  1
htmlnumwriter.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 <svtools/htmltokn.h>
21 #include <svtools/htmlkywd.hxx>
22 #include <svtools/htmlout.hxx>
23 #include <numrule.hxx>
24 #include <doc.hxx>
25 #include <ndtxt.hxx>
26 #include <pam.hxx>
27 
28 #include "htmlnum.hxx"
29 #include "wrthtml.hxx"
30 
31 #include <osl/diagnose.h>
32 
33 using namespace css;
34 
35 
37 {
38  m_pNextNumRuleInfo = nullptr;
39 
40  SwNodeOffset nPos = m_pCurrentPam->GetPoint()->nNode.GetIndex() + 1;
41 
42  bool bTable = false;
43  do
44  {
45  const SwNode* pNd = m_pDoc->GetNodes()[nPos];
46  if( pNd->IsTextNode() )
47  {
48  m_pNextNumRuleInfo.reset( new SwHTMLNumRuleInfo( *pNd->GetTextNode() ) );
49 
50  // Before a table we keep the old level if the same numbering is
51  // continued after the table and no new numbering is started.
52  // The table will get the indentation that corresponds to its
53  // numbering level during import.
54  if( bTable &&
55  m_pNextNumRuleInfo->GetNumRule()==GetNumInfo().GetNumRule() &&
56  !m_pNextNumRuleInfo->IsRestart() )
57  {
58  m_pNextNumRuleInfo->SetDepth( GetNumInfo().GetDepth() );
59  }
60  }
61  else if( pNd->IsTableNode() )
62  {
63  // A table is skipped so the node after table is viewed.
64  nPos = pNd->EndOfSectionIndex() + 1;
65  bTable = true;
66  }
67  else
68  {
69  // In all other case the numbering is over.
70  m_pNextNumRuleInfo.reset(new SwHTMLNumRuleInfo);
71  }
72  }
73  while( !m_pNextNumRuleInfo );
74 }
75 
77 {
78  m_pNextNumRuleInfo.reset();
79 }
80 
81 void SwHTMLWriter::SetNextNumInfo( std::unique_ptr<SwHTMLNumRuleInfo> pNxt )
82 {
83  m_pNextNumRuleInfo = std::move(pNxt);
84 }
85 
87  const SwHTMLNumRuleInfo& rInfo )
88 {
89  SwHTMLNumRuleInfo& rPrevInfo = rWrt.GetNumInfo();
90  bool bSameRule = rPrevInfo.GetNumRule() == rInfo.GetNumRule();
91  if( bSameRule && rPrevInfo.GetDepth() >= rInfo.GetDepth() &&
92  !rInfo.IsRestart() )
93  {
94  return rWrt;
95  }
96 
97  if (rWrt.mbXHTML && !rInfo.IsNumbered())
98  {
99  // If the list only consists of non-numbered text nodes, then don't start the list.
100  bool bAtLeastOneNumbered = false;
101  SwNodeOffset nPos = rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() + 1;
102  SwNumRule* pNumRule = nullptr;
103  while (true)
104  {
105  const SwNode* pNode = rWrt.m_pDoc->GetNodes()[nPos];
106  if (!pNode->IsTextNode())
107  {
108  break;
109  }
110 
111  const SwTextNode* pTextNode = pNode->GetTextNode();
112  if (!pTextNode->GetNumRule() || (pNumRule && pTextNode->GetNumRule() != pNumRule))
113  {
114  // Node is not in the same numbering as the previous one.
115  break;
116  }
117 
118  pNumRule = pTextNode->GetNumRule();
119  if (pTextNode->IsNumbered())
120  {
121  bAtLeastOneNumbered = true;
122  break;
123  }
124  ++nPos;
125  }
126 
127  if (!bAtLeastOneNumbered)
128  {
129  return rWrt;
130  }
131  }
132 
133  bool bStartValue = false;
134  if( !bSameRule && rInfo.GetDepth() )
135  {
136  OUString aName( rInfo.GetNumRule()->GetName() );
137  if( 0 != rWrt.m_aNumRuleNames.count( aName ) )
138  {
139  // The rule has been applied before
140  sal_Int16 eType = rInfo.GetNumRule()
141  ->Get( rInfo.GetDepth()-1 ).GetNumberingType();
142  if( SVX_NUM_CHAR_SPECIAL != eType && SVX_NUM_BITMAP != eType )
143  {
144  // If it's a numbering rule, the current number should be
145  // exported as start value, but only if there are no nodes
146  // within the numbering that have a lower level
147  bStartValue = true;
148  if( rInfo.GetDepth() > 1 )
149  {
151  rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() + 1;
152  do
153  {
154  const SwNode* pNd = rWrt.m_pDoc->GetNodes()[nPos];
155  if( pNd->IsTextNode() )
156  {
157  const SwTextNode *pTextNd = pNd->GetTextNode();
158  if( !pTextNd->GetNumRule() )
159  {
160  // node isn't numbered => check completed
161  break;
162  }
163 
164  OSL_ENSURE(! pTextNd->IsOutline(),
165  "outline not expected");
166 
167  if( pTextNd->GetActualListLevel() + 1 <
168  rInfo.GetDepth() )
169  {
170  if (rPrevInfo.GetDepth() == 0)
171  // previous node had no numbering => write start value
172  bStartValue = true;
173  else
174  // node is numbered, but level is lower
175  bStartValue = false;
176  // => check completed
177  break;
178  }
179  nPos++;
180  }
181  else if( pNd->IsTableNode() )
182  {
183  // skip table
184  nPos = pNd->EndOfSectionIndex() + 1;
185  }
186  else
187  {
188  // end node or sections start node -> check
189  // completed
190  break;
191  }
192  }
193  while( true );
194  }
195  }
196  }
197  else
198  {
199  rWrt.m_aNumRuleNames.insert( aName );
200  }
201  }
202 
203  OSL_ENSURE( rWrt.m_nLastParaToken == HtmlTokenId::NONE,
204  "<PRE> was not closed before <OL>." );
205  sal_uInt16 nPrevDepth =
206  (bSameRule && !rInfo.IsRestart()) ? rPrevInfo.GetDepth() : 0;
207 
208  for( sal_uInt16 i=nPrevDepth; i<rInfo.GetDepth(); i++ )
209  {
210  rWrt.OutNewLine(); // <OL>/<UL> in a new row
211 
212  rWrt.m_aBulletGrfs[i].clear();
213  OString sOut = "<" + rWrt.GetNamespace();
214  if (rWrt.mbXHTML && (nPrevDepth != 0 || i != 0))
215  {
216  sOut += OOO_STRING_SVTOOLS_HTML_li "><" + rWrt.GetNamespace();
217  }
218  const SwNumFormat& rNumFormat = rInfo.GetNumRule()->Get( i );
219  sal_Int16 eType = rNumFormat.GetNumberingType();
220  if( SVX_NUM_CHAR_SPECIAL == eType )
221  {
222  // unordered list: <UL>
224 
225  // determine the type by the bullet character
226  const char *pStr = nullptr;
227  switch( rNumFormat.GetBulletChar() )
228  {
231  break;
234  break;
237  break;
238  }
239 
240  if( pStr )
241  {
242  sOut += OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_type "=\"") + pStr + "\"";
243  }
244  }
245  else if( SVX_NUM_BITMAP == eType )
246  {
247  // Unordered list: <UL>
249  rWrt.Strm().WriteOString( sOut );
250  OutHTML_BulletImage( rWrt,
251  nullptr,
252  rNumFormat.GetBrush(),
253  rWrt.m_aBulletGrfs[i]);
254  }
255  else
256  {
257  // Ordered list: <OL>
259 
260  // determine the type by the format
261  char cType = 0;
262  switch( eType )
263  {
266  cType = 'A';
267  break;
270  cType = 'a';
271  break;
272  case SVX_NUM_ROMAN_UPPER:
273  cType = 'I';
274  break;
275  case SVX_NUM_ROMAN_LOWER:
276  cType = 'i';
277  break;
278  }
279  if( cType )
280  {
281  sOut += " " OOO_STRING_SVTOOLS_HTML_O_type "=\"" + OStringChar(cType) + "\"";
282  }
283 
284  sal_uInt16 nStartVal = rNumFormat.GetStart();
285  if( bStartValue && 1 == nStartVal && i == rInfo.GetDepth()-1 )
286  {
287  if ( rWrt.m_pCurrentPam->GetNode().GetTextNode()->GetNum() )
288  {
289  nStartVal = static_cast< sal_uInt16 >( rWrt.m_pCurrentPam->GetNode()
290  .GetTextNode()->GetNumberVector()[i] );
291  }
292  else
293  {
294  OSL_FAIL( "<OutHTML_NumberBulletListStart(..) - text node has no number." );
295  }
296  }
297  if( nStartVal != 1 )
298  {
299  sOut += " " OOO_STRING_SVTOOLS_HTML_O_start "=\"" + OString::number(static_cast<sal_Int32>(nStartVal)) + "\"";
300  }
301  }
302 
303  if (!sOut.isEmpty() && SVX_NUM_BITMAP != eType) // second condition to avoid adding extra ul, already done before.
304  rWrt.Strm().WriteOString( sOut );
305 
306  if( rWrt.m_bCfgOutStyles )
307  OutCSS1_NumberBulletListStyleOpt( rWrt, *rInfo.GetNumRule(), static_cast<sal_uInt8>(i) );
308 
309  rWrt.Strm().WriteChar( '>' );
310 
311  rWrt.IncIndentLevel(); // indent content of <OL>
312  }
313 
314  return rWrt;
315 }
316 
318  const SwHTMLNumRuleInfo& rNextInfo )
319 {
320  SwHTMLNumRuleInfo& rInfo = rWrt.GetNumInfo();
321  bool bSameRule = rNextInfo.GetNumRule() == rInfo.GetNumRule();
322  bool bListEnd = !bSameRule || rNextInfo.GetDepth() < rInfo.GetDepth() || rNextInfo.IsRestart();
323 
324  std::optional<bool> oAtLeastOneNumbered;
325  if (rWrt.mbXHTML && !rInfo.IsNumbered())
326  {
327  oAtLeastOneNumbered = false;
328  SwNodeOffset nPos = rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() - 1;
329  SwNumRule* pNumRule = nullptr;
330  while (true)
331  {
332  const SwNode* pNode = rWrt.m_pDoc->GetNodes()[nPos];
333  if (!pNode->IsTextNode())
334  {
335  break;
336  }
337 
338  const SwTextNode* pTextNode = pNode->GetTextNode();
339  if (!pTextNode->GetNumRule() || (pNumRule && pTextNode->GetNumRule() != pNumRule))
340  {
341  // Node is not in the same numbering as the next one.
342  break;
343  }
344 
345  pNumRule = pTextNode->GetNumRule();
346  if (pTextNode->IsNumbered())
347  {
348  oAtLeastOneNumbered = true;
349  break;
350  }
351  --nPos;
352  }
353  }
354 
355  if (rWrt.mbXHTML)
356  {
357  // The list is numbered if the previous text node is numbered or any other previous text
358  // node is numbered.
359  bool bPrevIsNumbered = rInfo.IsNumbered() || *oAtLeastOneNumbered;
360  // XHTML </li> for the list item content, if there is an open <li>.
361  if ((bListEnd && bPrevIsNumbered) || (!bListEnd && rNextInfo.IsNumbered()))
362  {
364  rWrt.Strm(), OStringConcatenation(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li),
365  false);
366  }
367  }
368 
369  if (!bListEnd)
370  {
371  return rWrt;
372  }
373 
374  if (rWrt.mbXHTML && !rInfo.IsNumbered())
375  {
376  // If the list only consisted of non-numbered text nodes, then don't end the list.
377  if (!*oAtLeastOneNumbered)
378  {
379  return rWrt;
380  }
381  }
382 
383  OSL_ENSURE( rWrt.m_nLastParaToken == HtmlTokenId::NONE,
384  "<PRE> was not closed before </OL>." );
385  sal_uInt16 nNextDepth =
386  (bSameRule && !rNextInfo.IsRestart()) ? rNextInfo.GetDepth() : 0;
387 
388  // MIB 23.7.97: We must loop backwards, to get the right order of </OL>/</UL>
389  for( sal_uInt16 i=rInfo.GetDepth(); i>nNextDepth; i-- )
390  {
391  rWrt.DecIndentLevel(); // indent content of <OL>
392  if( rWrt.m_bLFPossible )
393  rWrt.OutNewLine(); // </OL>/</UL> in a new line
394 
395  // a list is started or ended:
396  sal_Int16 eType = rInfo.GetNumRule()->Get( i-1 ).GetNumberingType();
397  OString aTag;
398  if( SVX_NUM_CHAR_SPECIAL == eType || SVX_NUM_BITMAP == eType)
400  else
402  HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OStringConcatenation(rWrt.GetNamespace() + aTag), false );
403  if (rWrt.mbXHTML && (nNextDepth != 0 || i != 1))
404  {
406  rWrt.Strm(), OStringConcatenation(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li),
407  /*bOn=*/false);
408  }
409  rWrt.m_bLFPossible = true;
410  }
411 
412  return rWrt;
413 }
414 
415 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SvxNumType GetNumberingType() const
SVX_NUM_CHARS_UPPER_LETTER_N
SwHTMLNumRuleInfo & GetNumInfo()
Definition: wrthtml.hxx:538
SwNodeOffset EndOfSectionIndex() const
Definition: node.hxx:687
bool mbXHTML
If XHTML markup should be written instead of HTML.
Definition: wrthtml.hxx:396
sal_UCS4 GetBulletChar() const
void FillNextNumInfo()
bool IsOutline() const
Returns if this text node is an outline.
Definition: ndtxt.cxx:4030
Writer & OutHTML_NumberBulletListStart(SwHTMLWriter &rWrt, const SwHTMLNumRuleInfo &rInfo)
#define OOO_STRING_SVTOOLS_HTML_O_start
const SvxBrushItem * GetBrush() const
SVX_NUM_CHARS_UPPER_LETTER
bool IsRestart() const
Definition: htmlnum.hxx:78
#define OOO_STRING_SVTOOLS_HTML_unorderlist
void DecIndentLevel()
Definition: wrthtml.hxx:511
const OUString & GetName() const
Definition: numrule.hxx:224
static SVT_DLLPUBLIC SvStream & Out_AsciiTag(SvStream &, std::string_view rStr, bool bOn=true)
int GetActualListLevel() const
Returns the actual list level of this text node, when it is a list item.
Definition: ndtxt.cxx:4140
void OutNewLine(bool bCheck=false)
Definition: wrthtml.cxx:1503
bool m_bLFPossible
Definition: wrthtml.hxx:381
std::set< OUString > m_aNumRuleNames
Definition: wrthtml.hxx:285
SVX_NUM_ROMAN_UPPER
SVX_NUM_ROMAN_LOWER
#define HTML_BULLETCHAR_DISC
Definition: htmlnum.hxx:32
#define OOO_STRING_SVTOOLS_HTML_li
Writer & OutCSS1_NumberBulletListStyleOpt(Writer &rWrt, const SwNumRule &rNumRule, sal_uInt8 nLevel)
Definition: css1atr.cxx:1809
DocumentType eType
SVX_NUM_BITMAP
SVX_NUM_CHAR_SPECIAL
SVX_NUM_CHARS_LOWER_LETTER
sal_uInt16 GetDepth() const
Definition: htmlnum.hxx:73
int i
#define OOO_STRING_SVTOOLS_HTML_ULTYPE_disc
void SetNextNumInfo(std::unique_ptr< SwHTMLNumRuleInfo > pNxt)
#define OOO_STRING_SVTOOLS_HTML_ULTYPE_square
SwNumRule * GetNumRule(bool bInParent=true) const
Returns numbering rule of this text node.
Definition: ndtxt.cxx:2834
OUString m_aBulletGrfs[MAXLEVEL]
Definition: wrthtml.hxx:297
bool IsNumbered() const
Definition: htmlnum.hxx:80
SwNodes & GetNodes()
Node is in which nodes-array/doc?
Definition: node.hxx:703
#define HTML_BULLETCHAR_CIRCLE
Definition: htmlnum.hxx:33
sal_uInt16 GetStart() const
SVX_NUM_CHARS_LOWER_LETTER_N
void ClearNextNumInfo()
const SwNumFormat & Get(sal_uInt16 i) const
Definition: number.cxx:86
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
SvStream & WriteOString(std::string_view rStr)
unsigned char sal_uInt8
bool IsNumbered(SwRootFrame const *pLayout=nullptr) const
Returns is this text node is numbered.
Definition: ndtxt.cxx:2902
void IncIndentLevel()
Definition: wrthtml.hxx:507
OUString aName
SwNodes & GetNodes()
Definition: doc.hxx:408
OString GetNamespace() const
Determines the prefix string needed to respect the requested namespace alias.
Definition: wrthtml.cxx:1552
SvStream & WriteChar(char nChar)
#define OOO_STRING_SVTOOLS_HTML_orderlist
std::shared_ptr< SwUnoCursor > m_pCurrentPam
Definition: shellio.hxx:410
Writer & OutHTML_NumberBulletListEnd(SwHTMLWriter &rWrt, const SwHTMLNumRuleInfo &rNextInfo)
SvStream & Strm()
Definition: writer.cxx:215
#define OOO_STRING_SVTOOLS_HTML_O_type
bool IsTableNode() const
Definition: node.hxx:650
bool m_bCfgOutStyles
Definition: wrthtml.hxx:341
#define OOO_STRING_SVTOOLS_HTML_ULTYPE_circle
#define HTML_BULLETCHAR_SQUARE
Definition: htmlnum.hxx:34
bool IsTextNode() const
Definition: node.hxx:646
SwNumRule * GetNumRule()
Definition: htmlnum.hxx:69
SwDoc * m_pDoc
Definition: shellio.hxx:408
sal_uInt16 nPos
HtmlTokenId m_nLastParaToken
Definition: wrthtml.hxx:312
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:864
Writer & OutHTML_BulletImage(Writer &rWrt, const char *pTag, const SvxBrushItem *pBrush, const OUString &rGraphicURL)
Base class of the Writer document model elements.
Definition: node.hxx:81