Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / filter / html / htmlatr.cxx
blob13db3a1ceac6a2b3792ecbb69d96e5e84f400161
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 <hintids.hxx>
21 #include <com/sun/star/i18n/ScriptType.hpp>
22 #include <com/sun/star/i18n/XBreakIterator.hpp>
23 #include <comphelper/string.hxx>
24 #include <utility>
25 #include <vcl/svapp.hxx>
26 #include <svtools/htmlout.hxx>
27 #include <svtools/htmlkywd.hxx>
28 #include <svtools/htmltokn.h>
29 #include <svl/whiter.hxx>
30 #include <sfx2/event.hxx>
31 #include <sfx2/htmlmode.hxx>
32 #include <editeng/escapementitem.hxx>
33 #include <editeng/formatbreakitem.hxx>
34 #include <editeng/boxitem.hxx>
35 #include <editeng/ulspitem.hxx>
36 #include <editeng/udlnitem.hxx>
37 #include <editeng/crossedoutitem.hxx>
38 #include <editeng/blinkitem.hxx>
39 #include <editeng/colritem.hxx>
40 #include <editeng/fontitem.hxx>
41 #include <editeng/fhgtitem.hxx>
42 #include <editeng/postitem.hxx>
43 #include <editeng/wghtitem.hxx>
44 #include <editeng/adjustitem.hxx>
45 #include <editeng/lrspitem.hxx>
46 #include <editeng/langitem.hxx>
47 #include <editeng/frmdiritem.hxx>
48 #include <fchrfmt.hxx>
49 #include <fmtautofmt.hxx>
50 #include <fmtfsize.hxx>
51 #include <fmtclds.hxx>
52 #include <fmtpdsc.hxx>
53 #include <fmtflcnt.hxx>
54 #include <fmtinfmt.hxx>
55 #include <txatbase.hxx>
56 #include <frmatr.hxx>
57 #include <charfmt.hxx>
58 #include <fmtfld.hxx>
59 #include <doc.hxx>
60 #include <IDocumentStylePoolAccess.hxx>
61 #include <pam.hxx>
62 #include <ndtxt.hxx>
63 #include <paratr.hxx>
64 #include <poolfmt.hxx>
65 #include <pagedesc.hxx>
66 #include <swtable.hxx>
67 #include <fldbas.hxx>
68 #include <breakit.hxx>
69 #include "htmlatr.hxx"
70 #include "htmlnum.hxx"
71 #include "wrthtml.hxx"
72 #include "htmlfly.hxx"
73 #include <numrule.hxx>
74 #include <rtl/character.hxx>
75 #include <osl/diagnose.h>
76 #include <deque>
78 #include <svtools/HtmlWriter.hxx>
79 #include <o3tl/string_view.hxx>
81 #include <memory>
82 #include <algorithm>
84 using namespace css;
86 HTMLOutEvent const aAnchorEventTable[] =
88 { OOO_STRING_SVTOOLS_HTML_O_SDonclick, OOO_STRING_SVTOOLS_HTML_O_onclick, SvMacroItemId::OnClick },
89 { OOO_STRING_SVTOOLS_HTML_O_SDonmouseover, OOO_STRING_SVTOOLS_HTML_O_onmouseover, SvMacroItemId::OnMouseOver },
90 { OOO_STRING_SVTOOLS_HTML_O_SDonmouseout, OOO_STRING_SVTOOLS_HTML_O_onmouseout, SvMacroItemId::OnMouseOut },
91 { nullptr, nullptr, SvMacroItemId::NONE }
94 static SwHTMLWriter& OutHTML_SvxAdjust( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
96 sal_uInt16 SwHTMLWriter::GetDefListLvl( std::u16string_view rNm, sal_uInt16 nPoolId )
98 if( nPoolId == RES_POOLCOLL_HTML_DD )
100 return 1 | HTML_DLCOLL_DD;
102 else if( nPoolId == RES_POOLCOLL_HTML_DT )
104 return 1 | HTML_DLCOLL_DT;
107 OUString sDTDD = OOO_STRING_SVTOOLS_HTML_dt " ";
108 if( o3tl::starts_with(rNm, sDTDD) )
109 // DefinitionList - term
110 return o3tl::narrowing<sal_uInt16>(o3tl::toInt32(rNm.substr( sDTDD.getLength() ))) | HTML_DLCOLL_DT;
112 sDTDD = OOO_STRING_SVTOOLS_HTML_dd " ";
113 if( o3tl::starts_with(rNm, sDTDD) )
114 // DefinitionList - definition
115 return o3tl::narrowing<sal_uInt16>(o3tl::toInt32(rNm.substr( sDTDD.getLength() ))) | HTML_DLCOLL_DD;
117 return 0;
120 void SwHTMLWriter::OutAndSetDefList( sal_uInt16 nNewLvl )
122 // possibly, we first need to start a new list
123 if( m_nDefListLvl < nNewLvl )
125 // output </pre> for the previous(!) paragraph, if required.
126 // Preferable, the <pre> is exported by OutHTML_SwFormatOff for the
127 // previous paragraph already, but that's not possible, because a very
128 // deep look at the next paragraph (this one) is required to figure
129 // out that a def list starts here.
131 ChangeParaToken( HtmlTokenId::NONE );
133 // write according to the level difference
134 for( sal_uInt16 i=m_nDefListLvl; i<nNewLvl; i++ )
136 if( m_bLFPossible )
137 OutNewLine();
138 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist) );
139 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_dd) );
140 IncIndentLevel();
141 m_bLFPossible = true;
144 else if( m_nDefListLvl > nNewLvl )
146 for( sal_uInt16 i=nNewLvl ; i < m_nDefListLvl; i++ )
148 DecIndentLevel();
149 if( m_bLFPossible )
150 OutNewLine();
151 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_dd), false );
152 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist), false );
153 m_bLFPossible = true;
157 m_nDefListLvl = nNewLvl;
160 void SwHTMLWriter::ChangeParaToken( HtmlTokenId nNew )
162 if( nNew != m_nLastParaToken && HtmlTokenId::PREFORMTXT_ON == m_nLastParaToken )
164 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_preformtxt), false );
165 m_bLFPossible = true;
167 m_nLastParaToken = nNew;
170 sal_uInt16 SwHTMLWriter::GetCSS1ScriptForScriptType( sal_uInt16 nScriptType )
172 sal_uInt16 nRet = CSS1_OUTMODE_ANY_SCRIPT;
174 switch( nScriptType )
176 case i18n::ScriptType::LATIN:
177 nRet = CSS1_OUTMODE_WESTERN;
178 break;
179 case i18n::ScriptType::ASIAN:
180 nRet = CSS1_OUTMODE_CJK;
181 break;
182 case i18n::ScriptType::COMPLEX:
183 nRet = CSS1_OUTMODE_CTL;
184 break;
187 return nRet;
190 // a single output function should be enough for all formats
192 * Output the formats as follows
193 * - output the tag for formats for which a corresponding HTML tag exist
194 * - for all the other formats, output a paragraph tag <P> and set bUserFormat
195 * - if a paragraph alignment is set for the supplied ItemSet of the node or
196 * for the ItemSet of the format, output an ALIGN=xxx if HTML allows it
197 * - In all cases, hard attribute is written as STYLE option.
198 * If bUserFormat is not set, only the supplied ItemSet is considered.
199 * Otherwise, attributes of the format are output as well.
202 namespace {
204 struct SwHTMLTextCollOutputInfo
206 OString aToken; // End token to be output
207 std::optional<SfxItemSet> moItemSet; // hard attribute
209 bool bInNumberBulletList; // in an enumerated list;
210 bool bParaPossible; // a </P> may be output additionally
211 bool bOutPara; // a </P> is supposed to be output
212 bool bOutDiv; // write a </DIV>
214 SwHTMLTextCollOutputInfo() :
215 bInNumberBulletList( false ),
216 bParaPossible( false ),
217 bOutPara( false ),
218 bOutDiv( false )
221 bool HasParaToken() const { return aToken.getLength()==1 && aToken[0]=='P'; }
222 bool ShouldOutputToken() const { return bOutPara || !HasParaToken(); }
227 SwHTMLFormatInfo::SwHTMLFormatInfo( const SwFormat *pF, SwDoc *pDoc, SwDoc *pTemplate,
228 bool bOutStyles,
229 LanguageType eDfltLang,
230 sal_uInt16 nCSS1Script )
231 : pFormat(pF)
232 , nLeftMargin(0)
233 , nRightMargin(0)
234 , nFirstLineIndent(0)
235 , nTopMargin(0)
236 , nBottomMargin(0)
237 , bScriptDependent( false )
239 sal_uInt16 nRefPoolId = 0;
240 // Get the selector of the format
241 sal_uInt16 nDeep = SwHTMLWriter::GetCSS1Selector( pFormat, aToken, aClass,
242 nRefPoolId );
243 OSL_ENSURE( nDeep ? !aToken.isEmpty() : aToken.isEmpty(),
244 "Something seems to be wrong with this token!" );
245 OSL_ENSURE( nDeep ? nRefPoolId != 0 : nRefPoolId == 0,
246 "Something seems to be wrong with the comparison style!" );
248 bool bTextColl = pFormat->Which() == RES_TXTFMTCOLL ||
249 pFormat->Which() == RES_CONDTXTFMTCOLL;
251 const SwFormat *pReferenceFormat = nullptr; // Comparison format
252 if( nDeep != 0 )
254 // It's an HTML-tag style or this style is derived from such
255 // a style.
256 if( !bOutStyles )
258 // if no styles are exported, it may be necessary to additionally
259 // write hard attribute
260 switch( nDeep )
262 case CSS1_FMT_ISTAG:
263 case CSS1_FMT_CMPREF:
264 // for HTML-tag styles the differences to the original
265 // (if available)
266 pReferenceFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId,
267 &pTemplate->getIDocumentStylePoolAccess() );
268 break;
270 default:
271 // otherwise, the differences to the HTML-tag style of the
272 // original or the ones to the current document, if it the
273 // HTML-tag style is not available
274 if( pTemplate )
275 pReferenceFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId,
276 &pTemplate->getIDocumentStylePoolAccess() );
277 else
278 pReferenceFormat = SwHTMLWriter::GetParentFormat( *pFormat, nDeep );
279 break;
283 else if( bTextColl )
285 // HTML-tag styles that are not derived from a paragraph style
286 // must be exported as hard attribute relative to the text-body
287 // style. For a 'not-styles' export, the one of the HTML style
288 // should be used as a reference
289 if( !bOutStyles && pTemplate )
290 pReferenceFormat = pTemplate->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false );
291 else
292 pReferenceFormat = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false );
295 if( pReferenceFormat || nDeep==0 )
297 moItemSet.emplace( *pFormat->GetAttrSet().GetPool(),
298 pFormat->GetAttrSet().GetRanges() );
299 // if the differences to a different style are supposed to be
300 // written, hard attribute is necessary. This is always true
301 // for styles that are not derived from HTML-tag styles.
303 moItemSet->Set( pFormat->GetAttrSet() );
305 if( pReferenceFormat )
306 SwHTMLWriter::SubtractItemSet( *moItemSet, pReferenceFormat->GetAttrSet(), true );
308 // delete ItemSet that is empty straight away. This will save work
309 // later on
310 if( !moItemSet->Count() )
312 moItemSet.reset();
316 if( !bTextColl )
317 return;
319 if( bOutStyles )
321 // We have to add hard attributes for any script dependent
322 // item that is not accessed by the style
323 static const sal_uInt16 aWhichIds[3][4] =
325 { RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
326 RES_CHRATR_POSTURE, RES_CHRATR_WEIGHT },
327 { RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
328 RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT },
329 { RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE,
330 RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT }
333 sal_uInt16 nRef = 0;
334 sal_uInt16 aSets[2] = {0,0};
335 switch( nCSS1Script )
337 case CSS1_OUTMODE_WESTERN:
338 nRef = 0;
339 aSets[0] = 1;
340 aSets[1] = 2;
341 break;
342 case CSS1_OUTMODE_CJK:
343 nRef = 1;
344 aSets[0] = 0;
345 aSets[1] = 2;
346 break;
347 case CSS1_OUTMODE_CTL:
348 nRef = 2;
349 aSets[0] = 0;
350 aSets[1] = 1;
351 break;
353 for( int i=0; i<4; ++i )
355 const SfxPoolItem& rRef = pFormat->GetFormatAttr( aWhichIds[nRef][i] );
356 for(sal_uInt16 nSet : aSets)
358 const SfxPoolItem& rSet = pFormat->GetFormatAttr( aWhichIds[nSet][i] );
359 if( rSet != rRef )
361 if( !moItemSet )
362 moItemSet.emplace( *pFormat->GetAttrSet().GetPool(),
363 pFormat->GetAttrSet().GetRanges() );
364 moItemSet->Put( rSet );
370 // remember all the different default spacings from the style or
371 // the comparison style.
372 SvxFirstLineIndentItem const& rFirstLine(
373 (pReferenceFormat ? pReferenceFormat : pFormat)->GetFirstLineIndent());
374 SvxTextLeftMarginItem const& rTextLeftMargin(
375 (pReferenceFormat ? pReferenceFormat : pFormat)->GetTextLeftMargin());
376 SvxRightMarginItem const& rRightMargin(
377 (pReferenceFormat ? pReferenceFormat : pFormat)->GetRightMargin());
378 nLeftMargin = rTextLeftMargin.GetTextLeft();
379 nRightMargin = rRightMargin.GetRight();
380 nFirstLineIndent = rFirstLine.GetTextFirstLineOffset();
382 const SvxULSpaceItem &rULSpace =
383 (pReferenceFormat ? pReferenceFormat : pFormat)->GetULSpace();
384 nTopMargin = rULSpace.GetUpper();
385 nBottomMargin = rULSpace.GetLower();
387 // export language if it differs from the default language
388 sal_uInt16 nWhichId =
389 SwHTMLWriter::GetLangWhichIdFromScript( nCSS1Script );
390 const SvxLanguageItem& rLang =
391 static_cast<const SvxLanguageItem&>(pFormat->GetFormatAttr( nWhichId ));
392 LanguageType eLang = rLang.GetLanguage();
393 if( eLang != eDfltLang )
395 if( !moItemSet )
396 moItemSet.emplace( *pFormat->GetAttrSet().GetPool(),
397 pFormat->GetAttrSet().GetRanges() );
398 moItemSet->Put( rLang );
401 static const sal_uInt16 aWhichIds[3] =
402 { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE,
403 RES_CHRATR_CTL_LANGUAGE };
404 for(sal_uInt16 i : aWhichIds)
406 if( i != nWhichId )
408 const SvxLanguageItem& rTmpLang =
409 static_cast<const SvxLanguageItem&>(pFormat->GetFormatAttr(i));
410 if( rTmpLang.GetLanguage() != eLang )
412 if( !moItemSet )
413 moItemSet.emplace( *pFormat->GetAttrSet().GetPool(),
414 pFormat->GetAttrSet().GetRanges() );
415 moItemSet->Put( rTmpLang );
422 SwHTMLFormatInfo::~SwHTMLFormatInfo()
426 static void OutHTML_SwFormat( SwHTMLWriter& rWrt, const SwFormat& rFormat,
427 const SfxItemSet *pNodeItemSet,
428 SwHTMLTextCollOutputInfo& rInfo )
430 OSL_ENSURE( RES_CONDTXTFMTCOLL==rFormat.Which() || RES_TXTFMTCOLL==rFormat.Which(),
431 "not a paragraph style" );
433 // First, some flags
434 sal_uInt16 nNewDefListLvl = 0;
435 sal_uInt16 nNumStart = USHRT_MAX;
436 bool bForceDL = false;
437 bool bDT = false;
438 rInfo.bInNumberBulletList = false; // Are we in a list?
439 bool bNumbered = false; // The current paragraph is numbered
440 bool bPara = false; // the current token is <P>
441 rInfo.bParaPossible = false; // a <P> may be additionally output
442 bool bNoEndTag = false; // don't output an end tag
444 rWrt.m_bNoAlign = false; // no ALIGN=... possible
446 if (rWrt.mbXHTML)
448 rWrt.m_bNoAlign = true;
451 sal_uInt8 nBulletGrfLvl = 255; // The bullet graphic we want to output
453 // Are we in a bulleted or numbered list?
454 const SwTextNode* pTextNd = rWrt.m_pCurrentPam->GetPointNode().GetTextNode();
456 SwHTMLNumRuleInfo aNumInfo;
457 if( rWrt.GetNextNumInfo() )
459 aNumInfo = *rWrt.GetNextNumInfo();
460 rWrt.ClearNextNumInfo();
462 else
464 aNumInfo.Set( *pTextNd );
467 if( aNumInfo.GetNumRule() )
469 rInfo.bInNumberBulletList = true;
470 nNewDefListLvl = 0;
472 // is the current paragraph numbered?
473 bNumbered = aNumInfo.IsNumbered();
474 sal_uInt8 nLvl = aNumInfo.GetLevel();
476 OSL_ENSURE( pTextNd->GetActualListLevel() == nLvl,
477 "Remembered Num level is wrong" );
478 OSL_ENSURE( bNumbered == pTextNd->IsCountedInList(),
479 "Remembered numbering state is wrong" );
481 if( bNumbered )
483 nBulletGrfLvl = nLvl; // only temporarily!!!
484 // #i57919#
485 // correction of re-factoring done by cws swnumtree:
486 // - <nNumStart> has to contain the restart value, if the
487 // numbering is restarted at this text node. Value <USHRT_MAX>
488 // indicates, that no additional restart value has to be written.
489 if ( pTextNd->IsListRestart() )
491 nNumStart = static_cast< sal_uInt16 >(pTextNd->GetActualListStartValue());
493 OSL_ENSURE( rWrt.m_nLastParaToken == HtmlTokenId::NONE,
494 "<PRE> was not closed before <LI>." );
498 // Now, we're getting the token and, if necessary, the class
499 std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(&rFormat));
500 SwHTMLFormatInfo *pFormatInfo;
501 SwHTMLFormatInfos::iterator it = rWrt.m_TextCollInfos.find( pTmpInfo );
502 if (it != rWrt.m_TextCollInfos.end())
504 pFormatInfo = it->get();
506 else
508 pFormatInfo = new SwHTMLFormatInfo( &rFormat, rWrt.m_pDoc, rWrt.m_xTemplate.get(),
509 rWrt.m_bCfgOutStyles, rWrt.m_eLang,
510 rWrt.m_nCSS1Script );
511 rWrt.m_TextCollInfos.insert(std::unique_ptr<SwHTMLFormatInfo>(pFormatInfo));
512 if( rWrt.m_aScriptParaStyles.count( rFormat.GetName() ) )
513 pFormatInfo->bScriptDependent = true;
516 // Now, we define what is possible due to the token
517 HtmlTokenId nToken = HtmlTokenId::NONE; // token for tag change
518 bool bOutNewLine = false; // only output a single LF?
519 if( !pFormatInfo->aToken.isEmpty() )
521 // It is an HTML-tag style or the style is derived from such a
522 // style.
523 rInfo.aToken = pFormatInfo->aToken;
525 if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_address)
527 rInfo.bParaPossible = true;
528 rWrt.m_bNoAlign = true;
530 else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_blockquote)
532 rInfo.bParaPossible = true;
533 rWrt.m_bNoAlign = true;
535 else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_parabreak)
537 bPara = true;
539 else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_preformtxt)
541 if (HtmlTokenId::PREFORMTXT_ON == rWrt.m_nLastParaToken)
543 bOutNewLine = true;
545 else
547 nToken = HtmlTokenId::PREFORMTXT_ON;
548 rWrt.m_bNoAlign = true;
549 bNoEndTag = true;
552 else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dt || rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dd)
554 bDT = rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dt;
555 rInfo.bParaPossible = !bDT;
556 rWrt.m_bNoAlign = true;
557 bForceDL = true;
560 else
562 // all styles that do not correspond to an HTML tag, or that are
563 // not derived from it, are exported as <P>
565 rInfo.aToken = OOO_STRING_SVTOOLS_HTML_parabreak;
566 bPara = true;
569 // If necessary, take the hard attribute from the style
570 if( pFormatInfo->moItemSet )
572 OSL_ENSURE(!rInfo.moItemSet, "Where does this ItemSet come from?");
573 rInfo.moItemSet.emplace( *pFormatInfo->moItemSet );
576 // additionally, add the hard attribute from the paragraph
577 if( pNodeItemSet )
579 if (rInfo.moItemSet)
580 rInfo.moItemSet->Put( *pNodeItemSet );
581 else
582 rInfo.moItemSet.emplace( *pNodeItemSet );
585 // we will need the lower spacing of the paragraph later on
586 const SvxULSpaceItem& rULSpace =
587 pNodeItemSet ? pNodeItemSet->Get(RES_UL_SPACE)
588 : rFormat.GetULSpace();
590 if( (rWrt.m_bOutHeader &&
591 rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() ==
592 rWrt.m_pCurrentPam->GetMark()->GetNodeIndex()) ||
593 rWrt.m_bOutFooter )
595 if( rWrt.m_bCfgOutStyles )
597 SvxULSpaceItem aULSpaceItem( rULSpace );
598 if( rWrt.m_bOutHeader )
599 aULSpaceItem.SetLower( rWrt.m_nHeaderFooterSpace );
600 else
601 aULSpaceItem.SetUpper( rWrt.m_nHeaderFooterSpace );
603 if (!rInfo.moItemSet)
605 rInfo.moItemSet.emplace(*rFormat.GetAttrSet().GetPool(), svl::Items<RES_UL_SPACE, RES_UL_SPACE>);
607 rInfo.moItemSet->Put( aULSpaceItem );
609 rWrt.m_bOutHeader = false;
610 rWrt.m_bOutFooter = false;
613 if( bOutNewLine )
615 // output a line break (without indentation) at the beginning of the
616 // paragraph, only
617 rInfo.aToken.clear(); // don't output an end tag
618 rWrt.Strm().WriteOString( SAL_NEWLINE_STRING );
620 return;
623 // should an ALIGN=... be written?
624 const SvxAdjustItem* pAdjItem = nullptr;
626 if( rInfo.moItemSet )
627 pAdjItem = rInfo.moItemSet->GetItemIfSet( RES_PARATR_ADJUST, false );
629 // Consider the lower spacing of the paragraph? (never in the last
630 // paragraph of tables)
631 bool bUseParSpace = !rWrt.m_bOutTable ||
632 (rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() !=
633 rWrt.m_pCurrentPam->GetMark()->GetNodeIndex());
634 // If styles are exported, indented paragraphs become definition lists
635 SvxFirstLineIndentItem const& rFirstLine(
636 pNodeItemSet ? pNodeItemSet->Get(RES_MARGIN_FIRSTLINE)
637 : rFormat.GetFirstLineIndent());
638 SvxTextLeftMarginItem const& rTextLeftMargin(
639 pNodeItemSet ? pNodeItemSet->Get(RES_MARGIN_TEXTLEFT)
640 : rFormat.GetTextLeftMargin());
641 if( (!rWrt.m_bCfgOutStyles || bForceDL) && !rInfo.bInNumberBulletList )
643 sal_Int32 nLeftMargin;
644 if( bForceDL )
645 nLeftMargin = rTextLeftMargin.GetTextLeft();
646 else
647 nLeftMargin = rTextLeftMargin.GetTextLeft() > pFormatInfo->nLeftMargin
648 ? rTextLeftMargin.GetTextLeft() - pFormatInfo->nLeftMargin
649 : 0;
651 if( nLeftMargin > 0 && rWrt.m_nDefListMargin > 0 )
653 nNewDefListLvl = static_cast< sal_uInt16 >((nLeftMargin + (rWrt.m_nDefListMargin/2)) /
654 rWrt.m_nDefListMargin);
655 if( nNewDefListLvl == 0 && bForceDL && !bDT )
656 nNewDefListLvl = 1;
658 else
660 // If the left margin is 0 or negative, emulating indent
661 // with <dd> does not work. We then set a def list only if
662 // the dd style is used.
663 nNewDefListLvl = (bForceDL&& !bDT) ? 1 : 0;
666 bool bIsNextTextNode =
667 rWrt.m_pDoc->GetNodes()[rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex()+1]
668 ->IsTextNode();
670 if( bForceDL && bDT )
672 // Instead of a DD we must use a DT from the level above this one.
673 nNewDefListLvl++;
675 else if( !nNewDefListLvl && !rWrt.m_bCfgOutStyles && bPara &&
676 rULSpace.GetLower()==0 &&
677 ((bUseParSpace && bIsNextTextNode) || rWrt.m_nDefListLvl==1) &&
678 (!pAdjItem || SvxAdjust::Left==pAdjItem->GetAdjust()) )
680 // Export paragraphs without a lower spacing as DT
681 nNewDefListLvl = 1;
682 bDT = true;
683 rInfo.bParaPossible = false;
684 rWrt.m_bNoAlign = true;
688 if( nNewDefListLvl != rWrt.m_nDefListLvl )
689 rWrt.OutAndSetDefList( nNewDefListLvl );
691 // if necessary, start a bulleted or numbered list
692 if( rInfo.bInNumberBulletList )
694 OSL_ENSURE( !rWrt.m_nDefListLvl, "DL cannot be inside OL!" );
695 OutHTML_NumberBulletListStart( rWrt, aNumInfo );
697 if( bNumbered )
699 if( !rWrt.m_aBulletGrfs[nBulletGrfLvl].isEmpty() )
700 bNumbered = false;
701 else
702 nBulletGrfLvl = 255;
706 // Take the defaults of the style, because they don't need to be
707 // exported
708 rWrt.m_nDfltLeftMargin = pFormatInfo->nLeftMargin;
709 rWrt.m_nDfltRightMargin = pFormatInfo->nRightMargin;
710 rWrt.m_nDfltFirstLineIndent = pFormatInfo->nFirstLineIndent;
712 if( rInfo.bInNumberBulletList )
714 if( !rWrt.IsHTMLMode( HTMLMODE_LSPACE_IN_NUMBER_BULLET ) )
715 rWrt.m_nDfltLeftMargin = rTextLeftMargin.GetTextLeft();
717 // In numbered lists, don't output a first line indent.
718 rWrt.m_nFirstLineIndent = rFirstLine.GetTextFirstLineOffset();
721 if( rInfo.bInNumberBulletList && bNumbered && bPara && !rWrt.m_bCfgOutStyles )
723 // a single LI doesn't have spacing
724 rWrt.m_nDfltTopMargin = 0;
725 rWrt.m_nDfltBottomMargin = 0;
727 else if( rWrt.m_nDefListLvl && bPara )
729 // a single DD doesn't have spacing, as well
730 rWrt.m_nDfltTopMargin = 0;
731 rWrt.m_nDfltBottomMargin = 0;
733 else
735 rWrt.m_nDfltTopMargin = pFormatInfo->nTopMargin;
736 // if in the last paragraph of a table the lower paragraph spacing
737 // is changed, Netscape doesn't get it. That's why we don't
738 // export anything here for now, by setting this spacing to the
739 // default value.
740 if( rWrt.m_bCfgNetscape4 && !bUseParSpace )
741 rWrt.m_nDfltBottomMargin = rULSpace.GetLower();
742 else
743 rWrt.m_nDfltBottomMargin = pFormatInfo->nBottomMargin;
746 if( rWrt.m_nDefListLvl )
748 rWrt.m_nLeftMargin =
749 (rWrt.m_nDefListLvl-1) * rWrt.m_nDefListMargin;
752 if( rWrt.m_bLFPossible && !rWrt.m_bFirstLine )
753 rWrt.OutNewLine(); // paragraph tag on a new line
754 rInfo.bOutPara = false;
756 // this is now our new token
757 rWrt.ChangeParaToken( nToken );
759 bool bHasParSpace = bUseParSpace && rULSpace.GetLower() > 0;
760 // XHTML doesn't allow character children for <blockquote>.
761 bool bXhtmlBlockQuote = rWrt.mbXHTML && rInfo.aToken == OOO_STRING_SVTOOLS_HTML_blockquote;
763 // if necessary, start a new list item
764 bool bNumberedForListItem = bNumbered;
765 if (!bNumberedForListItem)
767 // Open a list also for the leading unnumbered nodes (= list headers in ODF terminology);
768 // to do that, detect if this unnumbered node is the first in this list
769 const auto& rPrevListInfo = rWrt.GetNumInfo();
770 if (rPrevListInfo.GetNumRule() != aNumInfo.GetNumRule() || aNumInfo.IsRestart(rPrevListInfo)
771 || rPrevListInfo.GetDepth() < aNumInfo.GetDepth())
772 bNumberedForListItem = true;
774 if( rInfo.bInNumberBulletList && bNumberedForListItem )
776 HtmlWriter html(rWrt.Strm(), rWrt.maNamespace);
777 html.prettyPrint(rWrt.m_bPrettyPrint);
778 html.start(OOO_STRING_SVTOOLS_HTML_li);
779 if (!bNumbered)
781 // Handles list headers (<text:list-header> ODF element)
782 html.attribute(OOO_STRING_SVTOOLS_HTML_O_style, "display: block");
784 else if (USHRT_MAX != nNumStart)
785 html.attribute(OOO_STRING_SVTOOLS_HTML_O_value, OString::number(nNumStart));
786 // Finish the opening element, but don't close it.
787 html.characters("");
790 if( rWrt.m_nDefListLvl > 0 && !bForceDL )
792 OString aTag = bDT ? OOO_STRING_SVTOOLS_HTML_dt : OOO_STRING_SVTOOLS_HTML_dd;
793 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag) );
796 if( pAdjItem &&
797 rWrt.IsHTMLMode( HTMLMODE_NO_CONTROL_CENTERING ) &&
798 rWrt.HasControls() )
800 // The align=... attribute does behave strange in netscape
801 // if there are controls in a paragraph, because the control and
802 // all text behind the control does not recognize this attribute.
803 OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division;
804 rWrt.Strm().WriteOString( sOut );
806 rWrt.m_bTextAttr = false;
807 rWrt.m_bOutOpts = true;
808 OutHTML_SvxAdjust( rWrt, *pAdjItem );
809 rWrt.Strm().WriteChar( '>' );
810 pAdjItem = nullptr;
811 rWrt.m_bNoAlign = false;
812 rInfo.bOutDiv = true;
813 rWrt.IncIndentLevel();
814 rWrt.m_bLFPossible = true;
815 rWrt.OutNewLine();
818 // for BLOCKQUOTE, ADDRESS and DD we output another paragraph token, if
819 // - no styles are written and
820 // - a lower spacing or a paragraph alignment exists
821 // Also, XHTML does not allow character children in this context.
822 OString aToken = rInfo.aToken;
823 if( (!rWrt.m_bCfgOutStyles || rWrt.mbXHTML) && rInfo.bParaPossible && !bPara &&
824 (bHasParSpace || bXhtmlBlockQuote || pAdjItem) )
826 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + rInfo.aToken) );
827 aToken = OOO_STRING_SVTOOLS_HTML_parabreak;
828 bPara = true;
829 rWrt.m_bNoAlign = false;
832 LanguageType eLang;
833 if (rInfo.moItemSet)
834 eLang = static_cast<const SvxLanguageItem&>(rInfo.moItemSet->Get(SwHTMLWriter::GetLangWhichIdFromScript(rWrt.m_nCSS1Script))).GetLanguage();
835 else
836 eLang = rWrt.m_eLang;
838 if( rInfo.moItemSet )
840 static const TypedWhichId<SvxLanguageItem> aWhichIds[3] = { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE };
842 for(auto const & i : aWhichIds)
844 // export language if it differs from the default language only.
845 const SvxLanguageItem* pTmpItem = rInfo.moItemSet->GetItemIfSet( i );
846 if( pTmpItem && pTmpItem->GetLanguage() == eLang )
847 rInfo.moItemSet->ClearItem( i );
851 // and the text direction
852 SvxFrameDirection nDir = rWrt.GetHTMLDirection(
853 (pNodeItemSet ? pNodeItemSet->Get( RES_FRAMEDIR )
854 : rFormat.GetFrameDir() ).GetValue() );
856 // We only write a <P>, if
857 // - we are not inside OL/UL/DL, or
858 // - the paragraph of an OL/UL is not numbered or
859 // - styles are not exported and
860 // - a lower spacing, or
861 // - a paragraph alignment exists, or
862 // - styles are exported and
863 // - the text body style was changed, or
864 // - a user format is exported, or
865 // - a paragraph attribute exists
866 if( !bPara ||
867 (!rInfo.bInNumberBulletList && !rWrt.m_nDefListLvl) ||
868 (rInfo.bInNumberBulletList && !bNumbered) ||
869 (!rWrt.m_bCfgOutStyles &&
870 (bHasParSpace || bXhtmlBlockQuote || pAdjItem ||
871 (eLang != LANGUAGE_DONTKNOW && eLang != rWrt.m_eLang))) ||
872 nDir != rWrt.m_nDirection ||
873 rWrt.m_bCfgOutStyles )
875 // now, options are output
876 rWrt.m_bTextAttr = false;
877 rWrt.m_bOutOpts = true;
879 OString sOut = "<" + rWrt.GetNamespace() + aToken;
881 if( eLang != LANGUAGE_DONTKNOW && eLang != rWrt.m_eLang )
883 rWrt.Strm().WriteOString( sOut );
884 sOut = "";
885 rWrt.OutLanguage( eLang );
888 if( nDir != rWrt.m_nDirection )
890 if( !sOut.isEmpty() )
892 rWrt.Strm().WriteOString( sOut );
893 sOut = "";
895 rWrt.OutDirection( nDir );
898 if( rWrt.m_bCfgOutStyles &&
899 (!pFormatInfo->aClass.isEmpty() || pFormatInfo->bScriptDependent) )
901 sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"";
902 rWrt.Strm().WriteOString( sOut );
903 sOut = "";
904 OUString aClass( pFormatInfo->aClass );
905 if( pFormatInfo->bScriptDependent )
907 if( !aClass.isEmpty() )
908 aClass += "-";
909 switch( rWrt.m_nCSS1Script )
911 case CSS1_OUTMODE_WESTERN:
912 aClass += "western";
913 break;
914 case CSS1_OUTMODE_CJK:
915 aClass += "cjk";
916 break;
917 case CSS1_OUTMODE_CTL:
918 aClass += "ctl";
919 break;
922 HTMLOutFuncs::Out_String( rWrt.Strm(), aClass );
923 sOut += "\"";
925 rWrt.Strm().WriteOString( sOut );
926 sOut = "";
928 // if necessary, output alignment
929 if( !rWrt.m_bNoAlign && pAdjItem )
930 OutHTML_SvxAdjust( rWrt, *pAdjItem );
932 rWrt.m_bParaDotLeaders = bPara && rWrt.m_bCfgPrintLayout && rWrt.indexOfDotLeaders(
933 pTextNd->GetAnyFormatColl().GetPoolFormatId(), pTextNd->GetText()) > -1;
935 // and now, if necessary, the STYLE options
936 if (rWrt.m_bCfgOutStyles && rInfo.moItemSet)
938 OutCSS1_ParaTagStyleOpt( rWrt, *rInfo.moItemSet );
941 if (rWrt.m_bParaDotLeaders) {
942 sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\""
943 sCSS2_P_CLASS_leaders "\"><"
944 OOO_STRING_SVTOOLS_HTML_O_span;
945 rWrt.Strm().WriteOString( sOut );
946 sOut = "";
949 rWrt.Strm().WriteChar( '>' );
951 // is a </P> supposed to be written?
952 rInfo.bOutPara =
953 bPara &&
954 ( rWrt.m_bCfgOutStyles || bHasParSpace );
956 // if no end tag is supposed to be written, delete it
957 if( bNoEndTag )
958 rInfo.aToken.clear();
961 if( nBulletGrfLvl != 255 )
963 OSL_ENSURE( aNumInfo.GetNumRule(), "Where is the numbering gone???" );
964 OSL_ENSURE( nBulletGrfLvl < MAXLEVEL, "There are not this many layers." );
965 const SwNumFormat& rNumFormat = aNumInfo.GetNumRule()->Get(nBulletGrfLvl);
966 OutHTML_BulletImage( rWrt, OOO_STRING_SVTOOLS_HTML_image, rNumFormat.GetBrush(),
967 rWrt.m_aBulletGrfs[nBulletGrfLvl]);
970 rWrt.GetNumInfo() = aNumInfo;
972 // reset the defaults
973 rWrt.m_nDfltLeftMargin = 0;
974 rWrt.m_nDfltRightMargin = 0;
975 rWrt.m_nDfltFirstLineIndent = 0;
976 rWrt.m_nDfltTopMargin = 0;
977 rWrt.m_nDfltBottomMargin = 0;
978 rWrt.m_nLeftMargin = 0;
979 rWrt.m_nFirstLineIndent = 0;
982 static void OutHTML_SwFormatOff( SwHTMLWriter& rWrt, const SwHTMLTextCollOutputInfo& rInfo )
984 // if there is no token, we don't need to output anything
985 if( rInfo.aToken.isEmpty() )
987 rWrt.FillNextNumInfo();
988 const SwHTMLNumRuleInfo& rNextInfo = *rWrt.GetNextNumInfo();
989 // a bulleted list must be closed in PRE as well
990 if( rInfo.bInNumberBulletList )
993 const SwHTMLNumRuleInfo& rNRInfo = rWrt.GetNumInfo();
994 if( rNextInfo.GetNumRule() != rNRInfo.GetNumRule() ||
995 rNextInfo.GetDepth() != rNRInfo.GetDepth() ||
996 rNextInfo.IsNumbered() || rNextInfo.IsRestart(rNRInfo) )
997 rWrt.ChangeParaToken( HtmlTokenId::NONE );
998 OutHTML_NumberBulletListEnd( rWrt, rNextInfo );
1000 else if( rNextInfo.GetNumRule() != nullptr )
1001 rWrt.ChangeParaToken( HtmlTokenId::NONE );
1003 return;
1006 if( rInfo.ShouldOutputToken() )
1008 if( rWrt.m_bPrettyPrint && rWrt.m_bLFPossible )
1009 rWrt.OutNewLine( true );
1011 // if necessary, for BLOCKQUOTE, ADDRESS and DD another paragraph token
1012 // is output, if
1013 // - no styles are written and
1014 // - a lower spacing exists
1015 if( rInfo.bParaPossible && rInfo.bOutPara )
1016 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_parabreak), false );
1018 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + rInfo.aToken), false );
1019 rWrt.m_bLFPossible =
1020 rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dt &&
1021 rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dd &&
1022 rInfo.aToken != OOO_STRING_SVTOOLS_HTML_li;
1024 if( rInfo.bOutDiv )
1026 rWrt.DecIndentLevel();
1027 if( rWrt.m_bLFPossible )
1028 rWrt.OutNewLine();
1029 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
1030 rWrt.m_bLFPossible = true;
1033 // if necessary, close the list item, then close a bulleted or numbered list
1034 if( rInfo.bInNumberBulletList )
1036 rWrt.FillNextNumInfo();
1037 OutHTML_NumberBulletListEnd( rWrt, *rWrt.GetNextNumInfo() );
1041 namespace {
1043 class HTMLStartEndPos
1045 sal_Int32 m_nStart;
1046 sal_Int32 m_nEnd;
1047 std::unique_ptr<SfxPoolItem> m_pItem;
1049 public:
1051 HTMLStartEndPos( const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE );
1053 const SfxPoolItem* GetItem() const { return m_pItem.get(); }
1055 void SetStart(sal_Int32 nStt) { m_nStart = nStt; }
1056 sal_Int32 GetStart() const { return m_nStart; }
1058 sal_Int32 GetEnd() const { return m_nEnd; }
1059 void SetEnd(sal_Int32 nE) { m_nEnd = nE; }
1064 HTMLStartEndPos::HTMLStartEndPos(const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE)
1065 : m_nStart(nStt)
1066 , m_nEnd(nE)
1067 , m_pItem(rItem.Clone())
1070 typedef std::vector<HTMLStartEndPos *> HTMLStartEndPositions;
1072 namespace {
1074 enum HTMLOnOffState { HTML_NOT_SUPPORTED, // unsupported Attribute
1075 HTML_REAL_VALUE, // Attribute with value
1076 HTML_ON_VALUE, // Attribute is On-Tag
1077 HTML_OFF_VALUE, // Attribute is Off-Tag
1078 HTML_CHRFMT_VALUE, // Attribute for character format
1079 HTML_COLOR_VALUE, // Attribute for foreground color
1080 HTML_STYLE_VALUE, // Attribute must be exported as style
1081 HTML_DROPCAP_VALUE, // DropCap-Attribute
1082 HTML_AUTOFMT_VALUE }; // Attribute for automatic character styles
1084 class HTMLEndPosLst
1086 HTMLStartEndPositions m_aStartLst; // list, sorted for start positions
1087 HTMLStartEndPositions m_aEndLst; // list, sorted for end positions
1088 std::deque<sal_Int32> m_aScriptChgLst; // positions where script changes
1089 // 0 is not contained in this list,
1090 // but the text length
1091 // the script that is valid up to the position
1092 // contained in aScriptChgList at the same index
1093 std::vector<sal_uInt16> m_aScriptLst;
1095 SwDoc* m_pDoc; // the current document
1096 SwDoc* m_pTemplate; // the HTML template (or 0)
1097 std::optional<Color> m_xDefaultColor; // the default foreground colors
1098 std::set<OUString>& m_rScriptTextStyles;
1100 sal_uLong m_nHTMLMode;
1101 bool m_bOutStyles : 1; // are styles exported
1103 // Insert/remove a SttEndPos in/from the Start and End lists.
1104 // The end position is known.
1105 void InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos );
1106 void RemoveItem_( HTMLStartEndPositions::size_type nEndPos );
1108 // determine the 'type' of the attribute
1109 HTMLOnOffState GetHTMLItemState( const SfxPoolItem& rItem );
1111 // does a specific OnTag item exist
1112 bool ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos );
1114 // does an item exist that can be used to disable an attribute that
1115 // is exported the same way as the supplied item in the same range?
1116 bool ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos,
1117 sal_Int32 nEndPos );
1119 // adapt the end of a split item
1120 void FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
1121 HTMLStartEndPositions::size_type nStartPos );
1123 // insert an attribute in the lists and, if necessary, split it
1124 void InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
1125 sal_Int32 nEnd );
1127 // split an already existing attribute
1128 void SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart,
1129 sal_Int32 nEnd );
1131 // Insert without taking care of script
1132 void InsertNoScript( const SfxPoolItem& rItem, sal_Int32 nStart,
1133 sal_Int32 nEnd, SwHTMLFormatInfos& rFormatInfos,
1134 bool bParaAttrs );
1136 const SwHTMLFormatInfo *GetFormatInfo( const SwFormat& rFormat,
1137 SwHTMLFormatInfos& rFormatInfos );
1139 public:
1141 HTMLEndPosLst( SwDoc *pDoc, SwDoc* pTemplate, std::optional<Color> xDfltColor,
1142 bool bOutStyles, sal_uLong nHTMLMode,
1143 const OUString& rText, std::set<OUString>& rStyles );
1144 ~HTMLEndPosLst();
1146 // insert an attribute
1147 void Insert( const SfxPoolItem& rItem, sal_Int32 nStart, sal_Int32 nEnd,
1148 SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs=false );
1149 void Insert( const SfxItemSet& rItemSet, sal_Int32 nStart, sal_Int32 nEnd,
1150 SwHTMLFormatInfos& rFormatInfos, bool bDeep,
1151 bool bParaAttrs=false );
1152 void Insert( const SwDrawFrameFormat& rFormat, sal_Int32 nPos,
1153 SwHTMLFormatInfos& rFormatInfos );
1155 sal_uInt16 GetScriptAtPos( sal_Int32 nPos,
1156 sal_uInt16 nWeak );
1158 void OutStartAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos );
1159 void OutEndAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos );
1161 bool IsHTMLMode(sal_uLong nMode) const { return (m_nHTMLMode & nMode) != 0; }
1166 void HTMLEndPosLst::InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos )
1168 // Insert the attribute in the Start list behind all attributes that
1169 // were started before, or at the same position.
1170 sal_Int32 nStart = pPos->GetStart();
1171 HTMLStartEndPositions::size_type i {0};
1173 while (i < m_aStartLst.size() && m_aStartLst[i]->GetStart() <= nStart)
1174 ++i;
1175 m_aStartLst.insert(m_aStartLst.begin() + i, pPos);
1177 // the position in the End list was supplied
1178 m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
1181 void HTMLEndPosLst::RemoveItem_( HTMLStartEndPositions::size_type nEndPos )
1183 HTMLStartEndPos* pPos = m_aEndLst[nEndPos];
1185 // now, we are looking for it in the Start list
1186 HTMLStartEndPositions::iterator it = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos);
1187 OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!");
1188 if (it != m_aStartLst.end())
1189 m_aStartLst.erase(it);
1191 m_aEndLst.erase(m_aEndLst.begin() + nEndPos);
1193 delete pPos;
1196 HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const SfxPoolItem& rItem )
1198 HTMLOnOffState eState = HTML_NOT_SUPPORTED;
1199 switch( rItem.Which() )
1201 case RES_CHRATR_POSTURE:
1202 case RES_CHRATR_CJK_POSTURE:
1203 case RES_CHRATR_CTL_POSTURE:
1204 switch( static_cast<const SvxPostureItem&>(rItem).GetPosture() )
1206 case ITALIC_NORMAL:
1207 eState = HTML_ON_VALUE;
1208 break;
1209 case ITALIC_NONE:
1210 eState = HTML_OFF_VALUE;
1211 break;
1212 default:
1213 if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1214 eState = HTML_STYLE_VALUE;
1215 break;
1217 break;
1219 case RES_CHRATR_CROSSEDOUT:
1220 switch( rItem.StaticWhichCast(RES_CHRATR_CROSSEDOUT).GetStrikeout() )
1222 case STRIKEOUT_SINGLE:
1223 case STRIKEOUT_DOUBLE:
1224 eState = HTML_ON_VALUE;
1225 break;
1226 case STRIKEOUT_NONE:
1227 eState = HTML_OFF_VALUE;
1228 break;
1229 default:
1232 break;
1234 case RES_CHRATR_ESCAPEMENT:
1235 switch( static_cast<SvxEscapement>(rItem.StaticWhichCast(RES_CHRATR_ESCAPEMENT).GetEnumValue()) )
1237 case SvxEscapement::Superscript:
1238 case SvxEscapement::Subscript:
1239 eState = HTML_ON_VALUE;
1240 break;
1241 case SvxEscapement::Off:
1242 eState = HTML_OFF_VALUE;
1243 break;
1244 default:
1247 break;
1249 case RES_CHRATR_UNDERLINE:
1250 switch( rItem.StaticWhichCast(RES_CHRATR_UNDERLINE).GetLineStyle() )
1252 case LINESTYLE_SINGLE:
1253 eState = HTML_ON_VALUE;
1254 break;
1255 case LINESTYLE_NONE:
1256 eState = HTML_OFF_VALUE;
1257 break;
1258 default:
1259 if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1260 eState = HTML_STYLE_VALUE;
1261 break;
1263 break;
1265 case RES_CHRATR_OVERLINE:
1266 case RES_CHRATR_HIDDEN:
1267 if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1268 eState = HTML_STYLE_VALUE;
1269 break;
1271 case RES_CHRATR_WEIGHT:
1272 case RES_CHRATR_CJK_WEIGHT:
1273 case RES_CHRATR_CTL_WEIGHT:
1274 switch( static_cast<const SvxWeightItem&>(rItem).GetWeight() )
1276 case WEIGHT_BOLD:
1277 eState = HTML_ON_VALUE;
1278 break;
1279 case WEIGHT_NORMAL:
1280 eState = HTML_OFF_VALUE;
1281 break;
1282 default:
1283 if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1284 eState = HTML_STYLE_VALUE;
1285 break;
1287 break;
1289 case RES_CHRATR_BLINK:
1290 eState = rItem.StaticWhichCast(RES_CHRATR_BLINK).GetValue() ? HTML_ON_VALUE
1291 : HTML_OFF_VALUE;
1292 break;
1294 case RES_CHRATR_COLOR:
1295 eState = HTML_COLOR_VALUE;
1296 break;
1298 case RES_CHRATR_FONT:
1299 case RES_CHRATR_FONTSIZE:
1300 case RES_CHRATR_LANGUAGE:
1301 case RES_CHRATR_CJK_FONT:
1302 case RES_CHRATR_CJK_FONTSIZE:
1303 case RES_CHRATR_CJK_LANGUAGE:
1304 case RES_CHRATR_CTL_FONT:
1305 case RES_CHRATR_CTL_FONTSIZE:
1306 case RES_CHRATR_CTL_LANGUAGE:
1307 case RES_TXTATR_INETFMT:
1308 eState = HTML_REAL_VALUE;
1309 break;
1311 case RES_TXTATR_CHARFMT:
1312 eState = HTML_CHRFMT_VALUE;
1313 break;
1315 case RES_TXTATR_AUTOFMT:
1316 eState = HTML_AUTOFMT_VALUE;
1317 break;
1319 case RES_CHRATR_CASEMAP:
1320 eState = HTML_STYLE_VALUE;
1321 break;
1323 case RES_CHRATR_KERNING:
1324 eState = HTML_STYLE_VALUE;
1325 break;
1327 case RES_CHRATR_BACKGROUND:
1328 if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1329 eState = HTML_STYLE_VALUE;
1330 break;
1332 case RES_PARATR_DROP:
1333 eState = HTML_DROPCAP_VALUE;
1334 break;
1336 case RES_CHRATR_BOX:
1337 if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1338 eState = HTML_STYLE_VALUE;
1339 break;
1342 return eState;
1345 bool HTMLEndPosLst::ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos )
1347 for (auto pTest : m_aStartLst)
1349 if( pTest->GetStart() > nPos )
1351 // this attribute, and all attributes that follow, start later
1352 break;
1354 else if( pTest->GetEnd() > nPos )
1356 // the attribute starts before, or at, the current position and
1357 // ends after it
1358 const SfxPoolItem *pItem = pTest->GetItem();
1359 if( pItem->Which() == nWhich &&
1360 HTML_ON_VALUE == GetHTMLItemState(*pItem) )
1362 // an OnTag attribute was found
1363 return true;
1368 return false;
1371 bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos,
1372 sal_Int32 nEndPos )
1374 if( nWhich != RES_CHRATR_CROSSEDOUT &&
1375 nWhich != RES_CHRATR_UNDERLINE &&
1376 nWhich != RES_CHRATR_BLINK )
1378 return false;
1381 for (auto pTest : m_aStartLst)
1383 if( pTest->GetStart() > nStartPos )
1385 // this attribute, and all attributes that follow, start later
1386 break;
1388 else if( pTest->GetStart()==nStartPos &&
1389 pTest->GetEnd()==nEndPos )
1391 // the attribute starts before or at the current position and
1392 // ends after it
1393 const SfxPoolItem *pItem = pTest->GetItem();
1394 sal_uInt16 nTstWhich = pItem->Which();
1395 if( (nTstWhich == RES_CHRATR_CROSSEDOUT ||
1396 nTstWhich == RES_CHRATR_UNDERLINE ||
1397 nTstWhich == RES_CHRATR_BLINK) &&
1398 HTML_OFF_VALUE == GetHTMLItemState(*pItem) )
1400 // an OffTag attribute was found that is exported the same
1401 // way as the current item
1402 return true;
1407 return false;
1410 void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
1411 HTMLStartEndPositions::size_type nStartPos )
1413 // fix the end position accordingly
1414 pPos->SetEnd( nNewEnd );
1416 // remove the item from the End list
1417 HTMLStartEndPositions::iterator it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pPos);
1418 OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
1419 if (it != m_aEndLst.end())
1420 m_aEndLst.erase(it);
1422 // from now on, it is closed as the last one at the corresponding position
1423 HTMLStartEndPositions::size_type nEndPos {0};
1424 while (nEndPos < m_aEndLst.size() && m_aEndLst[nEndPos]->GetEnd() <= nNewEnd)
1425 ++nEndPos;
1426 m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
1428 // now, adjust the attributes that got started afterwards
1429 for (HTMLStartEndPositions::size_type i = nStartPos + 1; i < m_aStartLst.size(); ++i)
1431 HTMLStartEndPos* pTest = m_aStartLst[i];
1432 sal_Int32 nTestEnd = pTest->GetEnd();
1433 if( pTest->GetStart() >= nNewEnd )
1435 // the Test attribute and all the following ones start, after the
1436 // split attribute ends
1437 break;
1439 else if( nTestEnd > nNewEnd )
1441 // the Test attribute starts before the split attribute
1442 // ends, and ends afterwards, i.e., it must be split, as well
1444 // set the new end
1445 pTest->SetEnd( nNewEnd );
1447 // remove the attribute from the End list
1448 it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
1449 OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
1450 if (it != m_aEndLst.end())
1451 m_aEndLst.erase(it);
1453 // it now ends as the first attribute in the respective position.
1454 // We already know this position in the End list.
1455 m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pTest);
1457 // insert the 'rest' of the attribute
1458 InsertItem( *pTest->GetItem(), nNewEnd, nTestEnd );
1463 void HTMLEndPosLst::InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
1464 sal_Int32 nEnd )
1466 HTMLStartEndPositions::size_type i;
1467 for (i = 0; i < m_aEndLst.size(); i++)
1469 HTMLStartEndPos* pTest = m_aEndLst[i];
1470 sal_Int32 nTestEnd = pTest->GetEnd();
1471 if( nTestEnd <= nStart )
1473 // the Test attribute ends, before the new one starts
1474 continue;
1476 else if( nTestEnd < nEnd )
1478 if( pTest->GetStart() < nStart )
1480 // the Test attribute ends, before the new one ends. Thus, the
1481 // new attribute must be split.
1482 InsertItem_( new HTMLStartEndPos( rItem, nStart, nTestEnd ), i );
1483 nStart = nTestEnd;
1486 else
1488 // the Test attribute (and all that follow) ends, before the new
1489 // one ends
1490 break;
1494 // one attribute must still be inserted
1495 InsertItem_( new HTMLStartEndPos( rItem, nStart, nEnd ), i );
1498 void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart,
1499 sal_Int32 nEnd )
1501 sal_uInt16 nWhich = rItem.Which();
1503 // first, we must search for the old items by using the start list and
1504 // determine the new item range
1506 for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
1508 HTMLStartEndPos* pTest = m_aStartLst[i];
1509 sal_Int32 nTestStart = pTest->GetStart();
1510 sal_Int32 nTestEnd = pTest->GetEnd();
1512 if( nTestStart >= nEnd )
1514 // this attribute, and all that follow, start later
1515 break;
1517 else if( nTestEnd > nStart )
1519 // the Test attribute ends in the range that must be deleted
1520 const SfxPoolItem *pItem = pTest->GetItem();
1522 // only the corresponding OnTag attributes have to be considered
1523 if( pItem->Which() == nWhich &&
1524 HTML_ON_VALUE == GetHTMLItemState( *pItem ) )
1526 bool bDelete = true;
1528 if( nTestStart < nStart )
1530 // the start of the new attribute corresponds to the new
1531 // end of the attribute
1532 FixSplittedItem( pTest, nStart, i );
1533 bDelete = false;
1535 else
1537 // the Test item only starts after the new end of the
1538 // attribute. Therefore, it can be completely erased.
1539 m_aStartLst.erase(m_aStartLst.begin() + i);
1540 i--;
1542 HTMLStartEndPositions::iterator it
1543 = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
1544 OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
1545 if (it != m_aEndLst.end())
1546 m_aEndLst.erase(it);
1549 // if necessary, insert the second part of the split
1550 // attribute
1551 if( nTestEnd > nEnd )
1553 InsertItem( *pTest->GetItem(), nEnd, nTestEnd );
1556 if( bDelete )
1557 delete pTest;
1563 const SwHTMLFormatInfo *HTMLEndPosLst::GetFormatInfo( const SwFormat& rFormat,
1564 SwHTMLFormatInfos& rFormatInfos )
1566 SwHTMLFormatInfo *pFormatInfo;
1567 std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(&rFormat));
1568 SwHTMLFormatInfos::iterator it = rFormatInfos.find( pTmpInfo );
1569 if (it != rFormatInfos.end())
1571 pFormatInfo = it->get();
1573 else
1575 pFormatInfo = new SwHTMLFormatInfo(&rFormat, m_pDoc, m_pTemplate, m_bOutStyles);
1576 rFormatInfos.insert(std::unique_ptr<SwHTMLFormatInfo>(pFormatInfo));
1577 if (m_rScriptTextStyles.count(rFormat.GetName()))
1578 pFormatInfo->bScriptDependent = true;
1581 return pFormatInfo;
1584 HTMLEndPosLst::HTMLEndPosLst(SwDoc* pD, SwDoc* pTempl, std::optional<Color> xDfltCol, bool bStyles,
1585 sal_uLong nMode, const OUString& rText, std::set<OUString>& rStyles)
1586 : m_pDoc(pD)
1587 , m_pTemplate(pTempl)
1588 , m_xDefaultColor(std::move(xDfltCol))
1589 , m_rScriptTextStyles(rStyles)
1590 , m_nHTMLMode(nMode)
1591 , m_bOutStyles(bStyles)
1593 sal_Int32 nEndPos = rText.getLength();
1594 sal_Int32 nPos = 0;
1595 while( nPos < nEndPos )
1597 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( rText, nPos );
1598 nPos = g_pBreakIt->GetBreakIter()->endOfScript( rText, nPos, nScript );
1599 m_aScriptChgLst.push_back(nPos);
1600 m_aScriptLst.push_back(nScript);
1604 HTMLEndPosLst::~HTMLEndPosLst()
1606 OSL_ENSURE(m_aStartLst.empty(), "Start List not empty in destructor");
1607 OSL_ENSURE(m_aEndLst.empty(), "End List not empty in destructor");
1610 void HTMLEndPosLst::InsertNoScript( const SfxPoolItem& rItem,
1611 sal_Int32 nStart, sal_Int32 nEnd,
1612 SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs )
1614 // no range ?? in that case, don't take it, it will never take effect !!
1615 if( nStart == nEnd )
1616 return;
1618 bool bSet = false, bSplit = false;
1619 switch( GetHTMLItemState(rItem) )
1621 case HTML_ON_VALUE:
1622 // output the attribute, if it isn't 'on', already
1623 if( !ExistsOnTagItem( rItem.Which(), nStart ) )
1624 bSet = true;
1625 break;
1627 case HTML_OFF_VALUE:
1628 // If the corresponding attribute is 'on', split it.
1629 // Additionally, output it as Style, if it is not set for the
1630 // whole paragraph, because in that case it was already output
1631 // together with the paragraph tag.
1632 if( ExistsOnTagItem( rItem.Which(), nStart ) )
1633 bSplit = true;
1634 bSet = m_bOutStyles && !bParaAttrs && !ExistsOffTagItem(rItem.Which(), nStart, nEnd);
1635 break;
1637 case HTML_REAL_VALUE:
1638 // we can always output the attribute
1639 bSet = true;
1640 break;
1642 case HTML_STYLE_VALUE:
1643 // We can only output the attribute as CSS1. If it is set for
1644 // the paragraph, it was already output with the paragraph tag.
1645 // The only exception is the character-background attribute. This
1646 // attribute must always be handled like a Hint.
1647 bSet = m_bOutStyles
1648 && (!bParaAttrs || rItem.Which() == RES_CHRATR_BACKGROUND
1649 || rItem.Which() == RES_CHRATR_BOX || rItem.Which() == RES_CHRATR_OVERLINE);
1650 break;
1652 case HTML_CHRFMT_VALUE:
1654 OSL_ENSURE( RES_TXTATR_CHARFMT == rItem.Which(),
1655 "Not a character style after all" );
1656 const SwFormatCharFormat& rChrFormat = rItem.StaticWhichCast(RES_TXTATR_CHARFMT);
1657 const SwCharFormat* pFormat = rChrFormat.GetCharFormat();
1659 const SwHTMLFormatInfo *pFormatInfo = GetFormatInfo( *pFormat, rFormatInfos );
1660 if( !pFormatInfo->aToken.isEmpty() )
1662 // output the character style tag before the hard
1663 // attributes
1664 InsertItem( rItem, nStart, nEnd );
1666 if( pFormatInfo->moItemSet )
1668 Insert( *pFormatInfo->moItemSet, nStart, nEnd,
1669 rFormatInfos, true, bParaAttrs );
1672 break;
1674 case HTML_AUTOFMT_VALUE:
1676 OSL_ENSURE( RES_TXTATR_AUTOFMT == rItem.Which(),
1677 "Not an automatic style, after all" );
1678 const SwFormatAutoFormat& rAutoFormat = rItem.StaticWhichCast(RES_TXTATR_AUTOFMT);
1679 const std::shared_ptr<SfxItemSet>& pSet = rAutoFormat.GetStyleHandle();
1680 if( pSet )
1681 Insert( *pSet, nStart, nEnd, rFormatInfos, true, bParaAttrs );
1683 break;
1685 case HTML_COLOR_VALUE:
1686 // A foreground color as a paragraph attribute is only exported if
1687 // it is not the same as the default color.
1689 OSL_ENSURE( RES_CHRATR_COLOR == rItem.Which(),
1690 "Not a foreground color, after all" );
1691 Color aColor( rItem.StaticWhichCast(RES_CHRATR_COLOR).GetValue() );
1692 if( COL_AUTO == aColor )
1693 aColor = COL_BLACK;
1694 bSet = !bParaAttrs || !m_xDefaultColor || !m_xDefaultColor->IsRGBEqual(aColor);
1696 break;
1698 case HTML_DROPCAP_VALUE:
1700 OSL_ENSURE( RES_PARATR_DROP == rItem.Which(),
1701 "Not a drop cap, after all" );
1702 const SwFormatDrop& rDrop = rItem.StaticWhichCast(RES_PARATR_DROP);
1703 nEnd = nStart + rDrop.GetChars();
1704 if (!m_bOutStyles)
1706 // At least use the attributes of the character style
1707 const SwCharFormat *pCharFormat = rDrop.GetCharFormat();
1708 if( pCharFormat )
1710 Insert( pCharFormat->GetAttrSet(), nStart, nEnd,
1711 rFormatInfos, true, bParaAttrs );
1714 else
1716 bSet = true;
1719 break;
1720 default:
1724 if( bSet )
1725 InsertItem( rItem, nStart, nEnd );
1726 if( bSplit )
1727 SplitItem( rItem, nStart, nEnd );
1730 void HTMLEndPosLst::Insert( const SfxPoolItem& rItem,
1731 sal_Int32 nStart, sal_Int32 nEnd,
1732 SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs )
1734 bool bDependsOnScript = false, bDependsOnAnyScript = false;
1735 sal_uInt16 nScript = i18n::ScriptType::LATIN;
1736 switch( rItem.Which() )
1738 case RES_CHRATR_FONT:
1739 case RES_CHRATR_FONTSIZE:
1740 case RES_CHRATR_LANGUAGE:
1741 case RES_CHRATR_POSTURE:
1742 case RES_CHRATR_WEIGHT:
1743 bDependsOnScript = true;
1744 nScript = i18n::ScriptType::LATIN;
1745 break;
1747 case RES_CHRATR_CJK_FONT:
1748 case RES_CHRATR_CJK_FONTSIZE:
1749 case RES_CHRATR_CJK_LANGUAGE:
1750 case RES_CHRATR_CJK_POSTURE:
1751 case RES_CHRATR_CJK_WEIGHT:
1752 bDependsOnScript = true;
1753 nScript = i18n::ScriptType::ASIAN;
1754 break;
1756 case RES_CHRATR_CTL_FONT:
1757 case RES_CHRATR_CTL_FONTSIZE:
1758 case RES_CHRATR_CTL_LANGUAGE:
1759 case RES_CHRATR_CTL_POSTURE:
1760 case RES_CHRATR_CTL_WEIGHT:
1761 bDependsOnScript = true;
1762 nScript = i18n::ScriptType::COMPLEX;
1763 break;
1764 case RES_TXTATR_CHARFMT:
1766 const SwFormatCharFormat& rChrFormat = rItem.StaticWhichCast(RES_TXTATR_CHARFMT);
1767 const SwCharFormat* pFormat = rChrFormat.GetCharFormat();
1768 const SwHTMLFormatInfo *pFormatInfo = GetFormatInfo( *pFormat, rFormatInfos );
1769 if( pFormatInfo->bScriptDependent )
1771 bDependsOnScript = true;
1772 bDependsOnAnyScript = true;
1775 break;
1776 case RES_TXTATR_INETFMT:
1778 if (GetFormatInfo(*m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
1779 RES_POOLCHR_INET_NORMAL),
1780 rFormatInfos)
1781 ->bScriptDependent
1782 || GetFormatInfo(*m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
1783 RES_POOLCHR_INET_VISIT),
1784 rFormatInfos)
1785 ->bScriptDependent)
1787 bDependsOnScript = true;
1788 bDependsOnAnyScript = true;
1791 break;
1794 if( bDependsOnScript )
1796 sal_Int32 nPos = nStart;
1797 for (size_t i = 0; i < m_aScriptChgLst.size(); i++)
1799 sal_Int32 nChgPos = m_aScriptChgLst[i];
1800 if( nPos >= nChgPos )
1802 // the hint starts behind or at the next script change,
1803 // so we may continue with this position.
1804 continue;
1806 if( nEnd <= nChgPos )
1808 // the (rest of) the hint ends before or at the next script
1809 // change, so we can insert it, but only if it belongs
1810 // to the current script.
1811 if (bDependsOnAnyScript || nScript == m_aScriptLst[i])
1812 InsertNoScript( rItem, nPos, nEnd, rFormatInfos,
1813 bParaAttrs );
1814 break;
1817 // the hint starts before the next script change and ends behind
1818 // it, so we can insert a hint up to the next script change and
1819 // continue with the rest of the hint.
1820 if (bDependsOnAnyScript || nScript == m_aScriptLst[i])
1821 InsertNoScript( rItem, nPos, nChgPos, rFormatInfos, bParaAttrs );
1822 nPos = nChgPos;
1825 else
1827 InsertNoScript( rItem, nStart, nEnd, rFormatInfos, bParaAttrs );
1831 void HTMLEndPosLst::Insert( const SfxItemSet& rItemSet,
1832 sal_Int32 nStart, sal_Int32 nEnd,
1833 SwHTMLFormatInfos& rFormatInfos,
1834 bool bDeep, bool bParaAttrs )
1836 SfxWhichIter aIter( rItemSet );
1838 sal_uInt16 nWhich = aIter.FirstWhich();
1839 while( nWhich )
1841 const SfxPoolItem *pItem;
1842 if( SfxItemState::SET == aIter.GetItemState( bDeep, &pItem ) )
1844 Insert( *pItem, nStart, nEnd, rFormatInfos, bParaAttrs );
1847 nWhich = aIter.NextWhich();
1851 void HTMLEndPosLst::Insert( const SwDrawFrameFormat& rFormat, sal_Int32 nPos,
1852 SwHTMLFormatInfos& rFormatInfos )
1854 const SdrObject* pTextObj = SwHTMLWriter::GetMarqueeTextObj( rFormat );
1856 if( !pTextObj )
1857 return;
1859 // get the edit engine attributes of the object as SW attributes and
1860 // insert them as hints. Because of the amount of Hints the styles
1861 // are not considered!
1862 const SfxItemSet& rFormatItemSet = rFormat.GetAttrSet();
1863 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aItemSet( *rFormatItemSet.GetPool() );
1864 SwHTMLWriter::GetEEAttrsFromDrwObj( aItemSet, pTextObj );
1865 bool bOutStylesOld = m_bOutStyles;
1866 m_bOutStyles = false;
1867 Insert( aItemSet, nPos, nPos+1, rFormatInfos, false );
1868 m_bOutStyles = bOutStylesOld;
1871 sal_uInt16 HTMLEndPosLst::GetScriptAtPos( sal_Int32 nPos, sal_uInt16 nWeak )
1873 sal_uInt16 nRet = CSS1_OUTMODE_ANY_SCRIPT;
1875 size_t nScriptChgs = m_aScriptChgLst.size();
1876 size_t i=0;
1877 while (i < nScriptChgs && nPos >= m_aScriptChgLst[i])
1878 i++;
1879 OSL_ENSURE( i < nScriptChgs, "script list is too short" );
1880 if( i < nScriptChgs )
1882 if (i18n::ScriptType::WEAK == m_aScriptLst[i])
1883 nRet = nWeak;
1884 else
1885 nRet = SwHTMLWriter::GetCSS1ScriptForScriptType(m_aScriptLst[i]);
1888 return nRet;
1891 void HTMLEndPosLst::OutStartAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos )
1893 rWrt.m_bTagOn = true;
1895 // Character border attribute must be the first which is written out
1896 // because of border merge.
1897 HTMLStartEndPositions::size_type nCharBoxIndex = 0;
1898 while (nCharBoxIndex < m_aStartLst.size()
1899 && m_aStartLst[nCharBoxIndex]->GetItem()->Which() != RES_CHRATR_BOX)
1901 ++nCharBoxIndex;
1904 // the attributes of the start list are sorted in ascending order
1905 for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
1907 HTMLStartEndPos *pPos = nullptr;
1908 if (nCharBoxIndex < m_aStartLst.size())
1910 if( i == 0 )
1911 pPos = m_aStartLst[nCharBoxIndex];
1912 else if( i == nCharBoxIndex )
1913 pPos = m_aStartLst[0];
1914 else
1915 pPos = m_aStartLst[i];
1917 else
1918 pPos = m_aStartLst[i];
1920 sal_Int32 nStart = pPos->GetStart();
1921 if( nStart > nPos )
1923 // this attribute, and all that follow, will be opened later on
1924 break;
1926 else if( nStart == nPos )
1928 // output the attribute
1929 sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script;
1930 sal_uInt16 nWhich = pPos->GetItem()->Which();
1931 if( RES_TXTATR_CHARFMT == nWhich ||
1932 RES_TXTATR_INETFMT == nWhich ||
1933 RES_PARATR_DROP == nWhich )
1935 rWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script );
1937 HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it?
1938 Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt );
1939 rWrt.maStartedAttributes[pPos->GetItem()->Which()]++;
1940 rWrt.m_nCSS1Script = nCSS1Script;
1945 void HTMLEndPosLst::OutEndAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos )
1947 rWrt.m_bTagOn = false;
1949 // the attributes in the End list are sorted in ascending order
1950 HTMLStartEndPositions::size_type i {0};
1951 while (i < m_aEndLst.size())
1953 HTMLStartEndPos* pPos = m_aEndLst[i];
1954 sal_Int32 nEnd = pPos->GetEnd();
1956 if( SAL_MAX_INT32 == nPos || nEnd == nPos )
1958 HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it?
1959 // Skip closing span if next character span has the same border (border merge)
1960 bool bSkipOut = false;
1961 if( pPos->GetItem()->Which() == RES_CHRATR_BOX )
1963 HTMLStartEndPositions::iterator it
1964 = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos);
1965 OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!");
1966 if (it != m_aStartLst.end())
1967 ++it;
1968 while (it != m_aStartLst.end())
1970 HTMLStartEndPos *pEndPos = *it;
1971 if( pEndPos->GetItem()->Which() == RES_CHRATR_BOX &&
1972 *static_cast<const SvxBoxItem*>(pEndPos->GetItem()) ==
1973 *static_cast<const SvxBoxItem*>(pPos->GetItem()) )
1975 pEndPos->SetStart(pPos->GetStart());
1976 bSkipOut = true;
1977 break;
1979 ++it;
1982 if( !bSkipOut )
1984 Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt );
1985 rWrt.maStartedAttributes[pPos->GetItem()->Which()]--;
1987 RemoveItem_( i );
1989 else if( nEnd > nPos )
1991 // this attribute, and all that follow, are closed later on
1992 break;
1994 else
1996 // The attribute is closed before the current position. This
1997 // is not allowed, but we can handle it anyway.
1998 OSL_ENSURE( nEnd >= nPos,
1999 "The attribute should've been closed a long time ago" );
2000 i++;
2005 /* Output of the nodes*/
2006 SwHTMLWriter& OutHTML_SwTextNode( SwHTMLWriter& rWrt, const SwContentNode& rNode )
2008 const SwTextNode * pNd = &static_cast<const SwTextNode&>(rNode);
2010 const OUString& rStr = pNd->GetText();
2011 sal_Int32 nEnd = rStr.getLength();
2013 // special case: empty node and HR style (horizontal rule)
2014 // output a <HR>, only
2015 sal_uInt16 nPoolId = pNd->GetAnyFormatColl().GetPoolFormatId();
2017 // Handle horizontal rule <hr>
2018 if (!nEnd &&
2019 (RES_POOLCOLL_HTML_HR==nPoolId || pNd->GetAnyFormatColl().GetName() == OOO_STRING_SVTOOLS_HTML_horzrule))
2021 // then, the paragraph-anchored graphics/OLE objects in the paragraph
2022 // MIB 8.7.97: We enclose the line in a <PRE>. This means that the
2023 // spacings are wrong, but otherwise we get an empty paragraph
2024 // after the <HR> which is even uglier.
2025 rWrt.ChangeParaToken( HtmlTokenId::NONE );
2027 // Output all the nodes that are anchored to a frame
2028 rWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any );
2030 if( rWrt.m_bLFPossible )
2031 rWrt.OutNewLine(); // paragraph tag on a new line
2033 rWrt.m_bLFPossible = true;
2035 HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
2036 aHtml.prettyPrint(rWrt.m_bPrettyPrint);
2037 aHtml.start(OOO_STRING_SVTOOLS_HTML_horzrule);
2039 const SfxItemSet* pItemSet = pNd->GetpSwAttrSet();
2040 if( !pItemSet )
2042 aHtml.endAttribute();
2043 return rWrt;
2045 if (pItemSet->GetItemIfSet(RES_MARGIN_FIRSTLINE, false)
2046 || pItemSet->GetItemIfSet(RES_MARGIN_TEXTLEFT, false)
2047 || pItemSet->GetItemIfSet(RES_MARGIN_RIGHT, false))
2049 SvxFirstLineIndentItem const& rFirstLine(pItemSet->Get(RES_MARGIN_FIRSTLINE));
2050 SvxTextLeftMarginItem const& rTextLeftMargin(pItemSet->Get(RES_MARGIN_TEXTLEFT));
2051 SvxRightMarginItem const& rRightMargin(pItemSet->Get(RES_MARGIN_RIGHT));
2052 sal_Int32 const nLeft(rTextLeftMargin.GetLeft(rFirstLine));
2053 sal_Int32 const nRight(rRightMargin.GetRight());
2054 if( nLeft || nRight )
2056 const SwFrameFormat& rPgFormat =
2057 rWrt.m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool
2058 ( RES_POOLPAGE_HTML, false )->GetMaster();
2059 const SwFormatFrameSize& rSz = rPgFormat.GetFrameSize();
2060 const SvxLRSpaceItem& rLR = rPgFormat.GetLRSpace();
2061 const SwFormatCol& rCol = rPgFormat.GetCol();
2063 tools::Long nPageWidth = rSz.GetWidth() - rLR.GetLeft() - rLR.GetRight();
2065 if( 1 < rCol.GetNumCols() )
2066 nPageWidth /= rCol.GetNumCols();
2068 const SwTableNode* pTableNd = pNd->FindTableNode();
2069 if( pTableNd )
2071 const SwTableBox* pBox = pTableNd->GetTable().GetTableBox(
2072 pNd->StartOfSectionIndex() );
2073 if( pBox )
2074 nPageWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
2077 OString sWidth = OString::number(SwHTMLWriter::ToPixel(nPageWidth - nLeft - nRight));
2078 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_width, sWidth);
2080 if( !nLeft )
2081 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_left);
2082 else if( !nRight )
2083 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_right);
2084 else
2085 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_center);
2089 if( const SvxBoxItem* pBoxItem = pItemSet->GetItemIfSet( RES_BOX, false ))
2091 const editeng::SvxBorderLine* pBorderLine = pBoxItem->GetBottom();
2092 if( pBorderLine )
2094 sal_uInt16 nWidth = pBorderLine->GetScaledWidth();
2095 OString sWidth = OString::number(SwHTMLWriter::ToPixel(nWidth));
2096 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_size, sWidth);
2098 const Color& rBorderColor = pBorderLine->GetColor();
2099 if( !rBorderColor.IsRGBEqual( COL_GRAY ) )
2101 HtmlWriterHelper::applyColor(aHtml, OOO_STRING_SVTOOLS_HTML_O_color, rBorderColor);
2104 if( !pBorderLine->GetInWidth() )
2106 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_noshade, OOO_STRING_SVTOOLS_HTML_O_noshade);
2110 aHtml.end();
2111 return rWrt;
2114 // Do not export the empty nodes with 2pt fonts and standard style that
2115 // are inserted before tables and sections, but do export bookmarks
2116 // and paragraph anchored frames.
2117 if( !nEnd && (nPoolId == RES_POOLCOLL_STANDARD ||
2118 nPoolId == RES_POOLCOLL_TABLE ||
2119 nPoolId == RES_POOLCOLL_TABLE_HDLN) )
2121 // The current node is empty and contains the standard style ...
2122 const SvxFontHeightItem* pFontHeightItem;
2123 const SfxItemSet* pItemSet = pNd->GetpSwAttrSet();
2124 if( pItemSet && pItemSet->Count() &&
2125 (pFontHeightItem = pItemSet->GetItemIfSet( RES_CHRATR_FONTSIZE, false )) &&
2126 40 == pFontHeightItem->GetHeight() )
2128 // ... moreover, the 2pt font is set ...
2129 SwNodeOffset nNdPos = rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex();
2130 const SwNode *pNextNd = rWrt.m_pDoc->GetNodes()[nNdPos+1];
2131 const SwNode *pPrevNd = rWrt.m_pDoc->GetNodes()[nNdPos-1];
2132 bool bStdColl = nPoolId == RES_POOLCOLL_STANDARD;
2133 if( ( bStdColl && (pNextNd->IsTableNode() || pNextNd->IsSectionNode()) ) ||
2134 ( !bStdColl &&
2135 pNextNd->IsEndNode() &&
2136 pPrevNd->IsStartNode() &&
2137 SwTableBoxStartNode == pPrevNd->GetStartNode()->GetStartNodeType() ) )
2139 // ... and it is located before a table or a section
2140 rWrt.OutBookmarks();
2141 rWrt.m_bLFPossible = rWrt.m_nLastParaToken == HtmlTokenId::NONE;
2143 // Output all frames that are anchored to this node
2144 rWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any );
2145 rWrt.m_bLFPossible = false;
2147 return rWrt;
2152 // catch PageBreaks and PageDescs
2153 bool bPageBreakBehind = false;
2154 if( rWrt.m_bCfgFormFeed &&
2155 !(rWrt.m_bOutTable || rWrt.m_bOutFlyFrame) &&
2156 rWrt.m_pStartNdIdx->GetIndex() != rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() )
2158 bool bPageBreakBefore = false;
2159 const SfxItemSet* pItemSet = pNd->GetpSwAttrSet();
2161 if( pItemSet )
2163 const SwFormatPageDesc* pPageDescItem = pItemSet->GetItemIfSet( RES_PAGEDESC );
2164 if( pPageDescItem && pPageDescItem->GetPageDesc() )
2166 bPageBreakBefore = true;
2168 else if( const SvxFormatBreakItem* pItem = pItemSet->GetItemIfSet( RES_BREAK ) )
2170 switch( pItem->GetBreak() )
2172 case SvxBreak::PageBefore:
2173 bPageBreakBefore = true;
2174 break;
2175 case SvxBreak::PageAfter:
2176 bPageBreakBehind = true;
2177 break;
2178 case SvxBreak::PageBoth:
2179 bPageBreakBefore = true;
2180 bPageBreakBehind = true;
2181 break;
2182 default:
2183 break;
2188 if( bPageBreakBefore )
2189 rWrt.Strm().WriteChar( '\f' );
2192 // if necessary, open a form
2193 rWrt.OutForm();
2195 // Output the page-anchored frames that are 'anchored' to this node
2196 bool bFlysLeft = rWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Prefix );
2198 // Output all frames that are anchored to this node that are supposed to
2199 // be written before the paragraph tag.
2200 if( bFlysLeft )
2202 bFlysLeft = rWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Before );
2205 if( rWrt.m_pCurrentPam->GetPoint()->GetNode() == rWrt.m_pCurrentPam->GetMark()->GetNode() )
2207 nEnd = rWrt.m_pCurrentPam->GetMark()->GetContentIndex();
2210 // are there any hard attributes that must be written as options?
2211 rWrt.m_bTagOn = true;
2213 // now, output the tag of the paragraph
2214 const SwFormat& rFormat = pNd->GetAnyFormatColl();
2215 SwHTMLTextCollOutputInfo aFormatInfo;
2216 bool bOldLFPossible = rWrt.m_bLFPossible;
2217 OutHTML_SwFormat( rWrt, rFormat, pNd->GetpSwAttrSet(), aFormatInfo );
2219 // If we didn't open a new line before the paragraph tag, we do that now
2220 rWrt.m_bLFPossible = rWrt.m_nLastParaToken == HtmlTokenId::NONE;
2221 if( !bOldLFPossible && rWrt.m_bLFPossible )
2222 rWrt.OutNewLine();
2224 // then, the bookmarks (including end tag)
2225 rWrt.m_bOutOpts = false;
2226 rWrt.OutBookmarks();
2228 // now it's a good opportunity again for an LF - if it is still allowed
2229 // FIXME: for LOK case we set rWrt.m_nWishLineLen as -1, for now keep old flow
2230 // when LOK side will be fixed - don't insert new line at the beginning
2231 if( rWrt.m_bLFPossible && rWrt.m_bPrettyPrint && rWrt.m_nWishLineLen >= 0 &&
2232 rWrt.GetLineLen() >= rWrt.m_nWishLineLen )
2234 rWrt.OutNewLine();
2236 rWrt.m_bLFPossible = false;
2238 // find text that originates from an outline numbering
2239 sal_Int32 nOffset = 0;
2240 OUString aOutlineText;
2241 OUString aFullText;
2243 // export numbering string as plain text only for the outline numbering,
2244 // because the outline numbering isn't exported as a numbering - see <SwHTMLNumRuleInfo::Set(..)>
2245 if ( pNd->IsOutline() &&
2246 pNd->GetNumRule() == pNd->GetDoc().GetOutlineNumRule() )
2248 aOutlineText = pNd->GetNumString();
2249 nOffset = nOffset + aOutlineText.getLength();
2250 aFullText = aOutlineText;
2252 OUString aFootEndNoteSym;
2253 if( rWrt.m_pFormatFootnote )
2255 aFootEndNoteSym = rWrt.GetFootEndNoteSym( *rWrt.m_pFormatFootnote );
2256 nOffset = nOffset + aFootEndNoteSym.getLength();
2257 aFullText += aFootEndNoteSym;
2260 // Table of Contents or other paragraph with dot leaders?
2261 sal_Int32 nIndexTab = rWrt.indexOfDotLeaders( nPoolId, rStr );
2262 if (nIndexTab > -1)
2263 // skip part after the tabulator (page number)
2264 nEnd = nIndexTab;
2266 // are there any hard attributes that must be written as tags?
2267 aFullText += rStr;
2268 HTMLEndPosLst aEndPosLst( rWrt.m_pDoc, rWrt.m_xTemplate.get(),
2269 rWrt.m_xDfltColor, rWrt.m_bCfgOutStyles,
2270 rWrt.GetHTMLMode(), aFullText,
2271 rWrt.m_aScriptTextStyles );
2272 if( aFormatInfo.moItemSet )
2274 aEndPosLst.Insert( *aFormatInfo.moItemSet, 0, nEnd + nOffset,
2275 rWrt.m_CharFormatInfos, false, true );
2278 if( !aOutlineText.isEmpty() || rWrt.m_pFormatFootnote )
2280 // output paragraph attributes, so that the text gets the attributes of
2281 // the paragraph.
2282 aEndPosLst.OutStartAttrs( rWrt, 0 );
2284 // Theoretically, we would have to consider the character style of
2285 // the numbering. Because it cannot be set via the UI, let's ignore
2286 // it for now.
2288 if( !aOutlineText.isEmpty() )
2289 HTMLOutFuncs::Out_String( rWrt.Strm(), aOutlineText );
2291 if( rWrt.m_pFormatFootnote )
2293 rWrt.OutFootEndNoteSym( *rWrt.m_pFormatFootnote, aFootEndNoteSym,
2294 aEndPosLst.GetScriptAtPos( aOutlineText.getLength(), rWrt.m_nCSS1Script ) );
2295 rWrt.m_pFormatFootnote = nullptr;
2299 // for now, correct the start. I.e., if we only output part of the sentence,
2300 // the attributes must be correct there, as well!!
2301 rWrt.m_bTextAttr = true;
2303 size_t nAttrPos = 0;
2304 sal_Int32 nStrPos = rWrt.m_pCurrentPam->GetPoint()->GetContentIndex();
2305 const SwTextAttr * pHt = nullptr;
2306 const size_t nCntAttr = pNd->HasHints() ? pNd->GetSwpHints().Count() : 0;
2307 if( nCntAttr && nStrPos > ( pHt = pNd->GetSwpHints().Get(0) )->GetStart() )
2309 // Ok, there are earlier attributes that we must output
2310 do {
2311 aEndPosLst.OutEndAttrs( rWrt, nStrPos + nOffset );
2313 nAttrPos++;
2314 if( pHt->Which() == RES_TXTATR_FIELD
2315 || pHt->Which() == RES_TXTATR_ANNOTATION )
2316 continue;
2318 if ( pHt->End() && !pHt->HasDummyChar() )
2320 const sal_Int32 nHtEnd = *pHt->End(),
2321 nHtStt = pHt->GetStart();
2322 if( !rWrt.m_bWriteAll && nHtEnd <= nStrPos )
2323 continue;
2325 // don't consider empty hints at the beginning - or should we ??
2326 if( nHtEnd == nHtStt )
2327 continue;
2329 // add attribute to the list
2330 if( rWrt.m_bWriteAll )
2331 aEndPosLst.Insert( pHt->GetAttr(), nHtStt + nOffset,
2332 nHtEnd + nOffset,
2333 rWrt.m_CharFormatInfos );
2334 else
2336 sal_Int32 nTmpStt = nHtStt < nStrPos ? nStrPos : nHtStt;
2337 sal_Int32 nTmpEnd = std::min(nHtEnd, nEnd);
2338 aEndPosLst.Insert( pHt->GetAttr(), nTmpStt + nOffset,
2339 nTmpEnd + nOffset,
2340 rWrt.m_CharFormatInfos );
2342 continue;
2343 // but don't output it, that will be done later !!
2346 } while( nAttrPos < nCntAttr && nStrPos >
2347 ( pHt = pNd->GetSwpHints().Get( nAttrPos ) )->GetStart() );
2349 // so, let's output all collected attributes from the string pos on
2350 aEndPosLst.OutEndAttrs( rWrt, nStrPos + nOffset );
2351 aEndPosLst.OutStartAttrs( rWrt, nStrPos + nOffset );
2354 bool bWriteBreak = (HtmlTokenId::PREFORMTXT_ON != rWrt.m_nLastParaToken);
2355 if (bWriteBreak && (pNd->GetNumRule() || rWrt.mbReqIF))
2357 // One line-break is exactly one <br> in the ReqIF case.
2358 bWriteBreak = false;
2362 // Tabs are leading till there is a non-tab since the start of the paragraph.
2363 bool bLeadingTab = true;
2364 for( ; nStrPos < nEnd; nStrPos++ )
2366 // output the frames that are anchored to the current position
2367 if( bFlysLeft )
2369 aEndPosLst.OutEndAttrs( rWrt, nStrPos + nOffset );
2370 bFlysLeft = rWrt.OutFlyFrame( rNode.GetIndex(),
2371 nStrPos, HtmlPosition::Inside );
2374 bool bOutChar = true;
2375 const SwTextAttr * pTextHt = nullptr;
2376 if (nAttrPos < nCntAttr && pHt->GetStart() == nStrPos)
2378 do {
2379 if ( pHt->End() && !pHt->HasDummyChar() )
2381 if( *pHt->End() != nStrPos )
2383 // insert hints with end, if they don't start
2384 // an empty range (hints that don't start a range
2385 // are ignored)
2386 aEndPosLst.Insert( pHt->GetAttr(), nStrPos + nOffset,
2387 *pHt->End() + nOffset,
2388 rWrt.m_CharFormatInfos );
2391 else
2393 // hints without an end are output last
2394 OSL_ENSURE( !pTextHt, "Why is there already an attribute without an end?" );
2395 if( rWrt.m_nTextAttrsToIgnore>0 )
2397 rWrt.m_nTextAttrsToIgnore--;
2399 else
2401 pTextHt = pHt;
2402 SwFieldIds nFieldWhich;
2403 if( RES_TXTATR_FIELD != pHt->Which()
2404 || ( SwFieldIds::Postit != (nFieldWhich = static_cast<const SwFormatField&>(pHt->GetAttr()).GetField()->Which())
2405 && SwFieldIds::Script != nFieldWhich ) )
2407 bWriteBreak = false;
2410 bOutChar = false; // don't output 255
2412 } while( ++nAttrPos < nCntAttr && nStrPos ==
2413 ( pHt = pNd->GetSwpHints().Get( nAttrPos ) )->GetStart() );
2416 // Additionally, some draw formats can bring attributes
2417 if( pTextHt && RES_TXTATR_FLYCNT == pTextHt->Which() )
2419 const SwFrameFormat* pFrameFormat =
2420 pTextHt->GetAttr().StaticWhichCast(RES_TXTATR_FLYCNT).GetFrameFormat();
2422 if( RES_DRAWFRMFMT == pFrameFormat->Which() )
2423 aEndPosLst.Insert( *static_cast<const SwDrawFrameFormat *>(pFrameFormat),
2424 nStrPos + nOffset,
2425 rWrt.m_CharFormatInfos );
2428 aEndPosLst.OutEndAttrs( rWrt, nStrPos + nOffset );
2429 aEndPosLst.OutStartAttrs( rWrt, nStrPos + nOffset );
2431 if( pTextHt )
2433 rWrt.m_bLFPossible = rWrt.m_nLastParaToken == HtmlTokenId::NONE &&
2434 nStrPos > 0 &&
2435 rStr[nStrPos-1] == ' ';
2436 sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script;
2437 rWrt.m_nCSS1Script = aEndPosLst.GetScriptAtPos(
2438 nStrPos + nOffset, nCSS1Script );
2439 HTMLOutFuncs::FlushToAscii( rWrt.Strm() );
2440 Out( aHTMLAttrFnTab, pTextHt->GetAttr(), rWrt );
2441 rWrt.m_nCSS1Script = nCSS1Script;
2442 rWrt.m_bLFPossible = false;
2445 if( bOutChar )
2447 sal_uInt32 c = rStr[nStrPos];
2448 if( rtl::isHighSurrogate(c) && nStrPos < nEnd - 1 )
2450 const sal_Unicode d = rStr[nStrPos + 1];
2451 if( rtl::isLowSurrogate(d) )
2453 c = rtl::combineSurrogates(c, d);
2454 nStrPos++;
2458 // try to split a line after about 255 characters
2459 // at a space character unless in a PRE-context
2460 if( ' ' == c && rWrt.m_nLastParaToken == HtmlTokenId::NONE )
2462 sal_Int32 nLineLen;
2463 nLineLen = rWrt.GetLineLen();
2465 sal_Int32 nWordLen = rStr.indexOf( ' ', nStrPos+1 );
2466 if( nWordLen == -1 )
2467 nWordLen = nEnd;
2468 nWordLen -= nStrPos;
2470 if( rWrt.m_bPrettyPrint && rWrt.m_nWishLineLen >= 0 &&
2471 (nLineLen >= rWrt.m_nWishLineLen ||
2472 (nLineLen+nWordLen) >= rWrt.m_nWishLineLen ) )
2474 HTMLOutFuncs::FlushToAscii( rWrt.Strm() );
2475 rWrt.OutNewLine();
2476 bOutChar = false;
2480 if( bOutChar )
2482 if( 0x0a == c )
2484 HTMLOutFuncs::FlushToAscii( rWrt.Strm() );
2485 HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
2486 aHtml.prettyPrint(rWrt.m_bPrettyPrint);
2487 aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak);
2489 else if (c == CH_TXT_ATR_FORMELEMENT)
2491 // Placeholder for a single-point fieldmark.
2493 SwPosition aMarkPos = *rWrt.m_pCurrentPam->GetPoint();
2494 aMarkPos.AdjustContent( nStrPos - aMarkPos.GetContentIndex() );
2495 rWrt.OutPointFieldmarks(aMarkPos);
2497 else
2499 bool bConsumed = false;
2500 if (c == '\t')
2502 if (bLeadingTab && rWrt.m_nLeadingTabWidth.has_value())
2504 // Consume a tab if it's leading and we know the number of NBSPs to
2505 // be used as a replacement.
2506 for (sal_Int32 i = 0; i < *rWrt.m_nLeadingTabWidth; ++i)
2508 rWrt.Strm().WriteOString("&#160;");
2510 bConsumed = true;
2513 else
2515 // Not a tab -> later tabs are no longer leading.
2516 bLeadingTab = false;
2519 if (!bConsumed)
2521 HTMLOutFuncs::Out_Char(rWrt.Strm(), c);
2525 if (!rWrt.mbReqIF)
2527 // if a paragraph's last character is a hard line break
2528 // then we need to add an extra <br>
2529 // because browsers like Mozilla wouldn't add a line for the next paragraph
2530 bWriteBreak = (0x0a == c) &&
2531 (HtmlTokenId::PREFORMTXT_ON != rWrt.m_nLastParaToken);
2536 HTMLOutFuncs::FlushToAscii( rWrt.Strm() );
2539 aEndPosLst.OutEndAttrs( rWrt, SAL_MAX_INT32 );
2541 // Output the frames that are anchored to the last position
2542 if( bFlysLeft )
2543 bFlysLeft = rWrt.OutFlyFrame( rNode.GetIndex(),
2544 nEnd, HtmlPosition::Inside );
2545 OSL_ENSURE( !bFlysLeft, "Not all frames were saved!" );
2547 rWrt.m_bTextAttr = false;
2549 if( bWriteBreak )
2551 bool bEndOfCell = rWrt.m_bOutTable &&
2552 rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() ==
2553 rWrt.m_pCurrentPam->GetMark()->GetNodeIndex();
2555 if( bEndOfCell && !nEnd &&
2556 rWrt.IsHTMLMode(HTMLMODE_NBSP_IN_TABLES) )
2558 // If the last paragraph of a table cell is empty and we export
2559 // for the MS-IE, we write a &nbsp; instead of a <BR>
2560 rWrt.Strm().WriteChar( '&' ).WriteOString( OOO_STRING_SVTOOLS_HTML_S_nbsp ).WriteChar( ';' );
2562 else
2564 HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
2565 aHtml.prettyPrint(rWrt.m_bPrettyPrint);
2566 aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak);
2567 const SvxULSpaceItem& rULSpace = pNd->GetSwAttrSet().Get(RES_UL_SPACE);
2568 if (rULSpace.GetLower() > 0 && !bEndOfCell)
2570 aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak);
2572 rWrt.m_bLFPossible = true;
2576 if( rWrt.m_bClearLeft || rWrt.m_bClearRight )
2578 const char* pString;
2579 if( rWrt.m_bClearLeft )
2581 if( rWrt.m_bClearRight )
2582 pString = OOO_STRING_SVTOOLS_HTML_AL_all;
2583 else
2584 pString = OOO_STRING_SVTOOLS_HTML_AL_left;
2586 else
2588 pString = OOO_STRING_SVTOOLS_HTML_AL_right;
2591 HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
2592 aHtml.prettyPrint(rWrt.m_bPrettyPrint);
2593 aHtml.start(OOO_STRING_SVTOOLS_HTML_linebreak);
2594 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, pString);
2595 aHtml.end();
2597 rWrt.m_bClearLeft = false;
2598 rWrt.m_bClearRight = false;
2600 rWrt.m_bLFPossible = true;
2603 // if an LF is not allowed already, it is allowed once the paragraphs
2604 // ends with a ' '
2605 if( !rWrt.m_bLFPossible &&
2606 rWrt.m_nLastParaToken == HtmlTokenId::NONE &&
2607 nEnd > 0 && ' ' == rStr[nEnd-1] )
2608 rWrt.m_bLFPossible = true;
2610 // dot leaders: print the skipped page number in a different span element
2611 if (nIndexTab > -1) {
2612 OString sOut = OUStringToOString(rStr.subView(nIndexTab + 1), RTL_TEXTENCODING_ASCII_US);
2613 rWrt.Strm().WriteOString( Concat2View("</span><span>" + sOut + "</span>") );
2616 rWrt.m_bTagOn = false;
2617 OutHTML_SwFormatOff( rWrt, aFormatInfo );
2619 // if necessary, close a form
2620 rWrt.OutForm( false );
2622 if( bPageBreakBehind )
2623 rWrt.Strm().WriteChar( '\f' );
2625 return rWrt;
2628 // In CSS, "px" is 1/96 of inch: https://www.w3.org/TR/css3-values/#absolute-lengths
2629 sal_uInt32 SwHTMLWriter::ToPixel(sal_uInt32 nTwips)
2631 // if there is a Twip, there should be a pixel as well
2632 return nTwips
2633 ? std::max(o3tl::convert(nTwips, o3tl::Length::twip, o3tl::Length::px), sal_Int64(1))
2634 : 0;
2637 Size SwHTMLWriter::ToPixel(Size aTwips)
2639 return Size(ToPixel(aTwips.Width()), ToPixel(aTwips.Height()));
2642 static SwHTMLWriter& OutHTML_CSS1Attr( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2644 // if hints are currently written, we try to write the hint as an
2645 // CSS1 attribute
2647 if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
2648 OutCSS1_HintSpanTag( rWrt, rHt );
2650 return rWrt;
2653 /* File CHRATR.HXX: */
2655 static SwHTMLWriter& OutHTML_SvxColor( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2657 if( rWrt.m_bOutOpts )
2658 return rWrt;
2660 if( !rWrt.m_bTextAttr && rWrt.m_bCfgOutStyles && rWrt.m_bCfgPreferStyles )
2662 // don't write the font color as a tag, if styles are preferred to
2663 // normal tags
2664 return rWrt;
2667 if( rWrt.m_bTagOn )
2669 Color aColor( static_cast<const SvxColorItem&>(rHt).GetValue() );
2670 if( COL_AUTO == aColor )
2671 aColor = COL_BLACK;
2673 if (rWrt.mbXHTML)
2675 OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span
2676 " " OOO_STRING_SVTOOLS_HTML_O_style "=";
2677 rWrt.Strm().WriteOString(sOut);
2678 HTMLOutFuncs::Out_Color(rWrt.Strm(), aColor, /*bXHTML=*/true).WriteChar('>');
2680 else
2682 OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font " "
2683 OOO_STRING_SVTOOLS_HTML_O_color "=";
2684 rWrt.Strm().WriteOString( sOut );
2685 HTMLOutFuncs::Out_Color( rWrt.Strm(), aColor ).WriteChar( '>' );
2688 else
2690 if (rWrt.mbXHTML)
2691 HTMLOutFuncs::Out_AsciiTag(
2692 rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
2693 else
2694 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font), false );
2697 return rWrt;
2700 static SwHTMLWriter& OutHTML_SwPosture( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2702 if( rWrt.m_bOutOpts )
2703 return rWrt;
2705 const FontItalic nPosture = static_cast<const SvxPostureItem&>(rHt).GetPosture();
2706 if( ITALIC_NORMAL == nPosture )
2708 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_italic), rWrt.m_bTagOn );
2710 else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
2712 // maybe as CSS1 attribute?
2713 OutCSS1_HintSpanTag( rWrt, rHt );
2716 return rWrt;
2719 static SwHTMLWriter& OutHTML_SvxFont( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2721 if( rWrt.m_bOutOpts )
2722 return rWrt;
2724 if (IgnorePropertyForReqIF(rWrt.mbReqIF, "font-family", ""))
2726 return rWrt;
2729 if( rWrt.m_bTagOn )
2731 OUString aNames;
2732 SwHTMLWriter::PrepareFontList( static_cast<const SvxFontItem&>(rHt), aNames, 0,
2733 rWrt.IsHTMLMode(HTMLMODE_FONT_GENERIC) );
2734 if (rWrt.mbXHTML)
2736 OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span
2737 " " OOO_STRING_SVTOOLS_HTML_O_style "=\"font-family: ";
2738 rWrt.Strm().WriteOString(sOut);
2739 HTMLOutFuncs::Out_String(rWrt.Strm(), aNames)
2740 .WriteOString("\">");
2742 else
2744 OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font " "
2745 OOO_STRING_SVTOOLS_HTML_O_face "=\"";
2746 rWrt.Strm().WriteOString( sOut );
2747 HTMLOutFuncs::Out_String( rWrt.Strm(), aNames )
2748 .WriteOString( "\">" );
2751 else
2753 if (rWrt.mbXHTML)
2754 HTMLOutFuncs::Out_AsciiTag(
2755 rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
2756 else
2757 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font), false );
2760 return rWrt;
2763 static SwHTMLWriter& OutHTML_SvxFontHeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2765 if( rWrt.m_bOutOpts )
2766 return rWrt;
2768 if (IgnorePropertyForReqIF(rWrt.mbReqIF, "font-size", ""))
2770 return rWrt;
2773 if( rWrt.m_bTagOn )
2775 if (rWrt.mbXHTML)
2777 OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span;
2779 sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight();
2780 // Twips -> points.
2781 sal_uInt16 nSize = nHeight / 20;
2782 sOut += " " OOO_STRING_SVTOOLS_HTML_O_style "=\"font-size: "
2783 + OString::number(static_cast<sal_Int32>(nSize)) + "pt\"";
2784 rWrt.Strm().WriteOString(sOut);
2786 else
2788 OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font;
2790 sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight();
2791 sal_uInt16 nSize = rWrt.GetHTMLFontSize( nHeight );
2792 sOut += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" +
2793 OString::number(static_cast<sal_Int32>(nSize)) + "\"";
2794 rWrt.Strm().WriteOString( sOut );
2796 if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
2798 // always export font size as CSS option, too
2799 OutCSS1_HintStyleOpt( rWrt, rHt );
2802 rWrt.Strm().WriteChar( '>' );
2804 else
2806 if (rWrt.mbXHTML)
2807 HTMLOutFuncs::Out_AsciiTag(
2808 rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
2809 else
2810 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font), false );
2813 return rWrt;
2816 static SwHTMLWriter& OutHTML_SvxLanguage( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2818 if( rWrt.m_bOutOpts )
2819 return rWrt;
2821 LanguageType eLang = static_cast<const SvxLanguageItem &>(rHt).GetLanguage();
2822 if( LANGUAGE_DONTKNOW == eLang )
2823 return rWrt;
2825 if( rWrt.m_bTagOn )
2827 OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span;
2828 rWrt.Strm().WriteOString( sOut );
2829 rWrt.OutLanguage( static_cast<const SvxLanguageItem &>(rHt).GetLanguage() );
2830 rWrt.Strm().WriteChar( '>' );
2832 else
2834 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
2837 return rWrt;
2839 static SwHTMLWriter& OutHTML_SwWeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2841 if( rWrt.m_bOutOpts )
2842 return rWrt;
2844 const FontWeight nBold = static_cast<const SvxWeightItem&>(rHt).GetWeight();
2845 if( WEIGHT_BOLD == nBold )
2847 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_bold), rWrt.m_bTagOn );
2849 else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
2851 // maybe as CSS1 attribute ?
2852 OutCSS1_HintSpanTag( rWrt, rHt );
2855 return rWrt;
2858 static SwHTMLWriter& OutHTML_SwCrossedOut( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2860 if( rWrt.m_bOutOpts )
2861 return rWrt;
2863 // Because of Netscape, we output STRIKE and not S!
2864 const FontStrikeout nStrike = static_cast<const SvxCrossedOutItem&>(rHt).GetStrikeout();
2865 if( STRIKEOUT_NONE != nStrike && !rWrt.mbReqIF )
2867 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_strike), rWrt.m_bTagOn );
2869 else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
2871 // maybe as CSS1 attribute?
2872 OutCSS1_HintSpanTag( rWrt, rHt );
2875 return rWrt;
2878 static SwHTMLWriter& OutHTML_SvxEscapement( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2880 if( rWrt.m_bOutOpts )
2881 return rWrt;
2883 const SvxEscapement eEscape =
2884 static_cast<SvxEscapement>(static_cast<const SvxEscapementItem&>(rHt).GetEnumValue());
2885 OString aTag;
2886 switch( eEscape )
2888 case SvxEscapement::Superscript: aTag = OOO_STRING_SVTOOLS_HTML_superscript; break;
2889 case SvxEscapement::Subscript: aTag = OOO_STRING_SVTOOLS_HTML_subscript; break;
2890 default:
2894 if( !aTag.isEmpty() )
2896 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), rWrt.m_bTagOn );
2898 else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
2900 // maybe as CSS1 attribute?
2901 OutCSS1_HintSpanTag( rWrt, rHt );
2904 return rWrt;
2907 static SwHTMLWriter& OutHTML_SwUnderline( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2909 if( rWrt.m_bOutOpts )
2910 return rWrt;
2912 const FontLineStyle eUnder = static_cast<const SvxUnderlineItem&>(rHt).GetLineStyle();
2913 if( LINESTYLE_NONE != eUnder && !rWrt.mbReqIF )
2915 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_underline), rWrt.m_bTagOn );
2917 else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
2919 // maybe as CSS1 attribute?
2920 OutCSS1_HintSpanTag( rWrt, rHt );
2923 return rWrt;
2926 static SwHTMLWriter& OutHTML_SwFlyCnt( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2928 const SwFormatFlyCnt& rFlyCnt = static_cast<const SwFormatFlyCnt&>(rHt);
2930 const SwFrameFormat& rFormat = *rFlyCnt.GetFrameFormat();
2931 const SdrObject *pSdrObj = nullptr;
2933 SwHTMLFrameType eType = rWrt.GuessFrameType( rFormat, pSdrObj );
2934 AllHtmlFlags nMode = aHTMLOutFrameAsCharTable[eType][rWrt.m_nExportMode];
2935 rWrt.OutFrameFormat( nMode, rFormat, pSdrObj );
2936 return rWrt;
2939 // This is now our Blink item. Blinking is activated by setting the item to
2940 // true!
2941 static SwHTMLWriter& OutHTML_SwBlink( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
2943 if( rWrt.m_bOutOpts )
2944 return rWrt;
2946 if( static_cast<const SvxBlinkItem&>(rHt).GetValue() )
2948 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_blink), rWrt.m_bTagOn );
2950 else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
2952 // maybe as CSS1 attribute?
2953 OutCSS1_HintSpanTag( rWrt, rHt );
2956 return rWrt;
2959 SwHTMLWriter& OutHTML_INetFormat( SwHTMLWriter& rWrt, const SwFormatINetFormat& rINetFormat, bool bOn )
2961 OUString aURL( rINetFormat.GetValue() );
2962 const SvxMacroTableDtor *pMacTable = rINetFormat.GetMacroTable();
2963 bool bEvents = pMacTable != nullptr && !pMacTable->empty();
2965 // Anything to output at all?
2966 if( aURL.isEmpty() && !bEvents && rINetFormat.GetName().isEmpty() )
2967 return rWrt;
2969 // bOn controls if we are writing the opening or closing tag
2970 if( !bOn )
2972 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
2973 return rWrt;
2976 OString sOut("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor);
2978 bool bScriptDependent = false;
2980 const SwCharFormat* pFormat = rWrt.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
2981 RES_POOLCHR_INET_NORMAL );
2982 std::unique_ptr<SwHTMLFormatInfo> pFormatInfo(new SwHTMLFormatInfo(pFormat));
2983 auto const it = rWrt.m_CharFormatInfos.find( pFormatInfo );
2984 if (it != rWrt.m_CharFormatInfos.end())
2986 bScriptDependent = (*it)->bScriptDependent;
2989 if( !bScriptDependent )
2991 const SwCharFormat* pFormat = rWrt.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
2992 RES_POOLCHR_INET_VISIT );
2993 std::unique_ptr<SwHTMLFormatInfo> pFormatInfo(new SwHTMLFormatInfo(pFormat));
2994 auto const it = rWrt.m_CharFormatInfos.find( pFormatInfo );
2995 if (it != rWrt.m_CharFormatInfos.end())
2997 bScriptDependent = (*it)->bScriptDependent;
3001 if( bScriptDependent )
3003 sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"";
3004 const char* pStr = nullptr;
3005 switch( rWrt.m_nCSS1Script )
3007 case CSS1_OUTMODE_WESTERN:
3008 pStr = "western";
3009 break;
3010 case CSS1_OUTMODE_CJK:
3011 pStr = "cjk";
3012 break;
3013 case CSS1_OUTMODE_CTL:
3014 pStr = "ctl";
3015 break;
3017 sOut += pStr + OString::Concat("\"");
3020 rWrt.Strm().WriteOString( sOut );
3021 sOut = "";
3023 OUString sRel;
3025 if( !aURL.isEmpty() || bEvents )
3027 OUString sTmp( aURL.toAsciiUpperCase() );
3028 sal_Int32 nPos = sTmp.indexOf( "\" REL=" );
3029 if( nPos >= 0 )
3031 sRel = aURL.copy( nPos+1 );
3032 aURL = aURL.copy( 0, nPos);
3034 aURL = comphelper::string::strip(aURL, ' ');
3036 sOut += " " OOO_STRING_SVTOOLS_HTML_O_href "=\"";
3037 rWrt.Strm().WriteOString( sOut );
3038 rWrt.OutHyperlinkHRefValue( aURL );
3039 sOut = "\"";
3042 if( !rINetFormat.GetName().isEmpty() )
3044 sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\"";
3045 rWrt.Strm().WriteOString( sOut );
3046 HTMLOutFuncs::Out_String( rWrt.Strm(), rINetFormat.GetName() );
3047 sOut = "\"";
3050 const OUString& rTarget = rINetFormat.GetTargetFrame();
3051 if( !rTarget.isEmpty() )
3053 sOut += " " OOO_STRING_SVTOOLS_HTML_O_target "=\"";
3054 rWrt.Strm().WriteOString( sOut );
3055 HTMLOutFuncs::Out_String( rWrt.Strm(), rTarget );
3056 sOut = "\"";
3059 if( !sRel.isEmpty() )
3060 sOut += OUStringToOString(sRel, RTL_TEXTENCODING_ASCII_US);
3062 if( !sOut.isEmpty() )
3063 rWrt.Strm().WriteOString( sOut );
3065 if( bEvents )
3066 HTMLOutFuncs::Out_Events( rWrt.Strm(), *pMacTable, aAnchorEventTable,
3067 rWrt.m_bCfgStarBasic );
3068 rWrt.Strm().WriteOString( ">" );
3070 return rWrt;
3073 static SwHTMLWriter& OutHTML_SwFormatINetFormat( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
3075 if( rWrt.m_bOutOpts )
3076 return rWrt;
3078 const SwFormatINetFormat& rINetFormat = static_cast<const SwFormatINetFormat&>(rHt);
3080 if( rWrt.m_bTagOn )
3082 // if necessary, temporarily close an attribute that is still open
3083 if( !rWrt.m_aINetFormats.empty() )
3085 SwFormatINetFormat *pINetFormat =
3086 rWrt.m_aINetFormats.back();
3087 OutHTML_INetFormat( rWrt, *pINetFormat, false );
3090 // now, open the new one
3091 OutHTML_INetFormat( rWrt, rINetFormat, true );
3093 // and remember it
3094 SwFormatINetFormat *pINetFormat = new SwFormatINetFormat( rINetFormat );
3095 rWrt.m_aINetFormats.push_back( pINetFormat );
3097 else
3099 OutHTML_INetFormat( rWrt, rINetFormat, false );
3101 OSL_ENSURE( rWrt.m_aINetFormats.size(), "there must be a URL attribute missing" );
3102 if( !rWrt.m_aINetFormats.empty() )
3104 // get its own attribute from the stack
3105 SwFormatINetFormat *pINetFormat = rWrt.m_aINetFormats.back();
3106 rWrt.m_aINetFormats.pop_back();
3107 delete pINetFormat;
3110 if( !rWrt.m_aINetFormats.empty() )
3112 // there is still an attribute on the stack that must be reopened
3113 SwFormatINetFormat *pINetFormat = rWrt.m_aINetFormats.back();
3114 OutHTML_INetFormat( rWrt, *pINetFormat, true );
3118 return rWrt;
3121 static SwHTMLWriter& OutHTML_SwTextCharFormat( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
3123 if( rWrt.m_bOutOpts )
3124 return rWrt;
3126 const SwFormatCharFormat& rChrFormat = static_cast<const SwFormatCharFormat&>(rHt);
3127 const SwCharFormat* pFormat = rChrFormat.GetCharFormat();
3129 if( !pFormat )
3131 return rWrt;
3134 std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(pFormat));
3135 SwHTMLFormatInfos::const_iterator it = rWrt.m_CharFormatInfos.find(pTmpInfo);
3136 if (it == rWrt.m_CharFormatInfos.end())
3137 return rWrt;
3139 const SwHTMLFormatInfo *pFormatInfo = it->get();
3140 OSL_ENSURE( pFormatInfo, "Why is there no information about the character style?" );
3142 if( rWrt.m_bTagOn )
3144 OString sOut = "<" + rWrt.GetNamespace();
3145 if( !pFormatInfo->aToken.isEmpty() )
3146 sOut += pFormatInfo->aToken;
3147 else
3148 sOut += OOO_STRING_SVTOOLS_HTML_span;
3150 if( rWrt.m_bCfgOutStyles &&
3151 (!pFormatInfo->aClass.isEmpty() || pFormatInfo->bScriptDependent) )
3153 sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"";
3154 rWrt.Strm().WriteOString( sOut );
3155 OUString aClass( pFormatInfo->aClass );
3156 if( pFormatInfo->bScriptDependent )
3158 if( !aClass.isEmpty() )
3159 aClass += "-";
3160 switch( rWrt.m_nCSS1Script )
3162 case CSS1_OUTMODE_WESTERN:
3163 aClass += "western";
3164 break;
3165 case CSS1_OUTMODE_CJK:
3166 aClass += "cjk";
3167 break;
3168 case CSS1_OUTMODE_CTL:
3169 aClass += "ctl";
3170 break;
3173 HTMLOutFuncs::Out_String( rWrt.Strm(), aClass );
3174 sOut = "\"";
3176 sOut += ">";
3177 rWrt.Strm().WriteOString( sOut );
3179 else
3181 OString aTag = !pFormatInfo->aToken.isEmpty() ? pFormatInfo->aToken.getStr()
3182 : OOO_STRING_SVTOOLS_HTML_span;
3183 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
3186 return rWrt;
3189 static SwHTMLWriter& OutHTML_SvxAdjust( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
3191 if( !rWrt.m_bOutOpts || !rWrt.m_bTagOn )
3192 return rWrt;
3194 const SvxAdjustItem& rAdjust = static_cast<const SvxAdjustItem&>(rHt);
3195 const char* pStr = nullptr;
3196 switch( rAdjust.GetAdjust() )
3198 case SvxAdjust::Center: pStr = OOO_STRING_SVTOOLS_HTML_AL_center; break;
3199 case SvxAdjust::Left: pStr = OOO_STRING_SVTOOLS_HTML_AL_left; break;
3200 case SvxAdjust::Right: pStr = OOO_STRING_SVTOOLS_HTML_AL_right; break;
3201 case SvxAdjust::Block: pStr = OOO_STRING_SVTOOLS_HTML_AL_justify; break;
3202 default:
3205 if( pStr )
3207 OString sOut = OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"") +
3208 pStr + "\"";
3209 rWrt.Strm().WriteOString( sOut );
3212 return rWrt;
3216 * here, define the table for the HTML function pointers to the output
3217 * functions.
3220 SwAttrFnTab aHTMLAttrFnTab = {
3221 /* RES_CHRATR_CASEMAP */ OutHTML_CSS1Attr,
3222 /* RES_CHRATR_CHARSETCOLOR */ nullptr,
3223 /* RES_CHRATR_COLOR */ OutHTML_SvxColor,
3224 /* RES_CHRATR_CONTOUR */ nullptr,
3225 /* RES_CHRATR_CROSSEDOUT */ OutHTML_SwCrossedOut,
3226 /* RES_CHRATR_ESCAPEMENT */ OutHTML_SvxEscapement,
3227 /* RES_CHRATR_FONT */ OutHTML_SvxFont,
3228 /* RES_CHRATR_FONTSIZE */ OutHTML_SvxFontHeight,
3229 /* RES_CHRATR_KERNING */ OutHTML_CSS1Attr,
3230 /* RES_CHRATR_LANGUAGE */ OutHTML_SvxLanguage,
3231 /* RES_CHRATR_POSTURE */ OutHTML_SwPosture,
3232 /* RES_CHRATR_UNUSED1*/ nullptr,
3233 /* RES_CHRATR_SHADOWED */ nullptr,
3234 /* RES_CHRATR_UNDERLINE */ OutHTML_SwUnderline,
3235 /* RES_CHRATR_WEIGHT */ OutHTML_SwWeight,
3236 /* RES_CHRATR_WORDLINEMODE */ nullptr,
3237 /* RES_CHRATR_AUTOKERN */ nullptr,
3238 /* RES_CHRATR_BLINK */ OutHTML_SwBlink,
3239 /* RES_CHRATR_NOHYPHEN */ nullptr, // New: don't hyphenate
3240 /* RES_CHRATR_UNUSED2 */ nullptr,
3241 /* RES_CHRATR_BACKGROUND */ OutHTML_CSS1Attr, // New: character background
3242 /* RES_CHRATR_CJK_FONT */ OutHTML_SvxFont,
3243 /* RES_CHRATR_CJK_FONTSIZE */ OutHTML_SvxFontHeight,
3244 /* RES_CHRATR_CJK_LANGUAGE */ OutHTML_SvxLanguage,
3245 /* RES_CHRATR_CJK_POSTURE */ OutHTML_SwPosture,
3246 /* RES_CHRATR_CJK_WEIGHT */ OutHTML_SwWeight,
3247 /* RES_CHRATR_CTL_FONT */ OutHTML_SvxFont,
3248 /* RES_CHRATR_CTL_FONTSIZE */ OutHTML_SvxFontHeight,
3249 /* RES_CHRATR_CTL_LANGUAGE */ OutHTML_SvxLanguage,
3250 /* RES_CHRATR_CTL_POSTURE */ OutHTML_SwPosture,
3251 /* RES_CHRATR_CTL_WEIGHT */ OutHTML_SwWeight,
3252 /* RES_CHRATR_ROTATE */ nullptr,
3253 /* RES_CHRATR_EMPHASIS_MARK */ nullptr,
3254 /* RES_CHRATR_TWO_LINES */ nullptr,
3255 /* RES_CHRATR_SCALEW */ nullptr,
3256 /* RES_CHRATR_RELIEF */ nullptr,
3257 /* RES_CHRATR_HIDDEN */ OutHTML_CSS1Attr,
3258 /* RES_CHRATR_OVERLINE */ OutHTML_CSS1Attr,
3259 /* RES_CHRATR_RSID */ nullptr,
3260 /* RES_CHRATR_BOX */ OutHTML_CSS1Attr,
3261 /* RES_CHRATR_SHADOW */ nullptr,
3262 /* RES_CHRATR_HIGHLIGHT */ nullptr,
3263 /* RES_CHRATR_GRABBAG */ nullptr,
3264 /* RES_CHRATR_BIDIRTL */ nullptr,
3265 /* RES_CHRATR_IDCTHINT */ nullptr,
3267 /* RES_TXTATR_REFMARK */ nullptr,
3268 /* RES_TXTATR_TOXMARK */ nullptr,
3269 /* RES_TXTATR_META */ nullptr,
3270 /* RES_TXTATR_METAFIELD */ nullptr,
3271 /* RES_TXTATR_AUTOFMT */ nullptr,
3272 /* RES_TXTATR_INETFMT */ OutHTML_SwFormatINetFormat,
3273 /* RES_TXTATR_CHARFMT */ OutHTML_SwTextCharFormat,
3274 /* RES_TXTATR_CJK_RUBY */ nullptr,
3275 /* RES_TXTATR_UNKNOWN_CONTAINER */ nullptr,
3276 /* RES_TXTATR_INPUTFIELD */ OutHTML_SwFormatField,
3277 /* RES_TXTATR_CONTENTCONTROL */ nullptr,
3279 /* RES_TXTATR_FIELD */ OutHTML_SwFormatField,
3280 /* RES_TXTATR_FLYCNT */ OutHTML_SwFlyCnt,
3281 /* RES_TXTATR_FTN */ OutHTML_SwFormatFootnote,
3282 /* RES_TXTATR_ANNOTATION */ OutHTML_SwFormatField,
3283 /* RES_TXTATR_LINEBREAK */ OutHTML_SwFormatLineBreak,
3284 /* RES_TXTATR_DUMMY1 */ nullptr, // Dummy:
3286 /* RES_PARATR_LINESPACING */ nullptr,
3287 /* RES_PARATR_ADJUST */ OutHTML_SvxAdjust,
3288 /* RES_PARATR_SPLIT */ nullptr,
3289 /* RES_PARATR_ORPHANS */ nullptr,
3290 /* RES_PARATR_WIDOWS */ nullptr,
3291 /* RES_PARATR_TABSTOP */ nullptr,
3292 /* RES_PARATR_HYPHENZONE*/ nullptr,
3293 /* RES_PARATR_DROP */ OutHTML_CSS1Attr,
3294 /* RES_PARATR_REGISTER */ nullptr, // new: register-true
3295 /* RES_PARATR_NUMRULE */ nullptr, // Dummy:
3296 /* RES_PARATR_SCRIPTSPACE */ nullptr, // Dummy:
3297 /* RES_PARATR_HANGINGPUNCTUATION */ nullptr, // Dummy:
3298 /* RES_PARATR_FORBIDDEN_RULES */ nullptr, // new
3299 /* RES_PARATR_VERTALIGN */ nullptr, // new
3300 /* RES_PARATR_SNAPTOGRID*/ nullptr, // new
3301 /* RES_PARATR_CONNECT_TO_BORDER */ nullptr, // new
3302 /* RES_PARATR_OUTLINELEVEL */ nullptr,
3303 /* RES_PARATR_RSID */ nullptr,
3304 /* RES_PARATR_GRABBAG */ nullptr,
3306 /* RES_PARATR_LIST_ID */ nullptr, // new
3307 /* RES_PARATR_LIST_LEVEL */ nullptr, // new
3308 /* RES_PARATR_LIST_ISRESTART */ nullptr, // new
3309 /* RES_PARATR_LIST_RESTARTVALUE */ nullptr, // new
3310 /* RES_PARATR_LIST_ISCOUNTED */ nullptr, // new
3312 /* RES_FILL_ORDER */ nullptr,
3313 /* RES_FRM_SIZE */ nullptr,
3314 /* RES_PAPER_BIN */ nullptr,
3315 /* RES_MARGIN_FIRSTLINE */ nullptr,
3316 /* RES_MARGIN_TEXTLEFT */ nullptr,
3317 /* RES_MARGIN_RIGHT */ nullptr,
3318 /* RES_MARGIN_LEFT */ nullptr,
3319 /* RES_MARGIN_GUTTER */ nullptr,
3320 /* RES_MARGIN_GUTTER_RIGHT */ nullptr,
3321 /* RES_LR_SPACE */ nullptr,
3322 /* RES_UL_SPACE */ nullptr,
3323 /* RES_PAGEDESC */ nullptr,
3324 /* RES_BREAK */ nullptr,
3325 /* RES_CNTNT */ nullptr,
3326 /* RES_HEADER */ nullptr,
3327 /* RES_FOOTER */ nullptr,
3328 /* RES_PRINT */ nullptr,
3329 /* RES_OPAQUE */ nullptr,
3330 /* RES_PROTECT */ nullptr,
3331 /* RES_SURROUND */ nullptr,
3332 /* RES_VERT_ORIENT */ nullptr,
3333 /* RES_HORI_ORIENT */ nullptr,
3334 /* RES_ANCHOR */ nullptr,
3335 /* RES_BACKGROUND */ nullptr,
3336 /* RES_BOX */ nullptr,
3337 /* RES_SHADOW */ nullptr,
3338 /* RES_FRMMACRO */ nullptr,
3339 /* RES_COL */ nullptr,
3340 /* RES_KEEP */ nullptr,
3341 /* RES_URL */ nullptr,
3342 /* RES_EDIT_IN_READONLY */ nullptr,
3343 /* RES_LAYOUT_SPLIT */ nullptr,
3344 /* RES_CHAIN */ nullptr,
3345 /* RES_TEXTGRID */ nullptr,
3346 /* RES_LINENUMBER */ nullptr,
3347 /* RES_FTN_AT_TXTEND */ nullptr,
3348 /* RES_END_AT_TXTEND */ nullptr,
3349 /* RES_COLUMNBALANCE */ nullptr,
3350 /* RES_FRAMEDIR */ nullptr,
3351 /* RES_HEADER_FOOTER_EAT_SPACING */ nullptr,
3352 /* RES_ROW_SPLIT */ nullptr,
3353 /* RES_FLY_SPLIT */ nullptr,
3354 /* RES_FOLLOW_TEXT_FLOW */ nullptr,
3355 /* RES_COLLAPSING_BORDERS */ nullptr,
3356 /* RES_WRAP_INFLUENCE_ON_OBJPOS */ nullptr,
3357 /* RES_AUTO_STYLE */ nullptr,
3358 /* RES_FRMATR_STYLE_NAME */ nullptr,
3359 /* RES_FRMATR_CONDITIONAL_STYLE_NAME */ nullptr,
3360 /* RES_FRMATR_GRABBAG */ nullptr,
3361 /* RES_TEXT_VERT_ADJUST */ nullptr,
3362 /* RES_BACKGROUND_FULL_SIZE */ nullptr,
3363 /* RES_RTL_GUTTER */ nullptr,
3364 /* RES_DECORATIVE */ nullptr,
3366 /* RES_GRFATR_MIRRORGRF */ nullptr,
3367 /* RES_GRFATR_CROPGRF */ nullptr,
3368 /* RES_GRFATR_ROTATION */ nullptr,
3369 /* RES_GRFATR_LUMINANCE */ nullptr,
3370 /* RES_GRFATR_CONTRAST */ nullptr,
3371 /* RES_GRFATR_CHANNELR */ nullptr,
3372 /* RES_GRFATR_CHANNELG */ nullptr,
3373 /* RES_GRFATR_CHANNELB */ nullptr,
3374 /* RES_GRFATR_GAMMA */ nullptr,
3375 /* RES_GRFATR_INVERT */ nullptr,
3376 /* RES_GRFATR_TRANSPARENCY */ nullptr,
3377 /* RES_GRFATR_DRWAMODE */ nullptr,
3378 /* RES_GRFATR_DUMMY3 */ nullptr,
3379 /* RES_GRFATR_DUMMY4 */ nullptr,
3380 /* RES_GRFATR_DUMMY5 */ nullptr,
3382 /* RES_BOXATR_FORMAT */ nullptr,
3383 /* RES_BOXATR_FORMULA */ nullptr,
3384 /* RES_BOXATR_VALUE */ nullptr
3387 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */