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