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 <com/sun/star/text/HoriOrientation.hpp>
21 #include <com/sun/star/text/VertOrientation.hpp>
22 #include <com/sun/star/text/RelOrientation.hpp>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <hintids.hxx>
25 #include <tools/fract.hxx>
26 #include <svl/urihelper.hxx>
27 #include <vcl/svapp.hxx>
28 #include <sfx2/event.hxx>
29 #include <svtools/htmlkywd.hxx>
30 #include <svtools/htmlout.hxx>
31 #include <svtools/htmltokn.h>
32 #include <vcl/imap.hxx>
33 #include <vcl/imapobj.hxx>
34 #include <svtools/htmlcfg.hxx>
35 #include <svtools/HtmlWriter.hxx>
36 #include <svx/svdouno.hxx>
37 #include <svx/xoutbmp.hxx>
38 #include <editeng/boxitem.hxx>
39 #include <editeng/lrspitem.hxx>
40 #include <editeng/ulspitem.hxx>
41 #include <editeng/brushitem.hxx>
42 #include <sal/log.hxx>
43 #include <osl/diagnose.h>
44 #include <svx/svdograf.hxx>
45 
46 #include <fmtanchr.hxx>
47 #include <fmtornt.hxx>
48 #include <fmturl.hxx>
49 #include <fmtfsize.hxx>
50 #include <fmtclds.hxx>
51 #include <fmtcntnt.hxx>
52 #include <fmtsrnd.hxx>
53 #include <fmtinfmt.hxx>
54 #include <txtinet.hxx>
55 #include <frmatr.hxx>
56 #include <grfatr.hxx>
57 #include <flypos.hxx>
58 #include <ndgrf.hxx>
59 
60 #include <doc.hxx>
61 #include <ndtxt.hxx>
62 #include <pam.hxx>
63 #include <swerror.h>
64 #include <frmfmt.hxx>
65 #include "wrthtml.hxx"
66 #include "htmlatr.hxx"
67 #include "htmlfly.hxx"
68 #include "htmlreqifreader.hxx"
69 
70 using namespace css;
71 
72 const HtmlFrmOpts HTML_FRMOPTS_IMG_ALL        =
73     HtmlFrmOpts::Alt |
74     HtmlFrmOpts::Size |
75     HtmlFrmOpts::AnySize |
76     HtmlFrmOpts::Border |
77     HtmlFrmOpts::Name;
78 const HtmlFrmOpts HTML_FRMOPTS_IMG_CNTNR      =
79     HTML_FRMOPTS_IMG_ALL |
80     HtmlFrmOpts::AbsSize;
81 const HtmlFrmOpts HTML_FRMOPTS_IMG            =
82     HTML_FRMOPTS_IMG_ALL |
83     HtmlFrmOpts::Align |
84     HtmlFrmOpts::Space |
85     HtmlFrmOpts::BrClear;
86 const HtmlFrmOpts HTML_FRMOPTS_IMG_CSS1       =
87     HtmlFrmOpts::SAlign |
88     HtmlFrmOpts::SSpace;
89 
90 const HtmlFrmOpts HTML_FRMOPTS_DIV            =
91     HtmlFrmOpts::Id |
92     HtmlFrmOpts::SAlign |
93     HtmlFrmOpts::SSize |
94     HtmlFrmOpts::AnySize |
95     HtmlFrmOpts::AbsSize |
96     HtmlFrmOpts::SSpace |
97     HtmlFrmOpts::SBorder |
98     HtmlFrmOpts::SBackground |
99     HtmlFrmOpts::BrClear |
100     HtmlFrmOpts::Dir;
101 
102 const HtmlFrmOpts HTML_FRMOPTS_MULTICOL       =
103     HtmlFrmOpts::Id |
104     HtmlFrmOpts::Width |
105     HtmlFrmOpts::AnySize |
106     HtmlFrmOpts::AbsSize |
107     HtmlFrmOpts::Dir;
108 
109 const HtmlFrmOpts HTML_FRMOPTS_MULTICOL_CSS1  =
110     HtmlFrmOpts::SAlign |
111     HtmlFrmOpts::SSize |
112     HtmlFrmOpts::SSpace |
113     HtmlFrmOpts::SBorder|
114     HtmlFrmOpts::SBackground;
115 
116 const HtmlFrmOpts HTML_FRMOPTS_SPACER         =
117     HtmlFrmOpts::Align |
118     HtmlFrmOpts::Size |
119     HtmlFrmOpts::AnySize |
120     HtmlFrmOpts::BrClear |
121     HtmlFrmOpts::MarginSize |
122     HtmlFrmOpts::AbsSize;
123 
124 const HtmlFrmOpts HTML_FRMOPTS_CNTNR          =
125     HtmlFrmOpts::SAlign |
126     HtmlFrmOpts::SSpace |
127     HtmlFrmOpts::SWidth |
128     HtmlFrmOpts::AnySize |
129     HtmlFrmOpts::AbsSize |
130     HtmlFrmOpts::SPixSize;
131 
132 static Writer& OutHTML_FrameFormatTableNode( Writer& rWrt, const SwFrameFormat& rFrameFormat );
133 static Writer& OutHTML_FrameFormatAsMulticol( Writer& rWrt, const SwFrameFormat& rFormat,
134                                          bool bInCntnr );
135 static Writer& OutHTML_FrameFormatAsSpacer( Writer& rWrt, const SwFrameFormat& rFormat );
136 static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt,
137                                           const SwFrameFormat& rFrameFormat, bool bSpan );
138 static Writer& OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFormat, bool bPNGFallback );
139 
140 static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rFormat,
141                                       bool bInCntnr, bool bPNGFallback );
142 
143 static Writer& OutHTML_FrameFormatAsMarquee( Writer& rWrt, const SwFrameFormat& rFrameFormat,
144                                         const SdrObject& rSdrObj    );
145 
146 HTMLOutEvent const aImageEventTable[] =
147 {
148     { OOO_STRING_SVTOOLS_HTML_O_SDonload,       OOO_STRING_SVTOOLS_HTML_O_onload,   SvMacroItemId::OnImageLoadDone        },
149     { OOO_STRING_SVTOOLS_HTML_O_SDonabort,      OOO_STRING_SVTOOLS_HTML_O_onabort,  SvMacroItemId::OnImageLoadCancel       },
150     { OOO_STRING_SVTOOLS_HTML_O_SDonerror,      OOO_STRING_SVTOOLS_HTML_O_onerror,  SvMacroItemId::OnImageLoadError       },
151     { nullptr, nullptr, SvMacroItemId::NONE }
152 };
153 
154 HTMLOutEvent const aIMapEventTable[] =
155 {
156     { OOO_STRING_SVTOOLS_HTML_O_SDonmouseover,  OOO_STRING_SVTOOLS_HTML_O_onmouseover,  SvMacroItemId::OnMouseOver  },
157     { OOO_STRING_SVTOOLS_HTML_O_SDonmouseout,   OOO_STRING_SVTOOLS_HTML_O_onmouseout,   SvMacroItemId::OnMouseOut   },
158     { nullptr, nullptr, SvMacroItemId::NONE }
159 };
160 
GuessFrameType(const SwFrameFormat & rFrameFormat,const SdrObject * & rpSdrObj)161 sal_uInt16 SwHTMLWriter::GuessFrameType( const SwFrameFormat& rFrameFormat,
162                                    const SdrObject*& rpSdrObj )
163 {
164     SwHTMLFrameType eType;
165 
166     if( RES_DRAWFRMFMT == rFrameFormat.Which() )
167     {
168         // use an arbitrary draw object as the default value
169         eType = HTML_FRMTYPE_DRAW;
170 
171         const SdrObject *pObj =
172             SwHTMLWriter::GetMarqueeTextObj( static_cast<const SwDrawFrameFormat &>(rFrameFormat) );
173         if( pObj )
174         {
175             // scrolling text
176             rpSdrObj = pObj;
177             eType = HTML_FRMTYPE_MARQUEE;
178         }
179         else
180         {
181             pObj = GetHTMLControl( static_cast<const SwDrawFrameFormat &>(rFrameFormat) );
182 
183             if( pObj )
184             {
185                 // Form control
186                 rpSdrObj = pObj;
187                 eType = HTML_FRMTYPE_CONTROL;
188             }
189         }
190     }
191     else
192     {
193         // use a text frame as the default value
194         eType = HTML_FRMTYPE_TEXT;
195 
196         const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
197         sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
198         const SwNode* pNd = m_pDoc->GetNodes()[ nStt ];
199 
200         if( pNd->IsGrfNode() )
201         {
202             // graphic node
203             eType = HTML_FRMTYPE_GRF;
204         }
205         else if( pNd->IsOLENode() )
206         {
207             // applet, plugin, floating frame
208             eType = static_cast<SwHTMLFrameType>(GuessOLENodeFrameType( *pNd ));
209         }
210         else
211         {
212             sal_uLong nEnd = m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex();
213 
214             const SfxPoolItem* pItem;
215             const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
216             if( SfxItemState::SET == rItemSet.GetItemState( RES_COL,
217                                                        true, &pItem ) &&
218                 static_cast<const SwFormatCol *>(pItem)->GetNumCols() > 1 )
219             {
220                 // frame with columns
221                 eType = HTML_FRMTYPE_MULTICOL;
222             }
223             else if( pNd->IsTableNode() )
224             {
225                 const SwTableNode *pTableNd = pNd->GetTableNode();
226                 sal_uLong nTableEnd = pTableNd->EndOfSectionIndex();
227 
228                 if( nTableEnd+1 == nEnd )
229                 {
230                     // table
231                     eType = HTML_FRMTYPE_TABLE;
232                 }
233                 else if( nTableEnd+2 == nEnd )
234                 {
235                     // table with caption
236                     eType = HTML_FRMTYPE_TABLE_CAP;
237                 }
238             }
239             else if( pNd->IsTextNode() )
240             {
241                 const SwTextNode *pTextNd = pNd->GetTextNode();
242 
243                 bool bEmpty = false;
244                 if( nStt==nEnd-1 && !pTextNd->Len() )
245                 {
246                     // empty frame? Only if no frame is
247                     // anchored to the text or start node.
248                     bEmpty = true;
249                     if( m_pHTMLPosFlyFrames )
250                     {
251                         for( auto & pHTMLPosFlyFrame : *m_pHTMLPosFlyFrames )
252                         {
253                             sal_uLong nIdx = pHTMLPosFlyFrame->GetNdIndex().GetIndex();
254                             bEmpty = (nIdx != nStt) && (nIdx != nStt-1);
255                             if( !bEmpty || nIdx > nStt )
256                                 break;
257                         }
258                     }
259                 }
260                 if( bEmpty )
261                 {
262                     std::unique_ptr<SvxBrushItem> aBrush = rFrameFormat.makeBackgroundBrushItem();
263                     /// background is not empty, if it has a background graphic
264                     /// or its background color is not "no fill"/"auto fill".
265                     if( aBrush &&
266                         (GPOS_NONE != aBrush->GetGraphicPos() ||
267                         aBrush->GetColor() != COL_TRANSPARENT ))
268                     {
269                         bEmpty = false;
270                     }
271                 }
272                 if( bEmpty )
273                 {
274                     // empty frame
275                     eType = HTML_FRMTYPE_EMPTY;
276                 }
277                 else if( m_pDoc->GetNodes()[nStt+1]->IsTableNode() )
278                 {
279                     const SwTableNode *pTableNd =
280                         m_pDoc->GetNodes()[nStt+1]->GetTableNode();
281                     if( pTableNd->EndOfSectionIndex()+1 == nEnd )
282                     {
283                         // table with heading
284                         eType = HTML_FRMTYPE_TABLE_CAP;
285                     }
286                 }
287             }
288         }
289     }
290 
291     return static_cast< sal_uInt16 >(eType);
292 }
293 
CollectFlyFrames()294 void SwHTMLWriter::CollectFlyFrames()
295 {
296     OSL_ENSURE( HTML_CFG_MAX+1 == MAX_BROWSERS,
297             "number of browser configurations has changed" );
298 
299     SwPosFlyFrames aFlyPos(
300         m_pDoc->GetAllFlyFormats(m_bWriteAll ? nullptr : m_pCurrentPam.get(), true));
301 
302     for(const auto& rpItem : aFlyPos)
303     {
304         const SwFrameFormat& rFrameFormat = rpItem->GetFormat();
305         const SdrObject *pSdrObj = nullptr;
306         const SwPosition *pAPos;
307         const SwContentNode *pACNd;
308         SwHTMLFrameType eType = static_cast<SwHTMLFrameType>(GuessFrameType( rFrameFormat, pSdrObj ));
309 
310         AllHtmlFlags nMode;
311         const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
312         sal_Int16 eHoriRel = rFrameFormat.GetHoriOrient().GetRelationOrient();
313         switch( rAnchor.GetAnchorId() )
314         {
315         case RndStdIds::FLY_AT_PAGE:
316         case RndStdIds::FLY_AT_FLY:
317             nMode = aHTMLOutFramePageFlyTable[eType][m_nExportMode];
318             break;
319 
320         case RndStdIds::FLY_AT_PARA:
321             // frames that are anchored to a paragraph are only placed
322             // before the paragraph, if the paragraph has a
323             // spacing.
324             if( text::RelOrientation::FRAME == eHoriRel &&
325                 (pAPos = rAnchor.GetContentAnchor()) != nullptr &&
326                 (pACNd = pAPos->nNode.GetNode().GetContentNode()) != nullptr )
327             {
328                 const SvxLRSpaceItem& rLRItem =
329                     static_cast<const SvxLRSpaceItem&>(pACNd->GetAttr(RES_LR_SPACE));
330                 if( rLRItem.GetTextLeft() || rLRItem.GetRight() )
331                 {
332                     nMode = aHTMLOutFrameParaFrameTable[eType][m_nExportMode];
333                     break;
334                 }
335             }
336             nMode = aHTMLOutFrameParaPrtAreaTable[eType][m_nExportMode];
337             break;
338 
339         case RndStdIds::FLY_AT_CHAR:
340             if( text::RelOrientation::FRAME == eHoriRel || text::RelOrientation::PRINT_AREA == eHoriRel )
341                 nMode = aHTMLOutFrameParaPrtAreaTable[eType][m_nExportMode];
342             else
343                 nMode = aHTMLOutFrameParaOtherTable[eType][m_nExportMode];
344             break;
345 
346         default:
347             nMode = aHTMLOutFrameParaPrtAreaTable[eType][m_nExportMode];
348             break;
349         }
350 
351         if( !m_pHTMLPosFlyFrames )
352             m_pHTMLPosFlyFrames.reset(new SwHTMLPosFlyFrames);
353 
354         m_pHTMLPosFlyFrames->insert( std::make_unique<SwHTMLPosFlyFrame>(*rpItem, pSdrObj, nMode) );
355     }
356 }
357 
OutFlyFrame(sal_uLong nNdIdx,sal_Int32 nContentIdx,HtmlPosition nPos,HTMLOutContext * pContext)358 bool SwHTMLWriter::OutFlyFrame( sal_uLong nNdIdx, sal_Int32 nContentIdx, HtmlPosition nPos,
359                               HTMLOutContext *pContext )
360 {
361     bool bFlysLeft = false; // Are there still Flys left at the current node position?
362 
363     // OutFlyFrame can be called recursively. Thus, sometimes it is
364     // necessary to start over after a Fly was returned.
365     bool bRestart = true;
366     while( m_pHTMLPosFlyFrames && bRestart )
367     {
368         bFlysLeft = bRestart = false;
369 
370         // search for the beginning of the FlyFrames
371         size_t i {0};
372 
373         for( ; i < m_pHTMLPosFlyFrames->size() &&
374             (*m_pHTMLPosFlyFrames)[i]->GetNdIndex().GetIndex() < nNdIdx; i++ )
375             ;
376         for( ; !bRestart && i < m_pHTMLPosFlyFrames->size() &&
377             (*m_pHTMLPosFlyFrames)[i]->GetNdIndex().GetIndex() == nNdIdx; i++ )
378         {
379             SwHTMLPosFlyFrame *pPosFly = (*m_pHTMLPosFlyFrames)[i].get();
380             if( ( HtmlPosition::Any == nPos ||
381                   pPosFly->GetOutPos() == nPos ) &&
382                 pPosFly->GetContentIndex() == nContentIdx )
383             {
384                 // It is important to remove it first, because additional
385                 // elements or the whole array could be deleted on
386                 // deeper recursion levels.
387                 std::unique_ptr<SwHTMLPosFlyFrame> flyHolder = m_pHTMLPosFlyFrames->erase_extract(i);
388                 i--;
389                 if( m_pHTMLPosFlyFrames->empty() )
390                 {
391                     m_pHTMLPosFlyFrames.reset();
392                     bRestart = true;    // not really, only exit the loop
393                 }
394 
395                 if( pContext )
396                 {
397                     HTMLOutFuncs::FlushToAscii(Strm(), *pContext );
398                     pContext = nullptr; // one time only
399                 }
400 
401                 OutFrameFormat( pPosFly->GetOutMode(), pPosFly->GetFormat(),
402                                 pPosFly->GetSdrObject() );
403                 switch( pPosFly->GetOutFn() )
404                 {
405                 case HtmlOut::Div:
406                 case HtmlOut::Span:
407                 case HtmlOut::MultiCol:
408                 case HtmlOut::TableNode:
409                     bRestart = true; // It could become recursive here
410                     break;
411                 default: break;
412                 }
413             }
414             else
415             {
416                 bFlysLeft = true;
417             }
418         }
419     }
420 
421     return bFlysLeft;
422 }
423 
OutFrameFormat(AllHtmlFlags nMode,const SwFrameFormat & rFrameFormat,const SdrObject * pSdrObject)424 void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFrameFormat,
425                               const SdrObject *pSdrObject )
426 {
427     HtmlContainerFlags nCntnrMode = nMode.nContainer;
428     HtmlOut nOutMode = nMode.nOut;
429     OString aContainerStr;
430     if( HtmlContainerFlags::NONE != nCntnrMode )
431     {
432 
433         if( m_bLFPossible && HtmlContainerFlags::Div == nCntnrMode )
434             OutNewLine();
435 
436         OStringBuffer sOut;
437         aContainerStr = (HtmlContainerFlags::Div == nCntnrMode)
438                             ? OOO_STRING_SVTOOLS_HTML_division
439                             : OOO_STRING_SVTOOLS_HTML_span;
440         sOut.append("<" + GetNamespace() + aContainerStr + " "
441                 OOO_STRING_SVTOOLS_HTML_O_class "=\""
442                 "sd-abs-pos\"");
443         Strm().WriteOString( sOut.makeStringAndClear() );
444 
445         // Output a width for non-draw objects
446         HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_CNTNR;
447 
448         // For frames with columns we can also output the background
449         if( HtmlOut::MultiCol == nOutMode )
450             nFrameFlags |= HtmlFrmOpts::SBackground|HtmlFrmOpts::SBorder;
451 
452         if( IsHTMLMode( HTMLMODE_BORDER_NONE ) )
453            nFrameFlags |= HtmlFrmOpts::SNoBorder;
454         OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags, pSdrObject );
455         Strm().WriteChar( '>' );
456 
457         if( HtmlContainerFlags::Div == nCntnrMode )
458         {
459             IncIndentLevel();
460             m_bLFPossible = true;
461         }
462     }
463 
464     switch( nOutMode )
465     {
466     case HtmlOut::TableNode:      // OK
467         OSL_ENSURE( aContainerStr.isEmpty(), "Table: Container is not supposed to be here" );
468         OutHTML_FrameFormatTableNode( *this, rFrameFormat );
469         break;
470     case HtmlOut::GraphicNode:      // OK
471         OutHTML_FrameFormatGrfNode( *this, rFrameFormat, !aContainerStr.isEmpty(), /*bPNGFallback=*/true );
472         break;
473     case HtmlOut::OleNode:      // OK
474         OutHTML_FrameFormatOLENode( *this, rFrameFormat, !aContainerStr.isEmpty() );
475         break;
476     case HtmlOut::OleGraphic:       // OK
477         OutHTML_FrameFormatOLENodeGrf( *this, rFrameFormat, !aContainerStr.isEmpty() );
478         break;
479     case HtmlOut::Div:
480     case HtmlOut::Span:
481         OSL_ENSURE( aContainerStr.isEmpty(), "Div: Container is not supposed to be here" );
482         OutHTML_FrameFormatAsDivOrSpan( *this, rFrameFormat, HtmlOut::Span==nOutMode );
483         break;
484     case HtmlOut::MultiCol:     // OK
485         OutHTML_FrameFormatAsMulticol( *this, rFrameFormat, !aContainerStr.isEmpty() );
486         break;
487     case HtmlOut::Spacer:       // OK
488         OSL_ENSURE( aContainerStr.isEmpty(), "Spacer: Container is not supposed to be here" );
489         OutHTML_FrameFormatAsSpacer( *this, rFrameFormat );
490         break;
491     case HtmlOut::Control:      // OK
492         OutHTML_DrawFrameFormatAsControl( *this,
493                                     static_cast<const SwDrawFrameFormat &>(rFrameFormat), dynamic_cast<const SdrUnoObj&>(*pSdrObject),
494                                     !aContainerStr.isEmpty() );
495         break;
496     case HtmlOut::AMarquee:
497         OutHTML_FrameFormatAsMarquee( *this, rFrameFormat, *pSdrObject );
498         break;
499     case HtmlOut::Marquee:
500         OSL_ENSURE( aContainerStr.isEmpty(), "Marquee: Container is not supposed to be here" );
501         OutHTML_DrawFrameFormatAsMarquee( *this,
502                     static_cast<const SwDrawFrameFormat &>(rFrameFormat), *pSdrObject );
503         break;
504     case HtmlOut::GraphicFrame:
505         OutHTML_FrameFormatAsImage( *this, rFrameFormat, /*bPNGFallback=*/true );
506         break;
507     }
508 
509     if( HtmlContainerFlags::Div == nCntnrMode )
510     {
511         DecIndentLevel();
512         if( m_bLFPossible )
513             OutNewLine();
514         HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
515         m_bLFPossible = true;
516     }
517     else if( HtmlContainerFlags::Span == nCntnrMode )
518         HTMLOutFuncs::Out_AsciiTag( Strm(), OString(GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
519 }
520 
OutFrameFormatOptions(const SwFrameFormat & rFrameFormat,const OUString & rAlternateText,HtmlFrmOpts nFrameOpts)521 OString SwHTMLWriter::OutFrameFormatOptions( const SwFrameFormat &rFrameFormat,
522                                      const OUString& rAlternateText,
523                                      HtmlFrmOpts nFrameOpts )
524 {
525     OString sRetEndTags;
526     OStringBuffer sOut;
527     const SfxPoolItem* pItem;
528     const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
529 
530     // Name
531     if( (nFrameOpts & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) &&
532         !rFrameFormat.GetName().isEmpty() )
533     {
534         const char *pStr =
535             (nFrameOpts & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name;
536         sOut.append(OString::Concat(" ") + pStr + "=\"");
537         Strm().WriteOString( sOut.makeStringAndClear() );
538         HTMLOutFuncs::Out_String( Strm(), rFrameFormat.GetName(), m_eDestEnc, &m_aNonConvertableCharacters );
539         sOut.append('\"');
540     }
541 
542     // Name
543     if( nFrameOpts & HtmlFrmOpts::Dir )
544     {
545         SvxFrameDirection nDir = GetHTMLDirection( rItemSet );
546         Strm().WriteOString( sOut.makeStringAndClear() );
547         OutDirection( nDir );
548     }
549 
550     // ALT
551     if( (nFrameOpts & HtmlFrmOpts::Alt) && !rAlternateText.isEmpty() )
552     {
553         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_alt "=\"");
554         Strm().WriteOString( sOut.makeStringAndClear() );
555         HTMLOutFuncs::Out_String( Strm(), rAlternateText, m_eDestEnc, &m_aNonConvertableCharacters );
556         sOut.append('\"');
557     }
558 
559     // ALIGN
560     const char *pStr = nullptr;
561     RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
562     if( (nFrameOpts & HtmlFrmOpts::Align) &&
563         ((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) )
564     {
565         // MIB 12.3.98: Wouldn't it be more clever to left-align frames that
566         // are anchored to a paragraph if necessary, instead of inserting them
567         // as being anchored to characters?
568         const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
569         if( !(nFrameOpts & HtmlFrmOpts::SAlign) ||
570             text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
571             text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
572         {
573             pStr = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient()
574                         ? OOO_STRING_SVTOOLS_HTML_AL_right
575                         : OOO_STRING_SVTOOLS_HTML_AL_left;
576         }
577     }
578     if( (nFrameOpts & HtmlFrmOpts::Align) && !pStr &&
579         ( !(nFrameOpts & HtmlFrmOpts::SAlign) ||
580           (RndStdIds::FLY_AS_CHAR == eAnchorId) ) &&
581         SfxItemState::SET == rItemSet.GetItemState( RES_VERT_ORIENT, true, &pItem ))
582     {
583         switch( static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() )
584         {
585         case text::VertOrientation::LINE_TOP:     pStr = OOO_STRING_SVTOOLS_HTML_VA_top;        break;
586         case text::VertOrientation::CHAR_TOP:
587         case text::VertOrientation::BOTTOM:       pStr = OOO_STRING_SVTOOLS_HTML_VA_texttop;    break;  // not possible
588         case text::VertOrientation::LINE_CENTER:
589         case text::VertOrientation::CHAR_CENTER:  pStr = OOO_STRING_SVTOOLS_HTML_VA_absmiddle;  break;  // not possible
590         case text::VertOrientation::CENTER:       pStr = OOO_STRING_SVTOOLS_HTML_VA_middle;     break;
591         case text::VertOrientation::LINE_BOTTOM:
592         case text::VertOrientation::CHAR_BOTTOM:  pStr = OOO_STRING_SVTOOLS_HTML_VA_absbottom;  break;  // not possible
593         case text::VertOrientation::TOP:          pStr = OOO_STRING_SVTOOLS_HTML_VA_bottom;     break;
594         case text::VertOrientation::NONE:     break;
595         }
596     }
597     if( pStr )
598     {
599         sOut.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"") +
600                 pStr + "\"");
601     }
602 
603     // HSPACE and VSPACE
604     Size aTwipSpc( 0, 0 );
605     if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
606         SfxItemState::SET == rItemSet.GetItemState( RES_LR_SPACE, true, &pItem ))
607     {
608         aTwipSpc.setWidth(
609             ( static_cast<const SvxLRSpaceItem*>(pItem)->GetLeft() +
610                 static_cast<const SvxLRSpaceItem*>(pItem)->GetRight() ) / 2 );
611         m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width();
612     }
613     if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
614         SfxItemState::SET == rItemSet.GetItemState( RES_UL_SPACE, true, &pItem ))
615     {
616         aTwipSpc.setHeight(
617             ( static_cast<const SvxULSpaceItem*>(pItem)->GetUpper() +
618                 static_cast<const SvxULSpaceItem*>(pItem)->GetLower() ) / 2 );
619         m_nDfltTopMargin = m_nDfltBottomMargin = o3tl::narrowing<sal_uInt16>(aTwipSpc.Height());
620     }
621 
622     if( (nFrameOpts & HtmlFrmOpts::Space) &&
623         (aTwipSpc.Width() || aTwipSpc.Height()) &&
624         Application::GetDefaultDevice() )
625     {
626         Size aPixelSpc =
627             Application::GetDefaultDevice()->LogicToPixel( aTwipSpc,
628                                                 MapMode(MapUnit::MapTwip) );
629         if( !aPixelSpc.Width() && aTwipSpc.Width() )
630             aPixelSpc.setWidth( 1 );
631         if( !aPixelSpc.Height() && aTwipSpc.Height() )
632             aPixelSpc.setHeight( 1 );
633 
634         if( aPixelSpc.Width() )
635         {
636             sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_hspace
637                     "=\"" + OString::number(aPixelSpc.Width()) + "\"");
638         }
639 
640         if( aPixelSpc.Height() )
641         {
642             sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_vspace
643                     "=\"" + OString::number(aPixelSpc.Height()) + "\"");
644         }
645     }
646 
647     // The spacing must be considered for the size, if the corresponding flag
648     // is set.
649     if( nFrameOpts & HtmlFrmOpts::MarginSize )
650     {
651         aTwipSpc.setWidth( aTwipSpc.Width() * -2 );
652         aTwipSpc.setHeight( aTwipSpc.Height() * -2 );
653     }
654     else
655     {
656         aTwipSpc.setWidth( 0 );
657         aTwipSpc.setHeight( 0 );
658     }
659 
660     if( !(nFrameOpts & HtmlFrmOpts::AbsSize) &&
661         SfxItemState::SET == rItemSet.GetItemState( RES_BOX, true, &pItem ))
662     {
663         const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem);
664 
665         aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) );
666         aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) );
667         aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) );
668         aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) );
669     }
670 
671     // WIDTH and/or HEIGHT
672     // Output SwFrameSize::Variable/SwFrameSize::Minimum only, if ANYSIZE is set
673     if( (nFrameOpts & HtmlFrmOpts::Size) &&
674         SfxItemState::SET == rItemSet.GetItemState( RES_FRM_SIZE, true, &pItem ) &&
675         ( (nFrameOpts & HtmlFrmOpts::AnySize) ||
676           SwFrameSize::Fixed == static_cast<const SwFormatFrameSize *>(pItem)->GetHeightSizeType()) )
677     {
678         const SwFormatFrameSize *pFSItem = static_cast<const SwFormatFrameSize *>(pItem);
679         sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent();
680         sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent();
681 
682         // Size of the object in Twips without margins
683         Size aTwipSz( (nPercentWidth ? 0
684                                  : pFSItem->GetWidth()-aTwipSpc.Width()),
685                       (nPercentHeight ? 0
686                                   : pFSItem->GetHeight()-aTwipSpc.Height()) );
687 
688         OSL_ENSURE( !aTwipSz.IsEmpty(), "Frame size minus spacing  < 0!!!???" );
689         if( aTwipSz.Width() < 0 )
690             aTwipSz.setWidth( 0 );
691         if( aTwipSz.Height() < 0 )
692             aTwipSz.setHeight( 0 );
693 
694         Size aPixelSz( 0, 0 );
695         if( (aTwipSz.Width() || aTwipSz.Height()) &&
696             Application::GetDefaultDevice() )
697         {
698             aPixelSz =
699                 Application::GetDefaultDevice()->LogicToPixel( aTwipSz,
700                                                     MapMode(MapUnit::MapTwip) );
701             if( !aPixelSz.Width() && aTwipSz.Width() )
702                 aPixelSz.setWidth( 1 );
703             if( !aPixelSz.Height() && aTwipSz.Height() )
704                 aPixelSz.setHeight( 1 );
705         }
706 
707         if( (nFrameOpts & HtmlFrmOpts::Width) &&
708             ((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) )
709         {
710             sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
711             if( nPercentWidth )
712                 sOut.append(static_cast<sal_Int32>(nPercentWidth)).append('%');
713             else
714                 sOut.append(static_cast<sal_Int32>(aPixelSz.Width()));
715             sOut.append("\"");
716         }
717 
718         if( (nFrameOpts & HtmlFrmOpts::Height) &&
719             ((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) )
720         {
721             sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_height "=\"");
722             if( nPercentHeight )
723                 sOut.append(static_cast<sal_Int32>(nPercentHeight)).append('%');
724             else
725                 sOut.append(static_cast<sal_Int32>(aPixelSz.Height()));
726             sOut.append("\"");
727         }
728     }
729 
730     if (!sOut.isEmpty())
731         Strm().WriteOString( sOut.makeStringAndClear() );
732 
733     // Insert wrap for graphics that are anchored to a paragraph as
734     // <BR CLEAR=...> in the string
735     if( (nFrameOpts & HtmlFrmOpts::BrClear) &&
736         ((RndStdIds::FLY_AT_PARA == rFrameFormat.GetAnchor().GetAnchorId()) ||
737          (RndStdIds::FLY_AT_CHAR == rFrameFormat.GetAnchor().GetAnchorId())) &&
738         SfxItemState::SET == rItemSet.GetItemState( RES_SURROUND, true, &pItem ))
739     {
740         const SwFormatSurround* pSurround = static_cast<const SwFormatSurround*>(pItem);
741         sal_Int16 eHoriOri =    rFrameFormat.GetHoriOrient().GetHoriOrient();
742         pStr = nullptr;
743         css::text::WrapTextMode eSurround = pSurround->GetSurround();
744         bool bAnchorOnly = pSurround->IsAnchorOnly();
745         switch( eHoriOri )
746         {
747         case text::HoriOrientation::RIGHT:
748             {
749                 switch( eSurround )
750                 {
751                 case css::text::WrapTextMode_NONE:
752                 case css::text::WrapTextMode_RIGHT:
753                     pStr = OOO_STRING_SVTOOLS_HTML_AL_right;
754                     break;
755                 case css::text::WrapTextMode_LEFT:
756                 case css::text::WrapTextMode_PARALLEL:
757                     if( bAnchorOnly )
758                         m_bClearRight = true;
759                     break;
760                 default:
761                     ;
762                 }
763             }
764             break;
765 
766         default:
767             // If a frame is centered, it gets left aligned. This
768             // should be taken into account here, too.
769             {
770                 switch( eSurround )
771                 {
772                 case css::text::WrapTextMode_NONE:
773                 case css::text::WrapTextMode_LEFT:
774                     pStr = OOO_STRING_SVTOOLS_HTML_AL_left;
775                     break;
776                 case css::text::WrapTextMode_RIGHT:
777                 case css::text::WrapTextMode_PARALLEL:
778                     if( bAnchorOnly )
779                         m_bClearLeft = true;
780                     break;
781                 default:
782                     ;
783                 }
784             }
785             break;
786 
787         }
788 
789         if( pStr )
790         {
791             sOut.append("<" OOO_STRING_SVTOOLS_HTML_linebreak
792                     " " OOO_STRING_SVTOOLS_HTML_O_clear
793                     "=\"" + OString::Concat(pStr) + "\">");
794             sRetEndTags = sOut.makeStringAndClear();
795         }
796     }
797     return sRetEndTags;
798 }
799 
writeFrameFormatOptions(HtmlWriter & aHtml,const SwFrameFormat & rFrameFormat,std::u16string_view rAlternateText,HtmlFrmOpts nFrameOptions)800 void SwHTMLWriter::writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameFormat& rFrameFormat, std::u16string_view rAlternateText, HtmlFrmOpts nFrameOptions)
801 {
802     bool bReplacement = (nFrameOptions & HtmlFrmOpts::Replacement) || mbReqIF;
803     const SfxPoolItem* pItem;
804     const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
805 
806     // Name
807     if( (nFrameOptions & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) &&
808         !rFrameFormat.GetName().isEmpty() && !bReplacement)
809     {
810         const char* pAttributeName = (nFrameOptions & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name;
811         aHtml.attribute(pAttributeName, rFrameFormat.GetName());
812     }
813 
814     // Name
815     if (nFrameOptions & HtmlFrmOpts::Dir)
816     {
817         SvxFrameDirection nCurrentDirection = GetHTMLDirection(rItemSet);
818         OString sDirection = convertDirection(nCurrentDirection);
819         aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_dir, sDirection);
820     }
821 
822     // alt
823     if( (nFrameOptions & HtmlFrmOpts::Alt) && !rAlternateText.empty() && !bReplacement )
824     {
825         aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_alt, rAlternateText);
826     }
827 
828     // align
829     const char* pAlignString = nullptr;
830     RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
831     if( (nFrameOptions & HtmlFrmOpts::Align) &&
832         ((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) && !bReplacement)
833     {
834         const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
835         if( !(nFrameOptions & HtmlFrmOpts::SAlign) ||
836             text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
837             text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
838         {
839             pAlignString = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient()
840                         ? OOO_STRING_SVTOOLS_HTML_AL_right
841                         : OOO_STRING_SVTOOLS_HTML_AL_left;
842         }
843     }
844     if( (nFrameOptions & HtmlFrmOpts::Align) && !pAlignString &&
845         ( !(nFrameOptions & HtmlFrmOpts::SAlign) ||
846           (RndStdIds::FLY_AS_CHAR == eAnchorId) ) &&
847         SfxItemState::SET == rItemSet.GetItemState( RES_VERT_ORIENT, true, &pItem ))
848     {
849         switch( static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() )
850         {
851         case text::VertOrientation::LINE_TOP:     pAlignString = OOO_STRING_SVTOOLS_HTML_VA_top;        break;
852         case text::VertOrientation::CHAR_TOP:
853         case text::VertOrientation::BOTTOM:       pAlignString = OOO_STRING_SVTOOLS_HTML_VA_texttop;    break;
854         case text::VertOrientation::LINE_CENTER:
855         case text::VertOrientation::CHAR_CENTER:  pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absmiddle;  break;
856         case text::VertOrientation::CENTER:       pAlignString = OOO_STRING_SVTOOLS_HTML_VA_middle;     break;
857         case text::VertOrientation::LINE_BOTTOM:
858         case text::VertOrientation::CHAR_BOTTOM:  pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absbottom;  break;
859         case text::VertOrientation::TOP:          pAlignString = OOO_STRING_SVTOOLS_HTML_VA_bottom;     break;
860         case text::VertOrientation::NONE:     break;
861         }
862     }
863     if (pAlignString && !bReplacement)
864     {
865         aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, pAlignString);
866     }
867 
868     // hspace and vspace
869     Size aTwipSpc( 0, 0 );
870     if( (nFrameOptions & (HtmlFrmOpts::Space | HtmlFrmOpts::MarginSize)) &&
871         SfxItemState::SET == rItemSet.GetItemState( RES_LR_SPACE, true, &pItem ))
872     {
873         aTwipSpc.setWidth(
874             ( static_cast<const SvxLRSpaceItem*>(pItem)->GetLeft() +
875                 static_cast<const SvxLRSpaceItem*>(pItem)->GetRight() ) / 2 );
876         m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width();
877     }
878     if( (nFrameOptions & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
879         SfxItemState::SET == rItemSet.GetItemState( RES_UL_SPACE, true, &pItem ))
880     {
881         aTwipSpc.setHeight(
882             ( static_cast<const SvxULSpaceItem*>(pItem)->GetUpper() +
883                 static_cast<const SvxULSpaceItem*>(pItem)->GetLower() ) / 2 );
884         m_nDfltTopMargin = m_nDfltBottomMargin = o3tl::narrowing<sal_uInt16>(aTwipSpc.Height());
885     }
886 
887     if( (nFrameOptions & HtmlFrmOpts::Space) &&
888         (aTwipSpc.Width() || aTwipSpc.Height()) &&
889         Application::GetDefaultDevice() )
890     {
891         Size aPixelSpc =
892             Application::GetDefaultDevice()->LogicToPixel( aTwipSpc,
893                                                 MapMode(MapUnit::MapTwip) );
894         if( !aPixelSpc.Width() && aTwipSpc.Width() )
895             aPixelSpc.setWidth( 1 );
896         if( !aPixelSpc.Height() && aTwipSpc.Height() )
897             aPixelSpc.setHeight( 1 );
898 
899         if (aPixelSpc.Width())
900         {
901             aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_hspace, static_cast<sal_Int32>(aPixelSpc.Width()));
902         }
903 
904         if (aPixelSpc.Height())
905         {
906             aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_vspace, static_cast<sal_Int32>(aPixelSpc.Height()));
907         }
908     }
909 
910     // The spacing must be considered for the size, if the corresponding flag
911     // is set.
912     if( nFrameOptions & HtmlFrmOpts::MarginSize )
913     {
914         aTwipSpc.setWidth( aTwipSpc.Width() * -2 );
915         aTwipSpc.setHeight( aTwipSpc.Height() * -2 );
916     }
917     else
918     {
919         aTwipSpc.setWidth( 0 );
920         aTwipSpc.setHeight( 0 );
921     }
922 
923     if( !(nFrameOptions & HtmlFrmOpts::AbsSize) &&
924         SfxItemState::SET == rItemSet.GetItemState( RES_BOX, true, &pItem ))
925     {
926         const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem);
927 
928         aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) );
929         aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) );
930         aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) );
931         aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) );
932     }
933 
934     // "width" and/or "height"
935     // Only output SwFrameSize::Variable/SwFrameSize::Minimum if ANYSIZE is set
936     const SwFormatFrameSize* pFSItem = nullptr;
937     std::optional<SwFormatFrameSize> aFrameSize;
938     if (SfxItemState::SET == rItemSet.GetItemState( RES_FRM_SIZE, true, &pItem ))
939     {
940         pFSItem = static_cast<const SwFormatFrameSize *>(pItem);
941     }
942     else if (const SdrObject* pObject = rFrameFormat.FindSdrObject())
943     {
944         // Write size for Draw shapes as well.
945         const tools::Rectangle& rSnapRect = pObject->GetSnapRect();
946         aFrameSize.emplace();
947         aFrameSize->SetWidthSizeType(SwFrameSize::Fixed);
948         aFrameSize->SetWidth(rSnapRect.getWidth());
949         aFrameSize->SetHeightSizeType(SwFrameSize::Fixed);
950         aFrameSize->SetHeight(rSnapRect.getHeight());
951         pFSItem = &*aFrameSize;
952     }
953     if( (nFrameOptions & HtmlFrmOpts::Size) &&
954         pFSItem &&
955         ( (nFrameOptions & HtmlFrmOpts::AnySize) ||
956           SwFrameSize::Fixed == pFSItem->GetHeightSizeType()) )
957     {
958         sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent();
959         sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent();
960 
961         // Size of the object in Twips without margins
962         Size aTwipSz( (nPercentWidth ? 0
963                                  : pFSItem->GetWidth()-aTwipSpc.Width()),
964                       (nPercentHeight ? 0
965                                   : pFSItem->GetHeight()-aTwipSpc.Height()) );
966 
967         OSL_ENSURE( !aTwipSz.IsEmpty(), "Frame size minus spacing < 0!!!???" );
968         if( aTwipSz.Width() < 0 )
969             aTwipSz.setWidth( 0 );
970         if( aTwipSz.Height() < 0 )
971             aTwipSz.setHeight( 0 );
972 
973         Size aPixelSz( 0, 0 );
974         if( (aTwipSz.Width() || aTwipSz.Height()) &&
975             Application::GetDefaultDevice() )
976         {
977             aPixelSz =
978                 Application::GetDefaultDevice()->LogicToPixel( aTwipSz,
979                                                     MapMode(MapUnit::MapTwip) );
980             if( !aPixelSz.Width() && aTwipSz.Width() )
981                 aPixelSz.setWidth( 1 );
982             if( !aPixelSz.Height() && aTwipSz.Height() )
983                 aPixelSz.setHeight( 1 );
984         }
985 
986         if( (nFrameOptions & HtmlFrmOpts::Width) &&
987             ((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) )
988         {
989             OString sWidth;
990             if (nPercentWidth)
991                 sWidth = OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%";
992             else
993                 sWidth = OString::number(static_cast<sal_Int32>(aPixelSz.Width()));
994             aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_width, sWidth);
995         }
996 
997         if( (nFrameOptions & HtmlFrmOpts::Height) &&
998             ((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) )
999         {
1000             OString sHeight;
1001             if (nPercentHeight)
1002                 sHeight = OString::number(static_cast<sal_Int32>(nPercentHeight)) + "%";
1003             else
1004                 sHeight = OString::number(static_cast<sal_Int32>(aPixelSz.Height()));
1005             aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_height, sHeight);
1006         }
1007     }
1008 
1009     // Insert wrap for graphics that are anchored to a paragraph as
1010     // <BR CLEAR=...> in the string
1011 
1012     if( !((nFrameOptions & HtmlFrmOpts::BrClear) &&
1013         ((RndStdIds::FLY_AT_PARA == rFrameFormat.GetAnchor().GetAnchorId()) ||
1014          (RndStdIds::FLY_AT_CHAR == rFrameFormat.GetAnchor().GetAnchorId())) &&
1015         SfxItemState::SET == rItemSet.GetItemState( RES_SURROUND, true, &pItem )))
1016         return;
1017 
1018     const char* pSurroundString = nullptr;
1019 
1020     const SwFormatSurround* pSurround = static_cast<const SwFormatSurround*>(pItem);
1021     sal_Int16 eHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient();
1022     css::text::WrapTextMode eSurround = pSurround->GetSurround();
1023     bool bAnchorOnly = pSurround->IsAnchorOnly();
1024     switch( eHoriOri )
1025     {
1026         case text::HoriOrientation::RIGHT:
1027         {
1028             switch( eSurround )
1029             {
1030             case css::text::WrapTextMode_NONE:
1031             case css::text::WrapTextMode_RIGHT:
1032                 pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_right;
1033                 break;
1034             case css::text::WrapTextMode_LEFT:
1035             case css::text::WrapTextMode_PARALLEL:
1036                 if( bAnchorOnly )
1037                     m_bClearRight = true;
1038                 break;
1039             default:
1040                 ;
1041             }
1042         }
1043         break;
1044 
1045         default:
1046         // If a frame is centered, it gets left aligned. This
1047         // should be taken into account here, too.
1048         {
1049             switch( eSurround )
1050             {
1051             case css::text::WrapTextMode_NONE:
1052             case css::text::WrapTextMode_LEFT:
1053                 pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_left;
1054                 break;
1055             case css::text::WrapTextMode_RIGHT:
1056             case css::text::WrapTextMode_PARALLEL:
1057                 if( bAnchorOnly )
1058                     m_bClearLeft = true;
1059                 break;
1060             default:
1061                 break;
1062             }
1063         }
1064         break;
1065     }
1066 
1067     if (pSurroundString)
1068     {
1069         aHtml.start(OOO_STRING_SVTOOLS_HTML_linebreak);
1070         aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, pSurroundString);
1071         aHtml.end();
1072     }
1073 }
1074 
1075 namespace
1076 {
1077 
lclWriteOutImap(SwHTMLWriter & rHTMLWrt,const SfxItemSet & rItemSet,const SwFrameFormat & rFrameFormat,const Size & rRealSize,const ImageMap * pAltImgMap,const SwFormatURL * & pURLItem)1078 OUString lclWriteOutImap(SwHTMLWriter& rHTMLWrt, const SfxItemSet& rItemSet, const SwFrameFormat& rFrameFormat,
1079                          const Size& rRealSize, const ImageMap* pAltImgMap, const SwFormatURL*& pURLItem)
1080 {
1081     OUString aIMapName;
1082 
1083     const SfxPoolItem* pItem;
1084 
1085     // Only consider the URL attribute if no ImageMap was supplied
1086     if (!pAltImgMap && SfxItemState::SET == rItemSet.GetItemState( RES_URL, true, &pItem))
1087     {
1088         pURLItem = static_cast<const SwFormatURL*>( pItem);
1089     }
1090 
1091     // write ImageMap
1092     const ImageMap* pIMap = pAltImgMap;
1093     if( !pIMap && pURLItem )
1094     {
1095         pIMap = pURLItem->GetMap();
1096     }
1097 
1098     if (pIMap)
1099     {
1100         // make the name unique
1101         aIMapName = pIMap->GetName();
1102         OUString aNameBase;
1103         if (!aIMapName.isEmpty())
1104             aNameBase = aIMapName;
1105         else
1106             aNameBase = OOO_STRING_SVTOOLS_HTML_map;
1107 
1108         if (aIMapName.isEmpty())
1109             aIMapName = aNameBase + OUString::number(rHTMLWrt.m_nImgMapCnt);
1110 
1111         bool bFound;
1112         do
1113         {
1114             bFound = false;
1115             for (const OUString & rImgMapName : rHTMLWrt.m_aImgMapNames)
1116             {
1117                 // TODO: Unicode: Comparison is case insensitive for ASCII
1118                 // characters only now!
1119                 if (aIMapName.equalsIgnoreAsciiCase(rImgMapName))
1120                 {
1121                     bFound = true;
1122                     break;
1123                 }
1124             }
1125             if (bFound)
1126             {
1127                 rHTMLWrt.m_nImgMapCnt++;
1128                 aIMapName = aNameBase + OUString::number( rHTMLWrt.m_nImgMapCnt );
1129             }
1130         } while (bFound);
1131 
1132         bool bScale = false;
1133         Fraction aScaleX(1, 1);
1134         Fraction aScaleY(1, 1);
1135 
1136         const SwFormatFrameSize& rFrameSize = rFrameFormat.GetFrameSize();
1137         const SvxBoxItem& rBox = rFrameFormat.GetBox();
1138 
1139         if (!rFrameSize.GetWidthPercent() && rRealSize.Width())
1140         {
1141             SwTwips nWidth = rFrameSize.GetWidth();
1142             nWidth -= rBox.CalcLineSpace(SvxBoxItemLine::LEFT) + rBox.CalcLineSpace(SvxBoxItemLine::RIGHT);
1143 
1144             OSL_ENSURE( nWidth > 0, "Are there any graphics that are 0 twip wide!?" );
1145             if (nWidth <= 0) // should not happen
1146                 nWidth = 1;
1147 
1148             if (rRealSize.Width() != nWidth)
1149             {
1150                 aScaleX = Fraction(nWidth, rRealSize.Width());
1151                 bScale = true;
1152             }
1153         }
1154 
1155         if (!rFrameSize.GetHeightPercent() && rRealSize.Height())
1156         {
1157             SwTwips nHeight = rFrameSize.GetHeight();
1158 
1159             nHeight -= rBox.CalcLineSpace(SvxBoxItemLine::TOP) + rBox.CalcLineSpace(SvxBoxItemLine::BOTTOM);
1160 
1161             OSL_ENSURE( nHeight > 0, "Are there any graphics that are 0 twip high!?" );
1162             if (nHeight <= 0)
1163                 nHeight = 1;
1164 
1165             if (rRealSize.Height() != nHeight)
1166             {
1167                 aScaleY = Fraction(nHeight, rRealSize.Height());
1168                 bScale = true;
1169             }
1170         }
1171 
1172         rHTMLWrt.m_aImgMapNames.push_back(aIMapName);
1173 
1174         OString aIndMap, aIndArea;
1175         const char *pIndArea = nullptr, *pIndMap = nullptr;
1176 
1177         if (rHTMLWrt.m_bLFPossible)
1178         {
1179             rHTMLWrt.OutNewLine( true );
1180             aIndMap = rHTMLWrt.GetIndentString();
1181             aIndArea = rHTMLWrt.GetIndentString(1);
1182             pIndArea = aIndArea.getStr();
1183             pIndMap = aIndMap.getStr();
1184         }
1185 
1186         if (bScale)
1187         {
1188             ImageMap aScaledIMap(*pIMap);
1189             aScaledIMap.Scale(aScaleX, aScaleY);
1190             HTMLOutFuncs::Out_ImageMap( rHTMLWrt.Strm(), rHTMLWrt.GetBaseURL(), aScaledIMap, aIMapName,
1191                                         aIMapEventTable,
1192                                         rHTMLWrt.m_bCfgStarBasic,
1193                                         SAL_NEWLINE_STRING, pIndArea, pIndMap,
1194                                         rHTMLWrt.m_eDestEnc,
1195                                         &rHTMLWrt.m_aNonConvertableCharacters );
1196         }
1197         else
1198         {
1199             HTMLOutFuncs::Out_ImageMap( rHTMLWrt.Strm(), rHTMLWrt.GetBaseURL(), *pIMap, aIMapName,
1200                                         aIMapEventTable,
1201                                         rHTMLWrt.m_bCfgStarBasic,
1202                                         SAL_NEWLINE_STRING, pIndArea, pIndMap,
1203                                          rHTMLWrt.m_eDestEnc,
1204                                         &rHTMLWrt.m_aNonConvertableCharacters );
1205         }
1206     }
1207     return aIMapName;
1208 }
1209 
1210 }
1211 
OutHTML_ImageStart(HtmlWriter & rHtml,Writer & rWrt,const SwFrameFormat & rFrameFormat,const OUString & rGraphicURL,Graphic const & rGraphic,const OUString & rAlternateText,const Size & rRealSize,HtmlFrmOpts nFrameOpts,const char * pMarkType,const ImageMap * pAltImgMap,const OUString & rMimeType)1212 Writer& OutHTML_ImageStart( HtmlWriter& rHtml, Writer& rWrt, const SwFrameFormat &rFrameFormat,
1213                        const OUString& rGraphicURL,
1214                        Graphic const & rGraphic, const OUString& rAlternateText,
1215                        const Size &rRealSize, HtmlFrmOpts nFrameOpts,
1216                        const char *pMarkType,
1217                        const ImageMap *pAltImgMap,
1218                        const OUString& rMimeType )
1219 {
1220     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
1221     // <object data="..."> instead of <img src="...">
1222     bool bReplacement = (nFrameOpts & HtmlFrmOpts::Replacement) || rHTMLWrt.mbReqIF;
1223 
1224     if (rHTMLWrt.mbSkipImages)
1225         return rHTMLWrt;
1226 
1227     // if necessary, temporarily close an open attribute
1228     if( !rHTMLWrt.m_aINetFormats.empty() )
1229     {
1230         SwFormatINetFormat* pINetFormat = rHTMLWrt.m_aINetFormats.back();
1231         OutHTML_INetFormat( rWrt, *pINetFormat, false );
1232     }
1233 
1234     OUString aGraphicURL( rGraphicURL );
1235     if( !rHTMLWrt.mbEmbedImages && !HTMLOutFuncs::PrivateURLToInternalImg(aGraphicURL) && !rHTMLWrt.mpTempBaseURL )
1236         aGraphicURL = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), aGraphicURL);
1237 
1238     const SfxPoolItem* pItem;
1239     const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
1240 
1241     const SwFormatURL* pURLItem = nullptr;
1242     OUString aIMapName = lclWriteOutImap(rHTMLWrt, rItemSet, rFrameFormat, rRealSize, pAltImgMap, pURLItem);
1243 
1244     // put img into new line
1245     if( rHTMLWrt.m_bLFPossible )
1246         rHTMLWrt.OutNewLine( true );
1247 
1248     // <a name=...></a>...<img ...>
1249     if( pMarkType && !rFrameFormat.GetName().isEmpty() )
1250     {
1251         rHTMLWrt.OutImplicitMark( rFrameFormat.GetName(), pMarkType );
1252     }
1253 
1254     // URL -> <a>...<img ... >...</a>
1255     const SvxMacroItem *pMacItem = nullptr;
1256     if (SfxItemState::SET == rItemSet.GetItemState(RES_FRMMACRO, true, &pItem))
1257     {
1258         pMacItem = static_cast<const SvxMacroItem *>(pItem);
1259     }
1260 
1261     if (pURLItem || pMacItem)
1262     {
1263         OUString aMapURL;
1264         OUString aName;
1265         OUString aTarget;
1266 
1267         if(pURLItem)
1268         {
1269             aMapURL = pURLItem->GetURL();
1270             aName = pURLItem->GetName();
1271             aTarget = pURLItem->GetTargetFrameName();
1272         }
1273 
1274         bool bEvents = pMacItem && !pMacItem->GetMacroTable().empty();
1275 
1276         if( !aMapURL.isEmpty() || !aName.isEmpty() || !aTarget.isEmpty() || bEvents )
1277         {
1278             rHtml.start(OOO_STRING_SVTOOLS_HTML_anchor);
1279 
1280             // Output "href" element if a link or macro exists
1281             if( !aMapURL.isEmpty() || bEvents )
1282             {
1283                 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_href, OUStringToOString(rHTMLWrt.convertHyperlinkHRefValue(aMapURL), RTL_TEXTENCODING_UTF8));
1284             }
1285 
1286             if( !aName.isEmpty() )
1287             {
1288                 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_name, OUStringToOString(aName, RTL_TEXTENCODING_UTF8));
1289             }
1290 
1291             if( !aTarget.isEmpty() )
1292             {
1293                 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_target, OUStringToOString(aTarget, RTL_TEXTENCODING_UTF8));
1294             }
1295 
1296             if( pMacItem )
1297             {
1298                 const SvxMacroTableDtor& rMacTable = pMacItem->GetMacroTable();
1299                 if (!rMacTable.empty())
1300                 {
1301                     HtmlWriterHelper::applyEvents(rHtml, rMacTable, aAnchorEventTable, rHTMLWrt.m_bCfgStarBasic);
1302                 }
1303             }
1304         }
1305     }
1306 
1307     // <font color = ...>...<img ... >...</font>
1308     sal_uInt16 nBorderWidth = 0;
1309     if( (nFrameOpts & HtmlFrmOpts::Border) &&
1310         SfxItemState::SET == rItemSet.GetItemState( RES_BOX, true, &pItem ))
1311     {
1312         Size aTwipBorder( 0, 0 );
1313         const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem);
1314 
1315         const ::editeng::SvxBorderLine *pColBorderLine = nullptr;
1316         const ::editeng::SvxBorderLine *pBorderLine = pBoxItem->GetLeft();
1317         if( pBorderLine )
1318         {
1319             pColBorderLine = pBorderLine;
1320             aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() );
1321         }
1322 
1323         pBorderLine = pBoxItem->GetRight();
1324         if( pBorderLine )
1325         {
1326             pColBorderLine = pBorderLine;
1327             aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() );
1328         }
1329 
1330         pBorderLine = pBoxItem->GetTop();
1331         if( pBorderLine )
1332         {
1333             pColBorderLine = pBorderLine;
1334             aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() );
1335         }
1336 
1337         pBorderLine = pBoxItem->GetBottom();
1338         if( pBorderLine )
1339         {
1340             pColBorderLine = pBorderLine;
1341             aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() );
1342         }
1343 
1344         aTwipBorder.setWidth( aTwipBorder.Width() / 2 );
1345         aTwipBorder.setHeight( aTwipBorder.Height() / 2 );
1346 
1347         if( (aTwipBorder.Width() || aTwipBorder.Height()) &&
1348             Application::GetDefaultDevice() )
1349         {
1350             Size aPixelBorder =
1351                 Application::GetDefaultDevice()->LogicToPixel( aTwipBorder,
1352                                                     MapMode(MapUnit::MapTwip) );
1353             if( !aPixelBorder.Width() && aTwipBorder.Width() )
1354                 aPixelBorder.setWidth( 1 );
1355             if( !aPixelBorder.Height() && aTwipBorder.Height() )
1356                 aPixelBorder.setHeight( 1 );
1357 
1358             if( aPixelBorder.Width() )
1359                 aPixelBorder.setHeight( 0 );
1360 
1361             nBorderWidth =
1362                 o3tl::narrowing<sal_uInt16>(aPixelBorder.Width() + aPixelBorder.Height());
1363         }
1364 
1365         if( pColBorderLine )
1366         {
1367             rHtml.start(OOO_STRING_SVTOOLS_HTML_font);
1368             HtmlWriterHelper::applyColor(rHtml, OOO_STRING_SVTOOLS_HTML_O_color, pColBorderLine->GetColor());
1369         }
1370     }
1371 
1372     OString aTag(OOO_STRING_SVTOOLS_HTML_image);
1373     if (bReplacement)
1374         // Write replacement graphic of OLE object as <object>.
1375         aTag = OOO_STRING_SVTOOLS_HTML_object;
1376     rHtml.start(aTag);
1377 
1378     OStringBuffer sBuffer;
1379     if(rHTMLWrt.mbEmbedImages)
1380     {
1381         OUString aGraphicInBase64;
1382         if (XOutBitmap::GraphicToBase64(rGraphic, aGraphicInBase64))
1383         {
1384             sBuffer.append(OOO_STRING_SVTOOLS_HTML_O_data);
1385             sBuffer.append(":");
1386             sBuffer.append(OUStringToOString(aGraphicInBase64, RTL_TEXTENCODING_UTF8));
1387             rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_src, sBuffer.makeStringAndClear().getStr());
1388         }
1389         else
1390             rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD;
1391     }
1392     else
1393     {
1394         sBuffer.append(OUStringToOString(aGraphicURL, RTL_TEXTENCODING_UTF8));
1395         OString aAttribute(OOO_STRING_SVTOOLS_HTML_O_src);
1396         if (bReplacement)
1397             aAttribute = OOO_STRING_SVTOOLS_HTML_O_data;
1398         rHtml.attribute(aAttribute, sBuffer.makeStringAndClear().getStr());
1399     }
1400 
1401     if (bReplacement)
1402     {
1403         // Handle XHTML type attribute for OLE replacement images.
1404         if (!rMimeType.isEmpty())
1405             rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_type, rMimeType.toUtf8());
1406     }
1407 
1408     // Events
1409     if (SfxItemState::SET == rItemSet.GetItemState(RES_FRMMACRO, true, &pItem))
1410     {
1411         const SvxMacroTableDtor& rMacTable = static_cast<const SvxMacroItem *>(pItem)->GetMacroTable();
1412         if (!rMacTable.empty())
1413         {
1414             HtmlWriterHelper::applyEvents(rHtml, rMacTable, aImageEventTable, rHTMLWrt.m_bCfgStarBasic);
1415         }
1416     }
1417 
1418     // alt, align, width, height, hspace, vspace
1419     rHTMLWrt.writeFrameFormatOptions(rHtml, rFrameFormat, rAlternateText, nFrameOpts);
1420     if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) )
1421         rHTMLWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameOpts );
1422 
1423     if ((nFrameOpts & HtmlFrmOpts::Border) && !bReplacement)
1424     {
1425         rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_border, nBorderWidth);
1426     }
1427 
1428     if( pURLItem && pURLItem->IsServerMap() )
1429     {
1430         rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_ismap);
1431     }
1432 
1433     if( !aIMapName.isEmpty() )
1434     {
1435         rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_usemap, OUString("#" + aIMapName));
1436     }
1437 
1438     if (bReplacement)
1439     {
1440         // XHTML object replacement image's alternate text doesn't use the
1441         // "alt" attribute.
1442         if (rAlternateText.isEmpty())
1443             // Empty alternate text is not valid.
1444             rHtml.characters(" ");
1445         else
1446             rHtml.characters(rAlternateText.toUtf8());
1447     }
1448 
1449     return rHTMLWrt;
1450 }
1451 
OutHTML_ImageEnd(HtmlWriter & rHtml,Writer & rWrt)1452 Writer& OutHTML_ImageEnd( HtmlWriter& rHtml, Writer& rWrt )
1453 {
1454     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
1455     rHtml.flushStack();
1456 
1457     if( !rHTMLWrt.m_aINetFormats.empty() )
1458     {
1459         // There is still an attribute on the stack that has to be reopened
1460         SwFormatINetFormat *pINetFormat = rHTMLWrt.m_aINetFormats.back();
1461         OutHTML_INetFormat( rWrt, *pINetFormat, true );
1462     }
1463 
1464     return rHTMLWrt;
1465 }
1466 
OutHTML_BulletImage(Writer & rWrt,const char * pTag,const SvxBrushItem * pBrush,const OUString & rGraphicURL)1467 Writer& OutHTML_BulletImage( Writer& rWrt,
1468                              const char *pTag,
1469                              const SvxBrushItem* pBrush,
1470                              const OUString &rGraphicURL)
1471 {
1472     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
1473 
1474     OUString aGraphicInBase64;
1475     OUString aLink;
1476     if( pBrush )
1477     {
1478         aLink = pBrush->GetGraphicLink();
1479         if(rHTMLWrt.mbEmbedImages || aLink.isEmpty())
1480         {
1481             const Graphic* pGrf = pBrush->GetGraphic();
1482             if( pGrf )
1483             {
1484                 if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) )
1485                 {
1486                     rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD;
1487                 }
1488             }
1489         }
1490         else if(!aLink.isEmpty())
1491         {
1492             if( rHTMLWrt.m_bCfgCpyLinkedGrfs )
1493             {
1494                 rHTMLWrt.CopyLocalFileToINet( aLink );
1495             }
1496 
1497         }
1498     }
1499     else if(!rHTMLWrt.mbEmbedImages)
1500     {
1501         aLink = rGraphicURL;
1502     }
1503     if(!aLink.isEmpty())
1504     {
1505         if( !HTMLOutFuncs::PrivateURLToInternalImg(aLink) )
1506             aLink = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), aLink);
1507     }
1508 
1509     OStringBuffer sOut;
1510     if( pTag )
1511         sOut.append('<').append(pTag);
1512 
1513     sOut.append(' ');
1514     sOut.append(OOO_STRING_SVTOOLS_HTML_O_style "=\"");
1515     if(!aLink.isEmpty())
1516     {
1517         sOut.append(OOO_STRING_SVTOOLS_HTML_O_src "=\"");
1518         rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
1519         HTMLOutFuncs::Out_String( rWrt.Strm(), aLink, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters );
1520     }
1521     else
1522     {
1523         sOut.append("list-style-image: url("
1524                 OOO_STRING_SVTOOLS_HTML_O_data ":");
1525         rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
1526         HTMLOutFuncs::Out_String( rWrt.Strm(), aGraphicInBase64, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters );
1527         sOut.append(");");
1528     }
1529     sOut.append('\"');
1530 
1531     if (pTag)
1532         sOut.append('>');
1533     rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
1534 
1535     return rWrt;
1536 }
1537 
OutHTML_FrameFormatTableNode(Writer & rWrt,const SwFrameFormat & rFrameFormat)1538 static Writer& OutHTML_FrameFormatTableNode( Writer& rWrt, const SwFrameFormat& rFrameFormat )
1539 {
1540     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
1541 
1542     const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
1543     sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
1544     sal_uLong nEnd = rHTMLWrt.m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex();
1545 
1546     OUString aCaption;
1547     bool bTopCaption = false;
1548 
1549     // Not const, because GetTable won't be const sometime later
1550     SwNode *pNd = rHTMLWrt.m_pDoc->GetNodes()[ nStt ];
1551     SwTableNode *pTableNd = pNd->GetTableNode();
1552     const SwTextNode *pTextNd = pNd->GetTextNode();
1553     if( !pTableNd && pTextNd )
1554     {
1555         // Table with heading
1556         bTopCaption = true;
1557         pTableNd = rHTMLWrt.m_pDoc->GetNodes()[nStt+1]->GetTableNode();
1558     }
1559     OSL_ENSURE( pTableNd, "Frame does not contain a table" );
1560     if( pTableNd )
1561     {
1562         sal_uLong nTableEnd = pTableNd->EndOfSectionIndex();
1563         OSL_ENSURE( nTableEnd == nEnd - 1 ||
1564                 (nTableEnd == nEnd - 2 && !bTopCaption),
1565                 "Invalid frame content for a table" );
1566 
1567         if( nTableEnd == nEnd - 2 )
1568             pTextNd = rHTMLWrt.m_pDoc->GetNodes()[nTableEnd+1]->GetTextNode();
1569     }
1570     if( pTextNd )
1571         aCaption = pTextNd->GetText();
1572 
1573     if( pTableNd )
1574     {
1575         HTMLSaveData aSaveData( rHTMLWrt, pTableNd->GetIndex()+1,
1576                                 pTableNd->EndOfSectionIndex(),
1577                                    true, &rFrameFormat );
1578         rHTMLWrt.m_bOutFlyFrame = true;
1579         OutHTML_SwTableNode( rHTMLWrt, *pTableNd, &rFrameFormat, &aCaption,
1580                            bTopCaption );
1581     }
1582 
1583     return rWrt;
1584 }
1585 
OutHTML_FrameFormatAsMulticol(Writer & rWrt,const SwFrameFormat & rFrameFormat,bool bInCntnr)1586 static Writer & OutHTML_FrameFormatAsMulticol( Writer& rWrt,
1587                                           const SwFrameFormat& rFrameFormat,
1588                                           bool bInCntnr )
1589 {
1590     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
1591 
1592     rHTMLWrt.ChangeParaToken( HtmlTokenId::NONE );
1593 
1594     // Close the current <DL>!
1595     rHTMLWrt.OutAndSetDefList( 0 );
1596 
1597     // output as Multicol
1598     if( rHTMLWrt.m_bLFPossible )
1599         rHTMLWrt.OutNewLine();
1600 
1601     OStringBuffer sOut;
1602     sOut.append('<').append(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol);
1603 
1604     const SwFormatCol& rFormatCol = rFrameFormat.GetCol();
1605 
1606     // output the number of columns as COLS
1607     sal_uInt16 nCols = rFormatCol.GetNumCols();
1608     if( nCols )
1609     {
1610         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cols
1611                 "=\"" + OString::number(nCols) + "\"");
1612     }
1613 
1614     // the Gutter width (minimum value) as GUTTER
1615     sal_uInt16 nGutter = rFormatCol.GetGutterWidth( true );
1616     if( nGutter!=USHRT_MAX )
1617     {
1618         if( nGutter && Application::GetDefaultDevice() )
1619         {
1620             nGutter = o3tl::narrowing<sal_uInt16>(Application::GetDefaultDevice()
1621                             ->LogicToPixel( Size(nGutter,0),
1622                                             MapMode(MapUnit::MapTwip) ).Width());
1623         }
1624         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_gutter
1625                 "=\"" + OString::number(nGutter) + "\"");
1626     }
1627 
1628     rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
1629 
1630     // WIDTH
1631     HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_MULTICOL;
1632     if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
1633         nFrameFlags |= HTML_FRMOPTS_MULTICOL_CSS1;
1634     rHTMLWrt.OutFrameFormatOptions(rFrameFormat, OUString(), nFrameFlags);
1635     if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
1636         rHTMLWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags );
1637 
1638     rWrt.Strm().WriteChar( '>' );
1639 
1640     rHTMLWrt.m_bLFPossible = true;
1641     rHTMLWrt.IncIndentLevel();  // indent the content of Multicol
1642 
1643     const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
1644     sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex();
1645     const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
1646     OSL_ENSURE( pSttNd, "Where is the start node" );
1647 
1648     {
1649         // in a block, so that the old state can be restored in time
1650         // before the end
1651         HTMLSaveData aSaveData( rHTMLWrt, nStt+1,
1652                                 pSttNd->EndOfSectionIndex(),
1653                                    true, &rFrameFormat );
1654         rHTMLWrt.m_bOutFlyFrame = true;
1655         rHTMLWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
1656     }
1657 
1658     rHTMLWrt.DecIndentLevel();  // indent the content of Multicol;
1659     if( rHTMLWrt.m_bLFPossible )
1660         rHTMLWrt.OutNewLine();
1661     HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol), false );
1662     rHTMLWrt.m_bLFPossible = true;
1663 
1664     return rWrt;
1665 }
1666 
OutHTML_FrameFormatAsSpacer(Writer & rWrt,const SwFrameFormat & rFrameFormat)1667 static Writer& OutHTML_FrameFormatAsSpacer( Writer& rWrt, const SwFrameFormat& rFrameFormat )
1668 {
1669     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
1670 
1671     // if possible, output a line break before the graphic
1672     if( rHTMLWrt.m_bLFPossible )
1673         rHTMLWrt.OutNewLine( true );
1674 
1675     OString sOut =
1676         "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_spacer " "
1677         OOO_STRING_SVTOOLS_HTML_O_type "=\""
1678         OOO_STRING_SVTOOLS_HTML_SPTYPE_block "\"";
1679     rWrt.Strm().WriteOString( sOut );
1680 
1681     // ALIGN, WIDTH, HEIGHT
1682     OString aEndTags = rHTMLWrt.OutFrameFormatOptions(rFrameFormat, OUString(), HTML_FRMOPTS_SPACER);
1683 
1684     rWrt.Strm().WriteChar( '>' );
1685     if( !aEndTags.isEmpty() )
1686         rWrt.Strm().WriteOString( aEndTags );
1687 
1688     return rWrt;
1689 }
1690 
OutHTML_FrameFormatAsDivOrSpan(Writer & rWrt,const SwFrameFormat & rFrameFormat,bool bSpan)1691 static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt,
1692                                           const SwFrameFormat& rFrameFormat, bool bSpan)
1693 {
1694     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
1695 
1696     OString aTag;
1697     if( !bSpan )
1698     {
1699         rHTMLWrt.ChangeParaToken( HtmlTokenId::NONE );
1700 
1701         // Close the current <DL>!
1702         rHTMLWrt.OutAndSetDefList( 0 );
1703         aTag = OOO_STRING_SVTOOLS_HTML_division;
1704     }
1705     else
1706         aTag = OOO_STRING_SVTOOLS_HTML_span;
1707 
1708     // output as DIV
1709     if( rHTMLWrt.m_bLFPossible )
1710         rHTMLWrt.OutNewLine();
1711 
1712     OStringBuffer sOut;
1713     sOut.append('<').append(rHTMLWrt.GetNamespace() + aTag);
1714 
1715     rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
1716     HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_DIV;
1717     if( rHTMLWrt.IsHTMLMode( HTMLMODE_BORDER_NONE ) )
1718        nFrameFlags |= HtmlFrmOpts::SNoBorder;
1719     OString aEndTags = rHTMLWrt.OutFrameFormatOptions(rFrameFormat, OUString(), nFrameFlags);
1720     rHTMLWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags );
1721     rWrt.Strm().WriteChar( '>' );
1722 
1723     rHTMLWrt.IncIndentLevel();  // indent the content
1724     rHTMLWrt.m_bLFPossible = true;
1725 
1726     const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
1727     sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex();
1728 
1729     // Output frame-anchored frames that are anchored to the start node
1730     rHTMLWrt.OutFlyFrame( nStt, 0, HtmlPosition::Any );
1731 
1732     const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
1733     OSL_ENSURE( pSttNd, "Where is the start node" );
1734 
1735     {
1736         // in a block, so that the old state can be restored in time
1737         // before the end
1738         HTMLSaveData aSaveData( rHTMLWrt, nStt+1,
1739                                 pSttNd->EndOfSectionIndex(),
1740                                    true, &rFrameFormat );
1741         rHTMLWrt.m_bOutFlyFrame = true;
1742         rHTMLWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
1743     }
1744 
1745     rHTMLWrt.DecIndentLevel();  // indent the content of Multicol;
1746     if( rHTMLWrt.m_bLFPossible )
1747         rHTMLWrt.OutNewLine();
1748     HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + aTag), false );
1749 
1750     if( !aEndTags.isEmpty() )
1751         rWrt.Strm().WriteOString( aEndTags );
1752 
1753     return rWrt;
1754 }
1755 
1756 /// Starts the OLE version of an image in the ReqIF + OLE case.
OutHTML_ImageOLEStart(SwHTMLWriter & rHTMLWrt,const Graphic & rGraphic,const SwFrameFormat & rFrameFormat)1757 static void OutHTML_ImageOLEStart(SwHTMLWriter& rHTMLWrt, const Graphic& rGraphic,
1758                                   const SwFrameFormat& rFrameFormat)
1759 {
1760     if (rHTMLWrt.mbReqIF && rHTMLWrt.m_bExportImagesAsOLE)
1761     {
1762         // Write the original image as an RTF fragment.
1763         OUString aFileName;
1764         if (rHTMLWrt.GetOrigFileName())
1765             aFileName = *rHTMLWrt.GetOrigFileName();
1766         INetURLObject aURL(aFileName);
1767         OUString aName = aURL.getBase() + "_" + aURL.getExtension() + "_"
1768                          + OUString::number(rGraphic.GetChecksum(), 16);
1769         aURL.setBase(aName);
1770         aURL.setExtension(u"ole");
1771         aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1772 
1773         SvFileStream aOutStream(aFileName, StreamMode::WRITE);
1774         if (!SwReqIfReader::WrapGraphicInRtf(rGraphic, rFrameFormat, aOutStream))
1775             SAL_WARN("sw.html", "SwReqIfReader::WrapGraphicInRtf() failed");
1776 
1777         // Refer to this data.
1778         aFileName = URIHelper::simpleNormalizedMakeRelative(rHTMLWrt.GetBaseURL(), aFileName);
1779         rHTMLWrt.Strm().WriteOString(
1780             OString("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object));
1781         rHTMLWrt.Strm().WriteOString(OString(" data=\"" + aFileName.toUtf8() + "\""));
1782         rHTMLWrt.Strm().WriteOString(" type=\"text/rtf\"");
1783         rHTMLWrt.Strm().WriteOString(">");
1784         rHTMLWrt.OutNewLine();
1785     }
1786 }
1787 
1788 /// Ends the OLE version of an image in the ReqIF + OLE case.
OutHTML_ImageOLEEnd(SwHTMLWriter & rHTMLWrt)1789 static void OutHTML_ImageOLEEnd(SwHTMLWriter& rHTMLWrt)
1790 {
1791     if (rHTMLWrt.mbReqIF && rHTMLWrt.m_bExportImagesAsOLE)
1792     {
1793         rHTMLWrt.Strm().WriteOString(
1794             OString("</" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">"));
1795     }
1796 }
1797 
OutHTML_FrameFormatAsImage(Writer & rWrt,const SwFrameFormat & rFrameFormat,bool bPNGFallback)1798 static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFrameFormat, bool bPNGFallback)
1799 {
1800     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
1801     bool bWritePNGFallback = rHTMLWrt.mbReqIF && !rHTMLWrt.m_bExportImagesAsOLE && bPNGFallback;
1802 
1803     if (rHTMLWrt.mbSkipImages)
1804         return rWrt;
1805 
1806     ImageMap aIMap;
1807     std::optional<Size> aDPI;
1808     if (rHTMLWrt.m_nShapeDPI.has_value())
1809     {
1810         aDPI.emplace(*rHTMLWrt.m_nShapeDPI, *rHTMLWrt.m_nShapeDPI);
1811     }
1812     Graphic aGraphic( const_cast<SwFrameFormat &>(rFrameFormat).MakeGraphic( &aIMap, /*nMaximumQuadraticPixels=*/2100000, aDPI ) );
1813 
1814     if (rHTMLWrt.mbReqIF)
1815     {
1816         // ImageMap doesn't seem to be allowed in reqif.
1817         if (auto pGrafObj = dynamic_cast<const SdrGrafObj*>(rFrameFormat.FindSdrObject()))
1818         {
1819             aGraphic = pGrafObj->GetGraphic();
1820         }
1821         else
1822         {
1823             // We only have a bitmap, write that as PNG without any fallback.
1824             bWritePNGFallback = false;
1825         }
1826     }
1827 
1828     Size aSz( 0, 0 );
1829     OUString GraphicURL;
1830     OUString aMimeType("image/jpeg");
1831     if(!rHTMLWrt.mbEmbedImages)
1832     {
1833         if( rHTMLWrt.GetOrigFileName() )
1834             GraphicURL = *rHTMLWrt.GetOrigFileName();
1835 
1836         OUString aFilterName("JPG");
1837         XOutFlags nFlags = XOutFlags::UseGifIfPossible | XOutFlags::UseNativeIfPossible;
1838 
1839         if (rHTMLWrt.mbReqIF && !bWritePNGFallback)
1840         {
1841             // Writing image without fallback PNG in ReqIF mode: force PNG output.
1842             aFilterName = "PNG";
1843             nFlags = XOutFlags::NONE;
1844             aMimeType = "image/png";
1845         }
1846         else if (rHTMLWrt.mbReqIF)
1847         {
1848             // Original format is wanted, don't force JPG.
1849             aFilterName.clear();
1850             aMimeType.clear();
1851         }
1852 
1853         if( aGraphic.GetType() == GraphicType::NONE ||
1854             XOutBitmap::WriteGraphic( aGraphic, GraphicURL,
1855                                       aFilterName,
1856                                       nFlags ) != ERRCODE_NONE )
1857         {
1858             // empty or incorrect, because there is nothing to output
1859             rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD;
1860             return rWrt;
1861         }
1862 
1863         GraphicURL = URIHelper::SmartRel2Abs(
1864             INetURLObject(rWrt.GetBaseURL()), GraphicURL,
1865             URIHelper::GetMaybeFileHdl() );
1866 
1867     }
1868     uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY);
1869     if (xGraphic.is() && aMimeType.isEmpty())
1870         xGraphic->getPropertyValue("MimeType") >>= aMimeType;
1871 
1872     OutHTML_ImageOLEStart(rHTMLWrt, aGraphic, rFrameFormat);
1873 
1874     HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
1875     OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz,
1876                     HtmlFrmOpts::GenImgMask, "frame",
1877                     aIMap.GetIMapObjectCount() ? &aIMap : nullptr, aMimeType );
1878 
1879     GfxLink aLink = aGraphic.GetGfxLink();
1880     if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
1881     {
1882         OutHTML_FrameFormatAsImage( rWrt, rFrameFormat, /*bPNGFallback=*/false);
1883     }
1884 
1885     OutHTML_ImageEnd(aHtml, rWrt);
1886 
1887     OutHTML_ImageOLEEnd(rHTMLWrt);
1888 
1889     return rWrt;
1890 }
1891 
OutHTML_FrameFormatGrfNode(Writer & rWrt,const SwFrameFormat & rFrameFormat,bool bInCntnr,bool bPNGFallback)1892 static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rFrameFormat,
1893                                       bool bInCntnr, bool bPNGFallback )
1894 {
1895     SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
1896     bool bWritePNGFallback = rHTMLWrt.mbReqIF && !rHTMLWrt.m_bExportImagesAsOLE && bPNGFallback;
1897 
1898     if (rHTMLWrt.mbSkipImages)
1899         return rWrt;
1900 
1901     const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
1902     sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
1903     SwGrfNode *pGrfNd = rHTMLWrt.m_pDoc->GetNodes()[ nStt ]->GetGrfNode();
1904     OSL_ENSURE( pGrfNd, "Grf node expected" );
1905     if( !pGrfNd )
1906         return rWrt;
1907 
1908     HtmlFrmOpts nFrameFlags = bInCntnr ? HTML_FRMOPTS_IMG_CNTNR : HTML_FRMOPTS_IMG;
1909     if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
1910          nFrameFlags |= HTML_FRMOPTS_IMG_CSS1;
1911 
1912     Graphic aGraphic = pGrfNd->GetGraphic();
1913     OUString aGraphicURL;
1914     OUString aMimeType;
1915     if(!rHTMLWrt.mbEmbedImages)
1916     {
1917         const SwMirrorGrf& rMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf();
1918 
1919         if( !pGrfNd->IsLinkedFile() || MirrorGraph::Dont != rMirror.GetValue() )
1920         {
1921             // create a (mirrored) jpeg file
1922             if( rHTMLWrt.GetOrigFileName() )
1923                 aGraphicURL = *rHTMLWrt.GetOrigFileName();
1924             else
1925                 aGraphicURL = rHTMLWrt.GetBaseURL();
1926             pGrfNd->GetGrf( true );
1927 
1928             XOutFlags nFlags = XOutFlags::UseGifIfSensible |
1929                            XOutFlags::UseNativeIfPossible;
1930             switch( rMirror.GetValue() )
1931             {
1932             case MirrorGraph::Vertical: nFlags = XOutFlags::MirrorHorz; break;
1933             case MirrorGraph::Horizontal:    nFlags = XOutFlags::MirrorVert; break;
1934             case MirrorGraph::Both:
1935                 nFlags = XOutFlags::MirrorVert | XOutFlags::MirrorHorz;
1936                 break;
1937             default: break;
1938             }
1939 
1940             Size aMM100Size;
1941             const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
1942             aMM100Size = OutputDevice::LogicToLogic( rSize.GetSize(),
1943                             MapMode( MapUnit::MapTwip ), MapMode( MapUnit::Map100thMM ));
1944 
1945             OUString aFilterName("");
1946 
1947             if (rHTMLWrt.mbReqIF && !bWritePNGFallback)
1948             {
1949                 // Writing image without fallback PNG in ReqIF mode: force PNG
1950                 // output.
1951                 // But don't force it when writing the original format and we'll write PNG inside
1952                 // that.
1953                 aFilterName = "PNG";
1954                 nFlags &= ~XOutFlags::UseNativeIfPossible;
1955                 nFlags &= ~XOutFlags::UseGifIfSensible;
1956                 aMimeType = "image/png";
1957             }
1958 
1959             const Graphic& rGraphic = pGrfNd->GetGrf();
1960 
1961             // So that Graphic::IsTransparent() can report true.
1962             if (!rGraphic.isAvailable())
1963                 const_cast<Graphic&>(rGraphic).makeAvailable();
1964 
1965             ErrCode nErr = XOutBitmap::WriteGraphic( rGraphic, aGraphicURL,
1966                     aFilterName, nFlags, &aMM100Size );
1967             if( nErr )
1968             {
1969                 rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD;
1970                 return rWrt;
1971             }
1972             aGraphicURL = URIHelper::SmartRel2Abs(
1973                 INetURLObject(rWrt.GetBaseURL()), aGraphicURL,
1974                 URIHelper::GetMaybeFileHdl() );
1975         }
1976         else
1977         {
1978             pGrfNd->GetFileFilterNms( &aGraphicURL, nullptr );
1979             if( rHTMLWrt.m_bCfgCpyLinkedGrfs )
1980                 rWrt.CopyLocalFileToINet( aGraphicURL );
1981         }
1982 
1983     }
1984     uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY);
1985     if (xGraphic.is() && aMimeType.isEmpty())
1986         xGraphic->getPropertyValue("MimeType") >>= aMimeType;
1987 
1988     OutHTML_ImageOLEStart(rHTMLWrt, aGraphic, rFrameFormat);
1989 
1990     HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace);
1991     OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, aGraphicURL, aGraphic, pGrfNd->GetTitle(),
1992                   pGrfNd->GetTwipSize(), nFrameFlags, "graphic", nullptr, aMimeType );
1993 
1994     GfxLink aLink = aGraphic.GetGfxLink();
1995     if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
1996     {
1997         // Not OLE mode, outer format is not PNG: write inner PNG.
1998         OutHTML_FrameFormatGrfNode( rWrt, rFrameFormat,
1999                                       bInCntnr, /*bPNGFallback=*/false );
2000     }
2001 
2002     OutHTML_ImageEnd(aHtml, rWrt);
2003 
2004     OutHTML_ImageOLEEnd(rHTMLWrt);
2005 
2006     return rWrt;
2007 }
2008 
OutHTML_FrameFormatAsMarquee(Writer & rWrt,const SwFrameFormat & rFrameFormat,const SdrObject & rSdrObj)2009 static Writer& OutHTML_FrameFormatAsMarquee( Writer& rWrt, const SwFrameFormat& rFrameFormat,
2010                                   const SdrObject& rSdrObj  )
2011 {
2012     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2013 
2014     // get the edit engine attributes of the object as SW attributes and
2015     // sort them as Hints
2016     const SfxItemSet& rFormatItemSet = rFrameFormat.GetAttrSet();
2017     SfxItemSet aItemSet( *rFormatItemSet.GetPool(), svl::Items<RES_CHRATR_BEGIN,
2018                                                  RES_CHRATR_END>{} );
2019     SwHTMLWriter::GetEEAttrsFromDrwObj( aItemSet, &rSdrObj );
2020     bool bCfgOutStylesOld = rHTMLWrt.m_bCfgOutStyles;
2021     rHTMLWrt.m_bCfgOutStyles = false;
2022     rHTMLWrt.m_bTextAttr = true;
2023     rHTMLWrt.m_bTagOn = true;
2024     Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false );
2025     rHTMLWrt.m_bTextAttr = false;
2026 
2027     OutHTML_DrawFrameFormatAsMarquee( rHTMLWrt,
2028                                  static_cast<const SwDrawFrameFormat &>(rFrameFormat),
2029                                  rSdrObj );
2030     rHTMLWrt.m_bTextAttr = true;
2031     rHTMLWrt.m_bTagOn = false;
2032     Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false );
2033     rHTMLWrt.m_bTextAttr = false;
2034     rHTMLWrt.m_bCfgOutStyles = bCfgOutStylesOld;
2035 
2036     return rWrt;
2037 }
2038 
OutHTML_HeaderFooter(Writer & rWrt,const SwFrameFormat & rFrameFormat,bool bHeader)2039 Writer& OutHTML_HeaderFooter( Writer& rWrt, const SwFrameFormat& rFrameFormat,
2040                               bool bHeader )
2041 {
2042     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
2043 
2044     // output as Multicol
2045     rHTMLWrt.OutNewLine();
2046     OStringBuffer sOut;
2047     sOut.append(OOO_STRING_SVTOOLS_HTML_division " "
2048             OOO_STRING_SVTOOLS_HTML_O_title "=\"")
2049         .append( bHeader ? "header" : "footer" ).append("\"");
2050     HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + sOut) );
2051 
2052     rHTMLWrt.IncIndentLevel();  // indent the content of Multicol;
2053 
2054     // Piece a spacer for the spacing together. Because the
2055     // <DL> or </DL> always produces a space between paragraphs, it is
2056     // subtracted if necessary.
2057     const SvxULSpaceItem& rULSpace = rFrameFormat.GetULSpace();
2058     sal_uInt16 nSize = bHeader ? rULSpace.GetLower() : rULSpace.GetUpper();
2059     rHTMLWrt.m_nHeaderFooterSpace = nSize;
2060 
2061     OString aSpacer;
2062     if( rHTMLWrt.IsHTMLMode(HTMLMODE_VERT_SPACER) &&
2063         nSize > HTML_PARSPACE && Application::GetDefaultDevice() )
2064     {
2065         nSize -= HTML_PARSPACE;
2066         nSize = static_cast<sal_Int16>(Application::GetDefaultDevice()
2067             ->LogicToPixel( Size(nSize,0), MapMode(MapUnit::MapTwip) ).Width());
2068 
2069         aSpacer = OOO_STRING_SVTOOLS_HTML_spacer
2070                 " " OOO_STRING_SVTOOLS_HTML_O_type
2071                 "=\"" OOO_STRING_SVTOOLS_HTML_SPTYPE_vertical "\""
2072                 " " OOO_STRING_SVTOOLS_HTML_O_size
2073                 "=\"" + OString::number(nSize) + "\"";
2074     }
2075 
2076     const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
2077     sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex();
2078     const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
2079     OSL_ENSURE( pSttNd, "Where is the start node" );
2080 
2081     if( !bHeader && !aSpacer.isEmpty() )
2082     {
2083         rHTMLWrt.OutNewLine();
2084         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + aSpacer) );
2085     }
2086 
2087     {
2088         // in a block, so that the old state can be restored in time
2089         // before the end. pFlyFormat doesn't need to be set here, because
2090         // PageDesc attributes cannot occur here
2091         HTMLSaveData aSaveData( rHTMLWrt, nStt+1,
2092                                 pSttNd->EndOfSectionIndex() );
2093 
2094         if( bHeader )
2095             rHTMLWrt.m_bOutHeader = true;
2096         else
2097             rHTMLWrt.m_bOutFooter = true;
2098 
2099         rHTMLWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
2100     }
2101 
2102     if( bHeader && !aSpacer.isEmpty() )
2103     {
2104         rHTMLWrt.OutNewLine();
2105         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + aSpacer) );
2106     }
2107 
2108     rHTMLWrt.DecIndentLevel();  // indent the content of Multicol;
2109     rHTMLWrt.OutNewLine();
2110     HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
2111 
2112     rHTMLWrt.m_nHeaderFooterSpace = 0;
2113 
2114     return rWrt;
2115 }
2116 
AddLinkTarget(const OUString & rURL)2117 void SwHTMLWriter::AddLinkTarget( const OUString& rURL )
2118 {
2119     if( rURL.isEmpty() || rURL[0] != '#' )
2120         return;
2121 
2122     // There might be a '|' as delimiter (if the link has been inserted
2123     // freshly) or a '%7c' or a '%7C' if the document has been saved and
2124     // loaded already.
2125     sal_Int32 nPos = rURL.getLength();
2126     bool bFound = false, bEncoded = false;
2127     while( !bFound && nPos > 0 )
2128     {
2129         sal_Unicode c = rURL[ --nPos ];
2130         switch( c )
2131         {
2132         case cMarkSeparator:
2133             bFound = true;
2134             break;
2135         case '%':
2136             bFound = (rURL.getLength() - nPos) >=3 && rURL[ nPos+1 ] == '7';
2137             if(bFound)
2138             {
2139                 c = rURL[ nPos+2 ];
2140                 bFound = (c == 'C' || c == 'c');
2141             }
2142             if( bFound )
2143                 bEncoded = true;
2144         }
2145     }
2146     if( !bFound || nPos < 2 ) // at least "#a|..."
2147         return;
2148 
2149     OUString aURL( rURL.copy( 1 ) );
2150 
2151     // nPos-1+1/3 (-1 because of Erase)
2152     OUString sCmp = aURL.copy(bEncoded ? nPos+2 : nPos).replaceAll(" ","");
2153     if( sCmp.isEmpty() )
2154         return;
2155 
2156     sCmp = sCmp.toAsciiLowerCase();
2157 
2158     if( sCmp == "region" ||
2159         sCmp == "frame" ||
2160         sCmp == "graphic" ||
2161         sCmp == "ole" ||
2162         sCmp == "table" )
2163     {
2164         // Just remember it in a sorted array
2165         if( bEncoded )
2166         {
2167             aURL = aURL.replaceAt( nPos - 1, 3, OUString(cMarkSeparator)  );
2168         }
2169         m_aImplicitMarks.insert( aURL );
2170     }
2171     else if( sCmp == "outline" )
2172     {
2173         // Here, we need position and name. That's why we sort a
2174         // sal_uInt16 and a string array ourselves.
2175         OUString aOutline( aURL.copy( 0, nPos-1 ) );
2176         SwPosition aPos( *m_pCurrentPam->GetPoint() );
2177         if( m_pDoc->GotoOutline( aPos, aOutline ) )
2178         {
2179             sal_uInt32 nIdx = aPos.nNode.GetIndex();
2180 
2181             decltype(m_aOutlineMarkPoss)::size_type nIns=0;
2182             while( nIns < m_aOutlineMarkPoss.size() &&
2183                    m_aOutlineMarkPoss[nIns] < nIdx )
2184                 nIns++;
2185 
2186             m_aOutlineMarkPoss.insert( m_aOutlineMarkPoss.begin()+nIns, nIdx );
2187             if( bEncoded )
2188             {
2189                 aURL = aURL.replaceAt( nPos - 1, 3, OUString(cMarkSeparator) );
2190             }
2191             m_aOutlineMarks.insert( m_aOutlineMarks.begin()+nIns, aURL );
2192         }
2193     }
2194 }
2195 
CollectLinkTargets()2196 void SwHTMLWriter::CollectLinkTargets()
2197 {
2198     const SwTextINetFormat* pTextAttr;
2199 
2200     for (const SfxPoolItem* pItem : m_pDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
2201     {
2202         auto pINetFormat = dynamic_cast<const SwFormatINetFormat*>(pItem);
2203         const SwTextNode* pTextNd;
2204 
2205         if( pINetFormat &&
2206             nullptr != ( pTextAttr = pINetFormat->GetTextINetFormat()) &&
2207             nullptr != ( pTextNd = pTextAttr->GetpTextNode() ) &&
2208             pTextNd->GetNodes().IsDocNodes() )
2209         {
2210             AddLinkTarget( pINetFormat->GetValue() );
2211         }
2212     }
2213 
2214     for (const SfxPoolItem* pItem : m_pDoc->GetAttrPool().GetItemSurrogates(RES_URL))
2215     {
2216         auto pURL = dynamic_cast<const SwFormatURL*>(pItem);
2217         if( pURL )
2218         {
2219             AddLinkTarget( pURL->GetURL() );
2220             const ImageMap *pIMap = pURL->GetMap();
2221             if( pIMap )
2222             {
2223                 for( size_t i=0; i<pIMap->GetIMapObjectCount(); ++i )
2224                 {
2225                     const IMapObject* pObj = pIMap->GetIMapObject( i );
2226                     if( pObj )
2227                     {
2228                         AddLinkTarget( pObj->GetURL() );
2229                     }
2230                 }
2231             }
2232         }
2233     }
2234 }
2235 
2236 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2237