1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
50 #include <fmtpdsc.hxx>
51 #include <txatbase.hxx>
53 #include <charfmt.hxx>
60 #include <viewopt.hxx>
62 #include <poolfmt.hxx>
63 #include <pagedesc.hxx>
64 #include <section.hxx>
65 #include <swtable.hxx>
67 #include <fmtclds.hxx>
69 #include "wrthtml.hxx"
70 #include "htmlnum.hxx"
71 #include "htmlfly.hxx"
72 #include <swmodule.hxx>
73 #include <strings.hrc>
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
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
)
100 , m_eCSS1Unit(FieldUnit::NONE
)
101 , m_pStartNdIdx(nullptr)
102 , m_pCurrPageDesc(nullptr)
103 , m_pFormatFootnote(nullptr)
106 , m_nLastParaToken(HtmlTokenId::NONE
)
113 , m_nDfltLeftMargin(0)
114 , m_nDfltRightMargin(0)
115 , m_nFirstLineIndent(0)
116 , m_nDfltFirstLineIndent(0)
117 , m_nDfltTopMargin(0)
118 , m_nDfltBottomMargin(0)
122 , m_nDefListMargin(0)
123 , m_nHeaderFooterSpace(0)
124 , m_nTextAttrsToIgnore(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 )
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
);
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();
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())
277 m_nShapeDPI
.emplace(nVal
);
280 it
= aStoreMap
.find("SkipImages");
281 if (it
!= aStoreMap
.end())
288 it
= aStoreMap
.find("SkipHeaderFooter");
289 if (it
!= aStoreMap
.end())
293 mbSkipHeaderFooter
= bVal
;
296 // this option can be "on" together with any of above
297 it
= aStoreMap
.find("NoPrettyPrint");
298 if (it
!= aStoreMap
.end())
301 m_bPrettyPrint
= false;
304 it
= aStoreMap
.find("EmbedImages");
305 if (it
!= aStoreMap
.end())
309 mbEmbedImages
= bVal
;
312 it
= aStoreMap
.find("NoLineLimit");
313 if (it
!= aStoreMap
.end())
323 it
= aStoreMap
.find("XHTML");
324 if (it
!= aStoreMap
.end())
331 it
= aStoreMap
.find("XhtmlNs");
332 if (it
!= aStoreMap
.end())
337 maNamespace
= aVal
.toUtf8();
338 if (maNamespace
== "reqif-xhtml")
341 // XHTML is always just a fragment inside ReqIF.
342 mbSkipHeaderFooter
= true;
344 // XHTML namespace implies XHTML.
348 it
= aStoreMap
.find("LeadingTabWidth");
349 if (it
!= aStoreMap
.end())
353 m_nLeadingTabWidth
.emplace(nVal
);
357 ErrCode
SwHTMLWriter::WriteStream()
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;
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
); });
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();
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;
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;
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;
477 SvtScriptType nScript
= SvtLanguageOptions::GetScriptTypeOfLanguage( GetAppLanguage() );
480 case SvtScriptType::ASIAN
:
481 m_nCSS1Script
= CSS1_OUTMODE_CJK
;
483 case SvtScriptType::COMPLEX
:
484 m_nCSS1Script
= CSS1_OUTMODE_CTL
;
487 m_nCSS1Script
= CSS1_OUTMODE_WESTERN
;
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;
501 // respect table and section at document beginning
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();
524 // start with section node !!
525 m_pCurrentPam
->GetPoint()->Assign(*pSNd
);
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() );
537 "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_division
538 " " OOO_STRING_SVTOOLS_HTML_O_id
539 "=\"" + aName
+ "\">" +
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
549 m_aHTMLPosFlyFrames
.clear();
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);
563 CollectLinkTargets();
565 sal_uInt16 nHeaderAttrs
= 0;
566 m_pCurrPageDesc
= MakeHeader( nHeaderAttrs
);
568 m_bLFPossible
= true;
570 // output forms which contain only HiddenControls
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();
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
)
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();
605 OutHTML_HeaderFooter( *this, *pFooterFormat
, false );
610 if (!mbSkipHeaderFooter
)
612 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_body
), false );
614 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_html
), false );
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;
643 OSL_ENSURE( !m_xFootEndNotes
,
644 "SwHTMLWriter::Write: Footnotes not deleted by OutFootEndNotes" );
646 m_pCurrPageDesc
= nullptr;
650 for(OUString
& s
: m_aBulletGrfs
)
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" );
672 m_xTemplate
->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE
, bOldHTMLMode
);
680 static const SwFormatCol
*lcl_html_GetFormatCol( const SwSection
& rSection
,
681 const SwSectionFormat
& rFormat
)
683 if( SectionType::FileLink
== rSection
.GetType() )
686 const SwFormatCol
*pCol
= rFormat
.GetAttrSet().GetItemIfSet(RES_COL
,false);
687 if (pCol
->GetNumCols() > 1 )
693 static bool lcl_html_IsMultiColStart( const SwHTMLWriter
& rHTMLWrt
, SwNodeOffset nIndex
)
696 const SwSectionNode
*pSectNd
=
697 rHTMLWrt
.m_pDoc
->GetNodes()[nIndex
]->GetSectionNode();
700 const SwSection
& rSection
= pSectNd
->GetSection();
701 const SwSectionFormat
*pFormat
= rSection
.GetFormat();
702 if( pFormat
&& lcl_html_GetFormatCol( rSection
, *pFormat
) )
709 static bool lcl_html_IsMultiColEnd( const SwHTMLWriter
& rHTMLWrt
, SwNodeOffset nIndex
)
712 const SwEndNode
*pEndNd
= rHTMLWrt
.m_pDoc
->GetNodes()[nIndex
]->GetEndNode();
714 bRet
= lcl_html_IsMultiColStart( rHTMLWrt
,
715 pEndNd
->StartOfSectionIndex() );
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
);
739 HTMLOutFuncs::Out_String( rHTMLWrt
.Strm(), rName
);
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
);
756 const OUString
& aFName
= rSection
.GetLinkFileName();
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
= "ÿ";
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
);
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
);
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;
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
);
839 // If the next node is a columned section node, too, don't export
841 if( lcl_html_IsMultiColStart( rWrt
, nSectSttIdx
+1 ) )
844 // The same applies if the section end with another columned section.
845 if( lcl_html_IsMultiColEnd( rWrt
, nSectEndIdx
-1 ) )
848 // is there a columned section around this one?
849 const SwStartNode
*pSttNd
= rSectNd
.StartOfSectionNode();
852 pSurrSectNd
= pSttNd
->FindSectionNode();
855 const SwStartNode
*pBoxSttNd
= pSttNd
->FindTableBoxStartNode();
857 pBoxSttNd
->GetIndex() < pSurrSectNd
->GetIndex() )
859 pSurrSection
= &pSurrSectNd
->GetSection();
860 pSurrFormat
= pSurrSection
->GetFormat();
862 pSurrCol
= lcl_html_GetFormatCol( *pSurrSection
,
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
);
877 lcl_html_OutSectionStartTag( rWrt
, rSection
, *pFormat
, pCol
);
880 HTMLSaveData
aSaveData( rWrt
,
881 rWrt
.m_pCurrentPam
->GetPoint()->GetNodeIndex()+1,
882 rSectNd
.EndOfSectionIndex(),
884 rWrt
.Out_SwDoc( rWrt
.m_pCurrentPam
.get() );
887 rWrt
.m_pCurrentPam
->GetPoint()->Assign(*rSectNd
.EndOfSectionNode());
890 lcl_html_OutSectionEndTag( rWrt
);
892 // The surrounding section must be started again, except that it ends
893 // immediately behind the current one.
895 pSurrSectNd
->EndOfSectionIndex() - nSectEndIdx
> SwNodeOffset(1) &&
896 !lcl_html_IsMultiColStart( rWrt
, nSectEndIdx
+1 ) )
897 lcl_html_OutSectionStartTag( rWrt
, *pSurrSection
, *pSurrFormat
,
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
913 m_bWriteAll
= bSaveWriteAll
;
916 // search for first on PaM created FlyFrame
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
)
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() )
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
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;
997 pRefItem
= pRefFormat
->GetAttrSet().GetItemIfSet( RES_CHRATR_COLOR
);
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
1005 pColorItem
= pCItem
;
1009 Color
aColor( pCItem
->GetValue() );
1010 if( COL_AUTO
== aColor
)
1013 Color
aRefColor( pRefItem
->GetValue() );
1014 if( COL_AUTO
== aRefColor
)
1015 aRefColor
= COL_BLACK
;
1017 if( !aColor
.IsRGBEqual( aRefColor
) )
1018 pColorItem
= pCItem
;
1023 // The item was still set in the HTML template so we output the default
1024 pColorItem
= &rItemSet
.GetPool()->GetDefaultItem( RES_CHRATR_COLOR
);
1029 OString sOut
= OString::Concat(" ") + pTag
+ "=";
1030 rHWrt
.Strm().WriteOString( sOut
);
1031 Color
aColor( pColorItem
->GetValue() );
1032 if( COL_AUTO
== aColor
)
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()) )
1050 OSL_ENSURE( pTextNd
, "No Text-Node found" );
1051 if( !pTextNd
|| !pTextNd
->HasHints() )
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
);
1062 sal_Int32 nPos
= pHt
->GetStart();
1063 if( nPos
-nOldPos
> 1
1064 || ( pHt
->Which() != RES_TXTATR_FIELD
1065 && pHt
->Which() != RES_TXTATR_ANNOTATION
) )
1068 const SwFieldIds nFieldWhich
=
1069 static_cast<const SwFormatField
&>(pHt
->GetAttr()).GetField()->GetTyp()->Which();
1070 if( SwFieldIds::Postit
!=nFieldWhich
&&
1071 SwFieldIds::Script
!=nFieldWhich
)
1075 OutHTML_SwFormatField( *this, pHt
->GetAttr() );
1077 OSL_ENSURE( nAttrs
<SAL_MAX_UINT16
, "Too many attributes" );
1085 const SwPageDesc
*SwHTMLWriter::MakeHeader( sal_uInt16
&rHeaderAttrs
)
1088 if (!mbSkipHeaderFooter
)
1091 sOut
.append(OOO_STRING_SVTOOLS_HTML_doctype
" " OOO_STRING_SVTOOLS_XHTML_doctype11
);
1093 sOut
.append(OOO_STRING_SVTOOLS_HTML_doctype
" " OOO_STRING_SVTOOLS_HTML_doctype5
);
1094 HTMLOutFuncs::Out_AsciiTag( Strm(), sOut
.makeStringAndClear() ); // No GetNamespace() here.
1098 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_html
) );
1101 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_head
) );
1103 IncIndentLevel(); // indent content of <HEAD>
1106 OString sIndent
= GetIndentString();
1108 uno::Reference
<document::XDocumentProperties
> xDocProps
;
1109 SwDocShell
*pDocShell(m_pDoc
->GetDocShell());
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
,
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();
1141 else if( pNd
->IsTableNode() )
1143 pPageDesc
= pNd
->GetTableNode()->GetTable().GetFrameFormat()
1144 ->GetPageDesc().GetPageDesc();
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
1166 DecIndentLevel(); // indent content of <HEAD>
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!
1172 sOut
.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_body
);
1173 Strm().WriteOString( sOut
);
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 ),
1184 // colour of (un)visited links
1185 OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_link
,
1186 m_pDoc
->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL
),
1188 OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_vlink
,
1189 m_pDoc
->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_VISIT
),
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
);
1206 if( m_pDoc
->GetDocShell() ) // BASIC is possible only in case we have a DocShell
1207 OutBasicBodyEvents();
1209 Strm().WriteChar( '>' );
1212 // ReqIF: start xhtml.BlkStruct.class.
1213 HTMLOutFuncs::Out_AsciiTag(Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division
));
1218 void SwHTMLWriter::OutAnchor( const OUString
& rName
)
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.
1228 OStringBuffer
sOut("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor
" ");
1231 sOut
.append(OOO_STRING_SVTOOLS_HTML_O_name
"=\"");
1232 Strm().WriteOString( sOut
);
1234 HTMLOutFuncs::Out_String( Strm(), rName
).WriteOString( "\">" );
1238 // XHTML wants 'id' instead of 'name', also the value can't contain
1240 sOut
.append(OOO_STRING_SVTOOLS_HTML_O_id
"=\"");
1241 Strm().WriteOString( sOut
);
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() )
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();
1298 const sw::mark::IFieldmark
* pMark
= pMarkAccess
->getFieldmarkAt(rPos
);
1302 if (pMark
->GetFieldname() != ODF_FORMCHECKBOX
)
1305 const sw::mark::ICheckboxFieldmark
* pCheckBox
=
1306 dynamic_cast<const sw::mark::ICheckboxFieldmark
*>(pMark
);
1312 OOO_STRING_SVTOOLS_HTML_input
1314 OOO_STRING_SVTOOLS_HTML_O_type
1316 OOO_STRING_SVTOOLS_HTML_IT_checkbox
1319 if (pCheckBox
->IsChecked())
1322 OOO_STRING_SVTOOLS_HTML_O_checked
1324 OOO_STRING_SVTOOLS_HTML_O_checked
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
);
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
);
1396 const Graphic
* pGrf
= pBrushItem
->GetGraphic();
1397 OUString GraphicURL
= pBrushItem
->GetGraphicLink();
1398 if( mbEmbedImages
|| GraphicURL
.isEmpty())
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( '\"' );
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
;
1439 case CSS1_OUTMODE_CJK
:
1440 nWhichId
= RES_CHRATR_CJK_LANGUAGE
;
1442 case CSS1_OUTMODE_CTL
:
1443 nWhichId
= RES_CHRATR_CJK_LANGUAGE
;
1446 nWhichId
= RES_CHRATR_LANGUAGE
;
1452 void SwHTMLWriter::OutLanguage( LanguageType nLang
)
1454 // ReqIF mode: consumers would ignore language anyway.
1455 if (!(LANGUAGE_DONTKNOW
!= nLang
&& !mbReqIF
))
1458 OStringBuffer
sOut(" ");
1460 sOut
.append(OOO_STRING_SVTOOLS_XHTML_O_lang
);
1462 sOut
.append(OOO_STRING_SVTOOLS_HTML_O_lang
);
1464 Strm().WriteOString( sOut
);
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
1478 case SvxFrameDirection::Vertical_LR_TB
:
1479 nDir
= SvxFrameDirection::Horizontal_LR_TB
;
1481 case SvxFrameDirection::Vertical_RL_TB
:
1482 nDir
= SvxFrameDirection::Horizontal_RL_TB
;
1484 case SvxFrameDirection::Environment
:
1485 nDir
= m_nDirection
;
1493 void SwHTMLWriter::OutDirection( SvxFrameDirection nDir
)
1495 OString sConverted
= convertDirection(nDir
);
1496 if (!sConverted
.isEmpty())
1499 " " OOO_STRING_SVTOOLS_HTML_O_dir
1500 "=\"" + sConverted
+ "\"";
1501 Strm().WriteOString( sOut
);
1505 OString
SwHTMLWriter::convertDirection(SvxFrameDirection nDir
)
1510 case SvxFrameDirection::Horizontal_LR_TB
:
1511 case SvxFrameDirection::Vertical_LR_TB
:
1514 case SvxFrameDirection::Horizontal_RL_TB
:
1515 case SvxFrameDirection::Vertical_RL_TB
:
1523 OString
SwHTMLWriter::GetIndentString(sal_uInt16 nIncLvl
)
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;
1534 sIndentTabs
[nLevel
] = '\t';
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 )
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)
1589 OString
SwHTMLWriter::GetNamespace() const
1591 if (maNamespace
.isEmpty())
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
)
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.
1633 pOldNumRuleInfo
.reset( new SwHTMLNumRuleInfo( rWrt
.GetNumInfo() ) );
1634 pOldNextNumRuleInfo
= rWrt
.ReleaseNextNumInfo();
1638 rWrt
.ClearNextNumInfo();
1641 // The numbering will be in any case interrupted.
1642 rWrt
.GetNumInfo().Clear();
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
) );
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: */