1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <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>
77 
78 #include <svtools/HtmlWriter.hxx>
79 
80 #include <memory>
81 #include <algorithm>
82 
83 using namespace css;
84 
85 HTMLOutEvent const aAnchorEventTable[] =
86 {
87     { OOO_STRING_SVTOOLS_HTML_O_SDonclick,      OOO_STRING_SVTOOLS_HTML_O_onclick,      SvMacroItemId::OnClick },
88     { OOO_STRING_SVTOOLS_HTML_O_SDonmouseover,  OOO_STRING_SVTOOLS_HTML_O_onmouseover,  SvMacroItemId::OnMouseOver  },
89     { OOO_STRING_SVTOOLS_HTML_O_SDonmouseout,   OOO_STRING_SVTOOLS_HTML_O_onmouseout,   SvMacroItemId::OnMouseOut   },
90     { nullptr, nullptr, SvMacroItemId::NONE }
91 };
92 
93 static Writer& OutHTML_SvxAdjust( Writer& rWrt, const SfxPoolItem& rHt );
94 
GetDefListLvl(const OUString & rNm,sal_uInt16 nPoolId)95 sal_uInt16 SwHTMLWriter::GetDefListLvl( const OUString& rNm, sal_uInt16 nPoolId )
96 {
97     if( nPoolId == RES_POOLCOLL_HTML_DD )
98     {
99         return 1 | HTML_DLCOLL_DD;
100     }
101     else if( nPoolId == RES_POOLCOLL_HTML_DT )
102     {
103         return 1 | HTML_DLCOLL_DT;
104     }
105 
106     OUString sDTDD = OOO_STRING_SVTOOLS_HTML_dt " ";
107     if( rNm.startsWith(sDTDD) )
108         // DefinitionList - term
109         return o3tl::narrowing<sal_uInt16>(rNm.copy( sDTDD.getLength() ).toInt32()) | HTML_DLCOLL_DT;
110 
111     sDTDD = OOO_STRING_SVTOOLS_HTML_dd " ";
112     if( rNm.startsWith(sDTDD) )
113         // DefinitionList - definition
114         return o3tl::narrowing<sal_uInt16>(rNm.copy( sDTDD.getLength() ).toInt32()) | HTML_DLCOLL_DD;
115 
116     return 0;
117 }
118 
OutAndSetDefList(sal_uInt16 nNewLvl)119 void SwHTMLWriter::OutAndSetDefList( sal_uInt16 nNewLvl )
120 {
121     // possibly, we first need to start a new list
122     if( m_nDefListLvl < nNewLvl )
123     {
124         // output </pre> for the previous(!) paragraph, if required.
125         // Preferable, the <pre> is exported by OutHTML_SwFormatOff for the
126         // previous  paragraph already, but that's not possible, because a very
127         // deep look at the next paragraph (this one) is required to figure
128         // out that a def list starts here.
129 
130         ChangeParaToken( HtmlTokenId::NONE );
131 
132         // write according to the level difference
133         for( sal_uInt16 i=m_nDefListLvl; i<nNewLvl; i++ )
134         {
135             if( m_bLFPossible )
136                 OutNewLine();
137             HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist) );
138             IncIndentLevel();
139             m_bLFPossible = true;
140         }
141     }
142     else if( m_nDefListLvl > nNewLvl )
143     {
144         for( sal_uInt16 i=nNewLvl ; i < m_nDefListLvl; i++ )
145         {
146             DecIndentLevel();
147             if( m_bLFPossible )
148                 OutNewLine();
149             HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist), false );
150             m_bLFPossible = true;
151         }
152     }
153 
154     m_nDefListLvl = nNewLvl;
155 }
156 
ChangeParaToken(HtmlTokenId nNew)157 void SwHTMLWriter::ChangeParaToken( HtmlTokenId nNew )
158 {
159     if( nNew != m_nLastParaToken && HtmlTokenId::PREFORMTXT_ON == m_nLastParaToken )
160     {
161         HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_preformtxt), false );
162         m_bLFPossible = true;
163     }
164     m_nLastParaToken = nNew;
165 }
166 
GetCSS1ScriptForScriptType(sal_uInt16 nScriptType)167 sal_uInt16 SwHTMLWriter::GetCSS1ScriptForScriptType( sal_uInt16 nScriptType )
168 {
169     sal_uInt16 nRet = CSS1_OUTMODE_ANY_SCRIPT;
170 
171     switch( nScriptType )
172     {
173     case i18n::ScriptType::LATIN:
174         nRet = CSS1_OUTMODE_WESTERN;
175         break;
176     case i18n::ScriptType::ASIAN:
177         nRet = CSS1_OUTMODE_CJK;
178         break;
179     case i18n::ScriptType::COMPLEX:
180         nRet = CSS1_OUTMODE_CTL;
181         break;
182     }
183 
184     return nRet;
185 }
186 
187 // a single output function should be enough for all formats
188 /*
189  * Output the formats as follows
190  * - output the tag for formats for which a corresponding HTML tag exist
191  * - for all the other formats, output a paragraph tag <P> and set bUserFormat
192  * - if a paragraph alignment is set for the supplied ItemSet of the node or
193  *   for the ItemSet of the format, output an ALIGN=xxx if HTML allows it
194  * - In all cases, hard attribute is written as STYLE option.
195  *   If bUserFormat is not set, only the supplied ItemSet is considered.
196  *   Otherwise, attributes of the format are output as well.
197  */
198 
199 namespace {
200 
201 struct SwHTMLTextCollOutputInfo
202 {
203     OString aToken;        // End token to be output
204     std::unique_ptr<SfxItemSet> pItemSet;    // hard attribute
205 
206     bool bInNumberBulletList;         // in an enumerated list;
207     bool bParaPossible;         // a </P> may be output additionally
208     bool bOutPara;              // a </P> is supposed to be output
209     bool bOutDiv;               // write a </DIV>
210 
SwHTMLTextCollOutputInfo__anone1c4653c0111::SwHTMLTextCollOutputInfo211     SwHTMLTextCollOutputInfo() :
212         bInNumberBulletList( false ),
213         bParaPossible( false ),
214         bOutPara( false ),
215         bOutDiv( false )
216     {}
217 
HasParaToken__anone1c4653c0111::SwHTMLTextCollOutputInfo218     bool HasParaToken() const { return aToken.getLength()==1 && aToken[0]=='P'; }
ShouldOutputToken__anone1c4653c0111::SwHTMLTextCollOutputInfo219     bool ShouldOutputToken() const { return bOutPara || !HasParaToken(); }
220 };
221 
222 }
223 
SwHTMLFormatInfo(const SwFormat * pF,SwDoc * pDoc,SwDoc * pTemplate,bool bOutStyles,LanguageType eDfltLang,sal_uInt16 nCSS1Script)224 SwHTMLFormatInfo::SwHTMLFormatInfo( const SwFormat *pF, SwDoc *pDoc, SwDoc *pTemplate,
225                               bool bOutStyles,
226                               LanguageType eDfltLang,
227                               sal_uInt16 nCSS1Script )
228     : pFormat(pF)
229     , nLeftMargin(0)
230     , nRightMargin(0)
231     , nFirstLineIndent(0)
232     , nTopMargin(0)
233     , nBottomMargin(0)
234     , bScriptDependent( false )
235 {
236     sal_uInt16 nRefPoolId = 0;
237     // Get the selector of the format
238     sal_uInt16 nDeep = SwHTMLWriter::GetCSS1Selector( pFormat, aToken, aClass,
239                                                   nRefPoolId );
240     OSL_ENSURE( nDeep ? !aToken.isEmpty() : aToken.isEmpty(),
241             "Something seems to be wrong with this token!" );
242     OSL_ENSURE( nDeep ? nRefPoolId != 0 : nRefPoolId == 0,
243             "Something seems to be wrong with the comparison style!" );
244 
245     bool bTextColl = pFormat->Which() == RES_TXTFMTCOLL ||
246                     pFormat->Which() == RES_CONDTXTFMTCOLL;
247 
248     const SwFormat *pReferenceFormat = nullptr; // Comparison format
249     if( nDeep != 0 )
250     {
251         // It's an HTML-tag style or this style is derived from such
252         // a style.
253         if( !bOutStyles )
254         {
255             // if no styles are exported, it may be necessary to additionally
256             // write hard attribute
257             switch( nDeep )
258             {
259             case CSS1_FMT_ISTAG:
260             case CSS1_FMT_CMPREF:
261                 // for HTML-tag styles the differences to the original
262                 // (if available)
263                 pReferenceFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId,
264                                                         &pTemplate->getIDocumentStylePoolAccess() );
265                 break;
266 
267             default:
268                 // otherwise, the differences to the HTML-tag style of the
269                 // original or the ones to the current document, if it the
270                 // HTML-tag style is not available
271                 if( pTemplate )
272                     pReferenceFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId,
273                                                             &pTemplate->getIDocumentStylePoolAccess() );
274                 else
275                     pReferenceFormat = SwHTMLWriter::GetParentFormat( *pFormat, nDeep );
276                 break;
277             }
278         }
279     }
280     else if( bTextColl )
281     {
282         // HTML-tag styles that are not derived from a paragraph style
283         // must be exported as hard attribute relative to the text-body
284         // style. For a 'not-styles' export, the one of the HTML style
285         // should be used as a reference
286         if( !bOutStyles && pTemplate )
287             pReferenceFormat = pTemplate->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false );
288         else
289             pReferenceFormat = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false );
290     }
291 
292     if( pReferenceFormat || nDeep==0 )
293     {
294         pItemSet.reset( new SfxItemSet( *pFormat->GetAttrSet().GetPool(),
295                                        pFormat->GetAttrSet().GetRanges() ) );
296         // if the differences to a different style are supposed to be
297         // written, hard attribute is necessary. This is always true
298         // for styles that are not derived from HTML-tag styles.
299 
300         pItemSet->Set( pFormat->GetAttrSet() );
301 
302         if( pReferenceFormat )
303             SwHTMLWriter::SubtractItemSet( *pItemSet, pReferenceFormat->GetAttrSet(), true );
304 
305         // delete ItemSet that is empty straight away. This will save work
306         // later on
307         if( !pItemSet->Count() )
308         {
309             pItemSet.reset();
310         }
311     }
312 
313     if( !bTextColl )
314         return;
315 
316     if( bOutStyles )
317     {
318         // We have to add hard attributes for any script dependent
319         // item that is not accessed by the style
320         static const sal_uInt16 aWhichIds[3][4] =
321         {
322             { RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
323                 RES_CHRATR_POSTURE, RES_CHRATR_WEIGHT },
324             { RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
325                 RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT },
326             { RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE,
327                 RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT }
328         };
329 
330         sal_uInt16 nRef = 0;
331         sal_uInt16 aSets[2] = {0,0};
332         switch( nCSS1Script )
333         {
334         case CSS1_OUTMODE_WESTERN:
335             nRef = 0;
336             aSets[0] = 1;
337             aSets[1] = 2;
338             break;
339         case CSS1_OUTMODE_CJK:
340             nRef = 1;
341             aSets[0] = 0;
342             aSets[1] = 2;
343             break;
344         case CSS1_OUTMODE_CTL:
345             nRef = 2;
346             aSets[0] = 0;
347             aSets[1] = 1;
348             break;
349         }
350         for( int i=0; i<4; ++i )
351         {
352             const SfxPoolItem& rRef = pFormat->GetFormatAttr( aWhichIds[nRef][i] );
353             for(sal_uInt16 nSet : aSets)
354             {
355                 const SfxPoolItem& rSet = pFormat->GetFormatAttr( aWhichIds[nSet][i] );
356                 if( rSet != rRef )
357                 {
358                     if( !pItemSet )
359                         pItemSet.reset( new SfxItemSet( *pFormat->GetAttrSet().GetPool(),
360                                                    pFormat->GetAttrSet().GetRanges() ) );
361                     pItemSet->Put( rSet );
362                 }
363             }
364         }
365     }
366 
367     // remember all the different default spacings from the style or
368     // the comparison style.
369     const SvxLRSpaceItem &rLRSpace =
370         (pReferenceFormat ? pReferenceFormat : pFormat)->GetLRSpace();
371     nLeftMargin = rLRSpace.GetTextLeft();
372     nRightMargin = rLRSpace.GetRight();
373     nFirstLineIndent = rLRSpace.GetTextFirstLineOffset();
374 
375     const SvxULSpaceItem &rULSpace =
376         (pReferenceFormat ? pReferenceFormat : pFormat)->GetULSpace();
377     nTopMargin = rULSpace.GetUpper();
378     nBottomMargin = rULSpace.GetLower();
379 
380     // export language if it differs from the default language
381     sal_uInt16 nWhichId =
382         SwHTMLWriter::GetLangWhichIdFromScript( nCSS1Script );
383     const SvxLanguageItem& rLang =
384         static_cast<const SvxLanguageItem&>(pFormat->GetFormatAttr( nWhichId ));
385     LanguageType eLang = rLang.GetLanguage();
386     if( eLang != eDfltLang )
387     {
388         if( !pItemSet )
389             pItemSet.reset( new SfxItemSet( *pFormat->GetAttrSet().GetPool(),
390                                        pFormat->GetAttrSet().GetRanges() ) );
391         pItemSet->Put( rLang );
392     }
393 
394     static const sal_uInt16 aWhichIds[3] =
395         { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE,
396             RES_CHRATR_CTL_LANGUAGE };
397     for(sal_uInt16 i : aWhichIds)
398     {
399         if( i != nWhichId )
400         {
401             const SvxLanguageItem& rTmpLang =
402                 static_cast<const SvxLanguageItem&>(pFormat->GetFormatAttr(i));
403             if( rTmpLang.GetLanguage() != eLang )
404             {
405                 if( !pItemSet )
406                     pItemSet.reset( new SfxItemSet( *pFormat->GetAttrSet().GetPool(),
407                                                pFormat->GetAttrSet().GetRanges() ) );
408                 pItemSet->Put( rTmpLang );
409             }
410         }
411     }
412 
413 }
414 
~SwHTMLFormatInfo()415 SwHTMLFormatInfo::~SwHTMLFormatInfo()
416 {
417 }
418 
OutHTML_SwFormat(Writer & rWrt,const SwFormat & rFormat,const SfxItemSet * pNodeItemSet,SwHTMLTextCollOutputInfo & rInfo)419 static void OutHTML_SwFormat( Writer& rWrt, const SwFormat& rFormat,
420                     const SfxItemSet *pNodeItemSet,
421                     SwHTMLTextCollOutputInfo& rInfo )
422 {
423     OSL_ENSURE( RES_CONDTXTFMTCOLL==rFormat.Which() || RES_TXTFMTCOLL==rFormat.Which(),
424             "not a paragraph style" );
425 
426     SwHTMLWriter & rHWrt = static_cast<SwHTMLWriter&>(rWrt);
427 
428     // First, some flags
429     sal_uInt16 nNewDefListLvl = 0;
430     sal_uInt16 nNumStart = USHRT_MAX;
431     bool bForceDL = false;
432     bool bDT = false;
433     rInfo.bInNumberBulletList = false;    // Are we in a list?
434     bool bNumbered = false;         // The current paragraph is numbered
435     bool bPara = false;             // the current token is <P>
436     rInfo.bParaPossible = false;    // a <P> may be additionally output
437     bool bNoEndTag = false;         // don't output an end tag
438 
439     rHWrt.m_bNoAlign = false;       // no ALIGN=... possible
440 
441     if (rHWrt.mbXHTML)
442     {
443         rHWrt.m_bNoAlign = true;
444     }
445 
446     sal_uInt8 nBulletGrfLvl = 255;  // The bullet graphic we want to output
447 
448     // Are we in a bulleted or numbered list?
449     const SwTextNode* pTextNd = rWrt.m_pCurrentPam->GetNode().GetTextNode();
450 
451     SwHTMLNumRuleInfo aNumInfo;
452     if( rHWrt.GetNextNumInfo() )
453     {
454         aNumInfo = *rHWrt.GetNextNumInfo();
455         rHWrt.ClearNextNumInfo();
456     }
457     else
458     {
459         aNumInfo.Set( *pTextNd );
460     }
461 
462     if( aNumInfo.GetNumRule() )
463     {
464         rInfo.bInNumberBulletList = true;
465         nNewDefListLvl = 0;
466 
467         // is the current paragraph numbered?
468         bNumbered = aNumInfo.IsNumbered();
469         sal_uInt8 nLvl = aNumInfo.GetLevel();
470 
471         OSL_ENSURE( pTextNd->GetActualListLevel() == nLvl,
472                 "Remembered Num level is wrong" );
473         OSL_ENSURE( bNumbered == pTextNd->IsCountedInList(),
474                 "Remembered numbering state is wrong" );
475 
476         if( bNumbered )
477         {
478             nBulletGrfLvl = nLvl; // only temporarily!!!
479             // #i57919#
480             // correction of re-factoring done by cws swnumtree:
481             // - <nNumStart> has to contain the restart value, if the
482             //   numbering is restarted at this text node. Value <USHRT_MAX>
483             //   indicates, that no additional restart value has to be written.
484             if ( pTextNd->IsListRestart() )
485             {
486                 nNumStart = static_cast< sal_uInt16 >(pTextNd->GetActualListStartValue());
487             }
488             OSL_ENSURE( rHWrt.m_nLastParaToken == HtmlTokenId::NONE,
489                 "<PRE> was not closed before <LI>." );
490         }
491     }
492 
493     // Now, we're getting the token and, if necessary, the class
494     std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(&rFormat));
495     SwHTMLFormatInfo *pFormatInfo;
496     SwHTMLFormatInfos::iterator it = rHWrt.m_TextCollInfos.find( pTmpInfo );
497     if (it != rHWrt.m_TextCollInfos.end())
498     {
499         pFormatInfo = it->get();
500     }
501     else
502     {
503         pFormatInfo = new SwHTMLFormatInfo( &rFormat, rWrt.m_pDoc, rHWrt.m_xTemplate.get(),
504                                       rHWrt.m_bCfgOutStyles, rHWrt.m_eLang,
505                                       rHWrt.m_nCSS1Script );
506         rHWrt.m_TextCollInfos.insert(std::unique_ptr<SwHTMLFormatInfo>(pFormatInfo));
507         if( rHWrt.m_aScriptParaStyles.count( rFormat.GetName() ) )
508             pFormatInfo->bScriptDependent = true;
509     }
510 
511     // Now, we define what is possible due to the token
512     HtmlTokenId nToken = HtmlTokenId::NONE;          // token for tag change
513     bool bOutNewLine = false;   // only output a single LF?
514     if( !pFormatInfo->aToken.isEmpty() )
515     {
516         // It is an HTML-tag style or the style is derived from such a
517         // style.
518         rInfo.aToken = pFormatInfo->aToken;
519 
520         if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_address)
521         {
522             rInfo.bParaPossible = true;
523             rHWrt.m_bNoAlign = true;
524         }
525         else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_blockquote)
526         {
527             rInfo.bParaPossible = true;
528             rHWrt.m_bNoAlign = true;
529         }
530         else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_parabreak)
531         {
532             bPara = true;
533         }
534         else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_preformtxt)
535         {
536             if (HtmlTokenId::PREFORMTXT_ON == rHWrt.m_nLastParaToken)
537             {
538                 bOutNewLine = true;
539             }
540             else
541             {
542                 nToken = HtmlTokenId::PREFORMTXT_ON;
543                 rHWrt.m_bNoAlign = true;
544                 bNoEndTag = true;
545             }
546         }
547         else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dt || rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dd)
548         {
549             bDT = rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dt;
550             rInfo.bParaPossible = !bDT;
551             rHWrt.m_bNoAlign = true;
552             bForceDL = true;
553         }
554     }
555     else
556     {
557         // all styles that do not correspond to an HTML tag, or that are
558         // not derived from it, are exported as <P>
559 
560         rInfo.aToken = OOO_STRING_SVTOOLS_HTML_parabreak;
561         bPara = true;
562     }
563 
564     // If necessary, take the hard attribute from the style
565     if( pFormatInfo->pItemSet )
566     {
567         OSL_ENSURE(!rInfo.pItemSet, "Where does this ItemSet come from?");
568         rInfo.pItemSet.reset(new SfxItemSet( *pFormatInfo->pItemSet ));
569     }
570 
571     // additionally, add the hard attribute from the paragraph
572     if( pNodeItemSet )
573     {
574         if (rInfo.pItemSet)
575             rInfo.pItemSet->Put( *pNodeItemSet );
576         else
577             rInfo.pItemSet.reset(new SfxItemSet( *pNodeItemSet ));
578     }
579 
580     // we will need the lower spacing of the paragraph later on
581     const SvxULSpaceItem& rULSpace =
582         pNodeItemSet ? pNodeItemSet->Get(RES_UL_SPACE)
583                      : rFormat.GetULSpace();
584 
585     if( (rHWrt.m_bOutHeader &&
586          rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() ==
587             rWrt.m_pCurrentPam->GetMark()->nNode.GetIndex()) ||
588          rHWrt.m_bOutFooter )
589     {
590         if( rHWrt.m_bCfgOutStyles )
591         {
592             SvxULSpaceItem aULSpaceItem( rULSpace );
593             if( rHWrt.m_bOutHeader )
594                 aULSpaceItem.SetLower( rHWrt.m_nHeaderFooterSpace );
595             else
596                 aULSpaceItem.SetUpper( rHWrt.m_nHeaderFooterSpace );
597 
598             if (!rInfo.pItemSet)
599             {
600                 rInfo.pItemSet.reset(new SfxItemSet(*rFormat.GetAttrSet().GetPool(), svl::Items<RES_UL_SPACE, RES_UL_SPACE>{}));
601             }
602             rInfo.pItemSet->Put( aULSpaceItem );
603         }
604         rHWrt.m_bOutHeader = false;
605         rHWrt.m_bOutFooter = false;
606     }
607 
608     if( bOutNewLine )
609     {
610         // output a line break (without indentation) at the beginning of the
611         // paragraph, only
612         rInfo.aToken.clear();   // don't output an end tag
613         rWrt.Strm().WriteCharPtr( SAL_NEWLINE_STRING );
614 
615         return;
616     }
617 
618     // should an ALIGN=... be written?
619     const SfxPoolItem* pAdjItem = nullptr;
620     const SfxPoolItem* pItem;
621 
622     if( rInfo.pItemSet &&
623         SfxItemState::SET == rInfo.pItemSet->GetItemState( RES_PARATR_ADJUST,
624                                                       false, &pItem ) )
625     {
626         pAdjItem = pItem;
627     }
628 
629     // Consider the lower spacing of the paragraph? (never in the last
630     // paragraph of tables)
631     bool bUseParSpace = !rHWrt.m_bOutTable ||
632                         (rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() !=
633                          rWrt.m_pCurrentPam->GetMark()->nNode.GetIndex());
634     // If styles are exported, indented paragraphs become definition lists
635     const SvxLRSpaceItem& rLRSpace =
636         pNodeItemSet ? pNodeItemSet->Get(RES_LR_SPACE)
637                      : rFormat.GetLRSpace();
638     if( (!rHWrt.m_bCfgOutStyles || bForceDL) && !rInfo.bInNumberBulletList )
639     {
640         sal_Int32 nLeftMargin;
641         if( bForceDL )
642             nLeftMargin = rLRSpace.GetTextLeft();
643         else
644             nLeftMargin = rLRSpace.GetTextLeft() > pFormatInfo->nLeftMargin
645                 ? rLRSpace.GetTextLeft() - pFormatInfo->nLeftMargin
646                 : 0;
647 
648         if( nLeftMargin > 0 && rHWrt.m_nDefListMargin > 0 )
649         {
650             nNewDefListLvl = static_cast< sal_uInt16 >((nLeftMargin + (rHWrt.m_nDefListMargin/2)) /
651                                                     rHWrt.m_nDefListMargin);
652             if( nNewDefListLvl == 0 && bForceDL && !bDT )
653                 nNewDefListLvl = 1;
654         }
655         else
656         {
657             // If the left margin is 0 or negative, emulating indent
658             // with <dd> does not work. We then set a def list only if
659             // the dd style is used.
660             nNewDefListLvl = (bForceDL&& !bDT) ? 1 : 0;
661         }
662 
663         bool bIsNextTextNode =
664             rWrt.m_pDoc->GetNodes()[rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex()+1]
665                      ->IsTextNode();
666 
667         if( bForceDL && bDT )
668         {
669             // Instead of a DD we must use a DT from the level above this one.
670             nNewDefListLvl++;
671         }
672         else if( !nNewDefListLvl && !rHWrt.m_bCfgOutStyles && bPara &&
673                  rULSpace.GetLower()==0 &&
674                  ((bUseParSpace && bIsNextTextNode) || rHWrt.m_nDefListLvl==1) &&
675                  (!pAdjItem || SvxAdjust::Left==
676                     static_cast<const SvxAdjustItem *>(pAdjItem)->GetAdjust()) )
677         {
678             // Export paragraphs without a lower spacing as DT
679             nNewDefListLvl = 1;
680             bDT = true;
681             rInfo.bParaPossible = false;
682             rHWrt.m_bNoAlign = true;
683         }
684     }
685 
686     if( nNewDefListLvl != rHWrt.m_nDefListLvl )
687         rHWrt.OutAndSetDefList( nNewDefListLvl );
688 
689     // if necessary, start a bulleted or numbered list
690     if( rInfo.bInNumberBulletList )
691     {
692         OSL_ENSURE( !rHWrt.m_nDefListLvl, "DL cannot be inside OL!" );
693         OutHTML_NumberBulletListStart( rHWrt, aNumInfo );
694 
695         if( bNumbered )
696         {
697             if( !rHWrt.m_aBulletGrfs[nBulletGrfLvl].isEmpty()  )
698                 bNumbered = false;
699             else
700                 nBulletGrfLvl = 255;
701         }
702     }
703 
704     // Take the defaults of the style, because they don't need to be
705     // exported
706     rHWrt.m_nDfltLeftMargin = pFormatInfo->nLeftMargin;
707     rHWrt.m_nDfltRightMargin = pFormatInfo->nRightMargin;
708     rHWrt.m_nDfltFirstLineIndent = pFormatInfo->nFirstLineIndent;
709 
710     if( rInfo.bInNumberBulletList )
711     {
712         if( !rHWrt.IsHTMLMode( HTMLMODE_LSPACE_IN_NUMBER_BULLET ) )
713             rHWrt.m_nDfltLeftMargin = rLRSpace.GetTextLeft();
714 
715         // In numbered lists, don't output a first line indent.
716         rHWrt.m_nFirstLineIndent = rLRSpace.GetTextFirstLineOffset();
717     }
718 
719     if( rInfo.bInNumberBulletList && bNumbered && bPara && !rHWrt.m_bCfgOutStyles )
720     {
721         // a single LI doesn't have spacing
722         rHWrt.m_nDfltTopMargin = 0;
723         rHWrt.m_nDfltBottomMargin = 0;
724     }
725     else if( rHWrt.m_nDefListLvl && bPara )
726     {
727         // a single DD doesn't have spacing, as well
728         rHWrt.m_nDfltTopMargin = 0;
729         rHWrt.m_nDfltBottomMargin = 0;
730     }
731     else
732     {
733         rHWrt.m_nDfltTopMargin = pFormatInfo->nTopMargin;
734         // if in the last paragraph of a table the lower paragraph spacing
735         // is changed, Netscape doesn't get it. That's why we don't
736         // export anything here for now, by setting this spacing to the
737         // default value.
738         if( rHWrt.m_bCfgNetscape4 && !bUseParSpace )
739             rHWrt.m_nDfltBottomMargin = rULSpace.GetLower();
740         else
741             rHWrt.m_nDfltBottomMargin = pFormatInfo->nBottomMargin;
742     }
743 
744     if( rHWrt.m_nDefListLvl )
745     {
746         rHWrt.m_nLeftMargin =
747             (rHWrt.m_nDefListLvl-1) * rHWrt.m_nDefListMargin;
748     }
749 
750     if( rHWrt.m_bLFPossible && !rHWrt.m_bFirstLine )
751         rHWrt.OutNewLine(); // paragraph tag on a new line
752     rInfo.bOutPara = false;
753 
754     // this is now our new token
755     rHWrt.ChangeParaToken( nToken );
756 
757     bool bHasParSpace = bUseParSpace && rULSpace.GetLower() > 0;
758     // XHTML doesn't allow character children for <blockquote>.
759     bool bXhtmlBlockQuote = rHWrt.mbXHTML && rInfo.aToken == OOO_STRING_SVTOOLS_HTML_blockquote;
760 
761     // if necessary, start a new list item
762     if( rInfo.bInNumberBulletList && bNumbered )
763     {
764         HtmlWriter html(rWrt.Strm(), rHWrt.maNamespace);
765         html.start(OOO_STRING_SVTOOLS_HTML_li);
766         if( USHRT_MAX != nNumStart )
767             html.attribute(OOO_STRING_SVTOOLS_HTML_O_value, OString::number(nNumStart));
768         // Finish the opening element, but don't close it.
769         html.characters("");
770     }
771 
772     if( rHWrt.m_nDefListLvl > 0 && !bForceDL )
773     {
774         OString aTag = bDT ? OOO_STRING_SVTOOLS_HTML_dt : OOO_STRING_SVTOOLS_HTML_dd;
775         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHWrt.GetNamespace() + aTag) );
776     }
777 
778     if( pAdjItem &&
779         rHWrt.IsHTMLMode( HTMLMODE_NO_CONTROL_CENTERING ) &&
780         rHWrt.HasControls() )
781     {
782         // The align=... attribute does behave strange in netscape
783         // if there are controls in a paragraph, because the control and
784         // all text behind the control does not recognize this attribute.
785         OString sOut = "<" + rHWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division;
786         rWrt.Strm().WriteOString( sOut );
787 
788         rHWrt.m_bTextAttr = false;
789         rHWrt.m_bOutOpts = true;
790         OutHTML_SvxAdjust( rWrt, *pAdjItem );
791         rWrt.Strm().WriteChar( '>' );
792         pAdjItem = nullptr;
793         rHWrt.m_bNoAlign = false;
794         rInfo.bOutDiv = true;
795         rHWrt.IncIndentLevel();
796         rHWrt.m_bLFPossible = true;
797         rHWrt.OutNewLine();
798     }
799 
800     // for BLOCKQUOTE, ADDRESS and DD we output another paragraph token, if
801     // - no styles are written and
802     // - a lower spacing or a paragraph alignment exists
803     // Also, XHTML does not allow character children in this context.
804     OString aToken = rInfo.aToken;
805     if( (!rHWrt.m_bCfgOutStyles || rHWrt.mbXHTML) && rInfo.bParaPossible && !bPara &&
806         (bHasParSpace || bXhtmlBlockQuote || pAdjItem) )
807     {
808         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHWrt.GetNamespace() + rInfo.aToken) );
809         aToken = OOO_STRING_SVTOOLS_HTML_parabreak;
810         bPara = true;
811         rHWrt.m_bNoAlign = false;
812     }
813 
814     LanguageType eLang;
815     if (rInfo.pItemSet)
816         eLang = static_cast<const SvxLanguageItem&>(rInfo.pItemSet->Get(SwHTMLWriter::GetLangWhichIdFromScript(rHWrt.m_nCSS1Script))).GetLanguage();
817     else
818         eLang = rHWrt.m_eLang;
819 
820     if( rInfo.pItemSet )
821     {
822         static const sal_uInt16 aWhichIds[3] = { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE };
823 
824         for(sal_uInt16 i : aWhichIds)
825         {
826             // export language if it differs from the default language only.
827             const SfxPoolItem *pTmpItem;
828             if( SfxItemState::SET == rInfo.pItemSet->GetItemState( i,
829                         true, &pTmpItem ) &&
830                 static_cast<const SvxLanguageItem *>(pTmpItem)->GetLanguage() == eLang )
831                 rInfo.pItemSet->ClearItem( i );
832         }
833     }
834 
835     // and the text direction
836     SvxFrameDirection nDir = rHWrt.GetHTMLDirection(
837             (pNodeItemSet ? pNodeItemSet->Get( RES_FRAMEDIR )
838                           : rFormat.GetFrameDir() ).GetValue() );
839 
840     // We only write a <P>, if
841     // - we are not inside OL/UL/DL, or
842     // - the paragraph of an OL/UL is not numbered or
843     // - styles are not exported and
844     //      - a lower spacing, or
845     //      - a paragraph alignment exists, or
846     // - styles are exported and
847     //      - the text body style was changed, or
848     //      - a user format is exported, or
849     //      - a paragraph attribute exists
850     if( !bPara ||
851         (!rInfo.bInNumberBulletList && !rHWrt.m_nDefListLvl) ||
852         (rInfo.bInNumberBulletList && !bNumbered) ||
853         (!rHWrt.m_bCfgOutStyles &&
854          (bHasParSpace || bXhtmlBlockQuote || pAdjItem ||
855           (eLang != LANGUAGE_DONTKNOW && eLang != rHWrt.m_eLang))) ||
856         nDir != rHWrt.m_nDirection ||
857         rHWrt.m_bCfgOutStyles )
858     {
859         // now, options are output
860         rHWrt.m_bTextAttr = false;
861         rHWrt.m_bOutOpts = true;
862 
863         OString sOut = "<" + rHWrt.GetNamespace() + aToken;
864 
865         if( eLang != LANGUAGE_DONTKNOW && eLang != rHWrt.m_eLang )
866         {
867             rWrt.Strm().WriteOString( sOut );
868             sOut = "";
869             rHWrt.OutLanguage( eLang );
870         }
871 
872         if( nDir != rHWrt.m_nDirection )
873         {
874             if( !sOut.isEmpty() )
875             {
876                 rWrt.Strm().WriteOString( sOut );
877                 sOut = "";
878             }
879             rHWrt.OutDirection( nDir );
880         }
881 
882         if( rHWrt.m_bCfgOutStyles &&
883             (!pFormatInfo->aClass.isEmpty() || pFormatInfo->bScriptDependent) )
884         {
885             sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"";
886             rWrt.Strm().WriteOString( sOut );
887             sOut = "";
888             OUString aClass( pFormatInfo->aClass );
889             if( pFormatInfo->bScriptDependent )
890             {
891                 if( !aClass.isEmpty() )
892                    aClass += "-";
893                 switch( rHWrt.m_nCSS1Script )
894                 {
895                 case CSS1_OUTMODE_WESTERN:
896                     aClass += "western";
897                     break;
898                 case CSS1_OUTMODE_CJK:
899                     aClass += "cjk";
900                     break;
901                 case CSS1_OUTMODE_CTL:
902                     aClass += "ctl";
903                     break;
904                 }
905             }
906             HTMLOutFuncs::Out_String( rWrt.Strm(), aClass,
907                                       rHWrt.m_eDestEnc, &rHWrt.m_aNonConvertableCharacters );
908             sOut += "\"";
909         }
910         rWrt.Strm().WriteOString( sOut );
911         sOut = "";
912 
913         // if necessary, output alignment
914         if( !rHWrt.m_bNoAlign && pAdjItem )
915             OutHTML_SvxAdjust( rWrt, *pAdjItem );
916 
917         rHWrt.m_bParaDotLeaders = bPara && rHWrt.m_bCfgPrintLayout && rHWrt.indexOfDotLeaders(
918                 pTextNd->GetAnyFormatColl().GetPoolFormatId(), pTextNd->GetText()) > -1;
919 
920         // and now, if necessary, the STYLE options
921         if (rHWrt.m_bCfgOutStyles && rInfo.pItemSet)
922         {
923             OutCSS1_ParaTagStyleOpt( rWrt, *rInfo.pItemSet );
924         }
925 
926         if (rHWrt.m_bParaDotLeaders) {
927             sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\""
928                 sCSS2_P_CLASS_leaders "\"><"
929                 OOO_STRING_SVTOOLS_HTML_O_span;
930             rWrt.Strm().WriteOString( sOut );
931             sOut = "";
932         }
933 
934         rWrt.Strm().WriteChar( '>' );
935 
936         // is a </P> supposed to be written?
937         rInfo.bOutPara =
938             bPara &&
939             ( rHWrt.m_bCfgOutStyles || bHasParSpace );
940 
941         // if no end tag is supposed to be written, delete it
942         if( bNoEndTag )
943             rInfo.aToken.clear();
944     }
945 
946     if( nBulletGrfLvl != 255 )
947     {
948         OSL_ENSURE( aNumInfo.GetNumRule(), "Where is the numbering gone???" );
949         OSL_ENSURE( nBulletGrfLvl < MAXLEVEL, "There are not this many layers." );
950         const SwNumFormat& rNumFormat = aNumInfo.GetNumRule()->Get(nBulletGrfLvl);
951         OutHTML_BulletImage( rWrt, OOO_STRING_SVTOOLS_HTML_image, rNumFormat.GetBrush(),
952                 rHWrt.m_aBulletGrfs[nBulletGrfLvl]);
953     }
954 
955     rHWrt.GetNumInfo() = aNumInfo;
956 
957     // reset the defaults
958     rHWrt.m_nDfltLeftMargin = 0;
959     rHWrt.m_nDfltRightMargin = 0;
960     rHWrt.m_nDfltFirstLineIndent = 0;
961     rHWrt.m_nDfltTopMargin = 0;
962     rHWrt.m_nDfltBottomMargin = 0;
963     rHWrt.m_nLeftMargin = 0;
964     rHWrt.m_nFirstLineIndent = 0;
965 }
966 
OutHTML_SwFormatOff(Writer & rWrt,const SwHTMLTextCollOutputInfo & rInfo)967 static void OutHTML_SwFormatOff( Writer& rWrt, const SwHTMLTextCollOutputInfo& rInfo )
968 {
969     SwHTMLWriter & rHWrt = static_cast<SwHTMLWriter&>(rWrt);
970 
971     // if there is no token, we don't need to output anything
972     if( rInfo.aToken.isEmpty() )
973     {
974         rHWrt.FillNextNumInfo();
975         const SwHTMLNumRuleInfo& rNextInfo = *rHWrt.GetNextNumInfo();
976         // a bulleted list must be closed in PRE as well
977         if( rInfo.bInNumberBulletList )
978         {
979 
980             const SwHTMLNumRuleInfo& rNRInfo = rHWrt.GetNumInfo();
981             if( rNextInfo.GetNumRule() != rNRInfo.GetNumRule() ||
982                 rNextInfo.GetDepth() != rNRInfo.GetDepth() ||
983                 rNextInfo.IsNumbered() || rNextInfo.IsRestart() )
984                 rHWrt.ChangeParaToken( HtmlTokenId::NONE );
985             OutHTML_NumberBulletListEnd( rHWrt, rNextInfo );
986         }
987         else if( rNextInfo.GetNumRule() != nullptr )
988             rHWrt.ChangeParaToken( HtmlTokenId::NONE );
989 
990         return;
991     }
992 
993     if( rInfo.ShouldOutputToken() )
994     {
995         if( rHWrt.m_bLFPossible )
996             rHWrt.OutNewLine( true );
997 
998         // if necessary, for BLOCKQUOTE, ADDRESS and DD another paragraph token
999         // is output, if
1000         // - no styles are written and
1001         // - a lower spacing exists
1002         if( rInfo.bParaPossible && rInfo.bOutPara )
1003             HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_parabreak), false );
1004 
1005         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHWrt.GetNamespace() + rInfo.aToken), false );
1006         rHWrt.m_bLFPossible =
1007             rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dt &&
1008             rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dd &&
1009             rInfo.aToken != OOO_STRING_SVTOOLS_HTML_li;
1010     }
1011     if( rInfo.bOutDiv )
1012     {
1013         rHWrt.DecIndentLevel();
1014         if( rHWrt.m_bLFPossible )
1015             rHWrt.OutNewLine();
1016         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
1017         rHWrt.m_bLFPossible = true;
1018     }
1019 
1020     // if necessary, close the list item, then close a bulleted or numbered list
1021     if( rInfo.bInNumberBulletList )
1022     {
1023         rHWrt.FillNextNumInfo();
1024         OutHTML_NumberBulletListEnd( rHWrt, *rHWrt.GetNextNumInfo() );
1025     }
1026 }
1027 
1028 namespace {
1029 
1030 class HTMLStartEndPos
1031 {
1032     sal_Int32 m_nStart;
1033     sal_Int32 m_nEnd;
1034     std::unique_ptr<SfxPoolItem> m_pItem;
1035 
1036 public:
1037 
1038     HTMLStartEndPos( const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE );
1039 
GetItem() const1040     const SfxPoolItem* GetItem() const { return m_pItem.get(); }
1041 
SetStart(sal_Int32 nStt)1042     void SetStart(sal_Int32 nStt) { m_nStart = nStt; }
GetStart() const1043     sal_Int32 GetStart() const { return m_nStart; }
1044 
GetEnd() const1045     sal_Int32 GetEnd() const { return m_nEnd; }
SetEnd(sal_Int32 nE)1046     void SetEnd(sal_Int32 nE) { m_nEnd = nE; }
1047 };
1048 
1049 }
1050 
HTMLStartEndPos(const SfxPoolItem & rItem,sal_Int32 nStt,sal_Int32 nE)1051 HTMLStartEndPos::HTMLStartEndPos(const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE)
1052     : m_nStart(nStt)
1053     , m_nEnd(nE)
1054     , m_pItem(rItem.Clone())
1055 {}
1056 
1057 typedef std::vector<HTMLStartEndPos *> HTMLStartEndPositions;
1058 
1059 namespace {
1060 
1061 enum HTMLOnOffState { HTML_NOT_SUPPORTED,   // unsupported Attribute
1062                       HTML_REAL_VALUE,      // Attribute with value
1063                       HTML_ON_VALUE,        // Attribute is On-Tag
1064                       HTML_OFF_VALUE,       // Attribute is Off-Tag
1065                       HTML_CHRFMT_VALUE,    // Attribute for character format
1066                       HTML_COLOR_VALUE,     // Attribute for foreground color
1067                       HTML_STYLE_VALUE,     // Attribute must be exported as style
1068                       HTML_DROPCAP_VALUE,   // DropCap-Attribute
1069                       HTML_AUTOFMT_VALUE }; // Attribute for automatic character styles
1070 
1071 class HTMLEndPosLst
1072 {
1073     HTMLStartEndPositions m_aStartLst; // list, sorted for start positions
1074     HTMLStartEndPositions m_aEndLst; // list, sorted for end positions
1075     std::deque<sal_Int32> m_aScriptChgLst; // positions where script changes
1076         // 0 is not contained in this list,
1077         // but the text length
1078     // the script that is valid up to the position
1079     // contained in aScriptChgList at the same index
1080     std::vector<sal_uInt16> m_aScriptLst;
1081 
1082     SwDoc* m_pDoc; // the current document
1083     SwDoc* m_pTemplate; // the HTML template (or 0)
1084     std::optional<Color> m_xDefaultColor; // the default foreground colors
1085     std::set<OUString>& m_rScriptTextStyles;
1086 
1087     sal_uLong m_nHTMLMode;
1088     bool m_bOutStyles : 1; // are styles exported
1089 
1090     // Insert/remove a SttEndPos in/from the Start and End lists.
1091     // The end position is known.
1092     void InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos );
1093     void RemoveItem_( HTMLStartEndPositions::size_type nEndPos );
1094 
1095     // determine the 'type' of the attribute
1096     HTMLOnOffState GetHTMLItemState( const SfxPoolItem& rItem );
1097 
1098     // does a specific OnTag item exist
1099     bool ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos );
1100 
1101     // does an item exist that can be used to disable an attribute that
1102     // is exported the same way as the supplied item in the same range?
1103     bool ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos,
1104                                           sal_Int32 nEndPos );
1105 
1106     // adapt the end of a split item
1107     void FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
1108                             HTMLStartEndPositions::size_type nStartPos );
1109 
1110     // insert an attribute in the lists and, if necessary, split it
1111     void InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
1112                                                sal_Int32 nEnd );
1113 
1114     // split an already existing attribute
1115     void SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart,
1116                                               sal_Int32 nEnd );
1117 
1118     // Insert without taking care of script
1119     void InsertNoScript( const SfxPoolItem& rItem, sal_Int32 nStart,
1120                           sal_Int32 nEnd, SwHTMLFormatInfos& rFormatInfos,
1121                          bool bParaAttrs );
1122 
1123     const SwHTMLFormatInfo *GetFormatInfo( const SwFormat& rFormat,
1124                                      SwHTMLFormatInfos& rFormatInfos );
1125 
1126 public:
1127 
1128     HTMLEndPosLst( SwDoc *pDoc, SwDoc* pTemplate, std::optional<Color> xDfltColor,
1129                    bool bOutStyles, sal_uLong nHTMLMode,
1130                    const OUString& rText, std::set<OUString>& rStyles );
1131     ~HTMLEndPosLst();
1132 
1133     // insert an attribute
1134     void Insert( const SfxPoolItem& rItem, sal_Int32 nStart,  sal_Int32 nEnd,
1135                  SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs=false );
1136     void Insert( const SfxItemSet& rItemSet, sal_Int32 nStart, sal_Int32 nEnd,
1137                  SwHTMLFormatInfos& rFormatInfos, bool bDeep,
1138                  bool bParaAttrs=false );
1139     void Insert( const SwDrawFrameFormat& rFormat, sal_Int32 nPos,
1140                  SwHTMLFormatInfos& rFormatInfos );
1141 
1142     sal_uInt16 GetScriptAtPos( sal_Int32 nPos,
1143                                sal_uInt16 nWeak );
1144 
1145     void OutStartAttrs( SwHTMLWriter& rHWrt, sal_Int32 nPos,
1146                         HTMLOutContext *pContext = nullptr );
1147     void OutEndAttrs( SwHTMLWriter& rHWrt, sal_Int32 nPos,
1148                       HTMLOutContext *pContext );
1149 
IsHTMLMode(sal_uLong nMode) const1150     bool IsHTMLMode(sal_uLong nMode) const { return (m_nHTMLMode & nMode) != 0; }
1151 };
1152 
1153 }
1154 
InsertItem_(HTMLStartEndPos * pPos,HTMLStartEndPositions::size_type nEndPos)1155 void HTMLEndPosLst::InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos )
1156 {
1157     // Insert the attribute in the Start list behind all attributes that
1158     // were started before, or at the same position.
1159     sal_Int32 nStart = pPos->GetStart();
1160     HTMLStartEndPositions::size_type i {0};
1161 
1162     while (i < m_aStartLst.size() && m_aStartLst[i]->GetStart() <= nStart)
1163         ++i;
1164     m_aStartLst.insert(m_aStartLst.begin() + i, pPos);
1165 
1166     // the position in the End list was supplied
1167     m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
1168 }
1169 
RemoveItem_(HTMLStartEndPositions::size_type nEndPos)1170 void HTMLEndPosLst::RemoveItem_( HTMLStartEndPositions::size_type nEndPos )
1171 {
1172     HTMLStartEndPos* pPos = m_aEndLst[nEndPos];
1173 
1174     // now, we are looking for it in the Start list
1175     HTMLStartEndPositions::iterator it = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos);
1176     OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!");
1177     if (it != m_aStartLst.end())
1178         m_aStartLst.erase(it);
1179 
1180     m_aEndLst.erase(m_aEndLst.begin() + nEndPos);
1181 
1182     delete pPos;
1183 }
1184 
GetHTMLItemState(const SfxPoolItem & rItem)1185 HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const SfxPoolItem& rItem )
1186 {
1187     HTMLOnOffState eState = HTML_NOT_SUPPORTED;
1188     switch( rItem.Which() )
1189     {
1190     case RES_CHRATR_POSTURE:
1191     case RES_CHRATR_CJK_POSTURE:
1192     case RES_CHRATR_CTL_POSTURE:
1193         switch( static_cast<const SvxPostureItem&>(rItem).GetPosture() )
1194         {
1195         case ITALIC_NORMAL:
1196             eState = HTML_ON_VALUE;
1197             break;
1198         case ITALIC_NONE:
1199             eState = HTML_OFF_VALUE;
1200             break;
1201         default:
1202             if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1203                 eState = HTML_STYLE_VALUE;
1204             break;
1205         }
1206         break;
1207 
1208     case RES_CHRATR_CROSSEDOUT:
1209         switch( static_cast<const SvxCrossedOutItem&>(rItem).GetStrikeout() )
1210         {
1211         case STRIKEOUT_SINGLE:
1212         case STRIKEOUT_DOUBLE:
1213             eState = HTML_ON_VALUE;
1214             break;
1215         case STRIKEOUT_NONE:
1216             eState = HTML_OFF_VALUE;
1217             break;
1218         default:
1219             ;
1220         }
1221         break;
1222 
1223     case RES_CHRATR_ESCAPEMENT:
1224         switch( static_cast<SvxEscapement>(static_cast<const SvxEscapementItem&>(rItem).GetEnumValue()) )
1225         {
1226         case SvxEscapement::Superscript:
1227         case SvxEscapement::Subscript:
1228             eState = HTML_ON_VALUE;
1229             break;
1230         case SvxEscapement::Off:
1231             eState = HTML_OFF_VALUE;
1232             break;
1233         default:
1234             ;
1235         }
1236         break;
1237 
1238     case RES_CHRATR_UNDERLINE:
1239         switch( static_cast<const SvxUnderlineItem&>(rItem).GetLineStyle() )
1240         {
1241         case LINESTYLE_SINGLE:
1242             eState = HTML_ON_VALUE;
1243             break;
1244         case LINESTYLE_NONE:
1245             eState = HTML_OFF_VALUE;
1246             break;
1247         default:
1248             if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1249                 eState = HTML_STYLE_VALUE;
1250             break;
1251         }
1252         break;
1253 
1254     case RES_CHRATR_OVERLINE:
1255     case RES_CHRATR_HIDDEN:
1256         if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1257             eState = HTML_STYLE_VALUE;
1258         break;
1259 
1260     case RES_CHRATR_WEIGHT:
1261     case RES_CHRATR_CJK_WEIGHT:
1262     case RES_CHRATR_CTL_WEIGHT:
1263         switch( static_cast<const SvxWeightItem&>(rItem).GetWeight() )
1264         {
1265         case WEIGHT_BOLD:
1266             eState = HTML_ON_VALUE;
1267             break;
1268         case WEIGHT_NORMAL:
1269             eState = HTML_OFF_VALUE;
1270             break;
1271         default:
1272             if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1273                 eState = HTML_STYLE_VALUE;
1274             break;
1275         }
1276         break;
1277 
1278     case RES_CHRATR_BLINK:
1279         eState = static_cast<const SvxBlinkItem&>(rItem).GetValue() ? HTML_ON_VALUE
1280                                                          : HTML_OFF_VALUE;
1281         break;
1282 
1283     case RES_CHRATR_COLOR:
1284         eState = HTML_COLOR_VALUE;
1285         break;
1286 
1287     case RES_CHRATR_FONT:
1288     case RES_CHRATR_FONTSIZE:
1289     case RES_CHRATR_LANGUAGE:
1290     case RES_CHRATR_CJK_FONT:
1291     case RES_CHRATR_CJK_FONTSIZE:
1292     case RES_CHRATR_CJK_LANGUAGE:
1293     case RES_CHRATR_CTL_FONT:
1294     case RES_CHRATR_CTL_FONTSIZE:
1295     case RES_CHRATR_CTL_LANGUAGE:
1296     case RES_TXTATR_INETFMT:
1297         eState = HTML_REAL_VALUE;
1298         break;
1299 
1300     case RES_TXTATR_CHARFMT:
1301         eState = HTML_CHRFMT_VALUE;
1302         break;
1303 
1304     case RES_TXTATR_AUTOFMT:
1305         eState = HTML_AUTOFMT_VALUE;
1306         break;
1307 
1308     case RES_CHRATR_CASEMAP:
1309         eState = HTML_STYLE_VALUE;
1310         break;
1311 
1312     case RES_CHRATR_KERNING:
1313         eState = HTML_STYLE_VALUE;
1314         break;
1315 
1316     case RES_CHRATR_BACKGROUND:
1317         if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1318             eState = HTML_STYLE_VALUE;
1319         break;
1320 
1321     case RES_PARATR_DROP:
1322         eState = HTML_DROPCAP_VALUE;
1323         break;
1324 
1325     case RES_CHRATR_BOX:
1326         if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
1327             eState = HTML_STYLE_VALUE;
1328         break;
1329     }
1330 
1331     return eState;
1332 }
1333 
ExistsOnTagItem(sal_uInt16 nWhich,sal_Int32 nPos)1334 bool HTMLEndPosLst::ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos )
1335 {
1336     for (auto pTest : m_aStartLst)
1337     {
1338         if( pTest->GetStart() > nPos )
1339         {
1340             // this attribute, and all attributes that follow, start later
1341             break;
1342         }
1343         else if( pTest->GetEnd() > nPos )
1344         {
1345             // the attribute starts before, or at, the current position and
1346             // ends after it
1347             const SfxPoolItem *pItem = pTest->GetItem();
1348             if( pItem->Which() == nWhich &&
1349                 HTML_ON_VALUE == GetHTMLItemState(*pItem) )
1350             {
1351                 // an OnTag attribute was found
1352                 return true;
1353             }
1354         }
1355     }
1356 
1357     return false;
1358 }
1359 
ExistsOffTagItem(sal_uInt16 nWhich,sal_Int32 nStartPos,sal_Int32 nEndPos)1360 bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos,
1361                                       sal_Int32 nEndPos )
1362 {
1363     if( nWhich != RES_CHRATR_CROSSEDOUT &&
1364         nWhich != RES_CHRATR_UNDERLINE &&
1365         nWhich != RES_CHRATR_BLINK )
1366     {
1367         return false;
1368     }
1369 
1370     for (auto pTest : m_aStartLst)
1371     {
1372         if( pTest->GetStart() > nStartPos )
1373         {
1374             // this attribute, and all attributes that follow, start later
1375             break;
1376         }
1377         else if( pTest->GetStart()==nStartPos &&
1378                  pTest->GetEnd()==nEndPos )
1379         {
1380             // the attribute starts before or at the current position and
1381             // ends after it
1382             const SfxPoolItem *pItem = pTest->GetItem();
1383             sal_uInt16 nTstWhich = pItem->Which();
1384             if( (nTstWhich == RES_CHRATR_CROSSEDOUT ||
1385                  nTstWhich == RES_CHRATR_UNDERLINE ||
1386                  nTstWhich == RES_CHRATR_BLINK) &&
1387                 HTML_OFF_VALUE == GetHTMLItemState(*pItem) )
1388             {
1389                 // an OffTag attribute was found that is exported the same
1390                 // way as the current item
1391                 return true;
1392             }
1393         }
1394     }
1395 
1396     return false;
1397 }
1398 
FixSplittedItem(HTMLStartEndPos * pPos,sal_Int32 nNewEnd,HTMLStartEndPositions::size_type nStartPos)1399 void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
1400                                         HTMLStartEndPositions::size_type nStartPos )
1401 {
1402     // fix the end position accordingly
1403     pPos->SetEnd( nNewEnd );
1404 
1405     // remove the item from the End list
1406     HTMLStartEndPositions::iterator it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pPos);
1407     OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
1408     if (it != m_aEndLst.end())
1409         m_aEndLst.erase(it);
1410 
1411     // from now on, it is closed as the last one at the corresponding position
1412     HTMLStartEndPositions::size_type nEndPos {0};
1413     while (nEndPos < m_aEndLst.size() && m_aEndLst[nEndPos]->GetEnd() <= nNewEnd)
1414         ++nEndPos;
1415     m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
1416 
1417     // now, adjust the attributes that got started afterwards
1418     for (HTMLStartEndPositions::size_type i = nStartPos + 1; i < m_aStartLst.size(); ++i)
1419     {
1420         HTMLStartEndPos* pTest = m_aStartLst[i];
1421         sal_Int32 nTestEnd = pTest->GetEnd();
1422         if( pTest->GetStart() >= nNewEnd )
1423         {
1424             // the Test attribute and all the following ones start, after the
1425             // split attribute ends
1426             break;
1427         }
1428         else if( nTestEnd > nNewEnd )
1429         {
1430             // the Test attribute starts before the split attribute
1431             // ends, and ends afterwards, i.e., it must be split, as well
1432 
1433             // set the new end
1434             pTest->SetEnd( nNewEnd );
1435 
1436             // remove the attribute from the End list
1437             it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
1438             OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
1439             if (it != m_aEndLst.end())
1440                 m_aEndLst.erase(it);
1441 
1442             // it now ends as the first attribute in the respective position.
1443             // We already know this position in the End list.
1444             m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pTest);
1445 
1446             // insert the 'rest' of the attribute
1447             InsertItem( *pTest->GetItem(), nNewEnd, nTestEnd );
1448         }
1449     }
1450 }
1451 
InsertItem(const SfxPoolItem & rItem,sal_Int32 nStart,sal_Int32 nEnd)1452 void HTMLEndPosLst::InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
1453                                                           sal_Int32 nEnd )
1454 {
1455     HTMLStartEndPositions::size_type i;
1456     for (i = 0; i < m_aEndLst.size(); i++)
1457     {
1458         HTMLStartEndPos* pTest = m_aEndLst[i];
1459         sal_Int32 nTestEnd = pTest->GetEnd();
1460         if( nTestEnd <= nStart )
1461         {
1462             // the Test attribute ends, before the new one starts
1463             continue;
1464         }
1465         else if( nTestEnd < nEnd )
1466         {
1467             if( pTest->GetStart() < nStart )
1468             {
1469                 // the Test attribute ends, before the new one ends. Thus, the
1470                 // new attribute must be split.
1471                 InsertItem_( new HTMLStartEndPos( rItem, nStart, nTestEnd ), i );
1472                 nStart = nTestEnd;
1473             }
1474         }
1475         else
1476         {
1477             // the Test attribute (and all that follow) ends, before the new
1478             // one ends
1479             break;
1480         }
1481     }
1482 
1483     // one attribute must still be inserted
1484     InsertItem_( new HTMLStartEndPos( rItem, nStart, nEnd ), i );
1485 }
1486 
SplitItem(const SfxPoolItem & rItem,sal_Int32 nStart,sal_Int32 nEnd)1487 void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart,
1488                                                            sal_Int32 nEnd )
1489 {
1490     sal_uInt16 nWhich = rItem.Which();
1491 
1492     // first, we must search for the old items by using the start list and
1493     // determine the new item range
1494 
1495     for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
1496     {
1497         HTMLStartEndPos* pTest = m_aStartLst[i];
1498         sal_Int32 nTestStart = pTest->GetStart();
1499         sal_Int32 nTestEnd = pTest->GetEnd();
1500 
1501         if( nTestStart >= nEnd )
1502         {
1503             // this attribute, and all that follow, start later
1504             break;
1505         }
1506         else if( nTestEnd > nStart )
1507         {
1508             // the Test attribute ends in the range that must be deleted
1509             const SfxPoolItem *pItem = pTest->GetItem();
1510 
1511             // only the corresponding OnTag attributes have to be considered
1512             if( pItem->Which() == nWhich &&
1513                 HTML_ON_VALUE == GetHTMLItemState( *pItem ) )
1514             {
1515                 bool bDelete = true;
1516 
1517                 if( nTestStart < nStart )
1518                 {
1519                     // the start of the new attribute corresponds to the new
1520                     // end of the attribute
1521                     FixSplittedItem( pTest, nStart, i );
1522                     bDelete = false;
1523                 }
1524                 else
1525                 {
1526                     // the Test item only starts after the new end of the
1527                     // attribute. Therefore, it can be completely erased.
1528                     m_aStartLst.erase(m_aStartLst.begin() + i);
1529                     i--;
1530 
1531                     HTMLStartEndPositions::iterator it
1532                         = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
1533                     OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
1534                     if (it != m_aEndLst.end())
1535                         m_aEndLst.erase(it);
1536                 }
1537 
1538                 // if necessary, insert the second part of the split
1539                 // attribute
1540                 if( nTestEnd > nEnd )
1541                 {
1542                     InsertItem( *pTest->GetItem(), nEnd, nTestEnd );
1543                 }
1544 
1545                 if( bDelete )
1546                     delete pTest;
1547             }
1548         }
1549     }
1550 }
1551 
GetFormatInfo(const SwFormat & rFormat,SwHTMLFormatInfos & rFormatInfos)1552 const SwHTMLFormatInfo *HTMLEndPosLst::GetFormatInfo( const SwFormat& rFormat,
1553                                                 SwHTMLFormatInfos& rFormatInfos )
1554 {
1555     SwHTMLFormatInfo *pFormatInfo;
1556     std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(&rFormat));
1557     SwHTMLFormatInfos::iterator it = rFormatInfos.find( pTmpInfo );
1558     if (it != rFormatInfos.end())
1559     {
1560         pFormatInfo = it->get();
1561     }
1562     else
1563     {
1564         pFormatInfo = new SwHTMLFormatInfo(&rFormat, m_pDoc, m_pTemplate, m_bOutStyles);
1565         rFormatInfos.insert(std::unique_ptr<SwHTMLFormatInfo>(pFormatInfo));
1566         if (m_rScriptTextStyles.count(rFormat.GetName()))
1567             pFormatInfo->bScriptDependent = true;
1568     }
1569 
1570     return pFormatInfo;
1571 }
1572 
HTMLEndPosLst(SwDoc * pD,SwDoc * pTempl,std::optional<Color> xDfltCol,bool bStyles,sal_uLong nMode,const OUString & rText,std::set<OUString> & rStyles)1573 HTMLEndPosLst::HTMLEndPosLst(SwDoc* pD, SwDoc* pTempl, std::optional<Color> xDfltCol, bool bStyles,
1574                              sal_uLong nMode, const OUString& rText, std::set<OUString>& rStyles)
1575     : m_pDoc(pD)
1576     , m_pTemplate(pTempl)
1577     , m_xDefaultColor(std::move(xDfltCol))
1578     , m_rScriptTextStyles(rStyles)
1579     , m_nHTMLMode(nMode)
1580     , m_bOutStyles(bStyles)
1581 {
1582     sal_Int32 nEndPos = rText.getLength();
1583     sal_Int32 nPos = 0;
1584     while( nPos < nEndPos )
1585     {
1586         sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( rText, nPos );
1587         nPos = g_pBreakIt->GetBreakIter()->endOfScript( rText, nPos, nScript );
1588         m_aScriptChgLst.push_back(nPos);
1589         m_aScriptLst.push_back(nScript);
1590     }
1591 }
1592 
~HTMLEndPosLst()1593 HTMLEndPosLst::~HTMLEndPosLst()
1594 {
1595     OSL_ENSURE(m_aStartLst.empty(), "Start List not empty in destructor");
1596     OSL_ENSURE(m_aEndLst.empty(), "End List not empty in destructor");
1597 }
1598 
InsertNoScript(const SfxPoolItem & rItem,sal_Int32 nStart,sal_Int32 nEnd,SwHTMLFormatInfos & rFormatInfos,bool bParaAttrs)1599 void HTMLEndPosLst::InsertNoScript( const SfxPoolItem& rItem,
1600                             sal_Int32 nStart, sal_Int32 nEnd,
1601                             SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs )
1602 {
1603     // no range ?? in that case, don't take it, it will never take effect !!
1604     if( nStart == nEnd )
1605         return;
1606 
1607     bool bSet = false, bSplit = false;
1608     switch( GetHTMLItemState(rItem) )
1609     {
1610     case HTML_ON_VALUE:
1611         // output the attribute, if it isn't 'on', already
1612         if( !ExistsOnTagItem( rItem.Which(), nStart ) )
1613             bSet = true;
1614         break;
1615 
1616     case HTML_OFF_VALUE:
1617         // If the corresponding attribute is 'on', split it.
1618         // Additionally, output it as Style, if it is not set for the
1619         // whole paragraph, because in that case it was already output
1620         // together with the paragraph tag.
1621         if( ExistsOnTagItem( rItem.Which(), nStart ) )
1622             bSplit = true;
1623         bSet = m_bOutStyles && !bParaAttrs && !ExistsOffTagItem(rItem.Which(), nStart, nEnd);
1624         break;
1625 
1626     case HTML_REAL_VALUE:
1627         // we can always output the attribute
1628         bSet = true;
1629         break;
1630 
1631     case HTML_STYLE_VALUE:
1632         // We can only output the attribute as CSS1. If it is set for
1633         // the paragraph, it was already output with the paragraph tag.
1634         // The only exception is the character-background attribute. This
1635         // attribute must always be handled like a Hint.
1636         bSet = m_bOutStyles
1637                && (!bParaAttrs || rItem.Which() == RES_CHRATR_BACKGROUND
1638                    || rItem.Which() == RES_CHRATR_BOX || rItem.Which() == RES_CHRATR_OVERLINE);
1639         break;
1640 
1641     case HTML_CHRFMT_VALUE:
1642         {
1643             OSL_ENSURE( RES_TXTATR_CHARFMT == rItem.Which(),
1644                     "Not a character style after all" );
1645             const SwFormatCharFormat& rChrFormat = static_cast<const SwFormatCharFormat&>(rItem);
1646             const SwCharFormat* pFormat = rChrFormat.GetCharFormat();
1647 
1648             const SwHTMLFormatInfo *pFormatInfo = GetFormatInfo( *pFormat, rFormatInfos );
1649             if( !pFormatInfo->aToken.isEmpty() )
1650             {
1651                 // output the character style tag before the hard
1652                 // attributes
1653                 InsertItem( rItem, nStart, nEnd );
1654             }
1655             if( pFormatInfo->pItemSet )
1656             {
1657                 Insert( *pFormatInfo->pItemSet, nStart, nEnd,
1658                         rFormatInfos, true, bParaAttrs );
1659             }
1660         }
1661         break;
1662 
1663     case HTML_AUTOFMT_VALUE:
1664         {
1665             const SwFormatAutoFormat& rAutoFormat = static_cast<const SwFormatAutoFormat&>(rItem);
1666             const std::shared_ptr<SfxItemSet>& pSet = rAutoFormat.GetStyleHandle();
1667             if( pSet )
1668                 Insert( *pSet, nStart, nEnd, rFormatInfos, true, bParaAttrs );
1669         }
1670         break;
1671 
1672     case HTML_COLOR_VALUE:
1673         // A foreground color as a paragraph attribute is only exported if
1674         // it is not the same as the default color.
1675         {
1676             OSL_ENSURE( RES_CHRATR_COLOR == rItem.Which(),
1677                     "Not a foreground color, after all" );
1678             Color aColor( static_cast<const SvxColorItem&>(rItem).GetValue() );
1679             if( COL_AUTO == aColor )
1680                 aColor = COL_BLACK;
1681             bSet = !bParaAttrs || !m_xDefaultColor || !m_xDefaultColor->IsRGBEqual(aColor);
1682         }
1683         break;
1684 
1685     case HTML_DROPCAP_VALUE:
1686         {
1687             OSL_ENSURE( RES_PARATR_DROP == rItem.Which(),
1688                     "Not a drop cap, after all" );
1689             const SwFormatDrop& rDrop = static_cast<const SwFormatDrop&>(rItem);
1690             nEnd = nStart + rDrop.GetChars();
1691             if (!m_bOutStyles)
1692             {
1693                 // At least use the attributes of the character style
1694                 const SwCharFormat *pCharFormat = rDrop.GetCharFormat();
1695                 if( pCharFormat )
1696                 {
1697                     Insert( pCharFormat->GetAttrSet(), nStart, nEnd,
1698                             rFormatInfos, true, bParaAttrs );
1699                 }
1700             }
1701             else
1702             {
1703                 bSet = true;
1704             }
1705         }
1706         break;
1707     default:
1708         ;
1709     }
1710 
1711     if( bSet )
1712         InsertItem( rItem, nStart, nEnd );
1713     if( bSplit )
1714         SplitItem( rItem, nStart, nEnd );
1715 }
1716 
Insert(const SfxPoolItem & rItem,sal_Int32 nStart,sal_Int32 nEnd,SwHTMLFormatInfos & rFormatInfos,bool bParaAttrs)1717 void HTMLEndPosLst::Insert( const SfxPoolItem& rItem,
1718                             sal_Int32 nStart, sal_Int32 nEnd,
1719                             SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs )
1720 {
1721     bool bDependsOnScript = false, bDependsOnAnyScript = false;
1722     sal_uInt16 nScript = i18n::ScriptType::LATIN;
1723     switch( rItem.Which() )
1724     {
1725     case RES_CHRATR_FONT:
1726     case RES_CHRATR_FONTSIZE:
1727     case RES_CHRATR_LANGUAGE:
1728     case RES_CHRATR_POSTURE:
1729     case RES_CHRATR_WEIGHT:
1730         bDependsOnScript = true;
1731         nScript = i18n::ScriptType::LATIN;
1732         break;
1733 
1734     case RES_CHRATR_CJK_FONT:
1735     case RES_CHRATR_CJK_FONTSIZE:
1736     case RES_CHRATR_CJK_LANGUAGE:
1737     case RES_CHRATR_CJK_POSTURE:
1738     case RES_CHRATR_CJK_WEIGHT:
1739         bDependsOnScript = true;
1740         nScript = i18n::ScriptType::ASIAN;
1741         break;
1742 
1743     case RES_CHRATR_CTL_FONT:
1744     case RES_CHRATR_CTL_FONTSIZE:
1745     case RES_CHRATR_CTL_LANGUAGE:
1746     case RES_CHRATR_CTL_POSTURE:
1747     case RES_CHRATR_CTL_WEIGHT:
1748         bDependsOnScript = true;
1749         nScript = i18n::ScriptType::COMPLEX;
1750         break;
1751     case RES_TXTATR_CHARFMT:
1752         {
1753             const SwFormatCharFormat& rChrFormat = static_cast<const SwFormatCharFormat&>(rItem);
1754             const SwCharFormat* pFormat = rChrFormat.GetCharFormat();
1755             const SwHTMLFormatInfo *pFormatInfo = GetFormatInfo( *pFormat, rFormatInfos );
1756             if( pFormatInfo->bScriptDependent )
1757             {
1758                 bDependsOnScript = true;
1759                 bDependsOnAnyScript = true;
1760             }
1761         }
1762         break;
1763     case RES_TXTATR_INETFMT:
1764         {
1765             if (GetFormatInfo(*m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
1766                                   RES_POOLCHR_INET_NORMAL),
1767                               rFormatInfos)
1768                     ->bScriptDependent
1769                 || GetFormatInfo(*m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
1770                                      RES_POOLCHR_INET_VISIT),
1771                                  rFormatInfos)
1772                        ->bScriptDependent)
1773             {
1774                 bDependsOnScript = true;
1775                 bDependsOnAnyScript = true;
1776             }
1777         }
1778         break;
1779     }
1780 
1781     if( bDependsOnScript )
1782     {
1783         sal_Int32 nPos = nStart;
1784         for (size_t i = 0; i < m_aScriptChgLst.size(); i++)
1785         {
1786             sal_Int32 nChgPos = m_aScriptChgLst[i];
1787             if( nPos >= nChgPos )
1788             {
1789                 // the hint starts behind or at the next script change,
1790                 // so we may continue with this position.
1791                 continue;
1792             }
1793             if( nEnd <= nChgPos )
1794             {
1795                 // the (rest of) the hint ends before or at the next script
1796                 // change, so we can insert it, but only if it belongs
1797                 // to the current script.
1798                 if (bDependsOnAnyScript || nScript == m_aScriptLst[i])
1799                     InsertNoScript( rItem, nPos, nEnd, rFormatInfos,
1800                                     bParaAttrs );
1801                 break;
1802             }
1803 
1804             // the hint starts before the next script change and ends behind
1805             // it, so we can insert a hint up to the next script change and
1806             // continue with the rest of the hint.
1807             if (bDependsOnAnyScript || nScript == m_aScriptLst[i])
1808                 InsertNoScript( rItem, nPos, nChgPos, rFormatInfos, bParaAttrs );
1809             nPos = nChgPos;
1810         }
1811     }
1812     else
1813     {
1814         InsertNoScript( rItem, nStart, nEnd, rFormatInfos, bParaAttrs );
1815     }
1816 }
1817 
Insert(const SfxItemSet & rItemSet,sal_Int32 nStart,sal_Int32 nEnd,SwHTMLFormatInfos & rFormatInfos,bool bDeep,bool bParaAttrs)1818 void HTMLEndPosLst::Insert( const SfxItemSet& rItemSet,
1819                             sal_Int32 nStart, sal_Int32 nEnd,
1820                             SwHTMLFormatInfos& rFormatInfos,
1821                             bool bDeep, bool bParaAttrs )
1822 {
1823     SfxWhichIter aIter( rItemSet );
1824 
1825     sal_uInt16 nWhich = aIter.FirstWhich();
1826     while( nWhich )
1827     {
1828         const SfxPoolItem *pItem;
1829         if( SfxItemState::SET == rItemSet.GetItemState( nWhich, bDeep, &pItem ) )
1830         {
1831             Insert( *pItem, nStart, nEnd, rFormatInfos, bParaAttrs );
1832         }
1833 
1834         nWhich = aIter.NextWhich();
1835     }
1836 }
1837 
Insert(const SwDrawFrameFormat & rFormat,sal_Int32 nPos,SwHTMLFormatInfos & rFormatInfos)1838 void HTMLEndPosLst::Insert( const SwDrawFrameFormat& rFormat, sal_Int32 nPos,
1839                             SwHTMLFormatInfos& rFormatInfos )
1840 {
1841     const SdrObject* pTextObj = SwHTMLWriter::GetMarqueeTextObj( rFormat );
1842 
1843     if( !pTextObj )
1844         return;
1845 
1846     // get the edit engine attributes of the object as SW attributes and
1847     // insert them as hints. Because of the amount of Hints the styles
1848     // are not considered!
1849     const SfxItemSet& rFormatItemSet = rFormat.GetAttrSet();
1850     SfxItemSet aItemSet( *rFormatItemSet.GetPool(), svl::Items<RES_CHRATR_BEGIN,
1851                                                  RES_CHRATR_END>{} );
1852     SwHTMLWriter::GetEEAttrsFromDrwObj( aItemSet, pTextObj );
1853     bool bOutStylesOld = m_bOutStyles;
1854     m_bOutStyles = false;
1855     Insert( aItemSet, nPos, nPos+1, rFormatInfos, false );
1856     m_bOutStyles = bOutStylesOld;
1857 }
1858 
GetScriptAtPos(sal_Int32 nPos,sal_uInt16 nWeak)1859 sal_uInt16 HTMLEndPosLst::GetScriptAtPos( sal_Int32 nPos, sal_uInt16 nWeak )
1860 {
1861     sal_uInt16 nRet = CSS1_OUTMODE_ANY_SCRIPT;
1862 
1863     size_t nScriptChgs = m_aScriptChgLst.size();
1864     size_t i=0;
1865     while (i < nScriptChgs && nPos >= m_aScriptChgLst[i])
1866         i++;
1867     OSL_ENSURE( i < nScriptChgs, "script list is too short" );
1868     if( i < nScriptChgs )
1869     {
1870         if (i18n::ScriptType::WEAK == m_aScriptLst[i])
1871             nRet = nWeak;
1872         else
1873             nRet = SwHTMLWriter::GetCSS1ScriptForScriptType(m_aScriptLst[i]);
1874     }
1875 
1876     return nRet;
1877 }
1878 
OutStartAttrs(SwHTMLWriter & rHWrt,sal_Int32 nPos,HTMLOutContext * pContext)1879 void HTMLEndPosLst::OutStartAttrs( SwHTMLWriter& rHWrt, sal_Int32 nPos,
1880                                       HTMLOutContext *pContext  )
1881 {
1882     rHWrt.m_bTagOn = true;
1883 
1884     // Character border attribute must be the first which is written out
1885     // because of border merge.
1886     HTMLStartEndPositions::size_type nCharBoxIndex = 0;
1887     while (nCharBoxIndex < m_aStartLst.size()
1888            && m_aStartLst[nCharBoxIndex]->GetItem()->Which() != RES_CHRATR_BOX)
1889     {
1890         ++nCharBoxIndex;
1891     }
1892 
1893     // the attributes of the start list are sorted in ascending order
1894     for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
1895     {
1896         HTMLStartEndPos *pPos = nullptr;
1897         if (nCharBoxIndex < m_aStartLst.size())
1898         {
1899             if( i == 0 )
1900                 pPos = m_aStartLst[nCharBoxIndex];
1901             else if( i == nCharBoxIndex )
1902                 pPos = m_aStartLst[0];
1903             else
1904                 pPos = m_aStartLst[i];
1905         }
1906         else
1907             pPos = m_aStartLst[i];
1908 
1909         sal_Int32 nStart = pPos->GetStart();
1910         if( nStart > nPos )
1911         {
1912             // this attribute, and all that follow, will be opened later on
1913             break;
1914         }
1915         else if( nStart == nPos )
1916         {
1917             // output the attribute
1918             sal_uInt16 nCSS1Script = rHWrt.m_nCSS1Script;
1919             sal_uInt16 nWhich = pPos->GetItem()->Which();
1920             if( RES_TXTATR_CHARFMT == nWhich ||
1921                 RES_TXTATR_INETFMT == nWhich ||
1922                  RES_PARATR_DROP == nWhich )
1923             {
1924                 rHWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script );
1925             }
1926             if( pContext )
1927             {
1928                 HTMLOutFuncs::FlushToAscii( rHWrt.Strm(), *pContext );
1929                 pContext = nullptr; // one time only
1930             }
1931             Out( aHTMLAttrFnTab, *pPos->GetItem(), rHWrt );
1932             rHWrt.maStartedAttributes[pPos->GetItem()->Which()]++;
1933             rHWrt.m_nCSS1Script = nCSS1Script;
1934         }
1935     }
1936 }
1937 
OutEndAttrs(SwHTMLWriter & rHWrt,sal_Int32 nPos,HTMLOutContext * pContext=nullptr)1938 void HTMLEndPosLst::OutEndAttrs( SwHTMLWriter& rHWrt, sal_Int32 nPos,
1939                                      HTMLOutContext *pContext = nullptr )
1940 {
1941     rHWrt.m_bTagOn = false;
1942 
1943     // the attributes in the End list are sorted in ascending order
1944     HTMLStartEndPositions::size_type i {0};
1945     while (i < m_aEndLst.size())
1946     {
1947         HTMLStartEndPos* pPos = m_aEndLst[i];
1948         sal_Int32 nEnd = pPos->GetEnd();
1949 
1950         if( SAL_MAX_INT32 == nPos || nEnd == nPos )
1951         {
1952             if( pContext )
1953             {
1954                 HTMLOutFuncs::FlushToAscii( rHWrt.Strm(), *pContext );
1955                 pContext = nullptr; // one time only
1956             }
1957             // Skip closing span if next character span has the same border (border merge)
1958             bool bSkipOut = false;
1959             if( pPos->GetItem()->Which() == RES_CHRATR_BOX )
1960             {
1961                 HTMLStartEndPositions::iterator it
1962                     = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos);
1963                 OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!");
1964                 if (it != m_aStartLst.end())
1965                     ++it;
1966                 while (it != m_aStartLst.end())
1967                 {
1968                     HTMLStartEndPos *pEndPos = *it;
1969                     if( pEndPos->GetItem()->Which() == RES_CHRATR_BOX &&
1970                         *static_cast<const SvxBoxItem*>(pEndPos->GetItem()) ==
1971                         *static_cast<const SvxBoxItem*>(pPos->GetItem()) )
1972                     {
1973                         pEndPos->SetStart(pPos->GetStart());
1974                         bSkipOut = true;
1975                         break;
1976                     }
1977                     ++it;
1978                 }
1979             }
1980             if( !bSkipOut )
1981             {
1982                 Out( aHTMLAttrFnTab, *pPos->GetItem(), rHWrt );
1983                 rHWrt.maStartedAttributes[pPos->GetItem()->Which()]--;
1984             }
1985             RemoveItem_( i );
1986         }
1987         else if( nEnd > nPos )
1988         {
1989             // this attribute, and all that follow, are closed later on
1990             break;
1991         }
1992         else
1993         {
1994             // The attribute is closed before the current position. This
1995             // is not allowed, but we can handle it anyway.
1996             OSL_ENSURE( nEnd >= nPos,
1997                     "The attribute should've been closed a long time ago" );
1998             i++;
1999         }
2000     }
2001 }
2002 
2003 /* Output of the nodes*/
OutHTML_SwTextNode(Writer & rWrt,const SwContentNode & rNode)2004 Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode )
2005 {
2006     const SwTextNode * pNd = &static_cast<const SwTextNode&>(rNode);
2007     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2008 
2009     const OUString& rStr = pNd->GetText();
2010     sal_Int32 nEnd = rStr.getLength();
2011 
2012     // special case: empty node and HR style (horizontal rule)
2013     //               output a <HR>, only
2014     sal_uInt16 nPoolId = pNd->GetAnyFormatColl().GetPoolFormatId();
2015 
2016     // Handle horizontal rule <hr>
2017     if (!nEnd &&
2018         (RES_POOLCOLL_HTML_HR==nPoolId || pNd->GetAnyFormatColl().GetName() == OOO_STRING_SVTOOLS_HTML_horzrule))
2019     {
2020         // then, the paragraph-anchored graphics/OLE objects in the paragraph
2021         // MIB 8.7.97: We enclose the line in a <PRE>. This means that the
2022         // spacings are wrong, but otherwise we get an empty paragraph
2023         // after the <HR> which is even uglier.
2024         rHTMLWrt.ChangeParaToken( HtmlTokenId::NONE );
2025 
2026         // Output all the nodes that are anchored to a frame
2027         rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any );
2028 
2029         if( rHTMLWrt.m_bLFPossible )
2030             rHTMLWrt.OutNewLine(); // paragraph tag on a new line
2031 
2032         rHTMLWrt.m_bLFPossible = true;
2033 
2034         HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
2035         aHtml.start(OOO_STRING_SVTOOLS_HTML_horzrule);
2036 
2037         const SfxItemSet* pItemSet = pNd->GetpSwAttrSet();
2038         if( !pItemSet )
2039         {
2040             aHtml.endAttribute();
2041             return rHTMLWrt;
2042         }
2043         const SfxPoolItem* pItem;
2044         if( SfxItemState::SET == pItemSet->GetItemState( RES_LR_SPACE, false, &pItem ))
2045         {
2046             sal_Int32 nLeft = static_cast<const SvxLRSpaceItem*>(pItem)->GetLeft();
2047             sal_Int32 nRight = static_cast<const SvxLRSpaceItem*>(pItem)->GetRight();
2048             if( nLeft || nRight )
2049             {
2050                 const SwFrameFormat& rPgFormat =
2051                     rHTMLWrt.m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool
2052                     ( RES_POOLPAGE_HTML, false )->GetMaster();
2053                 const SwFormatFrameSize& rSz   = rPgFormat.GetFrameSize();
2054                 const SvxLRSpaceItem& rLR = rPgFormat.GetLRSpace();
2055                 const SwFormatCol& rCol = rPgFormat.GetCol();
2056 
2057                 tools::Long nPageWidth = rSz.GetWidth() - rLR.GetLeft() - rLR.GetRight();
2058 
2059                 if( 1 < rCol.GetNumCols() )
2060                     nPageWidth /= rCol.GetNumCols();
2061 
2062                 const SwTableNode* pTableNd = pNd->FindTableNode();
2063                 if( pTableNd )
2064                 {
2065                     const SwTableBox* pBox = pTableNd->GetTable().GetTableBox(
2066                                     pNd->StartOfSectionIndex() );
2067                     if( pBox )
2068                         nPageWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
2069                 }
2070 
2071                 OString sWidth = OString::number(SwHTMLWriter::ToPixel(nPageWidth - nLeft - nRight, false));
2072                 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_width, sWidth);
2073 
2074                 if( !nLeft )
2075                     aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_left);
2076                 else if( !nRight )
2077                     aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_right);
2078                 else
2079                     aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_center);
2080             }
2081         }
2082 
2083         if( SfxItemState::SET == pItemSet->GetItemState( RES_BOX, false, &pItem ))
2084         {
2085             const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem);
2086             const editeng::SvxBorderLine* pBorderLine = pBoxItem->GetBottom();
2087             if( pBorderLine )
2088             {
2089                 sal_uInt16 nWidth = pBorderLine->GetScaledWidth();
2090                 OString sWidth = OString::number(SwHTMLWriter::ToPixel(nWidth, false));
2091                 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_size, sWidth);
2092 
2093                 const Color& rBorderColor = pBorderLine->GetColor();
2094                 if( !rBorderColor.IsRGBEqual( COL_GRAY ) )
2095                 {
2096                     HtmlWriterHelper::applyColor(aHtml, OOO_STRING_SVTOOLS_HTML_O_color, rBorderColor);
2097                 }
2098 
2099                 if( !pBorderLine->GetInWidth() )
2100                 {
2101                     aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_noshade, OOO_STRING_SVTOOLS_HTML_O_noshade);
2102                 }
2103             }
2104         }
2105         aHtml.end();
2106         return rHTMLWrt;
2107     }
2108 
2109     // Do not export the empty nodes with 2pt fonts and standard style that
2110     // are inserted before tables and sections, but do export bookmarks
2111     // and paragraph anchored frames.
2112     if( !nEnd && (nPoolId == RES_POOLCOLL_STANDARD ||
2113                   nPoolId == RES_POOLCOLL_TABLE ||
2114                   nPoolId == RES_POOLCOLL_TABLE_HDLN) )
2115     {
2116         // The current node is empty and contains the standard style ...
2117         const SfxPoolItem* pItem;
2118         const SfxItemSet* pItemSet = pNd->GetpSwAttrSet();
2119         if( pItemSet && pItemSet->Count() &&
2120             SfxItemState::SET == pItemSet->GetItemState( RES_CHRATR_FONTSIZE, false, &pItem ) &&
2121             40 == static_cast<const SvxFontHeightItem *>(pItem)->GetHeight() )
2122         {
2123             // ... moreover, the 2pt font is set ...
2124             sal_uLong nNdPos = rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex();
2125             const SwNode *pNextNd = rWrt.m_pDoc->GetNodes()[nNdPos+1];
2126             const SwNode *pPrevNd = rWrt.m_pDoc->GetNodes()[nNdPos-1];
2127             bool bStdColl = nPoolId == RES_POOLCOLL_STANDARD;
2128             if( ( bStdColl && (pNextNd->IsTableNode() || pNextNd->IsSectionNode()) ) ||
2129                 ( !bStdColl &&
2130                    pNextNd->IsEndNode() &&
2131                    pPrevNd->IsStartNode() &&
2132                    SwTableBoxStartNode == pPrevNd->GetStartNode()->GetStartNodeType() ) )
2133             {
2134                 // ... and it is located before a table or a section
2135                 rHTMLWrt.OutBookmarks();
2136                 rHTMLWrt.m_bLFPossible = rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE;
2137 
2138                 // Output all frames that are anchored to this node
2139                 rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any );
2140                 rHTMLWrt.m_bLFPossible = false;
2141 
2142                 return rWrt;
2143             }
2144         }
2145     }
2146 
2147     // catch PageBreaks and PageDescs
2148     bool bPageBreakBehind = false;
2149     if( rHTMLWrt.m_bCfgFormFeed &&
2150         !(rHTMLWrt.m_bOutTable || rHTMLWrt.m_bOutFlyFrame) &&
2151         rHTMLWrt.m_pStartNdIdx->GetIndex() != rHTMLWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() )
2152     {
2153         bool bPageBreakBefore = false;
2154         const SfxPoolItem* pItem;
2155         const SfxItemSet* pItemSet = pNd->GetpSwAttrSet();
2156 
2157         if( pItemSet )
2158         {
2159             if( SfxItemState::SET == pItemSet->GetItemState( RES_PAGEDESC, true, &pItem ) &&
2160                 static_cast<const SwFormatPageDesc *>(pItem)->GetPageDesc() )
2161             {
2162                 bPageBreakBefore = true;
2163             }
2164             else if( SfxItemState::SET == pItemSet->GetItemState( RES_BREAK, true, &pItem ) )
2165             {
2166                 switch( static_cast<const SvxFormatBreakItem *>(pItem)->GetBreak() )
2167                 {
2168                 case SvxBreak::PageBefore:
2169                     bPageBreakBefore = true;
2170                     break;
2171                 case SvxBreak::PageAfter:
2172                     bPageBreakBehind = true;
2173                     break;
2174                 case SvxBreak::PageBoth:
2175                     bPageBreakBefore = true;
2176                     bPageBreakBehind = true;
2177                     break;
2178                 default:
2179                     break;
2180                 }
2181             }
2182         }
2183 
2184         if( bPageBreakBefore )
2185             rWrt.Strm().WriteChar( '\f' );
2186     }
2187 
2188     // if necessary, open a form
2189     rHTMLWrt.OutForm();
2190 
2191     // Output the page-anchored frames that are 'anchored' to this node
2192     bool bFlysLeft = rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Prefix );
2193 
2194     // Output all frames that are anchored to this node that are supposed to
2195     // be written before the paragraph tag.
2196     if( bFlysLeft )
2197     {
2198         bFlysLeft = rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Before );
2199     }
2200 
2201     if( rHTMLWrt.m_pCurrentPam->GetPoint()->nNode == rHTMLWrt.m_pCurrentPam->GetMark()->nNode )
2202     {
2203         nEnd = rHTMLWrt.m_pCurrentPam->GetMark()->nContent.GetIndex();
2204     }
2205 
2206     // are there any hard attributes that must be written as options?
2207     rHTMLWrt.m_bTagOn = true;
2208 
2209     // now, output the tag of the paragraph
2210     const SwFormat& rFormat = pNd->GetAnyFormatColl();
2211     SwHTMLTextCollOutputInfo aFormatInfo;
2212     bool bOldLFPossible = rHTMLWrt.m_bLFPossible;
2213     OutHTML_SwFormat( rWrt, rFormat, pNd->GetpSwAttrSet(), aFormatInfo );
2214 
2215     // If we didn't open a new line before the paragraph tag, we do that now
2216     rHTMLWrt.m_bLFPossible = rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE;
2217     if( !bOldLFPossible && rHTMLWrt.m_bLFPossible )
2218         rHTMLWrt.OutNewLine();
2219 
2220     // then, the bookmarks (including end tag)
2221     rHTMLWrt.m_bOutOpts = false;
2222     rHTMLWrt.OutBookmarks();
2223 
2224     // now it's a good opportunity again for an LF - if it is still allowed
2225     if( rHTMLWrt.m_bLFPossible &&
2226         rHTMLWrt.GetLineLen() >= rHTMLWrt.m_nWishLineLen )
2227     {
2228         rHTMLWrt.OutNewLine();
2229     }
2230     rHTMLWrt.m_bLFPossible = false;
2231 
2232     // find text that originates from an outline numbering
2233     sal_Int32 nOffset = 0;
2234     OUString aOutlineText;
2235     OUString aFullText;
2236 
2237     // export numbering string as plain text only for the outline numbering,
2238     // because the outline numbering isn't exported as a numbering - see <SwHTMLNumRuleInfo::Set(..)>
2239     if ( pNd->IsOutline() &&
2240          pNd->GetNumRule() == pNd->GetDoc().GetOutlineNumRule() )
2241     {
2242         aOutlineText = pNd->GetNumString();
2243         nOffset = nOffset + aOutlineText.getLength();
2244         aFullText = aOutlineText;
2245     }
2246     OUString aFootEndNoteSym;
2247     if( rHTMLWrt.m_pFormatFootnote )
2248     {
2249         aFootEndNoteSym = rHTMLWrt.GetFootEndNoteSym( *rHTMLWrt.m_pFormatFootnote );
2250         nOffset = nOffset + aFootEndNoteSym.getLength();
2251         aFullText += aFootEndNoteSym;
2252     }
2253 
2254     // Table of Contents or other paragraph with dot leaders?
2255     sal_Int32 nIndexTab = rHTMLWrt.indexOfDotLeaders( nPoolId, rStr );
2256     if (nIndexTab > -1)
2257         // skip part after the tabulator (page number)
2258         nEnd = nIndexTab;
2259 
2260     // are there any hard attributes that must be written as tags?
2261     aFullText += rStr;
2262     HTMLEndPosLst aEndPosLst( rWrt.m_pDoc, rHTMLWrt.m_xTemplate.get(),
2263                               rHTMLWrt.m_xDfltColor, rHTMLWrt.m_bCfgOutStyles,
2264                               rHTMLWrt.GetHTMLMode(), aFullText,
2265                                  rHTMLWrt.m_aScriptTextStyles );
2266     if( aFormatInfo.pItemSet )
2267     {
2268         aEndPosLst.Insert( *aFormatInfo.pItemSet, 0, nEnd + nOffset,
2269                            rHTMLWrt.m_CharFormatInfos, false, true );
2270     }
2271 
2272     if( !aOutlineText.isEmpty() || rHTMLWrt.m_pFormatFootnote )
2273     {
2274         // output paragraph attributes, so that the text gets the attributes of
2275         // the paragraph.
2276         aEndPosLst.OutStartAttrs( rHTMLWrt, 0 );
2277 
2278         // Theoretically, we would have to consider the character style of
2279         // the numbering. Because it cannot be set via the UI, let's ignore
2280         // it for now.
2281 
2282         if( !aOutlineText.isEmpty() )
2283             HTMLOutFuncs::Out_String( rWrt.Strm(), aOutlineText,
2284                                          rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters);
2285 
2286         if( rHTMLWrt.m_pFormatFootnote )
2287         {
2288             rHTMLWrt.OutFootEndNoteSym( *rHTMLWrt.m_pFormatFootnote, aFootEndNoteSym,
2289                                         aEndPosLst.GetScriptAtPos( aOutlineText.getLength(), rHTMLWrt.m_nCSS1Script ) );
2290             rHTMLWrt.m_pFormatFootnote = nullptr;
2291         }
2292     }
2293 
2294     // for now, correct the start. I.e., if we only output part of the sentence,
2295     // the attributes must be correct there, as well!!
2296     rHTMLWrt.m_bTextAttr = true;
2297 
2298     size_t nAttrPos = 0;
2299     sal_Int32 nStrPos = rHTMLWrt.m_pCurrentPam->GetPoint()->nContent.GetIndex();
2300     const SwTextAttr * pHt = nullptr;
2301     const size_t nCntAttr = pNd->HasHints() ? pNd->GetSwpHints().Count() : 0;
2302     if( nCntAttr && nStrPos > ( pHt = pNd->GetSwpHints().Get(0) )->GetStart() )
2303     {
2304         // Ok, there are earlier attributes that we must output
2305         do {
2306             aEndPosLst.OutEndAttrs( rHTMLWrt, nStrPos + nOffset );
2307 
2308             nAttrPos++;
2309             if( pHt->Which() == RES_TXTATR_FIELD
2310                 || pHt->Which() == RES_TXTATR_ANNOTATION )
2311                 continue;
2312 
2313             if ( pHt->End() && !pHt->HasDummyChar() )
2314             {
2315                 const sal_Int32 nHtEnd = *pHt->End(),
2316                        nHtStt = pHt->GetStart();
2317                 if( !rHTMLWrt.m_bWriteAll && nHtEnd <= nStrPos )
2318                     continue;
2319 
2320                 // don't consider empty hints at the beginning - or should we ??
2321                 if( nHtEnd == nHtStt )
2322                     continue;
2323 
2324                 // add attribute to the list
2325                 if( rHTMLWrt.m_bWriteAll )
2326                     aEndPosLst.Insert( pHt->GetAttr(), nHtStt + nOffset,
2327                                        nHtEnd + nOffset,
2328                                        rHTMLWrt.m_CharFormatInfos );
2329                 else
2330                 {
2331                     sal_Int32 nTmpStt = nHtStt < nStrPos ? nStrPos : nHtStt;
2332                     sal_Int32 nTmpEnd = std::min(nHtEnd, nEnd);
2333                     aEndPosLst.Insert( pHt->GetAttr(), nTmpStt + nOffset,
2334                                        nTmpEnd + nOffset,
2335                                        rHTMLWrt.m_CharFormatInfos );
2336                 }
2337                 continue;
2338                 // but don't output it, that will be done later !!
2339             }
2340 
2341         } while( nAttrPos < nCntAttr && nStrPos >
2342             ( pHt = pNd->GetSwpHints().Get( nAttrPos ) )->GetStart() );
2343 
2344         // so, let's output all collected attributes from the string pos on
2345         aEndPosLst.OutEndAttrs( rHTMLWrt, nStrPos + nOffset );
2346         aEndPosLst.OutStartAttrs( rHTMLWrt, nStrPos + nOffset );
2347     }
2348 
2349     bool bWriteBreak = (HtmlTokenId::PREFORMTXT_ON != rHTMLWrt.m_nLastParaToken);
2350     if( bWriteBreak && pNd->GetNumRule()  )
2351         bWriteBreak = false;
2352 
2353     {
2354         HTMLOutContext aContext( rHTMLWrt.m_eDestEnc );
2355 
2356         for( ; nStrPos < nEnd; nStrPos++ )
2357         {
2358             // output the frames that are anchored to the current position
2359             if( bFlysLeft )
2360             {
2361                 aEndPosLst.OutEndAttrs( rHTMLWrt, nStrPos + nOffset, &aContext );
2362                 bFlysLeft = rHTMLWrt.OutFlyFrame( rNode.GetIndex(),
2363                                                 nStrPos, HtmlPosition::Inside,
2364                                                 &aContext );
2365             }
2366 
2367             bool bOutChar = true;
2368             const SwTextAttr * pTextHt = nullptr;
2369             if (nAttrPos < nCntAttr && pHt->GetStart() == nStrPos)
2370             {
2371                 do {
2372                     if ( pHt->End() && !pHt->HasDummyChar() )
2373                     {
2374                         if( *pHt->End() != nStrPos )
2375                         {
2376                             // insert hints with end, if they don't start
2377                             // an empty range (hints that don't start a range
2378                             // are ignored)
2379                             aEndPosLst.Insert( pHt->GetAttr(), nStrPos + nOffset,
2380                                                *pHt->End() + nOffset,
2381                                                rHTMLWrt.m_CharFormatInfos );
2382                         }
2383                     }
2384                     else
2385                     {
2386                         // hints without an end are output last
2387                         OSL_ENSURE( !pTextHt, "Why is there already an attribute without an end?" );
2388                         if( rHTMLWrt.m_nTextAttrsToIgnore>0 )
2389                         {
2390                             rHTMLWrt.m_nTextAttrsToIgnore--;
2391                         }
2392                         else
2393                         {
2394                             pTextHt = pHt;
2395                             SwFieldIds nFieldWhich;
2396                             if( RES_TXTATR_FIELD != pHt->Which()
2397                                 || ( SwFieldIds::Postit != (nFieldWhich = static_cast<const SwFormatField&>(pHt->GetAttr()).GetField()->Which())
2398                                      && SwFieldIds::Script != nFieldWhich ) )
2399                             {
2400                                 bWriteBreak = false;
2401                             }
2402                         }
2403                         bOutChar = false;       // don't output 255
2404                     }
2405                 } while( ++nAttrPos < nCntAttr && nStrPos ==
2406                     ( pHt = pNd->GetSwpHints().Get( nAttrPos ) )->GetStart() );
2407             }
2408 
2409             // Additionally, some draw formats can bring attributes
2410             if( pTextHt && RES_TXTATR_FLYCNT == pTextHt->Which() )
2411             {
2412                 const SwFrameFormat* pFrameFormat =
2413                     static_cast<const SwFormatFlyCnt &>(pTextHt->GetAttr()).GetFrameFormat();
2414 
2415                 if( RES_DRAWFRMFMT == pFrameFormat->Which() )
2416                     aEndPosLst.Insert( *static_cast<const SwDrawFrameFormat *>(pFrameFormat),
2417                                         nStrPos + nOffset,
2418                                         rHTMLWrt.m_CharFormatInfos );
2419             }
2420 
2421             aEndPosLst.OutEndAttrs( rHTMLWrt, nStrPos + nOffset, &aContext );
2422             aEndPosLst.OutStartAttrs( rHTMLWrt, nStrPos + nOffset, &aContext );
2423 
2424             if( pTextHt )
2425             {
2426                 rHTMLWrt.m_bLFPossible = rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE &&
2427                                        nStrPos > 0 &&
2428                                        rStr[nStrPos-1] == ' ';
2429                 sal_uInt16 nCSS1Script = rHTMLWrt.m_nCSS1Script;
2430                 rHTMLWrt.m_nCSS1Script = aEndPosLst.GetScriptAtPos(
2431                                                 nStrPos + nOffset, nCSS1Script );
2432                 HTMLOutFuncs::FlushToAscii( rWrt.Strm(), aContext );
2433                 Out( aHTMLAttrFnTab, pTextHt->GetAttr(), rHTMLWrt );
2434                 rHTMLWrt.m_nCSS1Script = nCSS1Script;
2435                 rHTMLWrt.m_bLFPossible = false;
2436             }
2437 
2438             if( bOutChar )
2439             {
2440                 sal_uInt32 c = rStr[nStrPos];
2441                 if( rtl::isHighSurrogate(c) && nStrPos < nEnd - 1 )
2442                 {
2443                     const sal_Unicode d = rStr[nStrPos + 1];
2444                     if( rtl::isLowSurrogate(d) )
2445                     {
2446                         c = rtl::combineSurrogates(c, d);
2447                         nStrPos++;
2448                     }
2449                 }
2450 
2451                 // try to split a line after about 255 characters
2452                 // at a space character unless in a PRE-context
2453                 if( ' ' == c && rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE  )
2454                 {
2455                     sal_Int32 nLineLen;
2456                     nLineLen = rHTMLWrt.GetLineLen();
2457 
2458                     sal_Int32 nWordLen = rStr.indexOf( ' ', nStrPos+1 );
2459                     if( nWordLen == -1 )
2460                         nWordLen = nEnd;
2461                     nWordLen -= nStrPos;
2462 
2463                     if( nLineLen >= rHTMLWrt.m_nWishLineLen ||
2464                         (nLineLen+nWordLen) >= rHTMLWrt.m_nWishLineLen )
2465                     {
2466                         HTMLOutFuncs::FlushToAscii( rWrt.Strm(), aContext );
2467                         rHTMLWrt.OutNewLine();
2468                         bOutChar = false;
2469                     }
2470                 }
2471 
2472                 if( bOutChar )
2473                 {
2474                     if( 0x0a == c )
2475                     {
2476                         HTMLOutFuncs::FlushToAscii( rWrt.Strm(), aContext );
2477                         HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
2478                         aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak);
2479                     }
2480                     else if (c == CH_TXT_ATR_FORMELEMENT)
2481                     {
2482                         // Placeholder for a single-point fieldmark.
2483 
2484                         SwPosition aMarkPos = *rWrt.m_pCurrentPam->GetPoint();
2485                         aMarkPos.nContent += nStrPos - aMarkPos.nContent.GetIndex();
2486                         rHTMLWrt.OutPointFieldmarks(aMarkPos);
2487                     }
2488                     else
2489                         HTMLOutFuncs::Out_Char( rWrt.Strm(), c, aContext, &rHTMLWrt.m_aNonConvertableCharacters );
2490 
2491                     // if a paragraph's last character is a hard line break
2492                     // then we need to add an extra <br>
2493                     // because browsers like Mozilla wouldn't add a line for the next paragraph
2494                     bWriteBreak = (0x0a == c) &&
2495                                   (HtmlTokenId::PREFORMTXT_ON != rHTMLWrt.m_nLastParaToken);
2496                 }
2497             }
2498         }
2499         HTMLOutFuncs::FlushToAscii( rWrt.Strm(), aContext );
2500     }
2501 
2502     aEndPosLst.OutEndAttrs( rHTMLWrt, SAL_MAX_INT32 );
2503 
2504     // Output the frames that are anchored to the last position
2505     if( bFlysLeft )
2506         bFlysLeft = rHTMLWrt.OutFlyFrame( rNode.GetIndex(),
2507                                        nEnd, HtmlPosition::Inside );
2508     OSL_ENSURE( !bFlysLeft, "Not all frames were saved!" );
2509 
2510     rHTMLWrt.m_bTextAttr = false;
2511 
2512     if( bWriteBreak )
2513     {
2514         bool bEndOfCell = rHTMLWrt.m_bOutTable &&
2515                          rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() ==
2516                          rWrt.m_pCurrentPam->GetMark()->nNode.GetIndex();
2517 
2518         if( bEndOfCell && !nEnd &&
2519             rHTMLWrt.IsHTMLMode(HTMLMODE_NBSP_IN_TABLES) )
2520         {
2521             // If the last paragraph of a table cell is empty and we export
2522             // for the MS-IE, we write a &nbsp; instead of a <BR>
2523             rWrt.Strm().WriteChar( '&' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_S_nbsp ).WriteChar( ';' );
2524         }
2525         else
2526         {
2527             HtmlWriter aHtml(rHTMLWrt.Strm(), rHTMLWrt.maNamespace);
2528             aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak);
2529             const SvxULSpaceItem& rULSpace = pNd->GetSwAttrSet().Get(RES_UL_SPACE);
2530             if (rULSpace.GetLower() > 0 && !bEndOfCell)
2531             {
2532                 aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak);
2533             }
2534             rHTMLWrt.m_bLFPossible = true;
2535         }
2536     }
2537 
2538     if( rHTMLWrt.m_bClearLeft || rHTMLWrt.m_bClearRight )
2539     {
2540         const char* pString;
2541         if( rHTMLWrt.m_bClearLeft )
2542         {
2543             if( rHTMLWrt.m_bClearRight )
2544                 pString = OOO_STRING_SVTOOLS_HTML_AL_all;
2545             else
2546                 pString = OOO_STRING_SVTOOLS_HTML_AL_left;
2547         }
2548         else
2549         {
2550             pString = OOO_STRING_SVTOOLS_HTML_AL_right;
2551         }
2552 
2553         HtmlWriter aHtml(rHTMLWrt.Strm(), rHTMLWrt.maNamespace);
2554         aHtml.start(OOO_STRING_SVTOOLS_HTML_linebreak);
2555         aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, pString);
2556         aHtml.end();
2557 
2558         rHTMLWrt.m_bClearLeft = false;
2559         rHTMLWrt.m_bClearRight = false;
2560 
2561         rHTMLWrt.m_bLFPossible = true;
2562     }
2563 
2564     // if an LF is not allowed already, it is allowed once the paragraphs
2565     // ends with a ' '
2566     if( !rHTMLWrt.m_bLFPossible &&
2567         rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE &&
2568         nEnd > 0 && ' ' == rStr[nEnd-1] )
2569         rHTMLWrt.m_bLFPossible = true;
2570 
2571     // dot leaders: print the skipped page number in a different span element
2572     if (nIndexTab > -1) {
2573         OString sOut = OUStringToOString(rStr.subView(nIndexTab + 1), RTL_TEXTENCODING_ASCII_US);
2574         rWrt.Strm().WriteOString( OString("</span><span>" + sOut + "</span>") );
2575     }
2576 
2577     rHTMLWrt.m_bTagOn = false;
2578     OutHTML_SwFormatOff( rWrt, aFormatInfo );
2579 
2580     // if necessary, close a form
2581     rHTMLWrt.OutForm( false );
2582 
2583     if( bPageBreakBehind )
2584         rWrt.Strm().WriteChar( '\f' );
2585 
2586     return rHTMLWrt;
2587 }
2588 
ToPixel(sal_uInt32 nVal,const bool bVert)2589 sal_uInt32 SwHTMLWriter::ToPixel( sal_uInt32 nVal, const bool bVert )
2590 {
2591     if( Application::GetDefaultDevice() && nVal )
2592     {
2593         Size aSz( bVert ? 0 : nVal, bVert ? nVal : 0 );
2594         aSz = Application::GetDefaultDevice()->LogicToPixel(aSz, MapMode( MapUnit::MapTwip ));
2595         nVal = bVert ? aSz.Height() : aSz.Width();
2596         if( !nVal )     // if there is a Twip, there should be a pixel as well
2597             nVal = 1;
2598     }
2599     return nVal;
2600 }
2601 
OutHTML_CSS1Attr(Writer & rWrt,const SfxPoolItem & rHt)2602 static Writer& OutHTML_CSS1Attr( Writer& rWrt, const SfxPoolItem& rHt )
2603 {
2604     // if hints are currently written, we try to write the hint as an
2605     // CSS1 attribute
2606 
2607     if( static_cast<SwHTMLWriter&>(rWrt).m_bCfgOutStyles && static_cast<SwHTMLWriter&>(rWrt).m_bTextAttr )
2608         OutCSS1_HintSpanTag( rWrt, rHt );
2609 
2610     return rWrt;
2611 }
2612 
2613 /* File CHRATR.HXX: */
2614 
OutHTML_SvxColor(Writer & rWrt,const SfxPoolItem & rHt)2615 static Writer& OutHTML_SvxColor( Writer& rWrt, const SfxPoolItem& rHt )
2616 {
2617     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2618     if( rHTMLWrt.m_bOutOpts )
2619         return rWrt;
2620 
2621     if( !rHTMLWrt.m_bTextAttr && rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bCfgPreferStyles )
2622     {
2623         // don't write the font color as a tag, if styles are preferred to
2624         // normal tags
2625         return rWrt;
2626     }
2627 
2628     if( rHTMLWrt.m_bTagOn )
2629     {
2630         Color aColor( static_cast<const SvxColorItem&>(rHt).GetValue() );
2631         if( COL_AUTO == aColor )
2632             aColor = COL_BLACK;
2633 
2634         if (rHTMLWrt.mbXHTML)
2635         {
2636             OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span
2637                            " " OOO_STRING_SVTOOLS_HTML_O_style "=";
2638             rWrt.Strm().WriteOString(sOut);
2639             HTMLOutFuncs::Out_Color(rWrt.Strm(), aColor, /*bXHTML=*/true).WriteChar('>');
2640         }
2641         else
2642         {
2643             OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font " "
2644                 OOO_STRING_SVTOOLS_HTML_O_color "=";
2645             rWrt.Strm().WriteOString( sOut );
2646             HTMLOutFuncs::Out_Color( rWrt.Strm(), aColor ).WriteChar( '>' );
2647         }
2648     }
2649     else
2650     {
2651         if (rHTMLWrt.mbXHTML)
2652             HTMLOutFuncs::Out_AsciiTag(
2653                 rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
2654         else
2655             HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font), false );
2656     }
2657 
2658     return rWrt;
2659 }
2660 
OutHTML_SwPosture(Writer & rWrt,const SfxPoolItem & rHt)2661 static Writer& OutHTML_SwPosture( Writer& rWrt, const SfxPoolItem& rHt )
2662 {
2663     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2664     if( rHTMLWrt.m_bOutOpts )
2665         return rWrt;
2666 
2667     const FontItalic nPosture = static_cast<const SvxPostureItem&>(rHt).GetPosture();
2668     if( ITALIC_NORMAL == nPosture )
2669     {
2670         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_italic), rHTMLWrt.m_bTagOn );
2671     }
2672     else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr )
2673     {
2674         // maybe as CSS1 attribute?
2675         OutCSS1_HintSpanTag( rWrt, rHt );
2676     }
2677 
2678     return rWrt;
2679 }
2680 
OutHTML_SvxFont(Writer & rWrt,const SfxPoolItem & rHt)2681 static Writer& OutHTML_SvxFont( Writer& rWrt, const SfxPoolItem& rHt )
2682 {
2683     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2684     if( rHTMLWrt.m_bOutOpts )
2685         return rWrt;
2686 
2687     if (IgnorePropertyForReqIF(rHTMLWrt.mbReqIF, "font-family", ""))
2688     {
2689         return rWrt;
2690     }
2691 
2692     if( rHTMLWrt.m_bTagOn )
2693     {
2694         OUString aNames;
2695         SwHTMLWriter::PrepareFontList( static_cast<const SvxFontItem&>(rHt), aNames, 0,
2696                            rHTMLWrt.IsHTMLMode(HTMLMODE_FONT_GENERIC) );
2697         if (rHTMLWrt.mbXHTML)
2698         {
2699             OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span
2700                            " " OOO_STRING_SVTOOLS_HTML_O_style "=\"font-family: ";
2701             rWrt.Strm().WriteOString(sOut);
2702             HTMLOutFuncs::Out_String(rWrt.Strm(), aNames, rHTMLWrt.m_eDestEnc,
2703                                      &rHTMLWrt.m_aNonConvertableCharacters)
2704                 .WriteCharPtr("\">");
2705         }
2706         else
2707         {
2708             OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font " "
2709                 OOO_STRING_SVTOOLS_HTML_O_face "=\"";
2710             rWrt.Strm().WriteOString( sOut );
2711             HTMLOutFuncs::Out_String( rWrt.Strm(), aNames, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters )
2712                .WriteCharPtr( "\">" );
2713         }
2714     }
2715     else
2716     {
2717         if (rHTMLWrt.mbXHTML)
2718             HTMLOutFuncs::Out_AsciiTag(
2719                 rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
2720         else
2721             HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font), false );
2722     }
2723 
2724     return rWrt;
2725 }
2726 
OutHTML_SvxFontHeight(Writer & rWrt,const SfxPoolItem & rHt)2727 static Writer& OutHTML_SvxFontHeight( Writer& rWrt, const SfxPoolItem& rHt )
2728 {
2729     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2730     if( rHTMLWrt.m_bOutOpts )
2731         return rWrt;
2732 
2733     if (IgnorePropertyForReqIF(rHTMLWrt.mbReqIF, "font-size", ""))
2734     {
2735         return rWrt;
2736     }
2737 
2738     if( rHTMLWrt.m_bTagOn )
2739     {
2740         if (rHTMLWrt.mbXHTML)
2741         {
2742             OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span;
2743 
2744             sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight();
2745             // Twips -> points.
2746             sal_uInt16 nSize = nHeight / 20;
2747             sOut += " " OOO_STRING_SVTOOLS_HTML_O_style "=\"font-size: "
2748                     + OString::number(static_cast<sal_Int32>(nSize)) + "pt\"";
2749             rWrt.Strm().WriteOString(sOut);
2750         }
2751         else
2752         {
2753             OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font;
2754 
2755             sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight();
2756             sal_uInt16 nSize = rHTMLWrt.GetHTMLFontSize( nHeight );
2757             sOut += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" +
2758                 OString::number(static_cast<sal_Int32>(nSize)) + "\"";
2759             rWrt.Strm().WriteOString( sOut );
2760 
2761             if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr )
2762             {
2763                 // always export font size as CSS option, too
2764                 OutCSS1_HintStyleOpt( rWrt, rHt );
2765             }
2766         }
2767         rWrt.Strm().WriteChar( '>' );
2768     }
2769     else
2770     {
2771         if (rHTMLWrt.mbXHTML)
2772             HTMLOutFuncs::Out_AsciiTag(
2773                 rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
2774         else
2775             HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font), false );
2776     }
2777 
2778     return rWrt;
2779 }
2780 
OutHTML_SvxLanguage(Writer & rWrt,const SfxPoolItem & rHt)2781 static Writer& OutHTML_SvxLanguage( Writer& rWrt, const SfxPoolItem& rHt )
2782 {
2783     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2784     if( rHTMLWrt.m_bOutOpts )
2785         return rWrt;
2786 
2787     LanguageType eLang = static_cast<const SvxLanguageItem &>(rHt).GetLanguage();
2788     if( LANGUAGE_DONTKNOW == eLang )
2789         return rWrt;
2790 
2791     if( rHTMLWrt.m_bTagOn )
2792     {
2793         OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span;
2794         rWrt.Strm().WriteOString( sOut );
2795         rHTMLWrt.OutLanguage( static_cast<const SvxLanguageItem &>(rHt).GetLanguage() );
2796         rWrt.Strm().WriteChar( '>' );
2797     }
2798     else
2799     {
2800         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
2801     }
2802 
2803     return rWrt;
2804 }
OutHTML_SwWeight(Writer & rWrt,const SfxPoolItem & rHt)2805 static Writer& OutHTML_SwWeight( Writer& rWrt, const SfxPoolItem& rHt )
2806 {
2807     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2808     if( rHTMLWrt.m_bOutOpts )
2809         return rWrt;
2810 
2811     const FontWeight nBold = static_cast<const SvxWeightItem&>(rHt).GetWeight();
2812     if( WEIGHT_BOLD == nBold )
2813     {
2814         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_bold), rHTMLWrt.m_bTagOn );
2815     }
2816     else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr )
2817     {
2818         // maybe as CSS1 attribute ?
2819         OutCSS1_HintSpanTag( rWrt, rHt );
2820     }
2821 
2822     return rWrt;
2823 }
2824 
OutHTML_SwCrossedOut(Writer & rWrt,const SfxPoolItem & rHt)2825 static Writer& OutHTML_SwCrossedOut( Writer& rWrt, const SfxPoolItem& rHt )
2826 {
2827     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2828     if( rHTMLWrt.m_bOutOpts )
2829         return rWrt;
2830 
2831     // Because of Netscape, we output STRIKE and not S!
2832     const FontStrikeout nStrike = static_cast<const SvxCrossedOutItem&>(rHt).GetStrikeout();
2833     if( STRIKEOUT_NONE != nStrike && !rHTMLWrt.mbReqIF )
2834     {
2835         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_strike), rHTMLWrt.m_bTagOn );
2836     }
2837     else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr )
2838     {
2839         // maybe as CSS1 attribute?
2840         OutCSS1_HintSpanTag( rWrt, rHt );
2841     }
2842 
2843     return rWrt;
2844 }
2845 
OutHTML_SvxEscapement(Writer & rWrt,const SfxPoolItem & rHt)2846 static Writer& OutHTML_SvxEscapement( Writer& rWrt, const SfxPoolItem& rHt )
2847 {
2848     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2849     if( rHTMLWrt.m_bOutOpts )
2850         return rWrt;
2851 
2852     const SvxEscapement eEscape =
2853         static_cast<SvxEscapement>(static_cast<const SvxEscapementItem&>(rHt).GetEnumValue());
2854     OString aTag;
2855     switch( eEscape )
2856     {
2857     case SvxEscapement::Superscript: aTag = OOO_STRING_SVTOOLS_HTML_superscript; break;
2858     case SvxEscapement::Subscript: aTag = OOO_STRING_SVTOOLS_HTML_subscript; break;
2859     default:
2860         ;
2861     }
2862 
2863     if( !aTag.isEmpty() )
2864     {
2865         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + aTag), rHTMLWrt.m_bTagOn );
2866     }
2867     else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr )
2868     {
2869         // maybe as CSS1 attribute?
2870         OutCSS1_HintSpanTag( rWrt, rHt );
2871     }
2872 
2873     return rWrt;
2874 }
2875 
OutHTML_SwUnderline(Writer & rWrt,const SfxPoolItem & rHt)2876 static Writer& OutHTML_SwUnderline( Writer& rWrt, const SfxPoolItem& rHt )
2877 {
2878     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2879     if( rHTMLWrt.m_bOutOpts )
2880         return rWrt;
2881 
2882     const FontLineStyle eUnder = static_cast<const SvxUnderlineItem&>(rHt).GetLineStyle();
2883     if( LINESTYLE_NONE != eUnder && !rHTMLWrt.mbReqIF )
2884     {
2885         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_underline), rHTMLWrt.m_bTagOn );
2886     }
2887     else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr )
2888     {
2889         // maybe as CSS1 attribute?
2890         OutCSS1_HintSpanTag( rWrt, rHt );
2891     }
2892 
2893     return rWrt;
2894 }
2895 
OutHTML_SwFlyCnt(Writer & rWrt,const SfxPoolItem & rHt)2896 static Writer& OutHTML_SwFlyCnt( Writer& rWrt, const SfxPoolItem& rHt )
2897 {
2898     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2899     const SwFormatFlyCnt& rFlyCnt = static_cast<const SwFormatFlyCnt&>(rHt);
2900 
2901     const SwFrameFormat& rFormat = *rFlyCnt.GetFrameFormat();
2902     const SdrObject *pSdrObj = nullptr;
2903 
2904     SwHTMLFrameType eType =
2905         static_cast<SwHTMLFrameType>(rHTMLWrt.GuessFrameType( rFormat, pSdrObj ));
2906     AllHtmlFlags nMode = aHTMLOutFrameAsCharTable[eType][rHTMLWrt.m_nExportMode];
2907     rHTMLWrt.OutFrameFormat( nMode, rFormat, pSdrObj );
2908     return rWrt;
2909 }
2910 
2911 // This is now our Blink item. Blinking is activated by setting the item to
2912 // true!
OutHTML_SwBlink(Writer & rWrt,const SfxPoolItem & rHt)2913 static Writer& OutHTML_SwBlink( Writer& rWrt, const SfxPoolItem& rHt )
2914 {
2915     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2916     if( rHTMLWrt.m_bOutOpts )
2917         return rWrt;
2918 
2919     if( static_cast<const SvxBlinkItem&>(rHt).GetValue() )
2920     {
2921         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_blink), rHTMLWrt.m_bTagOn );
2922     }
2923     else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr )
2924     {
2925         // maybe as CSS1 attribute?
2926         OutCSS1_HintSpanTag( rWrt, rHt );
2927     }
2928 
2929     return rWrt;
2930 }
2931 
OutHTML_INetFormat(Writer & rWrt,const SwFormatINetFormat & rINetFormat,bool bOn)2932 Writer& OutHTML_INetFormat( Writer& rWrt, const SwFormatINetFormat& rINetFormat, bool bOn )
2933 {
2934     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2935 
2936     OUString aURL( rINetFormat.GetValue() );
2937     const SvxMacroTableDtor *pMacTable = rINetFormat.GetMacroTable();
2938     bool bEvents = pMacTable != nullptr && !pMacTable->empty();
2939 
2940     // Anything to output at all?
2941     if( aURL.isEmpty() && !bEvents && rINetFormat.GetName().isEmpty() )
2942         return rWrt;
2943 
2944     // bOn controls if we are writing the opening or closing tag
2945     if( !bOn )
2946     {
2947         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
2948         return rWrt;
2949     }
2950 
2951     OString sOut("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor);
2952 
2953     bool bScriptDependent = false;
2954     {
2955         const SwCharFormat* pFormat = rWrt.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
2956                  RES_POOLCHR_INET_NORMAL );
2957         std::unique_ptr<SwHTMLFormatInfo> pFormatInfo(new SwHTMLFormatInfo(pFormat));
2958         auto const it = rHTMLWrt.m_CharFormatInfos.find( pFormatInfo );
2959         if (it != rHTMLWrt.m_CharFormatInfos.end())
2960         {
2961             bScriptDependent = (*it)->bScriptDependent;
2962         }
2963     }
2964     if( !bScriptDependent )
2965     {
2966         const SwCharFormat* pFormat = rWrt.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
2967                  RES_POOLCHR_INET_VISIT );
2968         std::unique_ptr<SwHTMLFormatInfo> pFormatInfo(new SwHTMLFormatInfo(pFormat));
2969         auto const it = rHTMLWrt.m_CharFormatInfos.find( pFormatInfo );
2970         if (it != rHTMLWrt.m_CharFormatInfos.end())
2971         {
2972             bScriptDependent = (*it)->bScriptDependent;
2973         }
2974     }
2975 
2976     if( bScriptDependent )
2977     {
2978         sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"";
2979         const char* pStr = nullptr;
2980         switch( rHTMLWrt.m_nCSS1Script )
2981         {
2982         case CSS1_OUTMODE_WESTERN:
2983             pStr = "western";
2984             break;
2985         case CSS1_OUTMODE_CJK:
2986             pStr = "cjk";
2987             break;
2988         case CSS1_OUTMODE_CTL:
2989             pStr = "ctl";
2990             break;
2991         }
2992         sOut += pStr + OString::Concat("\"");
2993     }
2994 
2995     rWrt.Strm().WriteOString( sOut );
2996     sOut = "";
2997 
2998     OUString sRel;
2999 
3000     if( !aURL.isEmpty() || bEvents )
3001     {
3002         OUString sTmp( aURL.toAsciiUpperCase() );
3003         sal_Int32 nPos = sTmp.indexOf( "\" REL=" );
3004         if( nPos >= 0 )
3005         {
3006             sRel = aURL.copy( nPos+1 );
3007             aURL = aURL.copy( 0, nPos);
3008         }
3009         aURL = comphelper::string::strip(aURL, ' ');
3010 
3011         sOut += " " OOO_STRING_SVTOOLS_HTML_O_href "=\"";
3012         rWrt.Strm().WriteOString( sOut );
3013         rHTMLWrt.OutHyperlinkHRefValue( aURL );
3014         sOut = "\"";
3015     }
3016 
3017     if( !rINetFormat.GetName().isEmpty() )
3018     {
3019         sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\"";
3020         rWrt.Strm().WriteOString( sOut );
3021         HTMLOutFuncs::Out_String( rWrt.Strm(), rINetFormat.GetName(),
3022                                   rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters );
3023         sOut = "\"";
3024     }
3025 
3026     const OUString& rTarget = rINetFormat.GetTargetFrame();
3027     if( !rTarget.isEmpty() )
3028     {
3029         sOut += " " OOO_STRING_SVTOOLS_HTML_O_target "=\"";
3030         rWrt.Strm().WriteOString( sOut );
3031         HTMLOutFuncs::Out_String( rWrt.Strm(), rTarget, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters );
3032         sOut = "\"";
3033     }
3034 
3035     if( !sRel.isEmpty() )
3036         sOut += OUStringToOString(sRel, RTL_TEXTENCODING_ASCII_US);
3037 
3038     if( !sOut.isEmpty() )
3039         rWrt.Strm().WriteOString( sOut );
3040 
3041     if( bEvents )
3042         HTMLOutFuncs::Out_Events( rWrt.Strm(), *pMacTable, aAnchorEventTable,
3043                                   rHTMLWrt.m_bCfgStarBasic, rHTMLWrt.m_eDestEnc,
3044                                      &rHTMLWrt.m_aNonConvertableCharacters    );
3045     rWrt.Strm().WriteCharPtr( ">" );
3046 
3047     return rWrt;
3048 }
3049 
OutHTML_SwFormatINetFormat(Writer & rWrt,const SfxPoolItem & rHt)3050 static Writer& OutHTML_SwFormatINetFormat( Writer& rWrt, const SfxPoolItem& rHt )
3051 {
3052     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
3053 
3054     if( rHTMLWrt.m_bOutOpts )
3055         return rWrt;
3056 
3057     const SwFormatINetFormat& rINetFormat = static_cast<const SwFormatINetFormat&>(rHt);
3058 
3059     if( rHTMLWrt.m_bTagOn )
3060     {
3061         // if necessary, temporarily close an attribute that is still open
3062         if( !rHTMLWrt.m_aINetFormats.empty() )
3063         {
3064             SwFormatINetFormat *pINetFormat =
3065                 rHTMLWrt.m_aINetFormats.back();
3066             OutHTML_INetFormat( rWrt, *pINetFormat, false );
3067         }
3068 
3069         // now, open the new one
3070         OutHTML_INetFormat( rWrt, rINetFormat, true );
3071 
3072         // and remember it
3073         SwFormatINetFormat *pINetFormat = new SwFormatINetFormat( rINetFormat );
3074         rHTMLWrt.m_aINetFormats.push_back( pINetFormat );
3075     }
3076     else
3077     {
3078         OutHTML_INetFormat( rWrt, rINetFormat, false );
3079 
3080         OSL_ENSURE( rHTMLWrt.m_aINetFormats.size(), "there must be a URL attribute missing" );
3081         if( !rHTMLWrt.m_aINetFormats.empty() )
3082         {
3083             // get its own attribute from the stack
3084             SwFormatINetFormat *pINetFormat = rHTMLWrt.m_aINetFormats.back();
3085             rHTMLWrt.m_aINetFormats.pop_back();
3086             delete pINetFormat;
3087         }
3088 
3089         if( !rHTMLWrt.m_aINetFormats.empty() )
3090         {
3091             // there is still an attribute on the stack that must be reopened
3092             SwFormatINetFormat *pINetFormat = rHTMLWrt.m_aINetFormats.back();
3093             OutHTML_INetFormat( rWrt, *pINetFormat, true );
3094         }
3095     }
3096 
3097     return rWrt;
3098 }
3099 
OutHTML_SwTextCharFormat(Writer & rWrt,const SfxPoolItem & rHt)3100 static Writer& OutHTML_SwTextCharFormat( Writer& rWrt, const SfxPoolItem& rHt )
3101 {
3102     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
3103     if( rHTMLWrt.m_bOutOpts )
3104         return rWrt;
3105 
3106     const SwFormatCharFormat& rChrFormat = static_cast<const SwFormatCharFormat&>(rHt);
3107     const SwCharFormat* pFormat = rChrFormat.GetCharFormat();
3108 
3109     if( !pFormat )
3110     {
3111         return rWrt;
3112     }
3113 
3114     std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(pFormat));
3115     SwHTMLFormatInfos::const_iterator it = rHTMLWrt.m_CharFormatInfos.find(pTmpInfo);
3116     if (it == rHTMLWrt.m_CharFormatInfos.end())
3117         return rWrt;
3118 
3119     const SwHTMLFormatInfo *pFormatInfo = it->get();
3120     OSL_ENSURE( pFormatInfo, "Why is there no information about the character style?" );
3121 
3122     if( rHTMLWrt.m_bTagOn )
3123     {
3124         OString sOut = "<" + rHTMLWrt.GetNamespace();
3125         if( !pFormatInfo->aToken.isEmpty() )
3126             sOut += pFormatInfo->aToken;
3127         else
3128             sOut += OOO_STRING_SVTOOLS_HTML_span;
3129 
3130         if( rHTMLWrt.m_bCfgOutStyles &&
3131             (!pFormatInfo->aClass.isEmpty() || pFormatInfo->bScriptDependent) )
3132         {
3133             sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"";
3134             rWrt.Strm().WriteOString( sOut );
3135             OUString aClass( pFormatInfo->aClass );
3136             if( pFormatInfo->bScriptDependent )
3137             {
3138                 if( !aClass.isEmpty() )
3139                    aClass += "-";
3140                 switch( rHTMLWrt.m_nCSS1Script )
3141                 {
3142                 case CSS1_OUTMODE_WESTERN:
3143                     aClass += "western";
3144                     break;
3145                 case CSS1_OUTMODE_CJK:
3146                     aClass += "cjk";
3147                     break;
3148                 case CSS1_OUTMODE_CTL:
3149                     aClass += "ctl";
3150                     break;
3151                 }
3152             }
3153             HTMLOutFuncs::Out_String( rWrt.Strm(), aClass,
3154                                           rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters );
3155             sOut = "\"";
3156         }
3157         sOut += ">";
3158         rWrt.Strm().WriteOString( sOut );
3159     }
3160     else
3161     {
3162         OString aTag = !pFormatInfo->aToken.isEmpty() ? pFormatInfo->aToken.getStr()
3163                                                       : OOO_STRING_SVTOOLS_HTML_span;
3164         HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + aTag), false);
3165     }
3166 
3167     return rWrt;
3168 }
3169 
OutHTML_SvxAdjust(Writer & rWrt,const SfxPoolItem & rHt)3170 static Writer& OutHTML_SvxAdjust( Writer& rWrt, const SfxPoolItem& rHt )
3171 {
3172     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
3173     if( !rHTMLWrt.m_bOutOpts || !rHTMLWrt.m_bTagOn )
3174         return  rWrt;
3175 
3176     const SvxAdjustItem& rAdjust = static_cast<const SvxAdjustItem&>(rHt);
3177     const char* pStr = nullptr;
3178     switch( rAdjust.GetAdjust() )
3179     {
3180     case SvxAdjust::Center: pStr = OOO_STRING_SVTOOLS_HTML_AL_center; break;
3181     case SvxAdjust::Left: pStr = OOO_STRING_SVTOOLS_HTML_AL_left; break;
3182     case SvxAdjust::Right: pStr = OOO_STRING_SVTOOLS_HTML_AL_right; break;
3183     case SvxAdjust::Block: pStr = OOO_STRING_SVTOOLS_HTML_AL_justify; break;
3184     default:
3185         ;
3186     }
3187     if( pStr )
3188     {
3189         OString sOut = OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"") +
3190             pStr + "\"";
3191         rWrt.Strm().WriteOString( sOut );
3192     }
3193 
3194     return rWrt;
3195 }
3196 
3197 /*
3198  * here, define the table for the HTML function pointers to the output
3199  * functions.
3200  */
3201 
3202 SwAttrFnTab aHTMLAttrFnTab = {
3203 /* RES_CHRATR_CASEMAP   */          OutHTML_CSS1Attr,
3204 /* RES_CHRATR_CHARSETCOLOR  */      nullptr,
3205 /* RES_CHRATR_COLOR */              OutHTML_SvxColor,
3206 /* RES_CHRATR_CONTOUR   */          nullptr,
3207 /* RES_CHRATR_CROSSEDOUT    */      OutHTML_SwCrossedOut,
3208 /* RES_CHRATR_ESCAPEMENT    */      OutHTML_SvxEscapement,
3209 /* RES_CHRATR_FONT  */              OutHTML_SvxFont,
3210 /* RES_CHRATR_FONTSIZE  */          OutHTML_SvxFontHeight,
3211 /* RES_CHRATR_KERNING   */          OutHTML_CSS1Attr,
3212 /* RES_CHRATR_LANGUAGE  */          OutHTML_SvxLanguage,
3213 /* RES_CHRATR_POSTURE   */          OutHTML_SwPosture,
3214 /* RES_CHRATR_UNUSED1*/             nullptr,
3215 /* RES_CHRATR_SHADOWED  */          nullptr,
3216 /* RES_CHRATR_UNDERLINE */          OutHTML_SwUnderline,
3217 /* RES_CHRATR_WEIGHT    */          OutHTML_SwWeight,
3218 /* RES_CHRATR_WORDLINEMODE  */      nullptr,
3219 /* RES_CHRATR_AUTOKERN  */          nullptr,
3220 /* RES_CHRATR_BLINK */              OutHTML_SwBlink,
3221 /* RES_CHRATR_NOHYPHEN  */          nullptr, // New: don't hyphenate
3222 /* RES_CHRATR_UNUSED2 */            nullptr,
3223 /* RES_CHRATR_BACKGROUND */         OutHTML_CSS1Attr, // New: character background
3224 /* RES_CHRATR_CJK_FONT */           OutHTML_SvxFont,
3225 /* RES_CHRATR_CJK_FONTSIZE */       OutHTML_SvxFontHeight,
3226 /* RES_CHRATR_CJK_LANGUAGE */       OutHTML_SvxLanguage,
3227 /* RES_CHRATR_CJK_POSTURE */        OutHTML_SwPosture,
3228 /* RES_CHRATR_CJK_WEIGHT */         OutHTML_SwWeight,
3229 /* RES_CHRATR_CTL_FONT */           OutHTML_SvxFont,
3230 /* RES_CHRATR_CTL_FONTSIZE */       OutHTML_SvxFontHeight,
3231 /* RES_CHRATR_CTL_LANGUAGE */       OutHTML_SvxLanguage,
3232 /* RES_CHRATR_CTL_POSTURE */        OutHTML_SwPosture,
3233 /* RES_CHRATR_CTL_WEIGHT */         OutHTML_SwWeight,
3234 /* RES_CHRATR_ROTATE */             nullptr,
3235 /* RES_CHRATR_EMPHASIS_MARK */      nullptr,
3236 /* RES_CHRATR_TWO_LINES */          nullptr,
3237 /* RES_CHRATR_SCALEW */             nullptr,
3238 /* RES_CHRATR_RELIEF */             nullptr,
3239 /* RES_CHRATR_HIDDEN */             OutHTML_CSS1Attr,
3240 /* RES_CHRATR_OVERLINE */           OutHTML_CSS1Attr,
3241 /* RES_CHRATR_RSID */               nullptr,
3242 /* RES_CHRATR_BOX */                OutHTML_CSS1Attr,
3243 /* RES_CHRATR_SHADOW */             nullptr,
3244 /* RES_CHRATR_HIGHLIGHT */          nullptr,
3245 /* RES_CHRATR_GRABBAG */            nullptr,
3246 /* RES_CHRATR_BIDIRTL */            nullptr,
3247 /* RES_CHRATR_IDCTHINT */           nullptr,
3248 
3249 /* RES_TXTATR_REFMARK */            nullptr,
3250 /* RES_TXTATR_TOXMARK */            nullptr,
3251 /* RES_TXTATR_META */               nullptr,
3252 /* RES_TXTATR_METAFIELD */          nullptr,
3253 /* RES_TXTATR_AUTOFMT */            nullptr,
3254 /* RES_TXTATR_INETFMT */            OutHTML_SwFormatINetFormat,
3255 /* RES_TXTATR_CHARFMT */            OutHTML_SwTextCharFormat,
3256 /* RES_TXTATR_CJK_RUBY */           nullptr,
3257 /* RES_TXTATR_UNKNOWN_CONTAINER */  nullptr,
3258 /* RES_TXTATR_INPUTFIELD */         OutHTML_SwFormatField,
3259 
3260 /* RES_TXTATR_FIELD */              OutHTML_SwFormatField,
3261 /* RES_TXTATR_FLYCNT */             OutHTML_SwFlyCnt,
3262 /* RES_TXTATR_FTN */                OutHTML_SwFormatFootnote,
3263 /* RES_TXTATR_ANNOTATION */         OutHTML_SwFormatField,
3264 /* RES_TXTATR_DUMMY3 */             nullptr,
3265 /* RES_TXTATR_DUMMY1 */             nullptr, // Dummy:
3266 /* RES_TXTATR_DUMMY2 */             nullptr, // Dummy:
3267 
3268 /* RES_PARATR_LINESPACING   */      nullptr,
3269 /* RES_PARATR_ADJUST    */          OutHTML_SvxAdjust,
3270 /* RES_PARATR_SPLIT */              nullptr,
3271 /* RES_PARATR_WIDOWS    */          nullptr,
3272 /* RES_PARATR_ORPHANS   */          nullptr,
3273 /* RES_PARATR_TABSTOP   */          nullptr,
3274 /* RES_PARATR_HYPHENZONE*/          nullptr,
3275 /* RES_PARATR_DROP */               OutHTML_CSS1Attr,
3276 /* RES_PARATR_REGISTER */           nullptr, // new:  register-true
3277 /* RES_PARATR_NUMRULE */            nullptr, // Dummy:
3278 /* RES_PARATR_SCRIPTSPACE */        nullptr, // Dummy:
3279 /* RES_PARATR_HANGINGPUNCTUATION */ nullptr, // Dummy:
3280 /* RES_PARATR_FORBIDDEN_RULES */    nullptr, // new
3281 /* RES_PARATR_VERTALIGN */          nullptr, // new
3282 /* RES_PARATR_SNAPTOGRID*/          nullptr, // new
3283 /* RES_PARATR_CONNECT_TO_BORDER */  nullptr, // new
3284 /* RES_PARATR_OUTLINELEVEL */       nullptr,
3285 /* RES_PARATR_RSID */               nullptr,
3286 /* RES_PARATR_GRABBAG */            nullptr,
3287 
3288 /* RES_PARATR_LIST_ID */            nullptr, // new
3289 /* RES_PARATR_LIST_LEVEL */         nullptr, // new
3290 /* RES_PARATR_LIST_ISRESTART */     nullptr, // new
3291 /* RES_PARATR_LIST_RESTARTVALUE */  nullptr, // new
3292 /* RES_PARATR_LIST_ISCOUNTED */     nullptr, // new
3293 
3294 /* RES_FILL_ORDER   */              nullptr,
3295 /* RES_FRM_SIZE */                  nullptr,
3296 /* RES_PAPER_BIN    */              nullptr,
3297 /* RES_LR_SPACE */                  nullptr,
3298 /* RES_UL_SPACE */                  nullptr,
3299 /* RES_PAGEDESC */                  nullptr,
3300 /* RES_BREAK */                     nullptr,
3301 /* RES_CNTNT */                     nullptr,
3302 /* RES_HEADER */                    nullptr,
3303 /* RES_FOOTER */                    nullptr,
3304 /* RES_PRINT */                     nullptr,
3305 /* RES_OPAQUE */                    nullptr,
3306 /* RES_PROTECT */                   nullptr,
3307 /* RES_SURROUND */                  nullptr,
3308 /* RES_VERT_ORIENT */               nullptr,
3309 /* RES_HORI_ORIENT */               nullptr,
3310 /* RES_ANCHOR */                    nullptr,
3311 /* RES_BACKGROUND */                nullptr,
3312 /* RES_BOX  */                      nullptr,
3313 /* RES_SHADOW */                    nullptr,
3314 /* RES_FRMMACRO */                  nullptr,
3315 /* RES_COL */                       nullptr,
3316 /* RES_KEEP */                      nullptr,
3317 /* RES_URL */                       nullptr,
3318 /* RES_EDIT_IN_READONLY */          nullptr,
3319 /* RES_LAYOUT_SPLIT */              nullptr,
3320 /* RES_CHAIN */                     nullptr,
3321 /* RES_TEXTGRID */                  nullptr,
3322 /* RES_LINENUMBER */                nullptr,
3323 /* RES_FTN_AT_TXTEND */             nullptr,
3324 /* RES_END_AT_TXTEND */             nullptr,
3325 /* RES_COLUMNBALANCE */             nullptr,
3326 /* RES_FRAMEDIR */                  nullptr,
3327 /* RES_HEADER_FOOTER_EAT_SPACING */ nullptr,
3328 /* RES_ROW_SPLIT */                 nullptr,
3329 /* RES_FOLLOW_TEXT_FLOW */          nullptr,
3330 /* RES_COLLAPSING_BORDERS */        nullptr,
3331 /* RES_WRAP_INFLUENCE_ON_OBJPOS */  nullptr,
3332 /* RES_AUTO_STYLE */                nullptr,
3333 /* RES_FRMATR_STYLE_NAME */         nullptr,
3334 /* RES_FRMATR_CONDITIONAL_STYLE_NAME */ nullptr,
3335 /* RES_FRMATR_GRABBAG */            nullptr,
3336 /* RES_TEXT_VERT_ADJUST */          nullptr,
3337 
3338 /* RES_GRFATR_MIRRORGRF */          nullptr,
3339 /* RES_GRFATR_CROPGRF   */          nullptr,
3340 /* RES_GRFATR_ROTATION */           nullptr,
3341 /* RES_GRFATR_LUMINANCE */          nullptr,
3342 /* RES_GRFATR_CONTRAST */           nullptr,
3343 /* RES_GRFATR_CHANNELR */           nullptr,
3344 /* RES_GRFATR_CHANNELG */           nullptr,
3345 /* RES_GRFATR_CHANNELB */           nullptr,
3346 /* RES_GRFATR_GAMMA */              nullptr,
3347 /* RES_GRFATR_INVERT */             nullptr,
3348 /* RES_GRFATR_TRANSPARENCY */       nullptr,
3349 /* RES_GRFATR_DRWAMODE */           nullptr,
3350 /* RES_GRFATR_DUMMY1 */             nullptr,
3351 /* RES_GRFATR_DUMMY2 */             nullptr,
3352 /* RES_GRFATR_DUMMY3 */             nullptr,
3353 /* RES_GRFATR_DUMMY4 */             nullptr,
3354 /* RES_GRFATR_DUMMY5 */             nullptr,
3355 
3356 /* RES_BOXATR_FORMAT */             nullptr,
3357 /* RES_BOXATR_FORMULA */            nullptr,
3358 /* RES_BOXATR_VALUE */              nullptr
3359 };
3360 
3361 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3362