android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / filter / html / wrthtml.cxx
blob93335248ee154ba868d1aead65a6e37e650d4be8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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>
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>
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 <comphelper/scopeguard.hxx>
83 #include <unotools/tempfile.hxx>
84 #include <comphelper/sequenceashashmap.hxx>
85 #include <officecfg/Office/Common.hxx>
86 #include <officecfg/Office/Writer.hxx>
87 #include <comphelper/propertysequence.hxx>
88 #include <comphelper/sequence.hxx>
90 #define MAX_INDENT_LEVEL 20
92 using namespace css;
94 static char sIndentTabs[MAX_INDENT_LEVEL+2] =
95 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
97 SwHTMLWriter::SwHTMLWriter( const OUString& rBaseURL, std::u16string_view rFilterOptions )
98 : m_pNumRuleInfo(new SwHTMLNumRuleInfo)
99 , m_nHTMLMode(0)
100 , m_eCSS1Unit(FieldUnit::NONE)
101 , m_pStartNdIdx(nullptr)
102 , m_pCurrPageDesc(nullptr)
103 , m_pFormatFootnote(nullptr)
104 , m_nWarn(0)
105 , m_nLastLFPos(0)
106 , m_nLastParaToken(HtmlTokenId::NONE)
107 , m_nBkmkTabPos(-1)
108 , m_nImgMapCnt(1)
109 , m_nFormCntrlCnt(0)
110 , m_nEndNote(0)
111 , m_nFootNote(0)
112 , m_nLeftMargin(0)
113 , m_nDfltLeftMargin(0)
114 , m_nDfltRightMargin(0)
115 , m_nFirstLineIndent(0)
116 , m_nDfltFirstLineIndent(0)
117 , m_nDfltTopMargin(0)
118 , m_nDfltBottomMargin(0)
119 , m_nIndentLvl(0)
120 , m_nWishLineLen(70)
121 , m_nDefListLvl(0)
122 , m_nDefListMargin(0)
123 , m_nHeaderFooterSpace(0)
124 , m_nTextAttrsToIgnore(0)
125 , m_nExportMode(0)
126 , m_nCSS1OutMode(0)
127 , m_nCSS1Script(CSS1_OUTMODE_WESTERN)
128 , m_nDirection(SvxFrameDirection::Horizontal_LR_TB)
129 , m_eLang(LANGUAGE_DONTKNOW)
130 , m_bCfgOutStyles( false )
131 , m_bCfgPreferStyles( false )
132 , m_bCfgFormFeed( false )
133 , m_bCfgStarBasic( false )
134 , m_bCfgCpyLinkedGrfs( false )
135 , m_bFirstLine(true)
136 , m_bTagOn( false )
137 , m_bTextAttr( false )
138 , m_bOutOpts( false )
139 , m_bOutTable( false )
140 , m_bOutHeader( false )
141 , m_bOutFooter( false )
142 , m_bOutFlyFrame( false )
143 , m_bFirstCSS1Rule( false )
144 , m_bFirstCSS1Property( false )
145 , m_bCSS1IgnoreFirstPageDesc( false )
146 , m_bNoAlign( false )
147 , m_bClearLeft( false )
148 , m_bClearRight( false )
149 , m_bLFPossible( false )
150 , m_bPreserveForm( false )
151 , m_bCfgNetscape4( false )
152 , mbSkipImages(false)
153 , mbSkipHeaderFooter(false)
154 , mbEmbedImages(false)
155 , m_bCfgPrintLayout( false )
156 , m_bParaDotLeaders( false )
157 , m_bPrettyPrint( true )
159 SetBaseURL(rBaseURL);
161 if (rBaseURL.isEmpty())
163 // Paste: set base URL to a tempfile, so images are not lost.
164 mpTempBaseURL.reset(new utl::TempFileNamed());
165 mpTempBaseURL->EnableKillingFile();
166 SetBaseURL(mpTempBaseURL->GetURL());
169 SetupFilterOptions(rFilterOptions);
171 if (mbXHTML)
173 m_bNoAlign = true;
177 SwHTMLWriter::~SwHTMLWriter()
181 std::unique_ptr<SwHTMLNumRuleInfo> SwHTMLWriter::ReleaseNextNumInfo()
183 return std::move(m_pNextNumRuleInfo);
186 void SwHTMLWriter::SetupFilterOptions(SfxMedium& rMedium)
188 const SfxItemSet* pSet = rMedium.GetItemSet();
189 if (pSet == nullptr)
190 return;
192 uno::Sequence<beans::PropertyValue> aArgs = rMedium.GetArgs();
193 if (const SfxStringItem* pItem = pSet->GetItemIfSet( SID_FILE_FILTEROPTIONS ))
195 const OUString sFilterOptions = pItem->GetValue();
197 if (sFilterOptions.startsWith("{"))
199 std::vector<beans::PropertyValue> aArgsVec
200 = comphelper::JsonToPropertyValues(sFilterOptions.toUtf8());
201 aArgs = comphelper::containerToSequence(aArgsVec);
204 SetupFilterOptions(sFilterOptions);
207 SetupFilterFromPropertyValues(aArgs);
210 void SwHTMLWriter::SetupFilterOptions(std::u16string_view rFilterOptions)
212 comphelper::SequenceAsHashMap aStoreMap;
213 if (rFilterOptions.find(u"SkipImages") != std::u16string_view::npos)
215 aStoreMap["SkipImages"] <<= true;
217 else if (rFilterOptions.find(u"SkipHeaderFooter") != std::u16string_view::npos)
219 aStoreMap["SkipHeaderFooter"] <<= true;
221 else if (rFilterOptions.find(u"EmbedImages") != std::u16string_view::npos)
223 aStoreMap["EmbedImages"] <<= true;
226 // this option can be "on" together with any of above
227 if (rFilterOptions.find(u"NoLineLimit") != std::u16string_view::npos)
229 aStoreMap["NoLineLimit"] <<= true;
232 // this option can be "on" together with any of above
233 if (rFilterOptions.find(u"NoPrettyPrint") != std::u16string_view::npos)
235 aStoreMap["NoPrettyPrint"] <<= true;
238 const uno::Sequence<OUString> aOptionSeq
239 = comphelper::string::convertCommaSeparated(rFilterOptions);
240 static const OUStringLiteral aXhtmlNsKey(u"xhtmlns=");
241 for (const auto& rOption : aOptionSeq)
243 if (rOption == "XHTML")
245 aStoreMap["XHTML"] <<= true;
247 else if (rOption.startsWith(aXhtmlNsKey))
249 aStoreMap["XhtmlNs"] <<= rOption.copy(aXhtmlNsKey.getLength());
253 SetupFilterFromPropertyValues(aStoreMap.getAsConstPropertyValueList());
256 void SwHTMLWriter::SetupFilterFromPropertyValues(
257 const css::uno::Sequence<css::beans::PropertyValue>& rPropertyValues)
259 comphelper::SequenceAsHashMap aStoreMap(rPropertyValues);
260 auto it = aStoreMap.find("RTFOLEMimeType");
261 if (it != aStoreMap.end())
263 it->second >>= m_aRTFOLEMimeType;
266 it = aStoreMap.find("ExportImagesAsOLE");
267 if (it != aStoreMap.end())
269 it->second >>= m_bExportImagesAsOLE;
272 it = aStoreMap.find("ShapeDPI");
273 if (it != aStoreMap.end())
275 sal_Int32 nVal{};
276 it->second >>= nVal;
277 m_nShapeDPI.emplace(nVal);
280 it = aStoreMap.find("SkipImages");
281 if (it != aStoreMap.end())
283 bool bVal{};
284 it->second >>= bVal;
285 mbSkipImages = bVal;
288 it = aStoreMap.find("SkipHeaderFooter");
289 if (it != aStoreMap.end())
291 bool bVal{};
292 it->second >>= bVal;
293 mbSkipHeaderFooter = bVal;
296 // this option can be "on" together with any of above
297 it = aStoreMap.find("NoPrettyPrint");
298 if (it != aStoreMap.end())
300 m_nWishLineLen = -1;
301 m_bPrettyPrint = false;
304 it = aStoreMap.find("EmbedImages");
305 if (it != aStoreMap.end())
307 bool bVal{};
308 it->second >>= bVal;
309 mbEmbedImages = bVal;
312 it = aStoreMap.find("NoLineLimit");
313 if (it != aStoreMap.end())
315 bool bVal{};
316 it->second >>= bVal;
317 if (bVal)
319 m_nWishLineLen = -1;
323 it = aStoreMap.find("XHTML");
324 if (it != aStoreMap.end())
326 bool bVal{};
327 it->second >>= bVal;
328 mbXHTML = bVal;
331 it = aStoreMap.find("XhtmlNs");
332 if (it != aStoreMap.end())
334 OUString aVal;
335 it->second >>= aVal;
337 maNamespace = aVal.toUtf8();
338 if (maNamespace == "reqif-xhtml")
340 mbReqIF = true;
341 // XHTML is always just a fragment inside ReqIF.
342 mbSkipHeaderFooter = true;
344 // XHTML namespace implies XHTML.
345 mbXHTML = true;
348 it = aStoreMap.find("LeadingTabWidth");
349 if (it != aStoreMap.end())
351 sal_Int32 nVal{};
352 it->second >>= nVal;
353 m_nLeadingTabWidth.emplace(nVal);
357 ErrCode SwHTMLWriter::WriteStream()
359 if (!SW_MOD())
360 return ERRCODE_ABORT;
361 // Intercept paste output if requested.
362 char* pPasteEnv = getenv("SW_DEBUG_HTML_PASTE_TO");
363 std::unique_ptr<SvStream> pPasteStream;
364 SvStream* pOldPasteStream = nullptr;
365 if (pPasteEnv)
367 OUString aPasteStr;
368 if (osl::FileBase::getFileURLFromSystemPath(OUString::fromUtf8(pPasteEnv), aPasteStr)
369 == osl::FileBase::E_None)
371 pPasteStream.reset(new SvFileStream(aPasteStr, StreamMode::WRITE));
372 pOldPasteStream = &Strm();
373 SetStream(pPasteStream.get());
376 comphelper::ScopeGuard g([this, pOldPasteStream] { this->SetStream(pOldPasteStream); });
378 // font heights 1-7
379 m_aFontHeights[0] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_1::get() * 20;
380 m_aFontHeights[1] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_2::get() * 20;
381 m_aFontHeights[2] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_3::get() * 20;
382 m_aFontHeights[3] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_4::get() * 20;
383 m_aFontHeights[4] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_5::get() * 20;
384 m_aFontHeights[5] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_6::get() * 20;
385 m_aFontHeights[6] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_7::get() * 20;
387 // output styles anyway
388 // (then also top and bottom paragraph spacing)
389 m_nExportMode = SvxHtmlOptions::GetExportMode();
390 m_nHTMLMode = GetHtmlMode(nullptr);
392 if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_NS40 == m_nExportMode )
393 m_nHTMLMode |= HTMLMODE_BLOCK_SPACER;
395 if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_MSIE == m_nExportMode )
396 m_nHTMLMode |= (HTMLMODE_FLOAT_FRAME | HTMLMODE_LSPACE_IN_NUMBER_BULLET);
398 if( HTML_CFG_MSIE == m_nExportMode )
399 m_nHTMLMode |= HTMLMODE_NBSP_IN_TABLES;
401 if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_NS40 == m_nExportMode || HTML_CFG_MSIE == m_nExportMode )
402 m_nHTMLMode |= HTMLMODE_ABS_POS_FLY | HTMLMODE_ABS_POS_DRAW;
404 if( HTML_CFG_WRITER == m_nExportMode )
405 m_nHTMLMode |= HTMLMODE_FLY_MARGINS;
407 if( HTML_CFG_NS40 == m_nExportMode )
408 m_nHTMLMode |= HTMLMODE_BORDER_NONE;
410 m_nHTMLMode |= HTMLMODE_FONT_GENERIC;
412 if( HTML_CFG_NS40==m_nExportMode )
413 m_nHTMLMode |= HTMLMODE_NO_CONTROL_CENTERING;
415 m_bCfgOutStyles = IsHTMLMode(HTMLMODE_SOME_STYLES | HTMLMODE_FULL_STYLES);
416 m_bCfgNetscape4 = (HTML_CFG_NS40 == m_nExportMode);
418 if( IsHTMLMode(HTMLMODE_SOME_STYLES | HTMLMODE_FULL_STYLES) )
419 m_nHTMLMode |= HTMLMODE_PRINT_EXT;
421 m_eCSS1Unit = SW_MOD()->GetMetric( m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) );
423 // Only for the MS-IE we favour the export of styles.
424 m_bCfgPreferStyles = HTML_CFG_MSIE == m_nExportMode;
426 m_bCfgStarBasic = officecfg::Office::Common::Filter::HTML::Export::Basic::get();
428 m_bCfgFormFeed = !IsHTMLMode(HTMLMODE_PRINT_EXT);
429 m_bCfgCpyLinkedGrfs = officecfg::Office::Common::Filter::HTML::Export::LocalGraphic::get();
431 m_bCfgPrintLayout = SvxHtmlOptions::IsPrintLayoutExtension();
433 // get HTML template
434 bool bOldHTMLMode = false;
435 SwTextFormatColls::size_type nOldTextFormatCollCnt = 0;
436 SwCharFormats::size_type nOldCharFormatCnt = 0;
438 OSL_ENSURE( !m_xTemplate.is(), "Where is the HTML template coming from?" );
439 m_xTemplate = static_cast<HTMLReader*>(ReadHTML)->GetTemplateDoc(*m_pDoc);
440 if( m_xTemplate.is() )
442 bOldHTMLMode = m_xTemplate->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
443 m_xTemplate->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, true);
445 nOldTextFormatCollCnt = m_xTemplate->GetTextFormatColls()->size();
446 nOldCharFormatCnt = m_xTemplate->GetCharFormats()->size();
449 if( m_bShowProgress )
450 ::StartProgress( STR_STATSTR_W4WWRITE, 0, sal_Int32(m_pDoc->GetNodes().Count()),
451 m_pDoc->GetDocShell());
453 m_xDfltColor.reset();
454 m_xFootEndNotes.reset();
455 m_pFormatFootnote = nullptr;
456 m_bOutTable = m_bOutHeader = m_bOutFooter = m_bOutFlyFrame = false;
457 mxFormComps.clear();
458 m_nFormCntrlCnt = 0;
459 m_bPreserveForm = false;
460 m_bClearLeft = m_bClearRight = false;
461 m_bLFPossible = false;
463 m_nLeftMargin = m_nDfltLeftMargin = m_nDfltRightMargin = 0;
464 m_nDfltTopMargin = m_nDfltBottomMargin = 0;
465 m_nFirstLineIndent = m_nDfltFirstLineIndent = 0;
466 m_bFirstCSS1Property = m_bFirstCSS1Rule = false;
467 m_bCSS1IgnoreFirstPageDesc = false;
468 m_nIndentLvl = 0;
469 m_nLastLFPos = 0;
470 m_nDefListLvl = 0;
471 m_nDefListMargin = ((m_xTemplate.is() && !m_bCfgOutStyles) ? m_xTemplate.get() : m_pDoc)
472 ->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_HTML_DD, false )
473 ->GetTextLeftMargin().GetTextLeft();
474 m_nHeaderFooterSpace = 0;
475 m_nTextAttrsToIgnore = 0;
476 m_nCSS1OutMode = 0;
477 SvtScriptType nScript = SvtLanguageOptions::GetScriptTypeOfLanguage( GetAppLanguage() );
478 switch( nScript )
480 case SvtScriptType::ASIAN:
481 m_nCSS1Script = CSS1_OUTMODE_CJK;
482 break;
483 case SvtScriptType::COMPLEX:
484 m_nCSS1Script = CSS1_OUTMODE_CTL;
485 break;
486 default:
487 m_nCSS1Script = CSS1_OUTMODE_WESTERN;
488 break;
490 m_eLang = static_cast<const SvxLanguageItem&>(m_pDoc
491 ->GetDefault(GetLangWhichIdFromScript(m_nCSS1Script))).GetLanguage();
493 m_nFootNote = m_nEndNote = 0;
495 m_nWarn = ERRCODE_NONE;
496 GetNumInfo().Clear();
497 m_pNextNumRuleInfo = nullptr;
499 OString aStartTags;
501 // respect table and section at document beginning
503 if (m_bWriteAll)
505 while (const SwStartNode* pTNd = m_pCurrentPam->GetPointNode().FindTableBoxStartNode())
507 // start with table node !!
508 m_pCurrentPam->GetPoint()->Assign(*pTNd->FindTableNode());
510 if (m_bWriteOnlyFirstTable)
511 m_pCurrentPam->GetMark()->Assign(
512 *m_pCurrentPam->GetPointNode().EndOfSectionNode());
516 // first node (which can contain a page break)
517 m_pStartNdIdx = new SwNodeIndex( m_pCurrentPam->GetPoint()->GetNode() );
519 SwSectionNode * pSNd = m_pCurrentPam->GetPointNode().FindSectionNode();
520 while( pSNd )
522 if( m_bWriteAll )
524 // start with section node !!
525 m_pCurrentPam->GetPoint()->Assign(*pSNd);
527 else
529 OSL_ENSURE( SectionType::FileLink != pSNd->GetSection().GetType(),
530 "Export linked areas at document beginning is not implemented" );
532 // save only the tag of section
533 OString aName = HTMLOutFuncs::ConvertStringToHTML(
534 pSNd->GetSection().GetSectionName() );
536 aStartTags =
537 "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_division
538 " " OOO_STRING_SVTOOLS_HTML_O_id
539 "=\"" + aName + "\">" +
540 aStartTags;
542 // FindSectionNode() on a SectionNode return the same!
543 pSNd = pSNd->StartOfSectionNode()->FindSectionNode();
547 // create table of the floating frames, but only when the whole
548 // document is saved
549 m_aHTMLPosFlyFrames.clear();
550 CollectFlyFrames();
551 m_nLastParaToken = HtmlTokenId::NONE;
553 if (mbReqIF && !m_bWriteAll && m_pCurrentPam
554 && *m_pCurrentPam->GetPoint() == *m_pCurrentPam->GetMark()
555 && m_pCurrentPam->GetPoint()->GetNode().IsOLENode() && m_aHTMLPosFlyFrames.size() == 1)
557 // A single OLE object selection must be output: do it directly (without replacement)
558 OutHTML_FrameFormatOLENodeGrf(*this, m_aHTMLPosFlyFrames[0]->GetFormat(), true, false);
560 else
562 GetControls();
563 CollectLinkTargets();
565 sal_uInt16 nHeaderAttrs = 0;
566 m_pCurrPageDesc = MakeHeader( nHeaderAttrs );
568 m_bLFPossible = true;
570 // output forms which contain only HiddenControls
571 OutHiddenForms();
573 if( !aStartTags.isEmpty() )
574 Strm().WriteOString( aStartTags );
576 const SwFormatHeader *pFormatHeader;
577 const SfxItemSet& rPageItemSet = m_pCurrPageDesc->GetMaster().GetAttrSet();
578 if( !m_bWriteClipboardDoc && m_pDoc->GetDocShell() &&
579 (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) &&
580 !m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)) &&
581 (pFormatHeader = rPageItemSet.GetItemIfSet( RES_HEADER )) )
583 const SwFrameFormat *pHeaderFormat = pFormatHeader->GetHeaderFormat();
584 if( pHeaderFormat )
585 OutHTML_HeaderFooter( *this, *pHeaderFormat, true );
588 m_nTextAttrsToIgnore = nHeaderAttrs;
589 Out_SwDoc( m_pOrigPam );
590 m_nTextAttrsToIgnore = 0;
592 if( mxFormComps.is() )
593 OutForm( false, mxFormComps );
595 if( m_xFootEndNotes )
596 OutFootEndNotes();
598 const SwFormatFooter* pFormatFooter;
599 if( !m_bWriteClipboardDoc && m_pDoc->GetDocShell() &&
600 (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) && !m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)) &&
601 (pFormatFooter = rPageItemSet.GetItemIfSet( RES_FOOTER )) )
603 const SwFrameFormat *pFooterFormat = pFormatFooter->GetFooterFormat();
604 if( pFooterFormat )
605 OutHTML_HeaderFooter( *this, *pFooterFormat, false );
608 if( m_bLFPossible )
609 OutNewLine();
610 if (!mbSkipHeaderFooter)
612 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_body), false );
613 OutNewLine();
614 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_html), false );
616 else if (mbReqIF)
617 // ReqIF: end xhtml.BlkStruct.class.
618 HTMLOutFuncs::Out_AsciiTag(Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false);
620 // delete the table with floating frames
621 OSL_ENSURE( m_aHTMLPosFlyFrames.empty(), "Were not all frames output?" );
622 m_aHTMLPosFlyFrames.clear();
624 m_aHTMLControls.clear();
626 m_CharFormatInfos.clear();
627 m_TextCollInfos.clear();
628 m_aImgMapNames.clear();
629 m_aImplicitMarks.clear();
630 m_aOutlineMarks.clear();
631 m_aOutlineMarkPoss.clear();
632 m_aNumRuleNames.clear();
633 m_aScriptParaStyles.clear();
634 m_aScriptTextStyles.clear();
636 m_xDfltColor.reset();
638 delete m_pStartNdIdx;
639 m_pStartNdIdx = nullptr;
641 mxFormComps.clear();
643 OSL_ENSURE( !m_xFootEndNotes,
644 "SwHTMLWriter::Write: Footnotes not deleted by OutFootEndNotes" );
646 m_pCurrPageDesc = nullptr;
648 ClearNextNumInfo();
650 for(OUString & s : m_aBulletGrfs)
651 s.clear();
653 if( m_bShowProgress )
654 ::EndProgress( m_pDoc->GetDocShell() );
656 if( m_xTemplate.is() )
658 // delete character and paragraph templates created during export
659 auto nTextFormatCollCnt = m_xTemplate->GetTextFormatColls()->size();
660 while( nTextFormatCollCnt > nOldTextFormatCollCnt )
661 m_xTemplate->DelTextFormatColl( --nTextFormatCollCnt );
662 OSL_ENSURE( m_xTemplate->GetTextFormatColls()->size() == nOldTextFormatCollCnt,
663 "wrong number of TextFormatColls deleted" );
665 auto nCharFormatCnt = m_xTemplate->GetCharFormats()->size();
666 while( nCharFormatCnt > nOldCharFormatCnt )
667 m_xTemplate->DelCharFormat( --nCharFormatCnt );
668 OSL_ENSURE( m_xTemplate->GetCharFormats()->size() == nOldCharFormatCnt,
669 "wrong number of CharFormats deleted" );
671 // restore HTML mode
672 m_xTemplate->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, bOldHTMLMode);
674 m_xTemplate.clear();
677 return m_nWarn;
680 static const SwFormatCol *lcl_html_GetFormatCol( const SwSection& rSection,
681 const SwSectionFormat& rFormat )
683 if( SectionType::FileLink == rSection.GetType() )
684 return nullptr;
686 const SwFormatCol *pCol = rFormat.GetAttrSet().GetItemIfSet(RES_COL,false);
687 if (pCol->GetNumCols() > 1 )
688 return pCol;
690 return nullptr;
693 static bool lcl_html_IsMultiColStart( const SwHTMLWriter& rHTMLWrt, SwNodeOffset nIndex )
695 bool bRet = false;
696 const SwSectionNode *pSectNd =
697 rHTMLWrt.m_pDoc->GetNodes()[nIndex]->GetSectionNode();
698 if( pSectNd )
700 const SwSection& rSection = pSectNd->GetSection();
701 const SwSectionFormat *pFormat = rSection.GetFormat();
702 if( pFormat && lcl_html_GetFormatCol( rSection, *pFormat ) )
703 bRet = true;
706 return bRet;
709 static bool lcl_html_IsMultiColEnd( const SwHTMLWriter& rHTMLWrt, SwNodeOffset nIndex )
711 bool bRet = false;
712 const SwEndNode *pEndNd = rHTMLWrt.m_pDoc->GetNodes()[nIndex]->GetEndNode();
713 if( pEndNd )
714 bRet = lcl_html_IsMultiColStart( rHTMLWrt,
715 pEndNd->StartOfSectionIndex() );
717 return bRet;
720 static void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt,
721 const SwSection& rSection,
722 const SwSectionFormat& rFormat,
723 const SwFormatCol *pCol,
724 bool bContinued=false )
726 OSL_ENSURE( pCol || !bContinued, "Continuation of DIV" );
728 if( rHTMLWrt.m_bLFPossible )
729 rHTMLWrt.OutNewLine();
731 OStringBuffer sOut("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division);
733 const OUString& rName = rSection.GetSectionName();
734 if( !rName.isEmpty() && !bContinued )
736 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_id "=\"");
737 rHTMLWrt.Strm().WriteOString( sOut );
738 sOut.setLength(0);
739 HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), rName );
740 sOut.append('\"');
743 rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() );
744 if (!rHTMLWrt.mbXHTML)
746 SvxFrameDirection nDir = rHTMLWrt.GetHTMLDirection(rFormat.GetAttrSet());
747 rHTMLWrt.OutDirection(nDir);
750 if( SectionType::FileLink == rSection.GetType() )
752 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_href "=\"");
753 rHTMLWrt.Strm().WriteOString( sOut );
754 sOut.setLength(0);
756 const OUString& aFName = rSection.GetLinkFileName();
757 sal_Int32 nIdx{ 0 };
758 OUString aURL( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) );
759 OUString aFilter( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) );
760 OUString aSection( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) );
762 OUString aEncURL( URIHelper::simpleNormalizedMakeRelative(rHTMLWrt.GetBaseURL(), aURL ) );
763 sal_Unicode cDelim = 255U;
764 bool bURLContainsDelim = (-1 != aEncURL.indexOf( cDelim ) );
766 HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aEncURL );
767 const char* const pDelim = "&#255;";
768 if( !aFilter.isEmpty() || !aSection.isEmpty() || bURLContainsDelim )
769 rHTMLWrt.Strm().WriteOString( pDelim );
770 if( !aFilter.isEmpty() )
771 HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aFilter );
772 if( !aSection.isEmpty() || bURLContainsDelim )
773 rHTMLWrt.Strm().WriteOString( pDelim );
774 if( !aSection.isEmpty() )
776 aSection = aSection.replaceAll(u"%", u"%25");
777 aSection = aSection.replaceAll(OUStringChar(cDelim), u"%FF");
778 HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aSection );
780 sOut.append('\"');
782 else if( pCol )
784 // minimum gutter width
785 sal_uInt16 nGutter = pCol->GetGutterWidth( true );
786 if( nGutter!=USHRT_MAX )
788 nGutter = SwHTMLWriter::ToPixel(nGutter);
789 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_gutter "=\"" + OString::number(nGutter) + "\"");
793 rHTMLWrt.Strm().WriteOString( sOut );
794 sOut.setLength(0);
795 if( rHTMLWrt.IsHTMLMode( rHTMLWrt.m_bCfgOutStyles ? HTMLMODE_ON : 0 ) )
796 rHTMLWrt.OutCSS1_SectionFormatOptions( rFormat, pCol );
798 rHTMLWrt.Strm().WriteChar( '>' );
800 rHTMLWrt.m_bLFPossible = true;
801 if( !rName.isEmpty() && !bContinued )
802 rHTMLWrt.OutImplicitMark( rName, "region" );
804 rHTMLWrt.IncIndentLevel();
807 static void lcl_html_OutSectionEndTag( SwHTMLWriter& rHTMLWrt )
809 rHTMLWrt.DecIndentLevel();
810 if( rHTMLWrt.m_bLFPossible )
811 rHTMLWrt.OutNewLine();
812 HTMLOutFuncs::Out_AsciiTag( rHTMLWrt.Strm(), Concat2View(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
813 rHTMLWrt.m_bLFPossible = true;
816 static SwHTMLWriter& OutHTML_Section( SwHTMLWriter& rWrt, const SwSectionNode& rSectNd )
818 // End <PRE> and any <DL>, because a definition list's level may
819 // change inside the section.
820 rWrt.ChangeParaToken( HtmlTokenId::NONE );
821 rWrt.OutAndSetDefList( 0 );
823 const SwSection& rSection = rSectNd.GetSection();
824 const SwSectionFormat *pFormat = rSection.GetFormat();
825 OSL_ENSURE( pFormat, "Section without a format?" );
827 bool bStartTag = true;
828 bool bEndTag = true;
829 const SwSectionFormat *pSurrFormat = nullptr;
830 const SwSectionNode *pSurrSectNd = nullptr;
831 const SwSection *pSurrSection = nullptr;
832 const SwFormatCol *pSurrCol = nullptr;
834 SwNodeOffset nSectSttIdx = rSectNd.GetIndex();
835 SwNodeOffset nSectEndIdx = rSectNd.EndOfSectionIndex();
836 const SwFormatCol *pCol = lcl_html_GetFormatCol( rSection, *pFormat );
837 if( pCol )
839 // If the next node is a columned section node, too, don't export
840 // an empty section.
841 if( lcl_html_IsMultiColStart( rWrt, nSectSttIdx+1 ) )
842 bStartTag = false;
844 // The same applies if the section end with another columned section.
845 if( lcl_html_IsMultiColEnd( rWrt, nSectEndIdx-1 ) )
846 bEndTag = false;
848 // is there a columned section around this one?
849 const SwStartNode *pSttNd = rSectNd.StartOfSectionNode();
850 if( pSttNd )
852 pSurrSectNd = pSttNd->FindSectionNode();
853 if( pSurrSectNd )
855 const SwStartNode *pBoxSttNd = pSttNd->FindTableBoxStartNode();
856 if( !pBoxSttNd ||
857 pBoxSttNd->GetIndex() < pSurrSectNd->GetIndex() )
859 pSurrSection = &pSurrSectNd->GetSection();
860 pSurrFormat = pSurrSection->GetFormat();
861 if( pSurrFormat )
862 pSurrCol = lcl_html_GetFormatCol( *pSurrSection,
863 *pSurrFormat );
869 // The surrounding section must be closed before the current one is
870 // opened, except that it start immediately before the current one or
871 // another end immediately before the current one
872 if( pSurrCol && nSectSttIdx - pSurrSectNd->GetIndex() > SwNodeOffset(1) &&
873 !lcl_html_IsMultiColEnd( rWrt, nSectSttIdx-1 ) )
874 lcl_html_OutSectionEndTag( rWrt );
876 if( bStartTag )
877 lcl_html_OutSectionStartTag( rWrt, rSection, *pFormat, pCol );
880 HTMLSaveData aSaveData( rWrt,
881 rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex()+1,
882 rSectNd.EndOfSectionIndex(),
883 false, pFormat );
884 rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
887 rWrt.m_pCurrentPam->GetPoint()->Assign(*rSectNd.EndOfSectionNode());
889 if( bEndTag )
890 lcl_html_OutSectionEndTag( rWrt );
892 // The surrounding section must be started again, except that it ends
893 // immediately behind the current one.
894 if( pSurrCol &&
895 pSurrSectNd->EndOfSectionIndex() - nSectEndIdx > SwNodeOffset(1) &&
896 !lcl_html_IsMultiColStart( rWrt, nSectEndIdx+1 ) )
897 lcl_html_OutSectionStartTag( rWrt, *pSurrSection, *pSurrFormat,
898 pSurrCol, true );
900 return rWrt;
903 void SwHTMLWriter::Out_SwDoc( SwPaM* pPam )
905 bool bSaveWriteAll = m_bWriteAll; // save
906 bool bIncludeHidden = officecfg::Office::Writer::FilterFlags::HTML::IncludeHiddenText::get();
908 // search next text::Bookmark position from text::Bookmark table
909 m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1;
911 // output all areas of PaM's in the HTML file
912 do {
913 m_bWriteAll = bSaveWriteAll;
914 m_bFirstLine = true;
916 // search for first on PaM created FlyFrame
917 // still missing:
919 while( m_pCurrentPam->GetPoint()->GetNodeIndex() < m_pCurrentPam->GetMark()->GetNodeIndex() ||
920 (m_pCurrentPam->GetPoint()->GetNodeIndex() == m_pCurrentPam->GetMark()->GetNodeIndex() &&
921 m_pCurrentPam->GetPoint()->GetContentIndex() <= m_pCurrentPam->GetMark()->GetContentIndex()) )
923 SwNode& rNd = m_pCurrentPam->GetPointNode();
925 OSL_ENSURE( !(rNd.IsGrfNode() || rNd.IsOLENode()),
926 "Unexpected Grf- or OLE-Node here" );
928 if( rNd.IsTextNode() )
930 SwTextNode* pTextNd = rNd.GetTextNode();
931 if (!pTextNd->IsHidden() || bIncludeHidden)
933 if (!m_bFirstLine)
934 m_pCurrentPam->GetPoint()->Assign(*pTextNd, 0);
936 OutHTML_SwTextNode(*this, *pTextNd);
939 else if( rNd.IsTableNode() )
941 OutHTML_SwTableNode( *this, *rNd.GetTableNode(), nullptr );
942 m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1;
944 else if( rNd.IsSectionNode() )
946 SwSectionNode* pSectionNode = rNd.GetSectionNode();
947 if (!pSectionNode->GetSection().IsHiddenFlag() || bIncludeHidden)
949 OutHTML_Section( *this, *pSectionNode );
950 m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1;
953 else if( &rNd == &m_pDoc->GetNodes().GetEndOfContent() )
954 break;
956 m_pCurrentPam->GetPoint()->Adjust(SwNodeOffset(+1)); // move
957 SwNodeOffset nPos = m_pCurrentPam->GetPoint()->GetNodeIndex();
959 if( m_bShowProgress )
960 ::SetProgressState( sal_Int32(nPos), m_pDoc->GetDocShell() ); // How far ?
962 /* If only the selected area should be saved, so only the complete
963 * nodes should be saved, this means the first and n-th node
964 * partly, the 2nd till n-1 node complete. (complete means with
965 * all formats!)
967 m_bWriteAll = bSaveWriteAll ||
968 nPos != m_pCurrentPam->GetMark()->GetNodeIndex();
969 m_bFirstLine = false;
970 m_bOutFooter = false; // after one node no footer anymore
973 ChangeParaToken( HtmlTokenId::NONE ); // MIB 8.7.97: We're doing it here and not at the caller
974 OutAndSetDefList( 0 );
976 } while( CopyNextPam( &pPam ) ); // until all PaM's processed
978 m_bWriteAll = bSaveWriteAll; // reset to old values
981 // write the StyleTable, general data, header/footer/footnotes
982 static void OutBodyColor( const char* pTag, const SwFormat *pFormat,
983 SwHTMLWriter& rHWrt )
985 const SwFormat *pRefFormat = nullptr;
987 if( rHWrt.m_xTemplate.is() )
988 pRefFormat = SwHTMLWriter::GetTemplateFormat( pFormat->GetPoolFormatId(),
989 &rHWrt.m_xTemplate->getIDocumentStylePoolAccess() );
991 const SvxColorItem *pColorItem = nullptr;
993 const SfxItemSet& rItemSet = pFormat->GetAttrSet();
994 const SvxColorItem *pCItem = rItemSet.GetItemIfSet( RES_CHRATR_COLOR );
995 const SvxColorItem *pRefItem = nullptr;
996 if (pRefFormat)
997 pRefItem = pRefFormat->GetAttrSet().GetItemIfSet( RES_CHRATR_COLOR );
998 if( pCItem )
1000 // only when the item is set in the template of the current document
1001 // or has a different value as the in HTML template, it will be set
1003 if( !pRefItem )
1005 pColorItem = pCItem;
1007 else
1009 Color aColor( pCItem->GetValue() );
1010 if( COL_AUTO == aColor )
1011 aColor = COL_BLACK;
1013 Color aRefColor( pRefItem->GetValue() );
1014 if( COL_AUTO == aRefColor )
1015 aRefColor = COL_BLACK;
1017 if( !aColor.IsRGBEqual( aRefColor ) )
1018 pColorItem = pCItem;
1021 else if( pRefItem )
1023 // The item was still set in the HTML template so we output the default
1024 pColorItem = &rItemSet.GetPool()->GetDefaultItem( RES_CHRATR_COLOR );
1027 if( pColorItem )
1029 OString sOut = OString::Concat(" ") + pTag + "=";
1030 rHWrt.Strm().WriteOString( sOut );
1031 Color aColor( pColorItem->GetValue() );
1032 if( COL_AUTO == aColor )
1033 aColor = COL_BLACK;
1034 HTMLOutFuncs::Out_Color( rHWrt.Strm(), aColor );
1035 if( RES_POOLCOLL_STANDARD==pFormat->GetPoolFormatId() )
1036 rHWrt.m_xDfltColor = aColor;
1040 sal_uInt16 SwHTMLWriter::OutHeaderAttrs()
1042 SwNodeOffset nIdx = m_pCurrentPam->GetPoint()->GetNodeIndex();
1043 SwNodeOffset nEndIdx = m_pCurrentPam->GetMark()->GetNodeIndex();
1045 SwTextNode *pTextNd = nullptr;
1046 while( nIdx<=nEndIdx &&
1047 nullptr==(pTextNd=m_pDoc->GetNodes()[nIdx]->GetTextNode()) )
1048 nIdx++;
1050 OSL_ENSURE( pTextNd, "No Text-Node found" );
1051 if( !pTextNd || !pTextNd->HasHints() )
1052 return 0;
1054 sal_uInt16 nAttrs = 0;
1055 const size_t nCntAttr = pTextNd->GetSwpHints().Count();
1056 sal_Int32 nOldPos = 0;
1057 for( size_t i=0; i<nCntAttr; ++i )
1059 const SwTextAttr *pHt = pTextNd->GetSwpHints().Get(i);
1060 if( !pHt->End() )
1062 sal_Int32 nPos = pHt->GetStart();
1063 if( nPos-nOldPos > 1
1064 || ( pHt->Which() != RES_TXTATR_FIELD
1065 && pHt->Which() != RES_TXTATR_ANNOTATION ) )
1066 break;
1068 const SwFieldIds nFieldWhich =
1069 static_cast<const SwFormatField&>(pHt->GetAttr()).GetField()->GetTyp()->Which();
1070 if( SwFieldIds::Postit!=nFieldWhich &&
1071 SwFieldIds::Script!=nFieldWhich )
1072 break;
1074 OutNewLine();
1075 OutHTML_SwFormatField( *this, pHt->GetAttr() );
1076 nOldPos = nPos;
1077 OSL_ENSURE( nAttrs<SAL_MAX_UINT16, "Too many attributes" );
1078 nAttrs++;
1082 return nAttrs;
1085 const SwPageDesc *SwHTMLWriter::MakeHeader( sal_uInt16 &rHeaderAttrs )
1087 OStringBuffer sOut;
1088 if (!mbSkipHeaderFooter)
1090 if (mbXHTML)
1091 sOut.append(OOO_STRING_SVTOOLS_HTML_doctype " " OOO_STRING_SVTOOLS_XHTML_doctype11);
1092 else
1093 sOut.append(OOO_STRING_SVTOOLS_HTML_doctype " " OOO_STRING_SVTOOLS_HTML_doctype5);
1094 HTMLOutFuncs::Out_AsciiTag( Strm(), sOut.makeStringAndClear() ); // No GetNamespace() here.
1096 // build prelude
1097 OutNewLine();
1098 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_html) );
1100 OutNewLine();
1101 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_head) );
1103 IncIndentLevel(); // indent content of <HEAD>
1105 // DocumentInfo
1106 OString sIndent = GetIndentString();
1108 uno::Reference<document::XDocumentProperties> xDocProps;
1109 SwDocShell *pDocShell(m_pDoc->GetDocShell());
1110 if (pDocShell)
1112 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1113 pDocShell->GetModel(), uno::UNO_QUERY_THROW);
1114 xDocProps.set(xDPS->getDocumentProperties());
1117 // xDocProps may be null here (when copying)
1118 SfxFrameHTMLWriter::Out_DocInfo( Strm(), GetBaseURL(), xDocProps,
1119 sIndent.getStr() );
1121 // comments and meta-tags of first paragraph
1122 rHeaderAttrs = OutHeaderAttrs();
1124 OutFootEndNoteInfo();
1127 const SwPageDesc *pPageDesc = nullptr;
1129 // In none HTML documents the first set template will be exported
1130 // and if none is set the default template
1131 SwNodeOffset nNodeIdx = m_pCurrentPam->GetPoint()->GetNodeIndex();
1133 while( nNodeIdx < m_pDoc->GetNodes().Count() )
1135 SwNode *pNd = m_pDoc->GetNodes()[ nNodeIdx ];
1136 if( pNd->IsContentNode() )
1138 pPageDesc = pNd->GetContentNode()->GetAttr(RES_PAGEDESC).GetPageDesc();
1139 break;
1141 else if( pNd->IsTableNode() )
1143 pPageDesc = pNd->GetTableNode()->GetTable().GetFrameFormat()
1144 ->GetPageDesc().GetPageDesc();
1145 break;
1148 nNodeIdx++;
1151 if( !pPageDesc )
1152 pPageDesc = &m_pDoc->GetPageDesc( 0 );
1154 if (!mbSkipHeaderFooter)
1156 // and now ... the style sheet!!!
1157 if( m_bCfgOutStyles )
1159 OutStyleSheet( *pPageDesc );
1162 // and now ... the BASIC and JavaScript!
1163 if( m_pDoc->GetDocShell() ) // BASIC is possible only in case we have a DocShell
1164 OutBasic(*this);
1166 DecIndentLevel(); // indent content of <HEAD>
1167 OutNewLine();
1168 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_head), false );
1170 // the body won't be indented, because then everything would be indented!
1171 OutNewLine();
1172 sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_body);
1173 Strm().WriteOString( sOut );
1174 sOut.setLength(0);
1176 // language
1177 OutLanguage( m_eLang );
1179 // output text colour, when it was set in the default template or was changed
1180 OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_text,
1181 m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD, false ),
1182 *this );
1184 // colour of (un)visited links
1185 OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_link,
1186 m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL ),
1187 *this );
1188 OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_vlink,
1189 m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_VISIT ),
1190 *this );
1192 const SfxItemSet& rItemSet = pPageDesc->GetMaster().GetAttrSet();
1194 // fdo#86857 page styles now contain the XATTR_*, not RES_BACKGROUND
1195 std::unique_ptr<SvxBrushItem> const aBrushItem(getSvxBrushItemFromSourceSet(rItemSet, RES_BACKGROUND));
1196 OutBackground(aBrushItem.get(), true);
1198 m_nDirection = GetHTMLDirection( rItemSet );
1199 OutDirection( m_nDirection );
1201 if( m_bCfgOutStyles )
1203 OutCSS1_BodyTagStyleOpt( *this, rItemSet );
1205 // append events
1206 if( m_pDoc->GetDocShell() ) // BASIC is possible only in case we have a DocShell
1207 OutBasicBodyEvents();
1209 Strm().WriteChar( '>' );
1211 else if (mbReqIF)
1212 // ReqIF: start xhtml.BlkStruct.class.
1213 HTMLOutFuncs::Out_AsciiTag(Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division));
1215 return pPageDesc;
1218 void SwHTMLWriter::OutAnchor( const OUString& rName )
1220 if (mbReqIF)
1222 // <a id=".."> has to be unique inside the whole document, but
1223 // we only write a fragment, so we can't ensure the ID is indeed
1224 // unique. Just don't write anchors in the ReqIF case.
1225 return;
1228 OStringBuffer sOut("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor " ");
1229 if (!mbXHTML)
1231 sOut.append(OOO_STRING_SVTOOLS_HTML_O_name "=\"");
1232 Strm().WriteOString( sOut );
1233 sOut.setLength(0);
1234 HTMLOutFuncs::Out_String( Strm(), rName ).WriteOString( "\">" );
1236 else
1238 // XHTML wants 'id' instead of 'name', also the value can't contain
1239 // spaces.
1240 sOut.append(OOO_STRING_SVTOOLS_HTML_O_id "=\"");
1241 Strm().WriteOString( sOut );
1242 sOut.setLength(0);
1243 HTMLOutFuncs::Out_String( Strm(), rName.replace(' ', '_') ).WriteOString( "\">" );
1245 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
1248 void SwHTMLWriter::OutBookmarks()
1250 // fetch current bookmark
1251 const ::sw::mark::IMark* pBookmark = nullptr;
1252 IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess();
1253 if(m_nBkmkTabPos != -1)
1254 pBookmark = pMarkAccess->getAllMarksBegin()[m_nBkmkTabPos];
1255 // Output all bookmarks in this paragraph. The content position
1256 // for the moment isn't considered!
1257 SwNodeOffset nNode = m_pCurrentPam->GetPoint()->GetNodeIndex();
1258 while( m_nBkmkTabPos != -1
1259 && pBookmark->GetMarkPos().GetNodeIndex() == nNode )
1261 // The area of bookmarks is first ignored, because it's not read.
1263 // first the SWG specific data:
1264 if ( dynamic_cast< const ::sw::mark::IBookmark* >(pBookmark) && !pBookmark->GetName().isEmpty() )
1266 OutAnchor( pBookmark->GetName() );
1269 if( ++m_nBkmkTabPos >= pMarkAccess->getAllMarksCount() )
1270 m_nBkmkTabPos = -1;
1271 else
1272 pBookmark = pMarkAccess->getAllMarksBegin()[m_nBkmkTabPos];
1275 decltype(m_aOutlineMarkPoss)::size_type nPos;
1276 for( nPos = 0; nPos < m_aOutlineMarkPoss.size() &&
1277 m_aOutlineMarkPoss[nPos] < nNode; nPos++ )
1280 while( nPos < m_aOutlineMarkPoss.size() && m_aOutlineMarkPoss[nPos] == nNode )
1282 OUString sMark( m_aOutlineMarks[nPos] );
1283 OutAnchor( sMark.replace('?', '_') ); // '?' causes problems in IE/Netscape 5
1284 m_aOutlineMarkPoss.erase( m_aOutlineMarkPoss.begin()+nPos );
1285 m_aOutlineMarks.erase( m_aOutlineMarks.begin() + nPos );
1289 void SwHTMLWriter::OutPointFieldmarks( const SwPosition& rPos )
1291 // "point" fieldmarks that occupy single character space, as opposed to
1292 // range fieldmarks that are associated with start and end points.
1294 const IDocumentMarkAccess* pMarkAccess = m_pDoc->getIDocumentMarkAccess();
1295 if (!pMarkAccess)
1296 return;
1298 const sw::mark::IFieldmark* pMark = pMarkAccess->getFieldmarkAt(rPos);
1299 if (!pMark)
1300 return;
1302 if (pMark->GetFieldname() != ODF_FORMCHECKBOX)
1303 return;
1305 const sw::mark::ICheckboxFieldmark* pCheckBox =
1306 dynamic_cast<const sw::mark::ICheckboxFieldmark*>(pMark);
1308 if (!pCheckBox)
1309 return;
1311 OString aOut("<"
1312 OOO_STRING_SVTOOLS_HTML_input
1314 OOO_STRING_SVTOOLS_HTML_O_type
1315 "=\""
1316 OOO_STRING_SVTOOLS_HTML_IT_checkbox
1317 "\"");
1319 if (pCheckBox->IsChecked())
1321 aOut += " "
1322 OOO_STRING_SVTOOLS_HTML_O_checked
1323 "=\""
1324 OOO_STRING_SVTOOLS_HTML_O_checked
1325 "\"";
1328 aOut += "/>";
1329 Strm().WriteOString(aOut);
1331 // TODO : Handle other single-point fieldmark types here (if any).
1334 void SwHTMLWriter::OutImplicitMark( std::u16string_view rMark,
1335 const char *pMarkType )
1337 if( !rMark.empty() && !m_aImplicitMarks.empty() )
1339 OUString sMark(rMark + OUStringChar(cMarkSeparator) + OUString::createFromAscii(pMarkType));
1340 if( 0 != m_aImplicitMarks.erase( sMark ) )
1342 OutAnchor(sMark.replace('?', '_')); // '?' causes problems in IE/Netscape 5
1347 OUString SwHTMLWriter::convertHyperlinkHRefValue(const OUString& rURL)
1349 OUString sURL(rURL);
1350 sal_Int32 nPos = sURL.lastIndexOf(cMarkSeparator);
1351 if (nPos != -1)
1353 OUString sCompare = sURL.copy(nPos + 1).replaceAll(" ", "");
1354 if (!sCompare.isEmpty())
1356 sCompare = sCompare.toAsciiLowerCase();
1357 if( sCompare == "region" || sCompare == "frame" ||
1358 sCompare == "graphic" || sCompare == "ole" ||
1359 sCompare == "table" || sCompare == "outline" ||
1360 sCompare == "text" )
1362 sURL = sURL.replace( '?', '_' ); // '?' causes problems in IE/Netscape 5
1366 else if (!sURL.isEmpty() && sURL[0] != '#')
1368 // Link is not started from "#", so looks like external link. Encode this URL.
1369 INetURLObject aURL(sURL);
1370 if (!aURL.HasError())
1371 sURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1373 return URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), sURL );
1376 void SwHTMLWriter::OutHyperlinkHRefValue( const OUString& rURL )
1378 OUString sURL = convertHyperlinkHRefValue(rURL);
1379 HTMLOutFuncs::Out_String( Strm(), sURL );
1382 void SwHTMLWriter::OutBackground( const SvxBrushItem *pBrushItem, bool bGraphic )
1384 const Color &rBackColor = pBrushItem->GetColor();
1385 /// check, if background color is not "no fill"/"auto fill", instead of
1386 /// only checking, if transparency is not set.
1387 if( rBackColor != COL_TRANSPARENT )
1389 Strm().WriteOString( " " OOO_STRING_SVTOOLS_HTML_O_bgcolor "=" );
1390 HTMLOutFuncs::Out_Color( Strm(), rBackColor);
1393 if( !bGraphic )
1394 return;
1396 const Graphic* pGrf = pBrushItem->GetGraphic();
1397 OUString GraphicURL = pBrushItem->GetGraphicLink();
1398 if( mbEmbedImages || GraphicURL.isEmpty())
1400 if( pGrf )
1402 OUString aGraphicInBase64;
1403 if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) )
1405 m_nWarn = WARN_SWG_POOR_LOAD;
1407 Strm().WriteOString( " " OOO_STRING_SVTOOLS_HTML_O_background "=\"" );
1408 Strm().WriteOString( OOO_STRING_SVTOOLS_HTML_O_data ":" );
1409 HTMLOutFuncs::Out_String( Strm(), aGraphicInBase64 ).WriteChar( '\"' );
1412 else
1414 if( m_bCfgCpyLinkedGrfs )
1416 CopyLocalFileToINet( GraphicURL );
1418 OUString s( URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), GraphicURL));
1419 Strm().WriteOString(" " OOO_STRING_SVTOOLS_HTML_O_background "=\"" );
1420 HTMLOutFuncs::Out_String( Strm(), s );
1421 Strm().WriteOString("\"");
1426 void SwHTMLWriter::OutBackground( const SfxItemSet& rItemSet, bool bGraphic )
1428 if( const SvxBrushItem* pItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
1430 OutBackground( pItem, bGraphic );
1434 sal_uInt16 SwHTMLWriter::GetLangWhichIdFromScript( sal_uInt16 nScript )
1436 sal_uInt16 nWhichId;
1437 switch( nScript )
1439 case CSS1_OUTMODE_CJK:
1440 nWhichId = RES_CHRATR_CJK_LANGUAGE;
1441 break;
1442 case CSS1_OUTMODE_CTL:
1443 nWhichId = RES_CHRATR_CJK_LANGUAGE;
1444 break;
1445 default:
1446 nWhichId = RES_CHRATR_LANGUAGE;
1447 break;
1449 return nWhichId;
1452 void SwHTMLWriter::OutLanguage( LanguageType nLang )
1454 // ReqIF mode: consumers would ignore language anyway.
1455 if (!(LANGUAGE_DONTKNOW != nLang && !mbReqIF))
1456 return;
1458 OStringBuffer sOut(" ");
1459 if (mbXHTML)
1460 sOut.append(OOO_STRING_SVTOOLS_XHTML_O_lang);
1461 else
1462 sOut.append(OOO_STRING_SVTOOLS_HTML_O_lang);
1463 sOut.append("=\"");
1464 Strm().WriteOString( sOut );
1465 sOut.setLength(0);
1466 HTMLOutFuncs::Out_String( Strm(), LanguageTag::convertToBcp47(nLang) ).WriteChar( '"' );
1469 SvxFrameDirection SwHTMLWriter::GetHTMLDirection( const SfxItemSet& rItemSet ) const
1471 return GetHTMLDirection( rItemSet.Get( RES_FRAMEDIR ).GetValue() );
1474 SvxFrameDirection SwHTMLWriter::GetHTMLDirection( SvxFrameDirection nDir ) const
1476 switch( nDir )
1478 case SvxFrameDirection::Vertical_LR_TB:
1479 nDir = SvxFrameDirection::Horizontal_LR_TB;
1480 break;
1481 case SvxFrameDirection::Vertical_RL_TB:
1482 nDir = SvxFrameDirection::Horizontal_RL_TB;
1483 break;
1484 case SvxFrameDirection::Environment:
1485 nDir = m_nDirection;
1486 break;
1487 default: break;
1490 return nDir;
1493 void SwHTMLWriter::OutDirection( SvxFrameDirection nDir )
1495 OString sConverted = convertDirection(nDir);
1496 if (!sConverted.isEmpty())
1498 OString sOut =
1499 " " OOO_STRING_SVTOOLS_HTML_O_dir
1500 "=\"" + sConverted + "\"";
1501 Strm().WriteOString( sOut );
1505 OString SwHTMLWriter::convertDirection(SvxFrameDirection nDir)
1507 OString sConverted;
1508 switch (nDir)
1510 case SvxFrameDirection::Horizontal_LR_TB:
1511 case SvxFrameDirection::Vertical_LR_TB:
1512 sConverted = "ltr";
1513 break;
1514 case SvxFrameDirection::Horizontal_RL_TB:
1515 case SvxFrameDirection::Vertical_RL_TB:
1516 sConverted = "rtl";
1517 break;
1518 default: break;
1520 return sConverted;
1523 OString SwHTMLWriter::GetIndentString(sal_uInt16 nIncLvl)
1525 OString sRet;
1527 // somewhat cumbersome, but we have only one indent string!
1528 sal_uInt16 nLevel = m_nIndentLvl + nIncLvl;
1530 if( nLevel && nLevel <= MAX_INDENT_LEVEL)
1532 sIndentTabs[nLevel] = 0;
1533 sRet = sIndentTabs;
1534 sIndentTabs[nLevel] = '\t';
1537 return sRet;
1540 void SwHTMLWriter::OutNewLine( bool bCheck )
1542 if( !bCheck || (Strm().Tell()-m_nLastLFPos) > m_nIndentLvl )
1544 Strm().WriteOString( SAL_NEWLINE_STRING );
1545 m_nLastLFPos = Strm().Tell();
1548 if( m_nIndentLvl && m_nIndentLvl <= MAX_INDENT_LEVEL)
1550 sIndentTabs[m_nIndentLvl] = 0;
1551 Strm().WriteOString( sIndentTabs );
1552 sIndentTabs[m_nIndentLvl] = '\t';
1556 sal_uInt16 SwHTMLWriter::GetHTMLFontSize( sal_uInt32 nHeight ) const
1558 sal_uInt16 nSize = 1;
1559 for( sal_uInt16 i=6; i>0; i-- )
1561 if( nHeight > (m_aFontHeights[i] + m_aFontHeights[i-1])/2 )
1563 nSize = i+1;
1564 break;
1568 return nSize;
1571 // Paragraphs with Table of Contents and other index styles will be typeset with
1572 // dot leaders at the position of the last tabulator in PrintLayout (CSS2) mode
1573 sal_Int32 SwHTMLWriter::indexOfDotLeaders( sal_uInt16 nPoolId, std::u16string_view rStr )
1575 if (m_bCfgPrintLayout && ((nPoolId >= RES_POOLCOLL_TOX_CNTNT1 && nPoolId <= RES_POOLCOLL_TOX_CNTNT5) ||
1576 (nPoolId >= RES_POOLCOLL_TOX_IDX1 && nPoolId <= RES_POOLCOLL_TOX_IDX3) ||
1577 (nPoolId >= RES_POOLCOLL_TOX_USER1 && nPoolId <= RES_POOLCOLL_TOX_CNTNT10) ||
1578 nPoolId == RES_POOLCOLL_TOX_ILLUS1 || nPoolId == RES_POOLCOLL_TOX_TABLES1 ||
1579 nPoolId == RES_POOLCOLL_TOX_OBJECT1 ||
1580 (nPoolId >= RES_POOLCOLL_TOX_AUTHORITIES1 && nPoolId <= RES_POOLCOLL_TOX_USER10))) {
1581 size_t i = rStr.rfind('\t');
1582 // there are only ASCII (Latin-1) characters after the tabulator
1583 if (i != std::u16string_view::npos && OUStringToOString(rStr.substr(i + 1), RTL_TEXTENCODING_ASCII_US).indexOf('?') == -1)
1584 return i;
1586 return -1;
1589 OString SwHTMLWriter::GetNamespace() const
1591 if (maNamespace.isEmpty())
1592 return OString();
1594 return maNamespace + ":";
1597 // Structure caches the current data of the writer to output a
1598 // other part of the document, like e.g. header/footer
1599 HTMLSaveData::HTMLSaveData(SwHTMLWriter& rWriter, SwNodeOffset nStt,
1600 SwNodeOffset nEnd, bool bSaveNum,
1601 const SwFrameFormat *pFrameFormat)
1602 : rWrt(rWriter)
1603 , pOldPam(rWrt.m_pCurrentPam)
1604 , pOldEnd(rWrt.GetEndPaM())
1605 , nOldDefListLvl(rWrt.m_nDefListLvl)
1606 , nOldDirection(rWrt.m_nDirection)
1607 , bOldOutHeader(rWrt.m_bOutHeader)
1608 , bOldOutFooter(rWrt.m_bOutFooter)
1609 , bOldOutFlyFrame(rWrt.m_bOutFlyFrame)
1611 bOldWriteAll = rWrt.m_bWriteAll;
1613 rWrt.m_pCurrentPam = Writer::NewUnoCursor(*rWrt.m_pDoc, nStt, nEnd);
1615 // recognize table in special areas
1616 if( nStt != rWrt.m_pCurrentPam->GetMark()->GetNodeIndex() )
1618 const SwNode *pNd = rWrt.m_pDoc->GetNodes()[ nStt ];
1619 if( pNd->IsTableNode() || pNd->IsSectionNode() )
1620 rWrt.m_pCurrentPam->GetMark()->Assign(*pNd);
1623 rWrt.SetEndPaM( rWrt.m_pCurrentPam.get() );
1624 rWrt.m_pCurrentPam->Exchange( );
1625 rWrt.m_bWriteAll = true;
1626 rWrt.m_nDefListLvl = 0;
1627 rWrt.m_bOutHeader = rWrt.m_bOutFooter = false;
1629 // Maybe save the current numbering information, so that it can be started again.
1630 // Only then also the numbering information of the next paragraph will be valid.
1631 if( bSaveNum )
1633 pOldNumRuleInfo.reset( new SwHTMLNumRuleInfo( rWrt.GetNumInfo() ) );
1634 pOldNextNumRuleInfo = rWrt.ReleaseNextNumInfo();
1636 else
1638 rWrt.ClearNextNumInfo();
1641 // The numbering will be in any case interrupted.
1642 rWrt.GetNumInfo().Clear();
1644 if( pFrameFormat )
1645 rWrt.m_nDirection = rWrt.GetHTMLDirection( pFrameFormat->GetAttrSet() );
1648 HTMLSaveData::~HTMLSaveData()
1650 rWrt.m_pCurrentPam.reset(); // delete PaM again
1652 rWrt.m_pCurrentPam = pOldPam;
1653 rWrt.SetEndPaM( pOldEnd );
1654 rWrt.m_bWriteAll = bOldWriteAll;
1655 rWrt.m_nBkmkTabPos = bOldWriteAll ? rWrt.FindPos_Bkmk( *pOldPam->GetPoint() ) : -1;
1656 rWrt.m_nLastParaToken = HtmlTokenId::NONE;
1657 rWrt.m_nDefListLvl = nOldDefListLvl;
1658 rWrt.m_nDirection = nOldDirection;
1659 rWrt.m_bOutHeader = bOldOutHeader;
1660 rWrt.m_bOutFooter = bOldOutFooter;
1661 rWrt.m_bOutFlyFrame = bOldOutFlyFrame;
1663 // Maybe continue the numbering from before section. The numbering
1664 // of the next paragraph will be invalid in any case.
1665 if( pOldNumRuleInfo )
1667 rWrt.GetNumInfo().Set( *pOldNumRuleInfo );
1668 pOldNumRuleInfo.reset();
1669 rWrt.SetNextNumInfo( std::move(pOldNextNumRuleInfo) );
1671 else
1673 rWrt.GetNumInfo().Clear();
1674 rWrt.ClearNextNumInfo();
1678 void GetHTMLWriter( std::u16string_view rFilterOptions, const OUString& rBaseURL, WriterRef& xRet )
1680 xRet = new SwHTMLWriter( rBaseURL, rFilterOptions );
1683 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */