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 <stdlib.h>
21 #include <hintids.hxx>
22 #include <comphelper/string.hxx>
23 #include <svl/urihelper.hxx>
24 #include <svl/languageoptions.hxx>
25 #include <rtl/tencinfo.h>
26 #include <sfx2/linkmgr.hxx>
27 #include <sfx2/docfile.hxx>
28 
29 #include <svtools/htmlcfg.hxx>
30 #include <svtools/htmltokn.h>
31 #include <svtools/htmlkywd.hxx>
32 #include <vcl/svapp.hxx>
33 #include <i18nlangtag/languagetag.hxx>
34 #include <sfx2/frmhtmlw.hxx>
35 #include <svx/xoutbmp.hxx>
36 #include <svx/unobrushitemhelper.hxx>
37 #include <sfx2/htmlmode.hxx>
38 #include <editeng/lrspitem.hxx>
39 #include <editeng/colritem.hxx>
40 #include <editeng/brushitem.hxx>
41 #include <editeng/langitem.hxx>
42 #include <svl/stritem.hxx>
43 #include <editeng/frmdiritem.hxx>
44 
45 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
46 #include <com/sun/star/document/XDocumentProperties.hpp>
47 #include <com/sun/star/frame/XModel.hpp>
48 #include <fmthdft.hxx>
49 #include <fmtfld.hxx>
50 #include <fmtpdsc.hxx>
51 #include <txatbase.hxx>
52 #include <frmatr.hxx>
53 #include <charfmt.hxx>
54 #include <docary.hxx>
55 #include <pam.hxx>
56 #include <doc.hxx>
57 #include <ndtxt.hxx>
58 #include <mdiexp.hxx>
59 #include <fltini.hxx>
60 #include <viewopt.hxx>
61 #include <IMark.hxx>
62 #include <poolfmt.hxx>
63 #include <pagedesc.hxx>
64 #include <section.hxx>
65 #include <swtable.hxx>
66 #include <fldbas.hxx>
67 #include <fmtclds.hxx>
68 #include <docsh.hxx>
69 #include "wrthtml.hxx"
70 #include "htmlnum.hxx"
71 #include "htmlfly.hxx"
72 #include <swmodule.hxx>
73 #include <strings.hrc>
74 #include <swerror.h>
75 #include <rtl/strbuf.hxx>
76 #include <IDocumentSettingAccess.hxx>
77 #include <IDocumentStylePoolAccess.hxx>
78 #include <IDocumentMarkAccess.hxx>
79 #include <xmloff/odffields.hxx>
80 #include <tools/urlobj.hxx>
81 #include <osl/file.hxx>
82 #include <tools/stream.hxx>
83 #include <comphelper/scopeguard.hxx>
84 #include <unotools/tempfile.hxx>
85 #include <comphelper/sequenceashashmap.hxx>
86 
87 #define MAX_INDENT_LEVEL 20
88 
89 using namespace css;
90 
91 static char sIndentTabs[MAX_INDENT_LEVEL+2] =
92     "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
93 
SwHTMLWriter(const OUString & rBaseURL,const OUString & rFilterOptions)94 SwHTMLWriter::SwHTMLWriter( const OUString& rBaseURL, const OUString& rFilterOptions )
95     : m_pNumRuleInfo(new SwHTMLNumRuleInfo)
96     , m_nHTMLMode(0)
97     , m_eCSS1Unit(FieldUnit::NONE)
98     , mxFormComps()
99     , m_pStartNdIdx(nullptr)
100     , m_pCurrPageDesc(nullptr)
101     , m_pFormatFootnote(nullptr)
102     , m_nWarn(0)
103     , m_nLastLFPos(0)
104     , m_nLastParaToken(HtmlTokenId::NONE)
105     , m_nBkmkTabPos(-1)
106     , m_nImgMapCnt(1)
107     , m_nFormCntrlCnt(0)
108     , m_nEndNote(0)
109     , m_nFootNote(0)
110     , m_nLeftMargin(0)
111     , m_nDfltLeftMargin(0)
112     , m_nDfltRightMargin(0)
113     , m_nFirstLineIndent(0)
114     , m_nDfltFirstLineIndent(0)
115     , m_nDfltTopMargin(0)
116     , m_nDfltBottomMargin(0)
117     , m_nIndentLvl(0)
118     , m_nWishLineLen(0)
119     , m_nDefListLvl(0)
120     , m_nDefListMargin(0)
121     , m_nHeaderFooterSpace(0)
122     , m_nTextAttrsToIgnore(0)
123     , m_nExportMode(0)
124     , m_nCSS1OutMode(0)
125     , m_nCSS1Script(CSS1_OUTMODE_WESTERN)
126     , m_nDirection(SvxFrameDirection::Horizontal_LR_TB)
127     , m_eDestEnc(RTL_TEXTENCODING_MS_1252)
128     , m_eLang(LANGUAGE_DONTKNOW)
129     , m_bCfgOutStyles( false )
130     , m_bCfgPreferStyles( false )
131     , m_bCfgFormFeed( false )
132     , m_bCfgStarBasic( false )
133     , m_bCfgCpyLinkedGrfs( false )
134     , m_bFirstLine(true)
135     , m_bTagOn( false )
136     , m_bTextAttr( false )
137     , m_bOutOpts( false )
138     , m_bOutTable( false )
139     , m_bOutHeader( false )
140     , m_bOutFooter( false )
141     , m_bOutFlyFrame( false )
142     , m_bFirstCSS1Rule( false )
143     , m_bFirstCSS1Property( false )
144     , m_bCSS1IgnoreFirstPageDesc( false )
145     , m_bNoAlign( false )
146     , m_bClearLeft( false )
147     , m_bClearRight( false )
148     , m_bLFPossible( false )
149     , m_bPreserveForm( false )
150     , m_bCfgNetscape4( false )
151     , mbSkipImages(false)
152     , mbSkipHeaderFooter(false)
153     , mbEmbedImages(false)
154     , mbIndexingOutput(false)
155     , m_bCfgPrintLayout( false )
156     , m_bParaDotLeaders( false )
157 {
158     SetBaseURL(rBaseURL);
159 
160     if (rBaseURL.isEmpty())
161     {
162         // Paste: set base URL to a tempfile, so images are not lost.
163         mpTempBaseURL.reset(new utl::TempFile());
164         mpTempBaseURL->EnableKillingFile();
165         SetBaseURL(mpTempBaseURL->GetURL());
166     }
167 
168     SetupFilterOptions(rFilterOptions);
169 
170     if (mbXHTML)
171     {
172         m_bNoAlign = true;
173     }
174 }
175 
~SwHTMLWriter()176 SwHTMLWriter::~SwHTMLWriter()
177 {
178 }
179 
ReleaseNextNumInfo()180 std::unique_ptr<SwHTMLNumRuleInfo> SwHTMLWriter::ReleaseNextNumInfo()
181 {
182     return std::move(m_pNextNumRuleInfo);
183 }
184 
SetupFilterOptions(SfxMedium & rMedium)185 void SwHTMLWriter::SetupFilterOptions(SfxMedium& rMedium)
186 {
187     const SfxItemSet* pSet = rMedium.GetItemSet();
188     if (pSet == nullptr)
189         return;
190 
191     const SfxPoolItem* pItem;
192     if (pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) != SfxItemState::SET)
193         return;
194 
195 
196     const OUString sFilterOptions = static_cast<const SfxStringItem*>(pItem)->GetValue();
197     SetupFilterOptions(sFilterOptions);
198 
199     comphelper::SequenceAsHashMap aStoreMap(rMedium.GetArgs());
200     auto it = aStoreMap.find("RTFOLEMimeType");
201     if (it != aStoreMap.end())
202     {
203         it->second >>= m_aRTFOLEMimeType;
204     }
205 
206     it = aStoreMap.find("ExportImagesAsOLE");
207     if (it != aStoreMap.end())
208     {
209         it->second >>= m_bExportImagesAsOLE;
210     }
211 
212     it = aStoreMap.find("ShapeDPI");
213     if (it != aStoreMap.end())
214     {
215         sal_Int32 nVal{};
216         it->second >>= nVal;
217         m_nShapeDPI.emplace(nVal);
218     }
219 }
220 
SetupFilterOptions(const OUString & rFilterOptions)221 void SwHTMLWriter::SetupFilterOptions(const OUString& rFilterOptions)
222 {
223     if (rFilterOptions == "SkipImages")
224     {
225         mbSkipImages = true;
226     }
227     else if (rFilterOptions == "SkipHeaderFooter")
228     {
229         mbSkipHeaderFooter = true;
230     }
231     else if (rFilterOptions == "EmbedImages")
232     {
233         mbEmbedImages = true;
234     }
235     else if (rFilterOptions == "IndexingOutput")
236     {
237         mbIndexingOutput = true;
238         mbSkipHeaderFooter = true;
239         mbSkipImages = true;
240         mbXHTML = true;
241     }
242 
243     const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(rFilterOptions);
244     static const OUStringLiteral aXhtmlNsKey(u"xhtmlns=");
245     for (const auto& rOption : aOptionSeq)
246     {
247         if (rOption == "XHTML")
248             mbXHTML = true;
249         else if (rOption.startsWith(aXhtmlNsKey))
250         {
251             maNamespace = rOption.copy(aXhtmlNsKey.getLength()).toUtf8();
252             if (maNamespace == "reqif-xhtml")
253             {
254                 mbReqIF = true;
255                 // XHTML is always just a fragment inside ReqIF.
256                 mbSkipHeaderFooter = true;
257             }
258             // XHTML namespace implies XHTML.
259             mbXHTML = true;
260         }
261     }
262 }
263 
WriteStream()264 ErrCode SwHTMLWriter::WriteStream()
265 {
266     if (!SW_MOD())
267         return ERRCODE_ABORT;
268     // Intercept paste output if requested.
269     char* pPasteEnv = getenv("SW_DEBUG_HTML_PASTE_TO");
270     std::unique_ptr<SvStream> pPasteStream;
271     SvStream* pOldPasteStream = nullptr;
272     if (pPasteEnv)
273     {
274         OUString aPasteStr;
275         if (osl::FileBase::getFileURLFromSystemPath(OUString::fromUtf8(pPasteEnv), aPasteStr)
276                    == osl::FileBase::E_None)
277         {
278             pPasteStream.reset(new SvFileStream(aPasteStr, StreamMode::WRITE));
279             pOldPasteStream = &Strm();
280             SetStream(pPasteStream.get());
281         }
282     }
283     comphelper::ScopeGuard g([this, pOldPasteStream] { this->SetStream(pOldPasteStream); });
284 
285     HtmlWriter aHtmlWriter(Strm(), GetNamespace());
286 
287     SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get();
288 
289     // font heights 1-7
290     m_aFontHeights[0] = rHtmlOptions.GetFontSize( 0 ) * 20;
291     m_aFontHeights[1] = rHtmlOptions.GetFontSize( 1 ) * 20;
292     m_aFontHeights[2] = rHtmlOptions.GetFontSize( 2 ) * 20;
293     m_aFontHeights[3] = rHtmlOptions.GetFontSize( 3 ) * 20;
294     m_aFontHeights[4] = rHtmlOptions.GetFontSize( 4 ) * 20;
295     m_aFontHeights[5] = rHtmlOptions.GetFontSize( 5 ) * 20;
296     m_aFontHeights[6] = rHtmlOptions.GetFontSize( 6 ) * 20;
297 
298     // output styles anyway
299     // (then also top and bottom paragraph spacing)
300     m_nExportMode = rHtmlOptions.GetExportMode();
301     m_nHTMLMode = GetHtmlMode(nullptr);
302 
303     if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_NS40 == m_nExportMode )
304         m_nHTMLMode |= HTMLMODE_BLOCK_SPACER;
305 
306     if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_MSIE == m_nExportMode )
307         m_nHTMLMode |= (HTMLMODE_FLOAT_FRAME | HTMLMODE_LSPACE_IN_NUMBER_BULLET);
308 
309     if( HTML_CFG_MSIE == m_nExportMode )
310         m_nHTMLMode |= HTMLMODE_NBSP_IN_TABLES;
311 
312     if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_NS40 == m_nExportMode || HTML_CFG_MSIE == m_nExportMode )
313         m_nHTMLMode |= HTMLMODE_ABS_POS_FLY | HTMLMODE_ABS_POS_DRAW;
314 
315     if( HTML_CFG_WRITER == m_nExportMode )
316         m_nHTMLMode |= HTMLMODE_FLY_MARGINS;
317 
318     if( HTML_CFG_NS40 == m_nExportMode )
319         m_nHTMLMode |= HTMLMODE_BORDER_NONE;
320 
321     m_nHTMLMode |= HTMLMODE_FONT_GENERIC;
322 
323     if( HTML_CFG_NS40==m_nExportMode )
324         m_nHTMLMode |= HTMLMODE_NO_CONTROL_CENTERING;
325 
326     m_bCfgOutStyles = IsHTMLMode(HTMLMODE_SOME_STYLES | HTMLMODE_FULL_STYLES);
327     m_bCfgNetscape4 = (HTML_CFG_NS40 == m_nExportMode);
328 
329     if( IsHTMLMode(HTMLMODE_SOME_STYLES | HTMLMODE_FULL_STYLES) )
330         m_nHTMLMode |= HTMLMODE_PRINT_EXT;
331 
332     m_eCSS1Unit = SW_MOD()->GetMetric( m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) );
333 
334     bool bWriteUTF8 = m_bWriteClipboardDoc;
335     m_eDestEnc = bWriteUTF8 ? RTL_TEXTENCODING_UTF8 : rHtmlOptions.GetTextEncoding();
336     const char *pCharSet = rtl_getBestMimeCharsetFromTextEncoding( m_eDestEnc );
337     m_eDestEnc = rtl_getTextEncodingFromMimeCharset( pCharSet );
338 
339     // Only for the MS-IE we favour the export of styles.
340     m_bCfgPreferStyles = HTML_CFG_MSIE == m_nExportMode;
341 
342     m_bCfgStarBasic = rHtmlOptions.IsStarBasic();
343 
344     m_bCfgFormFeed = !IsHTMLMode(HTMLMODE_PRINT_EXT);
345     m_bCfgCpyLinkedGrfs = rHtmlOptions.IsSaveGraphicsLocal();
346 
347     m_bCfgPrintLayout = rHtmlOptions.IsPrintLayoutExtension();
348 
349     // get HTML template
350     bool bOldHTMLMode = false;
351     SwTextFormatColls::size_type nOldTextFormatCollCnt = 0;
352     SwCharFormats::size_type nOldCharFormatCnt = 0;
353 
354     OSL_ENSURE( !m_xTemplate.is(), "Where is the HTML template coming from?" );
355     m_xTemplate = static_cast<HTMLReader*>(ReadHTML)->GetTemplateDoc(*m_pDoc);
356     if( m_xTemplate.is() )
357     {
358         bOldHTMLMode = m_xTemplate->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
359         m_xTemplate->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, true);
360 
361         nOldTextFormatCollCnt = m_xTemplate->GetTextFormatColls()->size();
362         nOldCharFormatCnt = m_xTemplate->GetCharFormats()->size();
363     }
364 
365     if( m_bShowProgress )
366         ::StartProgress( STR_STATSTR_W4WWRITE, 0, m_pDoc->GetNodes().Count(),
367                          m_pDoc->GetDocShell());
368 
369     m_xDfltColor.reset();
370     m_xFootEndNotes.reset();
371     m_pFormatFootnote = nullptr;
372     m_bOutTable = m_bOutHeader = m_bOutFooter = m_bOutFlyFrame = false;
373     mxFormComps.clear();
374     m_nFormCntrlCnt = 0;
375     m_bPreserveForm = false;
376     m_bClearLeft = m_bClearRight = false;
377     m_bLFPossible = false;
378 
379     m_nLeftMargin = m_nDfltLeftMargin = m_nDfltRightMargin = 0;
380     m_nDfltTopMargin = m_nDfltBottomMargin = 0;
381     m_nFirstLineIndent = m_nDfltFirstLineIndent = 0;
382     m_bFirstCSS1Property = m_bFirstCSS1Rule = false;
383     m_bCSS1IgnoreFirstPageDesc = false;
384     m_nIndentLvl = 0;
385     m_nWishLineLen = 70;
386     m_nLastLFPos = 0;
387     m_nDefListLvl = 0;
388     m_nDefListMargin = ((m_xTemplate.is() && !m_bCfgOutStyles) ? m_xTemplate.get() : m_pDoc)
389         ->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_HTML_DD, false )
390         ->GetLRSpace().GetTextLeft();
391     m_nHeaderFooterSpace = 0;
392     m_nTextAttrsToIgnore = 0;
393     m_nCSS1OutMode = 0;
394     SvtScriptType nScript = SvtLanguageOptions::GetScriptTypeOfLanguage( GetAppLanguage() );
395     switch( nScript )
396     {
397     case SvtScriptType::ASIAN:
398         m_nCSS1Script = CSS1_OUTMODE_CJK;
399         break;
400     case SvtScriptType::COMPLEX:
401         m_nCSS1Script = CSS1_OUTMODE_CTL;
402         break;
403     default:
404         m_nCSS1Script = CSS1_OUTMODE_WESTERN;
405         break;
406     }
407     m_eLang = static_cast<const SvxLanguageItem&>(m_pDoc
408             ->GetDefault(GetLangWhichIdFromScript(m_nCSS1Script))).GetLanguage();
409 
410     m_nFootNote = m_nEndNote = 0;
411 
412     m_nWarn = ERRCODE_NONE;
413     GetNumInfo().Clear();
414     m_pNextNumRuleInfo = nullptr;
415 
416     OString aStartTags;
417 
418     // respect table and section at document beginning
419     {
420         SwTableNode * pTNd = m_pCurrentPam->GetNode().FindTableNode();
421         if( pTNd && m_bWriteAll )
422         {
423             // start with table node !!
424             m_pCurrentPam->GetPoint()->nNode = *pTNd;
425 
426             if( m_bWriteOnlyFirstTable )
427                 m_pCurrentPam->GetMark()->nNode = *pTNd->EndOfSectionNode();
428         }
429 
430         // first node (with can contain a page break)
431         m_pStartNdIdx = new SwNodeIndex( m_pCurrentPam->GetPoint()->nNode );
432 
433         SwSectionNode * pSNd = m_pCurrentPam->GetNode().FindSectionNode();
434         while( pSNd )
435         {
436             if( m_bWriteAll )
437             {
438                 // start with section node !!
439                 m_pCurrentPam->GetPoint()->nNode = *pSNd;
440             }
441             else
442             {
443                 OSL_ENSURE( SectionType::FileLink != pSNd->GetSection().GetType(),
444                         "Export linked areas at document beginning is not implemented" );
445 
446                 // save only the tag of section
447                 OString aName = HTMLOutFuncs::ConvertStringToHTML(
448                     pSNd->GetSection().GetSectionName(), m_eDestEnc,
449                     &m_aNonConvertableCharacters );
450 
451                 aStartTags =
452                     "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_division
453                     " " OOO_STRING_SVTOOLS_HTML_O_id
454                     "=\"" + aName + "\">" +
455                     aStartTags;
456             }
457             // FindSectionNode() on a SectionNode return the same!
458             pSNd = pSNd->StartOfSectionNode()->FindSectionNode();
459         }
460     }
461 
462     // create table of the floating frames, but only when the whole
463     // document is saved
464     m_pHTMLPosFlyFrames = nullptr;
465     CollectFlyFrames();
466     m_nLastParaToken = HtmlTokenId::NONE;
467     GetControls();
468     CollectLinkTargets();
469 
470     sal_uInt16 nHeaderAttrs = 0;
471     m_pCurrPageDesc = MakeHeader(aHtmlWriter, nHeaderAttrs);
472 
473     m_bLFPossible = true;
474 
475     // output forms which contain only HiddenControls
476     OutHiddenForms();
477 
478     if( !aStartTags.isEmpty() )
479         Strm().WriteOString( aStartTags );
480 
481     const SfxPoolItem *pItem;
482     const SfxItemSet& rPageItemSet = m_pCurrPageDesc->GetMaster().GetAttrSet();
483     if( !m_bWriteClipboardDoc && m_pDoc->GetDocShell() &&
484          (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) &&
485           !m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)) &&
486         SfxItemState::SET == rPageItemSet.GetItemState( RES_HEADER, true, &pItem) )
487     {
488         const SwFrameFormat *pHeaderFormat =
489             static_cast<const SwFormatHeader *>(pItem)->GetHeaderFormat();
490         if( pHeaderFormat )
491             OutHTML_HeaderFooter( *this, *pHeaderFormat, true );
492     }
493 
494     m_nTextAttrsToIgnore = nHeaderAttrs;
495     Out_SwDoc( m_pOrigPam );
496     m_nTextAttrsToIgnore = 0;
497 
498     if( mxFormComps.is() )
499         OutForm( false, mxFormComps );
500 
501     if( m_xFootEndNotes )
502         OutFootEndNotes();
503 
504     if( !m_bWriteClipboardDoc && m_pDoc->GetDocShell() &&
505         (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) && !m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE))  &&
506         SfxItemState::SET == rPageItemSet.GetItemState( RES_FOOTER, true, &pItem) )
507     {
508         const SwFrameFormat *pFooterFormat =
509             static_cast<const SwFormatFooter *>(pItem)->GetFooterFormat();
510         if( pFooterFormat )
511             OutHTML_HeaderFooter( *this, *pFooterFormat, false );
512     }
513 
514     if( m_bLFPossible )
515         OutNewLine();
516     if (!mbSkipHeaderFooter)
517     {
518         HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_body), false );
519         OutNewLine();
520         HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_html), false );
521     }
522     else if (mbReqIF)
523     {
524         // ReqIF: end xhtml.BlkStruct.class.
525         HTMLOutFuncs::Out_AsciiTag(Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false);
526     }
527     else if (mbIndexingOutput)
528     {
529         aHtmlWriter.end("indexing");
530     }
531 
532     // delete the table with floating frames
533     OSL_ENSURE( !m_pHTMLPosFlyFrames, "Were not all frames output?" );
534     m_pHTMLPosFlyFrames.reset();
535 
536     m_aHTMLControls.clear();
537 
538     m_CharFormatInfos.clear();
539     m_TextCollInfos.clear();
540     m_aImgMapNames.clear();
541     m_aImplicitMarks.clear();
542     m_aOutlineMarks.clear();
543     m_aOutlineMarkPoss.clear();
544     m_aNumRuleNames.clear();
545     m_aScriptParaStyles.clear();
546     m_aScriptTextStyles.clear();
547 
548     m_xDfltColor.reset();
549 
550     delete m_pStartNdIdx;
551     m_pStartNdIdx = nullptr;
552 
553     mxFormComps.clear();
554 
555     OSL_ENSURE( !m_xFootEndNotes,
556             "SwHTMLWriter::Write: Footnotes not deleted by OutFootEndNotes" );
557 
558     m_pCurrPageDesc = nullptr;
559 
560     ClearNextNumInfo();
561 
562     for(OUString & s : m_aBulletGrfs)
563         s.clear();
564 
565     m_aNonConvertableCharacters.clear();
566 
567     if( m_bShowProgress )
568         ::EndProgress( m_pDoc->GetDocShell() );
569 
570     if( m_xTemplate.is() )
571     {
572         // delete character and paragraph templates created during export
573         auto nTextFormatCollCnt = m_xTemplate->GetTextFormatColls()->size();
574         while( nTextFormatCollCnt > nOldTextFormatCollCnt )
575             m_xTemplate->DelTextFormatColl( --nTextFormatCollCnt );
576         OSL_ENSURE( m_xTemplate->GetTextFormatColls()->size() == nOldTextFormatCollCnt,
577                 "wrong number of TextFormatColls deleted" );
578 
579         auto nCharFormatCnt = m_xTemplate->GetCharFormats()->size();
580         while( nCharFormatCnt > nOldCharFormatCnt )
581             m_xTemplate->DelCharFormat( --nCharFormatCnt );
582         OSL_ENSURE( m_xTemplate->GetCharFormats()->size() == nOldCharFormatCnt,
583                 "wrong number of CharFormats deleted" );
584 
585         // restore HTML mode
586         m_xTemplate->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, bOldHTMLMode);
587 
588         m_xTemplate.clear();
589     }
590 
591     return m_nWarn;
592 }
593 
lcl_html_GetFormatCol(const SwSection & rSection,const SwSectionFormat & rFormat)594 static const SwFormatCol *lcl_html_GetFormatCol( const SwSection& rSection,
595                                        const SwSectionFormat& rFormat )
596 {
597     const SwFormatCol *pCol = nullptr;
598 
599     const SfxPoolItem* pItem;
600     if( SectionType::FileLink != rSection.GetType() &&
601         SfxItemState::SET == rFormat.GetAttrSet().GetItemState(RES_COL,false,&pItem) &&
602         static_cast<const SwFormatCol *>(pItem)->GetNumCols() > 1 )
603     {
604         pCol = static_cast<const SwFormatCol *>(pItem);
605     }
606 
607     return pCol;
608 }
609 
lcl_html_IsMultiColStart(const SwHTMLWriter & rHTMLWrt,sal_uLong nIndex)610 static bool lcl_html_IsMultiColStart( const SwHTMLWriter& rHTMLWrt, sal_uLong nIndex )
611 {
612     bool bRet = false;
613     const SwSectionNode *pSectNd =
614         rHTMLWrt.m_pDoc->GetNodes()[nIndex]->GetSectionNode();
615     if( pSectNd )
616     {
617         const SwSection& rSection = pSectNd->GetSection();
618         const SwSectionFormat *pFormat = rSection.GetFormat();
619         if( pFormat && lcl_html_GetFormatCol( rSection, *pFormat ) )
620             bRet = true;
621     }
622 
623     return bRet;
624 }
625 
lcl_html_IsMultiColEnd(const SwHTMLWriter & rHTMLWrt,sal_uLong nIndex)626 static bool lcl_html_IsMultiColEnd( const SwHTMLWriter& rHTMLWrt, sal_uLong nIndex )
627 {
628     bool bRet = false;
629     const SwEndNode *pEndNd = rHTMLWrt.m_pDoc->GetNodes()[nIndex]->GetEndNode();
630     if( pEndNd )
631         bRet = lcl_html_IsMultiColStart( rHTMLWrt,
632                                          pEndNd->StartOfSectionIndex() );
633 
634     return bRet;
635 }
636 
lcl_html_OutSectionStartTag(SwHTMLWriter & rHTMLWrt,const SwSection & rSection,const SwSectionFormat & rFormat,const SwFormatCol * pCol,bool bContinued=false)637 static void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt,
638                                      const SwSection& rSection,
639                                      const SwSectionFormat& rFormat,
640                                   const SwFormatCol *pCol,
641                                   bool bContinued=false )
642 {
643     OSL_ENSURE( pCol || !bContinued, "Continuation of DIV" );
644 
645     if( rHTMLWrt.m_bLFPossible )
646         rHTMLWrt.OutNewLine();
647 
648     OStringBuffer sOut;
649     sOut.append('<').append(OOO_STRING_SVTOOLS_HTML_division);
650 
651     const OUString& rName = rSection.GetSectionName();
652     if( !rName.isEmpty() && !bContinued )
653     {
654         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_id "=\"");
655         rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() );
656         HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), rName, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters );
657         sOut.append('\"');
658     }
659 
660     SvxFrameDirection nDir = rHTMLWrt.GetHTMLDirection( rFormat.GetAttrSet() );
661     rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() );
662     rHTMLWrt.OutDirection( nDir );
663 
664     if( SectionType::FileLink == rSection.GetType() )
665     {
666         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_href "=\"");
667         rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() );
668 
669         const OUString& aFName = rSection.GetLinkFileName();
670         sal_Int32 nIdx{ 0 };
671         OUString aURL( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) );
672         OUString aFilter( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) );
673         OUString aSection( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) );
674 
675         OUString aEncURL( URIHelper::simpleNormalizedMakeRelative(rHTMLWrt.GetBaseURL(), aURL ) );
676         sal_Unicode cDelim = 255U;
677         bool bURLContainsDelim = (-1 != aEncURL.indexOf( cDelim ) );
678 
679         HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aEncURL,
680                                   rHTMLWrt.m_eDestEnc,
681                                   &rHTMLWrt.m_aNonConvertableCharacters );
682         const char* const pDelim = "&#255;";
683         if( !aFilter.isEmpty() || !aSection.isEmpty() || bURLContainsDelim )
684             rHTMLWrt.Strm().WriteCharPtr( pDelim );
685         if( !aFilter.isEmpty() )
686             HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aFilter, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters );
687         if( !aSection.isEmpty() || bURLContainsDelim  )
688                 rHTMLWrt.Strm().WriteCharPtr( pDelim );
689         if( !aSection.isEmpty() )
690         {
691             sal_Int32 nPos = aSection.indexOf( '%' );
692             while( nPos != -1 )
693             {
694                 aSection = aSection.replaceAt(nPos, 1, "%25");
695                 nPos = aSection.indexOf( '%', nPos+3 );
696             }
697             nPos = aSection.indexOf( cDelim );
698             while( nPos != -1 )
699             {
700                 aSection = aSection.replaceAt(nPos, 1, "%FF" );
701                 nPos = aSection.indexOf( cDelim, nPos+3 );
702             }
703             HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aSection,
704                                       rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters );
705         }
706         sOut.append('\"');
707     }
708     else if( pCol )
709     {
710         // minimum gutter width
711         sal_uInt16 nGutter = pCol->GetGutterWidth( true );
712         if( nGutter!=USHRT_MAX )
713         {
714             if( nGutter && Application::GetDefaultDevice() )
715             {
716                 nGutter = o3tl::narrowing<sal_uInt16>(Application::GetDefaultDevice()
717                                 ->LogicToPixel( Size(nGutter, 0), MapMode(MapUnit::MapTwip) ).Width());
718             }
719             sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_gutter "=\"" + OString::number(nGutter) + "\"");
720         }
721     }
722 
723     rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() );
724     if( rHTMLWrt.IsHTMLMode( rHTMLWrt.m_bCfgOutStyles ? HTMLMODE_ON : 0 ) )
725         rHTMLWrt.OutCSS1_SectionFormatOptions( rFormat, pCol );
726 
727     rHTMLWrt.Strm().WriteChar( '>' );
728 
729     rHTMLWrt.m_bLFPossible = true;
730     if( !rName.isEmpty() && !bContinued )
731         rHTMLWrt.OutImplicitMark( rName, "region" );
732 
733     rHTMLWrt.IncIndentLevel();
734 }
735 
lcl_html_OutSectionEndTag(SwHTMLWriter & rHTMLWrt)736 static void lcl_html_OutSectionEndTag( SwHTMLWriter& rHTMLWrt )
737 {
738     rHTMLWrt.DecIndentLevel();
739     if( rHTMLWrt.m_bLFPossible )
740         rHTMLWrt.OutNewLine();
741     HTMLOutFuncs::Out_AsciiTag( rHTMLWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
742     rHTMLWrt.m_bLFPossible = true;
743 }
744 
OutHTML_Section(Writer & rWrt,const SwSectionNode & rSectNd)745 static Writer& OutHTML_Section( Writer& rWrt, const SwSectionNode& rSectNd )
746 {
747     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
748 
749     // End <PRE> and any <DL>, because a definition list's level may
750     // change inside the section.
751     rHTMLWrt.ChangeParaToken( HtmlTokenId::NONE );
752     rHTMLWrt.OutAndSetDefList( 0 );
753 
754     const SwSection& rSection = rSectNd.GetSection();
755     const SwSectionFormat *pFormat = rSection.GetFormat();
756     OSL_ENSURE( pFormat, "Section without a format?" );
757 
758     bool bStartTag = true;
759     bool bEndTag = true;
760     const SwSectionFormat *pSurrFormat = nullptr;
761     const SwSectionNode *pSurrSectNd = nullptr;
762     const SwSection *pSurrSection = nullptr;
763     const SwFormatCol *pSurrCol = nullptr;
764 
765     sal_uInt32 nSectSttIdx = rSectNd.GetIndex();
766     sal_uInt32 nSectEndIdx = rSectNd.EndOfSectionIndex();
767     const SwFormatCol *pCol = lcl_html_GetFormatCol( rSection, *pFormat );
768     if( pCol )
769     {
770         // If the next node is a columned section node, too, don't export
771         // an empty section.
772         if( lcl_html_IsMultiColStart( rHTMLWrt, nSectSttIdx+1 ) )
773             bStartTag = false;
774 
775         // The same applies if the section end with another columned section.
776         if( lcl_html_IsMultiColEnd( rHTMLWrt, nSectEndIdx-1 ) )
777             bEndTag = false;
778 
779         // is there a columned section around this one?
780         const SwStartNode *pSttNd = rSectNd.StartOfSectionNode();
781         if( pSttNd )
782         {
783             pSurrSectNd = pSttNd->FindSectionNode();
784             if( pSurrSectNd )
785             {
786                 const SwStartNode *pBoxSttNd = pSttNd->FindTableBoxStartNode();
787                 if( !pBoxSttNd ||
788                     pBoxSttNd->GetIndex() < pSurrSectNd->GetIndex() )
789                 {
790                     pSurrSection = &pSurrSectNd->GetSection();
791                     pSurrFormat = pSurrSection->GetFormat();
792                     if( pSurrFormat )
793                         pSurrCol = lcl_html_GetFormatCol( *pSurrSection,
794                                                        *pSurrFormat );
795                 }
796             }
797         }
798     }
799 
800     // The surrounding section must be closed before the current one is
801     // opened, except that it start immediately before the current one or
802     // another end immediately before the current one
803     if( pSurrCol && nSectSttIdx - pSurrSectNd->GetIndex() > 1 &&
804         !lcl_html_IsMultiColEnd( rHTMLWrt, nSectSttIdx-1 ) )
805         lcl_html_OutSectionEndTag( rHTMLWrt );
806 
807     if( bStartTag )
808         lcl_html_OutSectionStartTag( rHTMLWrt, rSection, *pFormat, pCol );
809 
810     {
811         HTMLSaveData aSaveData( rHTMLWrt,
812             rHTMLWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex()+1,
813             rSectNd.EndOfSectionIndex(),
814             false, pFormat );
815         rHTMLWrt.Out_SwDoc( rHTMLWrt.m_pCurrentPam.get() );
816     }
817 
818     rHTMLWrt.m_pCurrentPam->GetPoint()->nNode = *rSectNd.EndOfSectionNode();
819 
820     if( bEndTag )
821         lcl_html_OutSectionEndTag( rHTMLWrt );
822 
823     // The surrounding section must be started again, except that it ends
824     // immediately behind the current one.
825     if( pSurrCol &&
826         pSurrSectNd->EndOfSectionIndex() - nSectEndIdx > 1 &&
827         !lcl_html_IsMultiColStart( rHTMLWrt, nSectEndIdx+1 ) )
828         lcl_html_OutSectionStartTag( rHTMLWrt, *pSurrSection, *pSurrFormat,
829                                      pSurrCol, true );
830 
831     return rWrt;
832 }
833 
Out_SwDoc(SwPaM * pPam)834 void SwHTMLWriter::Out_SwDoc( SwPaM* pPam )
835 {
836     bool bSaveWriteAll = m_bWriteAll;     // save
837 
838     // search next text::Bookmark position from text::Bookmark table
839     m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1;
840 
841     // output all areas of PaM's in the HTML file
842     do {
843         m_bWriteAll = bSaveWriteAll;
844         m_bFirstLine = true;
845 
846         // search for first on PaM created FlyFrame
847         // still missing:
848 
849         while( m_pCurrentPam->GetPoint()->nNode.GetIndex() < m_pCurrentPam->GetMark()->nNode.GetIndex() ||
850               (m_pCurrentPam->GetPoint()->nNode.GetIndex() == m_pCurrentPam->GetMark()->nNode.GetIndex() &&
851                m_pCurrentPam->GetPoint()->nContent.GetIndex() <= m_pCurrentPam->GetMark()->nContent.GetIndex()) )
852         {
853             SwNode&  rNd = m_pCurrentPam->GetNode();
854 
855             OSL_ENSURE( !(rNd.IsGrfNode() || rNd.IsOLENode()),
856                     "Unexpected Grf- or OLE-Node here" );
857             if( rNd.IsTextNode() )
858             {
859                 SwTextNode* pTextNd = rNd.GetTextNode();
860 
861                 if( !m_bFirstLine )
862                     m_pCurrentPam->GetPoint()->nContent.Assign( pTextNd, 0 );
863 
864                 OutHTML_SwTextNode( *this, *pTextNd );
865             }
866             else if( rNd.IsTableNode() )
867             {
868                 OutHTML_SwTableNode( *this, *rNd.GetTableNode(), nullptr );
869                 m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1;
870             }
871             else if( rNd.IsSectionNode() )
872             {
873                 OutHTML_Section( *this, *rNd.GetSectionNode() );
874                 m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1;
875             }
876             else if( &rNd == &m_pDoc->GetNodes().GetEndOfContent() )
877                 break;
878 
879             ++m_pCurrentPam->GetPoint()->nNode;   // move
880             sal_uInt32 nPos = m_pCurrentPam->GetPoint()->nNode.GetIndex();
881 
882             if( m_bShowProgress )
883                 ::SetProgressState( nPos, m_pDoc->GetDocShell() );   // How far ?
884 
885             /* If only the selected area should be saved, so only the complete
886              * nodes should be saved, this means the first and n-th node
887              * partly, the 2nd till n-1 node complete. (complete means with
888              * all formats!)
889              */
890             m_bWriteAll = bSaveWriteAll ||
891                         nPos != m_pCurrentPam->GetMark()->nNode.GetIndex();
892             m_bFirstLine = false;
893             m_bOutFooter = false; // after one node no footer anymore
894         }
895 
896         ChangeParaToken( HtmlTokenId::NONE ); // MIB 8.7.97: We're doing it here and not at the caller
897         OutAndSetDefList( 0 );
898 
899     } while( CopyNextPam( &pPam ) );        // until all PaM's processed
900 
901     m_bWriteAll = bSaveWriteAll;          // reset to old values
902 }
903 
904 // write the StyleTable, general data, header/footer/footnotes
OutBodyColor(const char * pTag,const SwFormat * pFormat,SwHTMLWriter & rHWrt)905 static void OutBodyColor( const char* pTag, const SwFormat *pFormat,
906                           SwHTMLWriter& rHWrt )
907 {
908     const SwFormat *pRefFormat = nullptr;
909 
910     if( rHWrt.m_xTemplate.is() )
911         pRefFormat = SwHTMLWriter::GetTemplateFormat( pFormat->GetPoolFormatId(),
912                                                 &rHWrt.m_xTemplate->getIDocumentStylePoolAccess() );
913 
914     const SvxColorItem *pColorItem = nullptr;
915 
916     const SfxItemSet& rItemSet = pFormat->GetAttrSet();
917     const SfxPoolItem *pRefItem = nullptr, *pItem = nullptr;
918     bool bItemSet = SfxItemState::SET == rItemSet.GetItemState( RES_CHRATR_COLOR,
919                                                            true, &pItem);
920     bool bRefItemSet = pRefFormat &&
921         SfxItemState::SET == pRefFormat->GetAttrSet().GetItemState( RES_CHRATR_COLOR,
922                                                             true, &pRefItem);
923     if( bItemSet )
924     {
925         // only when the item is set in the template of the current document
926         // or has a different value as the in HTML template, it will be set
927         const SvxColorItem *pCItem = static_cast<const SvxColorItem*>(pItem);
928 
929         if( !bRefItemSet )
930         {
931             pColorItem = pCItem;
932         }
933         else
934         {
935             Color aColor( pCItem->GetValue() );
936             if( COL_AUTO == aColor )
937                 aColor = COL_BLACK;
938 
939             Color aRefColor( static_cast<const SvxColorItem*>(pRefItem)->GetValue() );
940             if( COL_AUTO == aRefColor )
941                 aRefColor = COL_BLACK;
942 
943             if( !aColor.IsRGBEqual( aRefColor ) )
944                 pColorItem = pCItem;
945         }
946     }
947     else if( bRefItemSet )
948     {
949         // The item was still set in the HTML template so we output the default
950         pColorItem = &rItemSet.GetPool()->GetDefaultItem( RES_CHRATR_COLOR );
951     }
952 
953     if( pColorItem )
954     {
955         OString sOut = OString::Concat(" ") + pTag + "=";
956         rHWrt.Strm().WriteOString( sOut );
957         Color aColor( pColorItem->GetValue() );
958         if( COL_AUTO == aColor )
959             aColor = COL_BLACK;
960         HTMLOutFuncs::Out_Color( rHWrt.Strm(), aColor );
961         if( RES_POOLCOLL_STANDARD==pFormat->GetPoolFormatId() )
962             rHWrt.m_xDfltColor = aColor;
963     }
964 }
965 
OutHeaderAttrs()966 sal_uInt16 SwHTMLWriter::OutHeaderAttrs()
967 {
968     sal_uLong nIdx = m_pCurrentPam->GetPoint()->nNode.GetIndex();
969     sal_uLong nEndIdx = m_pCurrentPam->GetMark()->nNode.GetIndex();
970 
971     SwTextNode *pTextNd = nullptr;
972     while( nIdx<=nEndIdx &&
973         nullptr==(pTextNd=m_pDoc->GetNodes()[nIdx]->GetTextNode()) )
974         nIdx++;
975 
976     OSL_ENSURE( pTextNd, "No Text-Node found" );
977     if( !pTextNd || !pTextNd->HasHints() )
978         return 0;
979 
980     sal_uInt16 nAttrs = 0;
981     const size_t nCntAttr = pTextNd->GetSwpHints().Count();
982     sal_Int32 nOldPos = 0;
983     for( size_t i=0; i<nCntAttr; ++i )
984     {
985         const SwTextAttr *pHt = pTextNd->GetSwpHints().Get(i);
986         if( !pHt->End() )
987         {
988             sal_Int32 nPos = pHt->GetStart();
989             if( nPos-nOldPos > 1
990                 || ( pHt->Which() != RES_TXTATR_FIELD
991                      && pHt->Which() != RES_TXTATR_ANNOTATION ) )
992                 break;
993 
994             const SwFieldIds nFieldWhich =
995                 static_cast<const SwFormatField&>(pHt->GetAttr()).GetField()->GetTyp()->Which();
996             if( SwFieldIds::Postit!=nFieldWhich &&
997                 SwFieldIds::Script!=nFieldWhich )
998                 break;
999 
1000             OutNewLine();
1001             OutHTML_SwFormatField( *this, pHt->GetAttr() );
1002             nOldPos = nPos;
1003             OSL_ENSURE( nAttrs<SAL_MAX_UINT16, "Too many attributes" );
1004             nAttrs++;
1005         }
1006     }
1007 
1008     return nAttrs;
1009 }
1010 
MakeHeader(HtmlWriter & rHtmlWriter,sal_uInt16 & rHeaderAttrs)1011 const SwPageDesc* SwHTMLWriter::MakeHeader(HtmlWriter & rHtmlWriter, sal_uInt16 &rHeaderAttrs )
1012 {
1013     OStringBuffer sOut;
1014     if (!mbSkipHeaderFooter)
1015     {
1016         if (mbXHTML)
1017             sOut.append(OOO_STRING_SVTOOLS_HTML_doctype " " OOO_STRING_SVTOOLS_XHTML_doctype11);
1018         else
1019             sOut.append(OOO_STRING_SVTOOLS_HTML_doctype " " OOO_STRING_SVTOOLS_HTML_doctype40);
1020         HTMLOutFuncs::Out_AsciiTag( Strm(), sOut.makeStringAndClear().getStr() ); // No GetNamespace() here.
1021 
1022         // build prelude
1023         OutNewLine();
1024         HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_html) );
1025 
1026         OutNewLine();
1027 
1028         HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_head) );
1029 
1030         IncIndentLevel();   // indent content of <HEAD>
1031 
1032         // DocumentInfo
1033         OString sIndent = GetIndentString();
1034 
1035         uno::Reference<document::XDocumentProperties> xDocProps;
1036         SwDocShell *pDocShell(m_pDoc->GetDocShell());
1037         if (pDocShell)
1038         {
1039             uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1040                                                                        pDocShell->GetModel(), uno::UNO_QUERY_THROW);
1041             xDocProps.set(xDPS->getDocumentProperties());
1042         }
1043 
1044         // xDocProps may be null here (when copying)
1045         SfxFrameHTMLWriter::Out_DocInfo( Strm(), GetBaseURL(), xDocProps,
1046                                          sIndent.getStr(), m_eDestEnc,
1047                                          &m_aNonConvertableCharacters );
1048 
1049         // comments and meta-tags of first paragraph
1050         rHeaderAttrs = OutHeaderAttrs();
1051 
1052         OutFootEndNoteInfo();
1053     }
1054     else if (mbIndexingOutput)
1055     {
1056         Strm().WriteCharPtr("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1057         Strm().WriteCharPtr(SAL_NEWLINE_STRING);
1058         rHtmlWriter.start("indexing");
1059         rHtmlWriter.characters("");
1060         Strm().WriteCharPtr(SAL_NEWLINE_STRING);
1061     }
1062 
1063     const SwPageDesc *pPageDesc = nullptr;
1064 
1065     // In none HTML documents the first set template will be exported
1066     // and if none is set the default template
1067     sal_uLong nNodeIdx = m_pCurrentPam->GetPoint()->nNode.GetIndex();
1068 
1069     while( nNodeIdx < m_pDoc->GetNodes().Count() )
1070     {
1071         SwNode *pNd = m_pDoc->GetNodes()[ nNodeIdx ];
1072         if( pNd->IsContentNode() )
1073         {
1074             pPageDesc = static_cast<const SwFormatPageDesc &>(pNd->GetContentNode()
1075                 ->GetAttr(RES_PAGEDESC)).GetPageDesc();
1076             break;
1077         }
1078         else if( pNd->IsTableNode() )
1079         {
1080             pPageDesc = pNd->GetTableNode()->GetTable().GetFrameFormat()
1081                            ->GetPageDesc().GetPageDesc();
1082             break;
1083         }
1084 
1085         nNodeIdx++;
1086     }
1087 
1088     if( !pPageDesc )
1089         pPageDesc = &m_pDoc->GetPageDesc( 0 );
1090 
1091     if (!mbSkipHeaderFooter)
1092     {
1093         // and now ... the style sheet!!!
1094         if( m_bCfgOutStyles )
1095         {
1096             OutStyleSheet( *pPageDesc );
1097         }
1098 
1099         // and now ... the BASIC and JavaScript!
1100         if( m_pDoc->GetDocShell() )   // BASIC is possible only in case we have a DocShell
1101             OutBasic(*this);
1102 
1103         DecIndentLevel();   // indent content of <HEAD>
1104         OutNewLine();
1105 
1106         HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_head), false );
1107 
1108         // the body won't be indented, because then everything would be indented!
1109         OutNewLine();
1110         sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_body);
1111         Strm().WriteOString( sOut.makeStringAndClear() );
1112 
1113         // language
1114         OutLanguage( m_eLang );
1115 
1116         // output text colour, when it was set in the default template or was changed
1117         OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_text,
1118                       m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD, false ),
1119                       *this );
1120 
1121         // colour of (un)visited links
1122         OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_link,
1123                       m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL ),
1124                       *this );
1125         OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_vlink,
1126                       m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_VISIT ),
1127                       *this );
1128 
1129         const SfxItemSet& rItemSet = pPageDesc->GetMaster().GetAttrSet();
1130 
1131         // fdo#86857 page styles now contain the XATTR_*, not RES_BACKGROUND
1132         std::unique_ptr<SvxBrushItem> const aBrushItem(getSvxBrushItemFromSourceSet(rItemSet, RES_BACKGROUND));
1133         OutBackground(aBrushItem.get(), true);
1134 
1135         m_nDirection = GetHTMLDirection( rItemSet );
1136         OutDirection( m_nDirection );
1137 
1138         if( m_bCfgOutStyles )
1139         {
1140             OutCSS1_BodyTagStyleOpt( *this, rItemSet );
1141         }
1142         // append events
1143         if( m_pDoc->GetDocShell() )   // BASIC is possible only in case we have a DocShell
1144             OutBasicBodyEvents();
1145 
1146         Strm().WriteChar( '>' );
1147     }
1148     else if (mbReqIF)
1149         // ReqIF: start xhtml.BlkStruct.class.
1150         HTMLOutFuncs::Out_AsciiTag(Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division));
1151 
1152     return pPageDesc;
1153 }
1154 
OutAnchor(const OUString & rName)1155 void SwHTMLWriter::OutAnchor( const OUString& rName )
1156 {
1157     OStringBuffer sOut;
1158     sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor " ");
1159     if (!mbXHTML)
1160     {
1161         sOut.append(OOO_STRING_SVTOOLS_HTML_O_name "=\"");
1162         Strm().WriteOString( sOut.makeStringAndClear() );
1163         HTMLOutFuncs::Out_String( Strm(), rName, m_eDestEnc, &m_aNonConvertableCharacters ).WriteCharPtr( "\">" );
1164     }
1165     else
1166     {
1167         // XHTML wants 'id' instead of 'name', also the value can't contain
1168         // spaces.
1169         sOut.append(OOO_STRING_SVTOOLS_HTML_O_id "=\"");
1170         Strm().WriteOString( sOut.makeStringAndClear() );
1171         HTMLOutFuncs::Out_String( Strm(), rName.replace(' ', '_'), m_eDestEnc, &m_aNonConvertableCharacters ).WriteCharPtr( "\">" );
1172     }
1173     HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
1174 }
1175 
OutBookmarks()1176 void SwHTMLWriter::OutBookmarks()
1177 {
1178     // fetch current bookmark
1179     const ::sw::mark::IMark* pBookmark = nullptr;
1180     IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess();
1181     if(m_nBkmkTabPos != -1)
1182         pBookmark = pMarkAccess->getAllMarksBegin()[m_nBkmkTabPos];
1183     // Output all bookmarks in this paragraph. The content position
1184     // for the moment isn't considered!
1185     sal_uInt32 nNode = m_pCurrentPam->GetPoint()->nNode.GetIndex();
1186     while( m_nBkmkTabPos != -1
1187            && pBookmark->GetMarkPos().nNode.GetIndex() == nNode )
1188     {
1189         // The area of bookmarks is first ignored, because it's not read.
1190 
1191         // first the SWG specific data:
1192         if ( dynamic_cast< const ::sw::mark::IBookmark* >(pBookmark) && !pBookmark->GetName().isEmpty() )
1193         {
1194             OutAnchor( pBookmark->GetName() );
1195         }
1196 
1197         if( ++m_nBkmkTabPos >= pMarkAccess->getAllMarksCount() )
1198             m_nBkmkTabPos = -1;
1199         else
1200             pBookmark = pMarkAccess->getAllMarksBegin()[m_nBkmkTabPos];
1201     }
1202 
1203     decltype(m_aOutlineMarkPoss)::size_type nPos;
1204     for( nPos = 0; nPos < m_aOutlineMarkPoss.size() &&
1205                    m_aOutlineMarkPoss[nPos] < nNode; nPos++ )
1206         ;
1207 
1208     while( nPos < m_aOutlineMarkPoss.size() && m_aOutlineMarkPoss[nPos] == nNode )
1209     {
1210         OUString sMark( m_aOutlineMarks[nPos] );
1211         OutAnchor( sMark.replace('?', '_') ); // '?' causes problems in IE/Netscape 5
1212         m_aOutlineMarkPoss.erase( m_aOutlineMarkPoss.begin()+nPos );
1213         m_aOutlineMarks.erase( m_aOutlineMarks.begin() + nPos );
1214     }
1215 }
1216 
OutPointFieldmarks(const SwPosition & rPos)1217 void SwHTMLWriter::OutPointFieldmarks( const SwPosition& rPos )
1218 {
1219     // "point" fieldmarks that occupy single character space, as opposed to
1220     // range fieldmarks that are associated with start and end points.
1221 
1222     const IDocumentMarkAccess* pMarkAccess = m_pDoc->getIDocumentMarkAccess();
1223     if (!pMarkAccess)
1224         return;
1225 
1226     const sw::mark::IFieldmark* pMark = pMarkAccess->getFieldmarkAt(rPos);
1227     if (!pMark)
1228         return;
1229 
1230     if (pMark->GetFieldname() != ODF_FORMCHECKBOX)
1231         return;
1232 
1233     const sw::mark::ICheckboxFieldmark* pCheckBox =
1234         dynamic_cast<const sw::mark::ICheckboxFieldmark*>(pMark);
1235 
1236     if (!pCheckBox)
1237         return;
1238 
1239     OString aOut("<"
1240         OOO_STRING_SVTOOLS_HTML_input
1241         " "
1242         OOO_STRING_SVTOOLS_HTML_O_type
1243         "=\""
1244         OOO_STRING_SVTOOLS_HTML_IT_checkbox
1245         "\"");
1246 
1247     if (pCheckBox->IsChecked())
1248     {
1249         aOut += " "
1250             OOO_STRING_SVTOOLS_HTML_O_checked
1251             "=\""
1252             OOO_STRING_SVTOOLS_HTML_O_checked
1253             "\"";
1254     }
1255 
1256     aOut += "/>";
1257     Strm().WriteOString(aOut);
1258 
1259     // TODO : Handle other single-point fieldmark types here (if any).
1260 }
1261 
OutImplicitMark(std::u16string_view rMark,const char * pMarkType)1262 void SwHTMLWriter::OutImplicitMark( std::u16string_view rMark,
1263                                     const char *pMarkType )
1264 {
1265     if( !rMark.empty() && !m_aImplicitMarks.empty() )
1266     {
1267         OUString sMark(rMark + OUStringChar(cMarkSeparator) + OUString::createFromAscii(pMarkType));
1268         if( 0 != m_aImplicitMarks.erase( sMark ) )
1269         {
1270             OutAnchor(sMark.replace('?', '_')); // '?' causes problems in IE/Netscape 5
1271         }
1272     }
1273 }
1274 
convertHyperlinkHRefValue(const OUString & rURL)1275 OUString SwHTMLWriter::convertHyperlinkHRefValue(const OUString& rURL)
1276 {
1277     OUString sURL(rURL);
1278     sal_Int32 nPos = sURL.lastIndexOf(cMarkSeparator);
1279     if (nPos != -1)
1280     {
1281         OUString sCompare = sURL.copy(nPos + 1).replaceAll(" ", "");
1282         if (!sCompare.isEmpty())
1283         {
1284             sCompare = sCompare.toAsciiLowerCase();
1285             if( sCompare == "region"  || sCompare == "frame"   ||
1286                 sCompare == "graphic" || sCompare == "ole"     ||
1287                 sCompare == "table"   || sCompare == "outline" ||
1288                 sCompare == "text" )
1289             {
1290                 sURL = sURL.replace( '?', '_' );   // '?' causes problems in IE/Netscape 5
1291             }
1292         }
1293     }
1294     else if (!sURL.isEmpty() && sURL[0] != '#')
1295     {
1296         // Link is not started from "#", so looks like external link. Encode this URL.
1297         INetURLObject aURL(sURL);
1298         sURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1299     }
1300     return URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), sURL );
1301 }
1302 
OutHyperlinkHRefValue(const OUString & rURL)1303 void SwHTMLWriter::OutHyperlinkHRefValue( const OUString& rURL )
1304 {
1305     OUString sURL = convertHyperlinkHRefValue(rURL);
1306     HTMLOutFuncs::Out_String( Strm(), sURL, m_eDestEnc, &m_aNonConvertableCharacters );
1307 }
1308 
OutBackground(const SvxBrushItem * pBrushItem,bool bGraphic)1309 void SwHTMLWriter::OutBackground( const SvxBrushItem *pBrushItem, bool bGraphic )
1310 {
1311     const Color &rBackColor = pBrushItem->GetColor();
1312     /// check, if background color is not "no fill"/"auto fill", instead of
1313     /// only checking, if transparency is not set.
1314     if( rBackColor != COL_TRANSPARENT )
1315     {
1316         Strm().WriteOString( " " OOO_STRING_SVTOOLS_HTML_O_bgcolor "=" );
1317         HTMLOutFuncs::Out_Color( Strm(), rBackColor);
1318     }
1319 
1320     if( !bGraphic )
1321         return;
1322 
1323     const Graphic* pGrf = pBrushItem->GetGraphic();
1324     OUString GraphicURL = pBrushItem->GetGraphicLink();
1325     if( mbEmbedImages || GraphicURL.isEmpty())
1326     {
1327         if( pGrf )
1328         {
1329             OUString aGraphicInBase64;
1330             if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) )
1331             {
1332                 m_nWarn = WARN_SWG_POOR_LOAD;
1333             }
1334             Strm().WriteCharPtr( " " OOO_STRING_SVTOOLS_HTML_O_background "=\"" );
1335             Strm().WriteCharPtr( OOO_STRING_SVTOOLS_HTML_O_data ":" );
1336             HTMLOutFuncs::Out_String( Strm(), aGraphicInBase64, m_eDestEnc, &m_aNonConvertableCharacters ).WriteChar( '\"' );
1337         }
1338     }
1339     else
1340     {
1341         if( m_bCfgCpyLinkedGrfs )
1342         {
1343             CopyLocalFileToINet( GraphicURL );
1344         }
1345         OUString s( URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), GraphicURL));
1346         Strm().WriteCharPtr(" " OOO_STRING_SVTOOLS_HTML_O_background "=\"" );
1347         HTMLOutFuncs::Out_String( Strm(), s, m_eDestEnc, &m_aNonConvertableCharacters );
1348         Strm().WriteCharPtr("\"");
1349 
1350     }
1351 }
1352 
OutBackground(const SfxItemSet & rItemSet,bool bGraphic)1353 void SwHTMLWriter::OutBackground( const SfxItemSet& rItemSet, bool bGraphic )
1354 {
1355     const SfxPoolItem* pItem;
1356     if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false,
1357                                                &pItem ))
1358     {
1359         OutBackground( static_cast<const SvxBrushItem*>(pItem), bGraphic );
1360     }
1361 }
1362 
GetLangWhichIdFromScript(sal_uInt16 nScript)1363 sal_uInt16 SwHTMLWriter::GetLangWhichIdFromScript( sal_uInt16 nScript )
1364 {
1365     sal_uInt16 nWhichId;
1366     switch( nScript )
1367     {
1368     case CSS1_OUTMODE_CJK:
1369         nWhichId = RES_CHRATR_CJK_LANGUAGE;
1370         break;
1371     case CSS1_OUTMODE_CTL:
1372         nWhichId = RES_CHRATR_CJK_LANGUAGE;
1373         break;
1374     default:
1375         nWhichId = RES_CHRATR_LANGUAGE;
1376         break;
1377     }
1378     return nWhichId;
1379 }
1380 
OutLanguage(LanguageType nLang)1381 void SwHTMLWriter::OutLanguage( LanguageType nLang )
1382 {
1383     // ReqIF mode: consumers would ignore language anyway.
1384     if (!(LANGUAGE_DONTKNOW != nLang && !mbReqIF))
1385         return;
1386 
1387     OStringBuffer sOut;
1388     sOut.append(' ');
1389     if (mbXHTML)
1390         sOut.append(OOO_STRING_SVTOOLS_XHTML_O_lang);
1391     else
1392         sOut.append(OOO_STRING_SVTOOLS_HTML_O_lang);
1393     sOut.append("=\"");
1394     Strm().WriteOString( sOut.makeStringAndClear() );
1395     HTMLOutFuncs::Out_String( Strm(), LanguageTag::convertToBcp47(nLang),
1396                               m_eDestEnc, &m_aNonConvertableCharacters ).WriteChar( '"' );
1397 }
1398 
GetHTMLDirection(const SfxItemSet & rItemSet) const1399 SvxFrameDirection SwHTMLWriter::GetHTMLDirection( const SfxItemSet& rItemSet ) const
1400 {
1401     return GetHTMLDirection( rItemSet.Get( RES_FRAMEDIR ).GetValue() );
1402 }
1403 
GetHTMLDirection(SvxFrameDirection nDir) const1404 SvxFrameDirection SwHTMLWriter::GetHTMLDirection( SvxFrameDirection nDir ) const
1405 {
1406     switch( nDir )
1407     {
1408     case SvxFrameDirection::Vertical_LR_TB:
1409         nDir = SvxFrameDirection::Horizontal_LR_TB;
1410         break;
1411     case SvxFrameDirection::Vertical_RL_TB:
1412         nDir = SvxFrameDirection::Horizontal_RL_TB;
1413         break;
1414     case SvxFrameDirection::Environment:
1415         nDir = m_nDirection;
1416         break;
1417     default: break;
1418     }
1419 
1420     return nDir;
1421 }
1422 
OutDirection(SvxFrameDirection nDir)1423 void SwHTMLWriter::OutDirection( SvxFrameDirection nDir )
1424 {
1425     OString sConverted = convertDirection(nDir);
1426     if (!sConverted.isEmpty())
1427     {
1428         OString sOut =
1429             " " OOO_STRING_SVTOOLS_HTML_O_dir
1430             "=\"" + sConverted + "\"";
1431         Strm().WriteOString( sOut );
1432     }
1433 }
1434 
convertDirection(SvxFrameDirection nDir)1435 OString SwHTMLWriter::convertDirection(SvxFrameDirection nDir)
1436 {
1437     OString sConverted;
1438     switch (nDir)
1439     {
1440     case SvxFrameDirection::Horizontal_LR_TB:
1441     case SvxFrameDirection::Vertical_LR_TB:
1442         sConverted = "ltr";
1443         break;
1444     case SvxFrameDirection::Horizontal_RL_TB:
1445     case SvxFrameDirection::Vertical_RL_TB:
1446         sConverted = "rtl";
1447         break;
1448     default: break;
1449     }
1450     return sConverted;
1451 }
1452 
GetIndentString(sal_uInt16 nIncLvl)1453 OString SwHTMLWriter::GetIndentString(sal_uInt16 nIncLvl)
1454 {
1455     OString sRet;
1456 
1457     // somewhat cumbersome, but we have only one indent string!
1458     sal_uInt16 nLevel = m_nIndentLvl + nIncLvl;
1459 
1460     if( nLevel && nLevel <= MAX_INDENT_LEVEL)
1461     {
1462         sIndentTabs[nLevel] = 0;
1463         sRet = sIndentTabs;
1464         sIndentTabs[nLevel] = '\t';
1465     }
1466 
1467     return sRet;
1468 }
1469 
OutNewLine(bool bCheck)1470 void SwHTMLWriter::OutNewLine( bool bCheck )
1471 {
1472     if( !bCheck || (Strm().Tell()-m_nLastLFPos) > m_nIndentLvl )
1473     {
1474         Strm().WriteCharPtr( SAL_NEWLINE_STRING );
1475         m_nLastLFPos = Strm().Tell();
1476     }
1477 
1478     if( m_nIndentLvl && m_nIndentLvl <= MAX_INDENT_LEVEL)
1479     {
1480         sIndentTabs[m_nIndentLvl] = 0;
1481         Strm().WriteCharPtr( sIndentTabs );
1482         sIndentTabs[m_nIndentLvl] = '\t';
1483     }
1484 }
1485 
GetHTMLFontSize(sal_uInt32 nHeight) const1486 sal_uInt16 SwHTMLWriter::GetHTMLFontSize( sal_uInt32 nHeight ) const
1487 {
1488     sal_uInt16 nSize = 1;
1489     for( sal_uInt16 i=6; i>0; i-- )
1490     {
1491         if( nHeight > (m_aFontHeights[i] + m_aFontHeights[i-1])/2 )
1492         {
1493             nSize = i+1;
1494             break;
1495         }
1496     }
1497 
1498     return nSize;
1499 }
1500 
1501 // Paragraphs with Table of Contents and other index styles will be typeset with
1502 // dot leaders at the position of the last tabulator in PrintLayout (CSS2) mode
indexOfDotLeaders(sal_uInt16 nPoolId,const OUString & rStr)1503 sal_Int32 SwHTMLWriter::indexOfDotLeaders( sal_uInt16 nPoolId, const OUString& rStr )
1504 {
1505     if (m_bCfgPrintLayout && ((nPoolId >= RES_POOLCOLL_TOX_CNTNT1 && nPoolId <= RES_POOLCOLL_TOX_CNTNT5) ||
1506         (nPoolId >= RES_POOLCOLL_TOX_IDX1 && nPoolId <= RES_POOLCOLL_TOX_IDX3) ||
1507         (nPoolId >= RES_POOLCOLL_TOX_USER1 && nPoolId <= RES_POOLCOLL_TOX_CNTNT10) ||
1508         nPoolId == RES_POOLCOLL_TOX_ILLUS1 || nPoolId == RES_POOLCOLL_TOX_TABLES1 ||
1509         nPoolId == RES_POOLCOLL_TOX_OBJECT1 ||
1510         (nPoolId >= RES_POOLCOLL_TOX_AUTHORITIES1 && nPoolId <= RES_POOLCOLL_TOX_USER10))) {
1511              sal_Int32 i = rStr.lastIndexOf('\t');
1512              // there are only ASCII (Latin-1) characters after the tabulator
1513              if (i > -1 && OUStringToOString(rStr.subView(i + 1), RTL_TEXTENCODING_ASCII_US).indexOf('?') == -1)
1514                  return i;
1515     }
1516     return -1;
1517 }
1518 
GetNamespace() const1519 OString SwHTMLWriter::GetNamespace() const
1520 {
1521     if (maNamespace.isEmpty())
1522         return OString();
1523 
1524     return maNamespace + ":";
1525 }
1526 
1527 // Structure caches the current data of the writer to output a
1528 // other part of the document, like e.g. header/footer
HTMLSaveData(SwHTMLWriter & rWriter,sal_uLong nStt,sal_uLong nEnd,bool bSaveNum,const SwFrameFormat * pFrameFormat)1529 HTMLSaveData::HTMLSaveData(SwHTMLWriter& rWriter, sal_uLong nStt,
1530                            sal_uLong nEnd, bool bSaveNum,
1531                            const SwFrameFormat *pFrameFormat)
1532     : rWrt(rWriter)
1533     , pOldPam(rWrt.m_pCurrentPam)
1534     , pOldEnd(rWrt.GetEndPaM())
1535     , nOldDefListLvl(rWrt.m_nDefListLvl)
1536     , nOldDirection(rWrt.m_nDirection)
1537     , bOldOutHeader(rWrt.m_bOutHeader)
1538     , bOldOutFooter(rWrt.m_bOutFooter)
1539     , bOldOutFlyFrame(rWrt.m_bOutFlyFrame)
1540 {
1541     bOldWriteAll = rWrt.m_bWriteAll;
1542 
1543     rWrt.m_pCurrentPam = Writer::NewUnoCursor(*rWrt.m_pDoc, nStt, nEnd);
1544 
1545     // recognize table in special areas
1546     if( nStt != rWrt.m_pCurrentPam->GetMark()->nNode.GetIndex() )
1547     {
1548         const SwNode *pNd = rWrt.m_pDoc->GetNodes()[ nStt ];
1549         if( pNd->IsTableNode() || pNd->IsSectionNode() )
1550             rWrt.m_pCurrentPam->GetMark()->nNode = nStt;
1551     }
1552 
1553     rWrt.SetEndPaM( rWrt.m_pCurrentPam.get() );
1554     rWrt.m_pCurrentPam->Exchange( );
1555     rWrt.m_bWriteAll = true;
1556     rWrt.m_nDefListLvl = 0;
1557     rWrt.m_bOutHeader = rWrt.m_bOutFooter = false;
1558 
1559     // Maybe save the current numbering information, so that it can be started again.
1560     // Only then also the numbering information of the next paragraph will be valid.
1561     if( bSaveNum )
1562     {
1563         pOldNumRuleInfo.reset( new SwHTMLNumRuleInfo( rWrt.GetNumInfo() ) );
1564         pOldNextNumRuleInfo = rWrt.ReleaseNextNumInfo();
1565     }
1566     else
1567     {
1568         rWrt.ClearNextNumInfo();
1569     }
1570 
1571     // The numbering will be in any case interrupted.
1572     rWrt.GetNumInfo().Clear();
1573 
1574     if( pFrameFormat )
1575         rWrt.m_nDirection = rWrt.GetHTMLDirection( pFrameFormat->GetAttrSet() );
1576 }
1577 
~HTMLSaveData()1578 HTMLSaveData::~HTMLSaveData()
1579 {
1580     rWrt.m_pCurrentPam.reset(); // delete PaM again
1581 
1582     rWrt.m_pCurrentPam = pOldPam;
1583     rWrt.SetEndPaM( pOldEnd );
1584     rWrt.m_bWriteAll = bOldWriteAll;
1585     rWrt.m_nBkmkTabPos = bOldWriteAll ? rWrt.FindPos_Bkmk( *pOldPam->GetPoint() ) : -1;
1586     rWrt.m_nLastParaToken = HtmlTokenId::NONE;
1587     rWrt.m_nDefListLvl = nOldDefListLvl;
1588     rWrt.m_nDirection = nOldDirection;
1589     rWrt.m_bOutHeader = bOldOutHeader;
1590     rWrt.m_bOutFooter = bOldOutFooter;
1591     rWrt.m_bOutFlyFrame = bOldOutFlyFrame;
1592 
1593     // Maybe continue the numbering from before section. The numbering
1594     // of the next paragraph will be invalid in any case.
1595     if( pOldNumRuleInfo )
1596     {
1597         rWrt.GetNumInfo().Set( *pOldNumRuleInfo );
1598         pOldNumRuleInfo.reset();
1599         rWrt.SetNextNumInfo( std::move(pOldNextNumRuleInfo) );
1600     }
1601     else
1602     {
1603         rWrt.GetNumInfo().Clear();
1604         rWrt.ClearNextNumInfo();
1605     }
1606 }
1607 
GetHTMLWriter(const OUString & rFilterOptions,const OUString & rBaseURL,WriterRef & xRet)1608 void GetHTMLWriter( const OUString& rFilterOptions, const OUString& rBaseURL, WriterRef& xRet )
1609 {
1610     xRet = new SwHTMLWriter( rBaseURL, rFilterOptions );
1611 }
1612 
1613 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1614