Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / filter / html / htmlflywriter.cxx
blob535fcb59fae26a6d4f288765cc35c621f0219522
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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 #include <comphelper/xmlencode.hxx>
47 #include <fmtanchr.hxx>
48 #include <fmtornt.hxx>
49 #include <fmturl.hxx>
50 #include <fmtfsize.hxx>
51 #include <fmtclds.hxx>
52 #include <fmtcntnt.hxx>
53 #include <fmtsrnd.hxx>
54 #include <fmtinfmt.hxx>
55 #include <txtinet.hxx>
56 #include <frmatr.hxx>
57 #include <grfatr.hxx>
58 #include <flypos.hxx>
59 #include <ndgrf.hxx>
61 #include <doc.hxx>
62 #include <ndtxt.hxx>
63 #include <pam.hxx>
64 #include <swerror.h>
65 #include <frmfmt.hxx>
66 #include "wrthtml.hxx"
67 #include "htmlatr.hxx"
68 #include "htmlfly.hxx"
69 #include "htmlreqifreader.hxx"
71 using namespace css;
73 const HtmlFrmOpts HTML_FRMOPTS_IMG_ALL =
74 HtmlFrmOpts::Alt |
75 HtmlFrmOpts::Size |
76 HtmlFrmOpts::AnySize |
77 HtmlFrmOpts::Border |
78 HtmlFrmOpts::Name;
79 const HtmlFrmOpts HTML_FRMOPTS_IMG_CNTNR =
80 HTML_FRMOPTS_IMG_ALL |
81 HtmlFrmOpts::AbsSize;
82 const HtmlFrmOpts HTML_FRMOPTS_IMG =
83 HTML_FRMOPTS_IMG_ALL |
84 HtmlFrmOpts::Align |
85 HtmlFrmOpts::Space |
86 HtmlFrmOpts::BrClear;
87 const HtmlFrmOpts HTML_FRMOPTS_IMG_CSS1 =
88 HtmlFrmOpts::SAlign |
89 HtmlFrmOpts::SSpace;
91 const HtmlFrmOpts HTML_FRMOPTS_DIV =
92 HtmlFrmOpts::Id |
93 HtmlFrmOpts::SAlign |
94 HtmlFrmOpts::SSize |
95 HtmlFrmOpts::AnySize |
96 HtmlFrmOpts::AbsSize |
97 HtmlFrmOpts::SSpace |
98 HtmlFrmOpts::SBorder |
99 HtmlFrmOpts::SBackground |
100 HtmlFrmOpts::BrClear |
101 HtmlFrmOpts::Dir;
103 const HtmlFrmOpts HTML_FRMOPTS_MULTICOL =
104 HtmlFrmOpts::Id |
105 HtmlFrmOpts::Width |
106 HtmlFrmOpts::AnySize |
107 HtmlFrmOpts::AbsSize |
108 HtmlFrmOpts::Dir;
110 const HtmlFrmOpts HTML_FRMOPTS_MULTICOL_CSS1 =
111 HtmlFrmOpts::SAlign |
112 HtmlFrmOpts::SSize |
113 HtmlFrmOpts::SSpace |
114 HtmlFrmOpts::SBorder|
115 HtmlFrmOpts::SBackground;
117 const HtmlFrmOpts HTML_FRMOPTS_SPACER =
118 HtmlFrmOpts::Align |
119 HtmlFrmOpts::Size |
120 HtmlFrmOpts::AnySize |
121 HtmlFrmOpts::BrClear |
122 HtmlFrmOpts::MarginSize |
123 HtmlFrmOpts::AbsSize;
125 const HtmlFrmOpts HTML_FRMOPTS_CNTNR =
126 HtmlFrmOpts::SAlign |
127 HtmlFrmOpts::SSpace |
128 HtmlFrmOpts::SWidth |
129 HtmlFrmOpts::AnySize |
130 HtmlFrmOpts::AbsSize |
131 HtmlFrmOpts::SPixSize;
133 static SwHTMLWriter& OutHTML_FrameFormatTableNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat );
134 static SwHTMLWriter& OutHTML_FrameFormatAsMulticol( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat,
135 bool bInCntnr );
136 static SwHTMLWriter& OutHTML_FrameFormatAsSpacer( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat );
137 static SwHTMLWriter& OutHTML_FrameFormatAsDivOrSpan( SwHTMLWriter& rWrt,
138 const SwFrameFormat& rFrameFormat, bool bSpan );
139 static SwHTMLWriter& OutHTML_FrameFormatAsImage( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat, bool bPNGFallback );
141 static SwHTMLWriter& OutHTML_FrameFormatGrfNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat,
142 bool bInCntnr, bool bPNGFallback );
144 static SwHTMLWriter& OutHTML_FrameFormatAsMarquee( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
145 const SdrObject& rSdrObj );
147 HTMLOutEvent const aImageEventTable[] =
149 { OOO_STRING_SVTOOLS_HTML_O_SDonload, OOO_STRING_SVTOOLS_HTML_O_onload, SvMacroItemId::OnImageLoadDone },
150 { OOO_STRING_SVTOOLS_HTML_O_SDonabort, OOO_STRING_SVTOOLS_HTML_O_onabort, SvMacroItemId::OnImageLoadCancel },
151 { OOO_STRING_SVTOOLS_HTML_O_SDonerror, OOO_STRING_SVTOOLS_HTML_O_onerror, SvMacroItemId::OnImageLoadError },
152 { nullptr, nullptr, SvMacroItemId::NONE }
155 HTMLOutEvent const aIMapEventTable[] =
157 { OOO_STRING_SVTOOLS_HTML_O_SDonmouseover, OOO_STRING_SVTOOLS_HTML_O_onmouseover, SvMacroItemId::OnMouseOver },
158 { OOO_STRING_SVTOOLS_HTML_O_SDonmouseout, OOO_STRING_SVTOOLS_HTML_O_onmouseout, SvMacroItemId::OnMouseOut },
159 { nullptr, nullptr, SvMacroItemId::NONE }
162 SwHTMLFrameType SwHTMLWriter::GuessFrameType( const SwFrameFormat& rFrameFormat,
163 const SdrObject*& rpSdrObj )
165 SwHTMLFrameType eType;
167 if( RES_DRAWFRMFMT == rFrameFormat.Which() )
169 // use an arbitrary draw object as the default value
170 eType = HTML_FRMTYPE_DRAW;
172 const SdrObject *pObj =
173 SwHTMLWriter::GetMarqueeTextObj( static_cast<const SwDrawFrameFormat &>(rFrameFormat) );
174 if( pObj )
176 // scrolling text
177 rpSdrObj = pObj;
178 eType = HTML_FRMTYPE_MARQUEE;
180 else
182 pObj = GetHTMLControl( static_cast<const SwDrawFrameFormat &>(rFrameFormat) );
184 if( pObj )
186 // Form control
187 rpSdrObj = pObj;
188 eType = HTML_FRMTYPE_CONTROL;
192 else
194 // use a text frame as the default value
195 eType = HTML_FRMTYPE_TEXT;
197 const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
198 SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
199 const SwNode* pNd = m_pDoc->GetNodes()[ nStt ];
201 if( pNd->IsGrfNode() )
203 // graphic node
204 eType = HTML_FRMTYPE_GRF;
206 else if( pNd->IsOLENode() )
208 // applet, plugin, floating frame
209 eType = GuessOLENodeFrameType( *pNd );
211 else
213 SwNodeOffset nEnd = m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex();
215 const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
216 const SwFormatCol* pFormatCol = rItemSet.GetItemIfSet( RES_COL );
217 if( pFormatCol && pFormatCol->GetNumCols() > 1 )
219 // frame with columns
220 eType = HTML_FRMTYPE_MULTICOL;
222 else if( pNd->IsTableNode() )
224 const SwTableNode *pTableNd = pNd->GetTableNode();
225 SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex();
227 if( nTableEnd+1 == nEnd )
229 // table
230 eType = HTML_FRMTYPE_TABLE;
232 else if( nTableEnd+2 == nEnd )
234 // table with caption
235 eType = HTML_FRMTYPE_TABLE_CAP;
238 else if( pNd->IsTextNode() )
240 const SwTextNode *pTextNd = pNd->GetTextNode();
242 bool bEmpty = false;
243 if( nStt==nEnd-1 && !pTextNd->Len() )
245 // empty frame? Only if no frame is
246 // anchored to the text or start node.
247 bEmpty = true;
248 for( auto & pHTMLPosFlyFrame : m_aHTMLPosFlyFrames )
250 SwNodeOffset nIdx = pHTMLPosFlyFrame->GetNdIndex().GetIndex();
251 bEmpty = (nIdx != nStt) && (nIdx != nStt-1);
252 if( !bEmpty || nIdx > nStt )
253 break;
256 if( bEmpty )
258 std::unique_ptr<SvxBrushItem> aBrush = rFrameFormat.makeBackgroundBrushItem();
259 /// background is not empty, if it has a background graphic
260 /// or its background color is not "no fill"/"auto fill".
261 if( GPOS_NONE != aBrush->GetGraphicPos() ||
262 aBrush->GetColor() != COL_TRANSPARENT )
264 bEmpty = false;
267 if( bEmpty )
269 // empty frame
270 eType = HTML_FRMTYPE_EMPTY;
272 else if( m_pDoc->GetNodes()[nStt+1]->IsTableNode() )
274 const SwTableNode *pTableNd =
275 m_pDoc->GetNodes()[nStt+1]->GetTableNode();
276 if( pTableNd->EndOfSectionIndex()+1 == nEnd )
278 // table with heading
279 eType = HTML_FRMTYPE_TABLE_CAP;
286 return eType;
289 void SwHTMLWriter::CollectFlyFrames()
291 OSL_ENSURE( HTML_CFG_MAX+1 == MAX_BROWSERS,
292 "number of browser configurations has changed" );
294 SwPosFlyFrames aFlyPos(
295 m_pDoc->GetAllFlyFormats(m_bWriteAll ? nullptr : m_pCurrentPam.get(), true));
297 for(const SwPosFlyFrame& rItem : aFlyPos)
299 const SwFrameFormat& rFrameFormat = rItem.GetFormat();
300 const SdrObject *pSdrObj = nullptr;
301 const SwNode *pAnchorNode;
302 const SwContentNode *pACNd;
303 SwHTMLFrameType eType = GuessFrameType( rFrameFormat, pSdrObj );
305 AllHtmlFlags nMode;
306 const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
307 sal_Int16 eHoriRel = rFrameFormat.GetHoriOrient().GetRelationOrient();
308 switch( rAnchor.GetAnchorId() )
310 case RndStdIds::FLY_AT_PAGE:
311 case RndStdIds::FLY_AT_FLY:
312 nMode = aHTMLOutFramePageFlyTable[eType][m_nExportMode];
313 break;
315 case RndStdIds::FLY_AT_PARA:
316 // frames that are anchored to a paragraph are only placed
317 // before the paragraph, if the paragraph has a
318 // spacing.
319 if( text::RelOrientation::FRAME == eHoriRel &&
320 (pAnchorNode = rAnchor.GetAnchorNode()) != nullptr &&
321 (pACNd = pAnchorNode->GetContentNode()) != nullptr )
323 const SvxTextLeftMarginItem& rTextLeftMargin =
324 pACNd->GetAttr(RES_MARGIN_TEXTLEFT);
325 const SvxRightMarginItem& rRightMargin =
326 pACNd->GetAttr(RES_MARGIN_RIGHT);
327 if (rTextLeftMargin.GetTextLeft() || rRightMargin.GetRight())
329 nMode = aHTMLOutFrameParaFrameTable[eType][m_nExportMode];
330 break;
333 nMode = aHTMLOutFrameParaPrtAreaTable[eType][m_nExportMode];
334 break;
336 case RndStdIds::FLY_AT_CHAR:
337 if( text::RelOrientation::FRAME == eHoriRel || text::RelOrientation::PRINT_AREA == eHoriRel )
338 nMode = aHTMLOutFrameParaPrtAreaTable[eType][m_nExportMode];
339 else
340 nMode = aHTMLOutFrameParaOtherTable[eType][m_nExportMode];
341 break;
343 default:
344 nMode = aHTMLOutFrameParaPrtAreaTable[eType][m_nExportMode];
345 break;
348 m_aHTMLPosFlyFrames.insert( std::make_unique<SwHTMLPosFlyFrame>(rItem, pSdrObj, nMode) );
352 bool SwHTMLWriter::OutFlyFrame( SwNodeOffset nNdIdx, sal_Int32 nContentIdx, HtmlPosition nPos )
354 bool bFlysLeft = false; // Are there still Flys left at the current node position?
356 // OutFlyFrame can be called recursively. Thus, sometimes it is
357 // necessary to start over after a Fly was returned.
358 bool bRestart = true;
359 while( !m_aHTMLPosFlyFrames.empty() && bRestart )
361 bFlysLeft = bRestart = false;
363 // search for the beginning of the FlyFrames
364 size_t i {0};
366 for( ; i < m_aHTMLPosFlyFrames.size() &&
367 m_aHTMLPosFlyFrames[i]->GetNdIndex().GetIndex() < nNdIdx; i++ )
369 for( ; !bRestart && i < m_aHTMLPosFlyFrames.size() &&
370 m_aHTMLPosFlyFrames[i]->GetNdIndex().GetIndex() == nNdIdx; i++ )
372 SwHTMLPosFlyFrame *pPosFly = m_aHTMLPosFlyFrames[i].get();
373 if( ( HtmlPosition::Any == nPos ||
374 pPosFly->GetOutPos() == nPos ) &&
375 pPosFly->GetContentIndex() == nContentIdx )
377 // It is important to remove it first, because additional
378 // elements or the whole array could be deleted on
379 // deeper recursion levels.
380 std::unique_ptr<SwHTMLPosFlyFrame> flyHolder = m_aHTMLPosFlyFrames.erase_extract(i);
381 i--;
382 if( m_aHTMLPosFlyFrames.empty() )
384 bRestart = true; // not really, only exit the loop
387 HTMLOutFuncs::FlushToAscii(Strm()); // it was one time only; do we still need it?
389 OutFrameFormat( pPosFly->GetOutMode(), pPosFly->GetFormat(),
390 pPosFly->GetSdrObject() );
391 switch( pPosFly->GetOutFn() )
393 case HtmlOut::Div:
394 case HtmlOut::Span:
395 case HtmlOut::MultiCol:
396 case HtmlOut::TableNode:
397 bRestart = true; // It could become recursive here
398 break;
399 default: break;
402 else
404 bFlysLeft = true;
409 return bFlysLeft;
412 void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFrameFormat,
413 const SdrObject *pSdrObject )
415 HtmlContainerFlags nCntnrMode = nMode.nContainer;
416 HtmlOut nOutMode = nMode.nOut;
417 OString aContainerStr;
418 if( HtmlContainerFlags::NONE != nCntnrMode )
421 if( m_bLFPossible && HtmlContainerFlags::Div == nCntnrMode )
422 OutNewLine();
424 OStringBuffer sOut;
425 aContainerStr = (HtmlContainerFlags::Div == nCntnrMode)
426 ? OOO_STRING_SVTOOLS_HTML_division
427 : OOO_STRING_SVTOOLS_HTML_span;
428 sOut.append("<" + GetNamespace() + aContainerStr + " "
429 OOO_STRING_SVTOOLS_HTML_O_class "=\""
430 "sd-abs-pos\"");
431 Strm().WriteOString( sOut );
432 sOut.setLength(0);
434 // Output a width for non-draw objects
435 HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_CNTNR;
437 // For frames with columns we can also output the background
438 if( HtmlOut::MultiCol == nOutMode )
439 nFrameFlags |= HtmlFrmOpts::SBackground|HtmlFrmOpts::SBorder;
441 if( IsHTMLMode( HTMLMODE_BORDER_NONE ) )
442 nFrameFlags |= HtmlFrmOpts::SNoBorder;
443 OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags, pSdrObject );
444 Strm().WriteChar( '>' );
446 if( HtmlContainerFlags::Div == nCntnrMode )
448 IncIndentLevel();
449 m_bLFPossible = true;
453 switch( nOutMode )
455 case HtmlOut::TableNode: // OK
456 OSL_ENSURE( aContainerStr.isEmpty(), "Table: Container is not supposed to be here" );
457 OutHTML_FrameFormatTableNode( *this, rFrameFormat );
458 break;
459 case HtmlOut::GraphicNode: // OK
460 OutHTML_FrameFormatGrfNode( *this, rFrameFormat, !aContainerStr.isEmpty(), /*bPNGFallback=*/true );
461 break;
462 case HtmlOut::OleNode: // OK
463 OutHTML_FrameFormatOLENode( *this, rFrameFormat, !aContainerStr.isEmpty() );
464 break;
465 case HtmlOut::OleGraphic: // OK
466 OutHTML_FrameFormatOLENodeGrf( *this, rFrameFormat, !aContainerStr.isEmpty() );
467 break;
468 case HtmlOut::Div:
469 case HtmlOut::Span:
470 OSL_ENSURE( aContainerStr.isEmpty(), "Div: Container is not supposed to be here" );
471 OutHTML_FrameFormatAsDivOrSpan( *this, rFrameFormat, HtmlOut::Span==nOutMode );
472 break;
473 case HtmlOut::MultiCol: // OK
474 OutHTML_FrameFormatAsMulticol( *this, rFrameFormat, !aContainerStr.isEmpty() );
475 break;
476 case HtmlOut::Spacer: // OK
477 OSL_ENSURE( aContainerStr.isEmpty(), "Spacer: Container is not supposed to be here" );
478 OutHTML_FrameFormatAsSpacer( *this, rFrameFormat );
479 break;
480 case HtmlOut::Control: // OK
481 OutHTML_DrawFrameFormatAsControl( *this,
482 static_cast<const SwDrawFrameFormat &>(rFrameFormat), dynamic_cast<const SdrUnoObj&>(*pSdrObject),
483 !aContainerStr.isEmpty() );
484 break;
485 case HtmlOut::AMarquee:
486 OutHTML_FrameFormatAsMarquee( *this, rFrameFormat, *pSdrObject );
487 break;
488 case HtmlOut::Marquee:
489 OSL_ENSURE( aContainerStr.isEmpty(), "Marquee: Container is not supposed to be here" );
490 OutHTML_DrawFrameFormatAsMarquee( *this,
491 static_cast<const SwDrawFrameFormat &>(rFrameFormat), *pSdrObject );
492 break;
493 case HtmlOut::GraphicFrame:
494 OutHTML_FrameFormatAsImage( *this, rFrameFormat, /*bPNGFallback=*/true );
495 break;
498 if( HtmlContainerFlags::Div == nCntnrMode )
500 DecIndentLevel();
501 if( m_bLFPossible )
502 OutNewLine();
503 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
504 m_bLFPossible = true;
506 else if( HtmlContainerFlags::Span == nCntnrMode )
507 HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
510 OString SwHTMLWriter::OutFrameFormatOptions( const SwFrameFormat &rFrameFormat,
511 const OUString& rAlternateText,
512 HtmlFrmOpts nFrameOpts )
514 OString sRetEndTags;
515 OStringBuffer sOut;
516 const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
518 // Name
519 if( (nFrameOpts & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) &&
520 !rFrameFormat.GetName().isEmpty() )
522 const char *pStr =
523 (nFrameOpts & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name;
524 sOut.append(OString::Concat(" ") + pStr + "=\"");
525 Strm().WriteOString( sOut );
526 sOut.setLength(0);
527 HTMLOutFuncs::Out_String( Strm(), rFrameFormat.GetName() );
528 sOut.append('\"');
531 // Name
532 if( nFrameOpts & HtmlFrmOpts::Dir )
534 SvxFrameDirection nDir = GetHTMLDirection( rItemSet );
535 Strm().WriteOString( sOut );
536 sOut.setLength(0);
537 OutDirection( nDir );
540 // ALT
541 if( (nFrameOpts & HtmlFrmOpts::Alt) && !rAlternateText.isEmpty() )
543 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_alt "=\"");
544 Strm().WriteOString( sOut );
545 sOut.setLength(0);
546 HTMLOutFuncs::Out_String( Strm(), rAlternateText );
547 sOut.append('\"');
550 // ALIGN
551 const char *pStr = nullptr;
552 RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
553 if( (nFrameOpts & HtmlFrmOpts::Align) &&
554 ((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) )
556 // MIB 12.3.98: Wouldn't it be more clever to left-align frames that
557 // are anchored to a paragraph if necessary, instead of inserting them
558 // as being anchored to characters?
559 const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
560 if( !(nFrameOpts & HtmlFrmOpts::SAlign) ||
561 text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
562 text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
564 pStr = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient()
565 ? OOO_STRING_SVTOOLS_HTML_AL_right
566 : OOO_STRING_SVTOOLS_HTML_AL_left;
569 const SwFormatVertOrient* pVertOrient;
570 if( (nFrameOpts & HtmlFrmOpts::Align) && !pStr &&
571 ( !(nFrameOpts & HtmlFrmOpts::SAlign) ||
572 (RndStdIds::FLY_AS_CHAR == eAnchorId) ) &&
573 (pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT )) )
575 switch( pVertOrient->GetVertOrient() )
577 case text::VertOrientation::LINE_TOP: pStr = OOO_STRING_SVTOOLS_HTML_VA_top; break;
578 case text::VertOrientation::CHAR_TOP:
579 case text::VertOrientation::BOTTOM: pStr = OOO_STRING_SVTOOLS_HTML_VA_texttop; break; // not possible
580 case text::VertOrientation::LINE_CENTER:
581 case text::VertOrientation::CHAR_CENTER: pStr = OOO_STRING_SVTOOLS_HTML_VA_absmiddle; break; // not possible
582 case text::VertOrientation::CENTER: pStr = OOO_STRING_SVTOOLS_HTML_VA_middle; break;
583 case text::VertOrientation::LINE_BOTTOM:
584 case text::VertOrientation::CHAR_BOTTOM: pStr = OOO_STRING_SVTOOLS_HTML_VA_absbottom; break; // not possible
585 case text::VertOrientation::TOP: pStr = OOO_STRING_SVTOOLS_HTML_VA_bottom; break;
586 case text::VertOrientation::NONE: break;
589 if( pStr )
591 sOut.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"") +
592 pStr + "\"");
595 // HSPACE and VSPACE
596 Size aTwipSpc( 0, 0 );
597 const SvxLRSpaceItem* pLRSpaceItem;
598 if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
599 (pLRSpaceItem = rItemSet.GetItemIfSet( RES_LR_SPACE )) )
601 aTwipSpc.setWidth(
602 ( pLRSpaceItem->GetLeft() + pLRSpaceItem->GetRight() ) / 2 );
603 m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width();
605 const SvxULSpaceItem* pULSpaceItem;
606 if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
607 (pULSpaceItem = rItemSet.GetItemIfSet( RES_UL_SPACE )) )
609 aTwipSpc.setHeight(
610 ( pULSpaceItem->GetUpper() + pULSpaceItem->GetLower() ) / 2 );
611 m_nDfltTopMargin = m_nDfltBottomMargin = o3tl::narrowing<sal_uInt16>(aTwipSpc.Height());
614 if( (nFrameOpts & HtmlFrmOpts::Space) &&
615 (aTwipSpc.Width() || aTwipSpc.Height()) &&
616 !mbReqIF )
618 Size aPixelSpc = SwHTMLWriter::ToPixel(aTwipSpc);
620 if( aPixelSpc.Width() )
622 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_hspace
623 "=\"" + OString::number(aPixelSpc.Width()) + "\"");
626 if( aPixelSpc.Height() )
628 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_vspace
629 "=\"" + OString::number(aPixelSpc.Height()) + "\"");
633 // The spacing must be considered for the size, if the corresponding flag
634 // is set.
635 if( nFrameOpts & HtmlFrmOpts::MarginSize )
637 aTwipSpc.setWidth( aTwipSpc.Width() * -2 );
638 aTwipSpc.setHeight( aTwipSpc.Height() * -2 );
640 else
642 aTwipSpc.setWidth( 0 );
643 aTwipSpc.setHeight( 0 );
646 const SvxBoxItem* pBoxItem;
647 if( !(nFrameOpts & HtmlFrmOpts::AbsSize) &&
648 (pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
650 aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) );
651 aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) );
652 aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) );
653 aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) );
656 // WIDTH and/or HEIGHT
657 // Output SwFrameSize::Variable/SwFrameSize::Minimum only, if ANYSIZE is set
658 const SwFormatFrameSize *pFSItem;
659 if( (nFrameOpts & HtmlFrmOpts::Size) &&
660 (pFSItem = rItemSet.GetItemIfSet( RES_FRM_SIZE )) &&
661 ( (nFrameOpts & HtmlFrmOpts::AnySize) ||
662 SwFrameSize::Fixed == pFSItem->GetHeightSizeType()) )
664 sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent();
665 sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent();
667 // Size of the object in Twips without margins
668 Size aTwipSz( (nPercentWidth ? 0
669 : pFSItem->GetWidth()-aTwipSpc.Width()),
670 (nPercentHeight ? 0
671 : pFSItem->GetHeight()-aTwipSpc.Height()) );
673 OSL_ENSURE( aTwipSz.Width() >= 0 && aTwipSz.Height() >= 0, "Frame size minus spacing < 0!!!???" );
674 if( aTwipSz.Width() < 0 )
675 aTwipSz.setWidth( 0 );
676 if( aTwipSz.Height() < 0 )
677 aTwipSz.setHeight( 0 );
679 Size aPixelSz(SwHTMLWriter::ToPixel(aTwipSz));
681 if( (nFrameOpts & HtmlFrmOpts::Width) &&
682 ((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) )
684 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
685 if( nPercentWidth )
686 sOut.append(OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%");
687 else
688 sOut.append(static_cast<sal_Int32>(aPixelSz.Width()));
689 sOut.append("\"");
692 if( (nFrameOpts & HtmlFrmOpts::Height) &&
693 ((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) )
695 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_height "=\"");
696 if( nPercentHeight )
697 sOut.append(OString::number(static_cast<sal_Int32>(nPercentHeight)) + "%");
698 else
699 sOut.append(static_cast<sal_Int32>(aPixelSz.Height()));
700 sOut.append("\"");
704 if (!sOut.isEmpty())
706 Strm().WriteOString( sOut );
707 sOut.setLength(0);
710 // Insert wrap for graphics that are anchored to a paragraph as
711 // <BR CLEAR=...> in the string
712 const SwFormatSurround* pSurround;
713 if( (nFrameOpts & HtmlFrmOpts::BrClear) &&
714 ((RndStdIds::FLY_AT_PARA == rFrameFormat.GetAnchor().GetAnchorId()) ||
715 (RndStdIds::FLY_AT_CHAR == rFrameFormat.GetAnchor().GetAnchorId())) &&
716 (pSurround = rItemSet.GetItemIfSet( RES_SURROUND )) )
718 sal_Int16 eHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient();
719 pStr = nullptr;
720 css::text::WrapTextMode eSurround = pSurround->GetSurround();
721 bool bAnchorOnly = pSurround->IsAnchorOnly();
722 switch( eHoriOri )
724 case text::HoriOrientation::RIGHT:
726 switch( eSurround )
728 case css::text::WrapTextMode_NONE:
729 case css::text::WrapTextMode_RIGHT:
730 pStr = OOO_STRING_SVTOOLS_HTML_AL_right;
731 break;
732 case css::text::WrapTextMode_LEFT:
733 case css::text::WrapTextMode_PARALLEL:
734 if( bAnchorOnly )
735 m_bClearRight = true;
736 break;
737 default:
741 break;
743 default:
744 // If a frame is centered, it gets left aligned. This
745 // should be taken into account here, too.
747 switch( eSurround )
749 case css::text::WrapTextMode_NONE:
750 case css::text::WrapTextMode_LEFT:
751 pStr = OOO_STRING_SVTOOLS_HTML_AL_left;
752 break;
753 case css::text::WrapTextMode_RIGHT:
754 case css::text::WrapTextMode_PARALLEL:
755 if( bAnchorOnly )
756 m_bClearLeft = true;
757 break;
758 default:
762 break;
766 if( pStr )
768 sOut.append("<" OOO_STRING_SVTOOLS_HTML_linebreak
769 " " OOO_STRING_SVTOOLS_HTML_O_clear
770 "=\"" + OString::Concat(pStr) + "\">");
771 sRetEndTags = sOut.makeStringAndClear();
774 return sRetEndTags;
777 void SwHTMLWriter::writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameFormat& rFrameFormat, std::u16string_view rAlternateText, HtmlFrmOpts nFrameOptions)
779 bool bReplacement = (nFrameOptions & HtmlFrmOpts::Replacement) || mbReqIF;
780 const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
782 // Name
783 if( (nFrameOptions & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) &&
784 !rFrameFormat.GetName().isEmpty() && !bReplacement)
786 const char* pAttributeName = (nFrameOptions & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name;
787 aHtml.attribute(pAttributeName, rFrameFormat.GetName());
790 // Name
791 if (nFrameOptions & HtmlFrmOpts::Dir)
793 SvxFrameDirection nCurrentDirection = GetHTMLDirection(rItemSet);
794 OString sDirection = convertDirection(nCurrentDirection);
795 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_dir, sDirection);
798 // alt
799 if( (nFrameOptions & HtmlFrmOpts::Alt) && !rAlternateText.empty() && !bReplacement )
801 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_alt, rAlternateText);
804 // align
805 std::string_view pAlignString;
806 RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
807 if( (nFrameOptions & HtmlFrmOpts::Align) &&
808 ((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) && !bReplacement)
810 const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
811 if( !(nFrameOptions & HtmlFrmOpts::SAlign) ||
812 text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
813 text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
815 pAlignString = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient()
816 ? std::string_view(OOO_STRING_SVTOOLS_HTML_AL_right)
817 : std::string_view(OOO_STRING_SVTOOLS_HTML_AL_left);
820 const SwFormatVertOrient* pVertOrient;
821 if( (nFrameOptions & HtmlFrmOpts::Align) && pAlignString.empty() &&
822 ( !(nFrameOptions & HtmlFrmOpts::SAlign) ||
823 (RndStdIds::FLY_AS_CHAR == eAnchorId) ) &&
824 (pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT )) )
826 switch( pVertOrient->GetVertOrient() )
828 case text::VertOrientation::LINE_TOP: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_top; break;
829 case text::VertOrientation::CHAR_TOP:
830 case text::VertOrientation::BOTTOM: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_texttop; break;
831 case text::VertOrientation::LINE_CENTER:
832 case text::VertOrientation::CHAR_CENTER: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absmiddle; break;
833 case text::VertOrientation::CENTER: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_middle; break;
834 case text::VertOrientation::LINE_BOTTOM:
835 case text::VertOrientation::CHAR_BOTTOM: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absbottom; break;
836 case text::VertOrientation::TOP: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_bottom; break;
837 case text::VertOrientation::NONE: break;
840 if (!pAlignString.empty() && !bReplacement)
842 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, pAlignString);
845 // hspace and vspace
846 Size aTwipSpc( 0, 0 );
847 const SvxLRSpaceItem* pLRSpaceItem;
848 if( (nFrameOptions & (HtmlFrmOpts::Space | HtmlFrmOpts::MarginSize)) &&
849 (pLRSpaceItem = rItemSet.GetItemIfSet( RES_LR_SPACE )) )
851 aTwipSpc.setWidth(
852 ( pLRSpaceItem->GetLeft() + pLRSpaceItem->GetRight() ) / 2 );
853 m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width();
855 const SvxULSpaceItem* pULSpaceItem;
856 if( (nFrameOptions & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
857 (pULSpaceItem = rItemSet.GetItemIfSet( RES_UL_SPACE )) )
859 aTwipSpc.setHeight(
860 ( pULSpaceItem->GetUpper() + pULSpaceItem->GetLower() ) / 2 );
861 m_nDfltTopMargin = m_nDfltBottomMargin = o3tl::narrowing<sal_uInt16>(aTwipSpc.Height());
864 if( (nFrameOptions & HtmlFrmOpts::Space) &&
865 (aTwipSpc.Width() || aTwipSpc.Height()) &&
866 !mbReqIF )
868 Size aPixelSpc = SwHTMLWriter::ToPixel(aTwipSpc);
870 if (aPixelSpc.Width())
872 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_hspace, static_cast<sal_Int32>(aPixelSpc.Width()));
875 if (aPixelSpc.Height())
877 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_vspace, static_cast<sal_Int32>(aPixelSpc.Height()));
881 // The spacing must be considered for the size, if the corresponding flag
882 // is set.
883 if( nFrameOptions & HtmlFrmOpts::MarginSize )
885 aTwipSpc.setWidth( aTwipSpc.Width() * -2 );
886 aTwipSpc.setHeight( aTwipSpc.Height() * -2 );
888 else
890 aTwipSpc.setWidth( 0 );
891 aTwipSpc.setHeight( 0 );
894 const SvxBoxItem* pBoxItem;
895 if( !(nFrameOptions & HtmlFrmOpts::AbsSize) &&
896 (pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
898 aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) );
899 aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) );
900 aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) );
901 aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) );
904 // "width" and/or "height"
905 // Only output SwFrameSize::Variable/SwFrameSize::Minimum if ANYSIZE is set
906 std::optional<SwFormatFrameSize> aFrameSize;
907 const SwFormatFrameSize* pFSItem = rItemSet.GetItemIfSet( RES_FRM_SIZE );
908 const SdrObject* pObject;
909 if (!pFSItem && (pObject = rFrameFormat.FindSdrObject()))
911 // Write size for Draw shapes as well.
912 const tools::Rectangle& rSnapRect = pObject->GetSnapRect();
913 aFrameSize.emplace();
914 aFrameSize->SetWidthSizeType(SwFrameSize::Fixed);
915 aFrameSize->SetWidth(rSnapRect.getOpenWidth());
916 aFrameSize->SetHeightSizeType(SwFrameSize::Fixed);
917 aFrameSize->SetHeight(rSnapRect.getOpenHeight());
918 pFSItem = &*aFrameSize;
920 if( (nFrameOptions & HtmlFrmOpts::Size) &&
921 pFSItem &&
922 ( (nFrameOptions & HtmlFrmOpts::AnySize) ||
923 SwFrameSize::Fixed == pFSItem->GetHeightSizeType()) )
925 sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent();
926 sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent();
928 // Size of the object in Twips without margins
929 Size aTwipSz( (nPercentWidth && nPercentWidth != 255 ? 0
930 : pFSItem->GetWidth()-aTwipSpc.Width()),
931 (nPercentHeight && nPercentHeight != 255 ? 0
932 : pFSItem->GetHeight()-aTwipSpc.Height()) );
934 OSL_ENSURE( aTwipSz.Width() >= 0 && aTwipSz.Height() >= 0, "Frame size minus spacing < 0!!!???" );
935 if( aTwipSz.Width() < 0 )
936 aTwipSz.setWidth( 0 );
937 if( aTwipSz.Height() < 0 )
938 aTwipSz.setHeight( 0 );
940 Size aPixelSz(SwHTMLWriter::ToPixel(aTwipSz));
942 if( (nFrameOptions & HtmlFrmOpts::Width) &&
943 ((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) )
945 OString sWidth;
946 if (nPercentWidth)
948 if (nPercentWidth == 255)
950 if (nPercentHeight)
952 sWidth = "auto";
954 else
956 sWidth = OString::number(static_cast<sal_Int32>(aPixelSz.Width()));
959 else
961 sWidth = OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%";
964 else
965 sWidth = OString::number(static_cast<sal_Int32>(aPixelSz.Width()));
966 if (!mbXHTML || sWidth != "auto")
968 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_width, sWidth);
972 if( (nFrameOptions & HtmlFrmOpts::Height) &&
973 ((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) )
975 OString sHeight;
976 if (nPercentHeight)
978 if (nPercentHeight == 255)
980 if (nPercentWidth)
982 sHeight = "auto";
984 else
986 sHeight = OString::number(static_cast<sal_Int32>(aPixelSz.Height()));
989 else
991 sHeight = OString::number(static_cast<sal_Int32>(nPercentHeight)) + "%";
994 else
995 sHeight = OString::number(static_cast<sal_Int32>(aPixelSz.Height()));
996 if (!mbXHTML || sHeight != "auto")
998 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_height, sHeight);
1003 // Insert wrap for graphics that are anchored to a paragraph as
1004 // <BR CLEAR=...> in the string
1006 if( !(nFrameOptions & HtmlFrmOpts::BrClear) )
1007 return;
1008 RndStdIds nAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
1009 if (RndStdIds::FLY_AT_PARA != nAnchorId && RndStdIds::FLY_AT_CHAR != nAnchorId)
1010 return;
1011 const SwFormatSurround* pSurround = rItemSet.GetItemIfSet( RES_SURROUND );
1012 if (!pSurround)
1013 return;
1015 std::string_view pSurroundString;
1017 sal_Int16 eHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient();
1018 css::text::WrapTextMode eSurround = pSurround->GetSurround();
1019 bool bAnchorOnly = pSurround->IsAnchorOnly();
1020 switch( eHoriOri )
1022 case text::HoriOrientation::RIGHT:
1024 switch( eSurround )
1026 case css::text::WrapTextMode_NONE:
1027 case css::text::WrapTextMode_RIGHT:
1028 pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_right;
1029 break;
1030 case css::text::WrapTextMode_LEFT:
1031 case css::text::WrapTextMode_PARALLEL:
1032 if( bAnchorOnly )
1033 m_bClearRight = true;
1034 break;
1035 default:
1039 break;
1041 default:
1042 // If a frame is centered, it gets left aligned. This
1043 // should be taken into account here, too.
1045 switch( eSurround )
1047 case css::text::WrapTextMode_NONE:
1048 case css::text::WrapTextMode_LEFT:
1049 pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_left;
1050 break;
1051 case css::text::WrapTextMode_RIGHT:
1052 case css::text::WrapTextMode_PARALLEL:
1053 if( bAnchorOnly )
1054 m_bClearLeft = true;
1055 break;
1056 default:
1057 break;
1060 break;
1063 if (!pSurroundString.empty())
1065 aHtml.start(OOO_STRING_SVTOOLS_HTML_linebreak);
1066 aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, pSurroundString);
1067 aHtml.end();
1071 namespace
1074 OUString lclWriteOutImap(SwHTMLWriter& rWrt, const SfxItemSet& rItemSet, const SwFrameFormat& rFrameFormat,
1075 const Size& rRealSize, const ImageMap* pAltImgMap, const SwFormatURL*& pURLItem)
1077 OUString aIMapName;
1079 // Only consider the URL attribute if no ImageMap was supplied
1080 if (!pAltImgMap)
1081 pURLItem = rItemSet.GetItemIfSet( RES_URL );
1083 // write ImageMap
1084 const ImageMap* pIMap = pAltImgMap;
1085 if( !pIMap && pURLItem )
1087 pIMap = pURLItem->GetMap();
1090 if (pIMap)
1092 // make the name unique
1093 aIMapName = pIMap->GetName();
1094 OUString aNameBase;
1095 if (!aIMapName.isEmpty())
1096 aNameBase = aIMapName;
1097 else
1098 aNameBase = OOO_STRING_SVTOOLS_HTML_map;
1100 if (aIMapName.isEmpty())
1101 aIMapName = aNameBase + OUString::number(rWrt.m_nImgMapCnt);
1103 bool bFound;
1106 bFound = false;
1107 for (const OUString & rImgMapName : rWrt.m_aImgMapNames)
1109 // TODO: Unicode: Comparison is case insensitive for ASCII
1110 // characters only now!
1111 if (aIMapName.equalsIgnoreAsciiCase(rImgMapName))
1113 bFound = true;
1114 break;
1117 if (bFound)
1119 rWrt.m_nImgMapCnt++;
1120 aIMapName = aNameBase + OUString::number( rWrt.m_nImgMapCnt );
1122 } while (bFound);
1124 bool bScale = false;
1125 Fraction aScaleX(1, 1);
1126 Fraction aScaleY(1, 1);
1128 const SwFormatFrameSize& rFrameSize = rFrameFormat.GetFrameSize();
1129 const SvxBoxItem& rBox = rFrameFormat.GetBox();
1131 if (!rFrameSize.GetWidthPercent() && rRealSize.Width())
1133 SwTwips nWidth = rFrameSize.GetWidth();
1134 nWidth -= rBox.CalcLineSpace(SvxBoxItemLine::LEFT) + rBox.CalcLineSpace(SvxBoxItemLine::RIGHT);
1136 OSL_ENSURE( nWidth > 0, "Are there any graphics that are 0 twip wide!?" );
1137 if (nWidth <= 0) // should not happen
1138 nWidth = 1;
1140 if (rRealSize.Width() != nWidth)
1142 aScaleX = Fraction(nWidth, rRealSize.Width());
1143 bScale = true;
1147 if (!rFrameSize.GetHeightPercent() && rRealSize.Height())
1149 SwTwips nHeight = rFrameSize.GetHeight();
1151 nHeight -= rBox.CalcLineSpace(SvxBoxItemLine::TOP) + rBox.CalcLineSpace(SvxBoxItemLine::BOTTOM);
1153 OSL_ENSURE( nHeight > 0, "Are there any graphics that are 0 twip high!?" );
1154 if (nHeight <= 0)
1155 nHeight = 1;
1157 if (rRealSize.Height() != nHeight)
1159 aScaleY = Fraction(nHeight, rRealSize.Height());
1160 bScale = true;
1164 rWrt.m_aImgMapNames.push_back(aIMapName);
1166 OString aIndMap, aIndArea;
1167 const char *pIndArea = nullptr, *pIndMap = nullptr;
1169 if (rWrt.m_bLFPossible)
1171 rWrt.OutNewLine( true );
1172 aIndMap = rWrt.GetIndentString();
1173 aIndArea = rWrt.GetIndentString(1);
1174 pIndArea = aIndArea.getStr();
1175 pIndMap = aIndMap.getStr();
1178 if (bScale)
1180 ImageMap aScaledIMap(*pIMap);
1181 aScaledIMap.Scale(aScaleX, aScaleY);
1182 HTMLOutFuncs::Out_ImageMap( rWrt.Strm(), rWrt.GetBaseURL(), aScaledIMap, aIMapName,
1183 aIMapEventTable,
1184 rWrt.m_bCfgStarBasic,
1185 SAL_NEWLINE_STRING, pIndArea, pIndMap );
1187 else
1189 HTMLOutFuncs::Out_ImageMap( rWrt.Strm(), rWrt.GetBaseURL(), *pIMap, aIMapName,
1190 aIMapEventTable,
1191 rWrt.m_bCfgStarBasic,
1192 SAL_NEWLINE_STRING, pIndArea, pIndMap );
1195 return aIMapName;
1198 OUString getFrameFormatText(const SwFrameFormat& rFrameFormat)
1200 const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
1201 const SwNodeIndex* pSttIx = rFlyContent.GetContentIdx();
1202 if (!pSttIx)
1203 return {};
1205 const SwNodeOffset nStt = pSttIx->GetIndex();
1206 const auto& nodes = rFrameFormat.GetDoc()->GetNodes();
1207 const SwNodeOffset nEnd = nodes[nStt]->EndOfSectionIndex();
1209 OUStringBuffer result;
1210 for (SwNodeOffset i = nStt + 1; i < nEnd; ++i)
1212 if (const auto* pTextNd = nodes[i]->GetTextNode())
1214 if (!result.isEmpty())
1215 result.append("\n");
1216 result.append(comphelper::string::encodeForXml(pTextNd->GetExpandText(
1217 nullptr, 0, -1, true, true, false,
1218 ExpandMode::ExpandFields | ExpandMode::HideInvisible | ExpandMode::HideDeletions
1219 | ExpandMode::HideFieldmarkCommands)));
1223 return result.makeStringAndClear();
1228 SwHTMLWriter& OutHTML_ImageStart( HtmlWriter& rHtml, SwHTMLWriter& rWrt, const SwFrameFormat &rFrameFormat,
1229 const OUString& rGraphicURL,
1230 Graphic const & rGraphic, const OUString& rAlternateText,
1231 const Size &rRealSize, HtmlFrmOpts nFrameOpts,
1232 const char *pMarkType,
1233 const ImageMap *pAltImgMap,
1234 std::u16string_view rMimeType )
1236 // <object data="..."> instead of <img src="...">
1237 bool bReplacement = (nFrameOpts & HtmlFrmOpts::Replacement) || rWrt.mbReqIF;
1239 if (rWrt.mbSkipImages)
1240 return rWrt;
1242 // if necessary, temporarily close an open attribute
1243 if( !rWrt.m_aINetFormats.empty() )
1245 SwFormatINetFormat* pINetFormat = rWrt.m_aINetFormats.back();
1246 OutHTML_INetFormat( rWrt, *pINetFormat, false );
1249 OUString aGraphicURL( rGraphicURL );
1250 if( !rWrt.mbEmbedImages && !HTMLOutFuncs::PrivateURLToInternalImg(aGraphicURL) && !rWrt.mpTempBaseURL )
1251 aGraphicURL = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), aGraphicURL);
1253 const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
1255 const SwFormatURL* pURLItem = nullptr;
1256 OUString aIMapName = lclWriteOutImap(rWrt, rItemSet, rFrameFormat, rRealSize, pAltImgMap, pURLItem);
1258 // put img into new line
1259 if( rWrt.m_bLFPossible )
1260 rWrt.OutNewLine( true );
1262 // <a name=...></a>...<img ...>
1263 if( pMarkType && !rFrameFormat.GetName().isEmpty() )
1265 rWrt.OutImplicitMark( rFrameFormat.GetName(), pMarkType );
1268 // URL -> <a>...<img ... >...</a>
1269 const SvxMacroItem *pMacItem = rItemSet.GetItemIfSet(RES_FRMMACRO);
1271 if (pURLItem || pMacItem)
1273 OUString aMapURL;
1274 OUString aName;
1275 OUString aTarget;
1277 if(pURLItem)
1279 aMapURL = pURLItem->GetURL();
1280 aName = pURLItem->GetName();
1281 aTarget = pURLItem->GetTargetFrameName();
1284 bool bEvents = pMacItem && !pMacItem->GetMacroTable().empty();
1286 if( !aMapURL.isEmpty() || !aName.isEmpty() || !aTarget.isEmpty() || bEvents )
1288 rHtml.start(OOO_STRING_SVTOOLS_HTML_anchor);
1290 // Output "href" element if a link or macro exists
1291 if( !aMapURL.isEmpty() || bEvents )
1293 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_href, rWrt.convertHyperlinkHRefValue(aMapURL));
1296 if( !aName.isEmpty() )
1298 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_name, aName);
1301 if( !aTarget.isEmpty() )
1303 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_target, aTarget);
1306 if( pMacItem )
1308 const SvxMacroTableDtor& rMacTable = pMacItem->GetMacroTable();
1309 if (!rMacTable.empty())
1311 HtmlWriterHelper::applyEvents(rHtml, rMacTable, aAnchorEventTable, rWrt.m_bCfgStarBasic);
1317 // <font color = ...>...<img ... >...</font>
1318 sal_uInt16 nBorderWidth = 0;
1319 const SvxBoxItem* pBoxItem;
1320 if( (nFrameOpts & HtmlFrmOpts::Border) &&
1321 (pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
1323 Size aTwipBorder( 0, 0 );
1324 const ::editeng::SvxBorderLine *pColBorderLine = nullptr;
1325 const ::editeng::SvxBorderLine *pBorderLine = pBoxItem->GetLeft();
1326 if( pBorderLine )
1328 pColBorderLine = pBorderLine;
1329 aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() );
1332 pBorderLine = pBoxItem->GetRight();
1333 if( pBorderLine )
1335 pColBorderLine = pBorderLine;
1336 aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() );
1339 pBorderLine = pBoxItem->GetTop();
1340 if( pBorderLine )
1342 pColBorderLine = pBorderLine;
1343 aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() );
1346 pBorderLine = pBoxItem->GetBottom();
1347 if( pBorderLine )
1349 pColBorderLine = pBorderLine;
1350 aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() );
1353 aTwipBorder.setWidth( aTwipBorder.Width() / 2 );
1354 aTwipBorder.setHeight( aTwipBorder.Height() / 2 );
1356 if( (aTwipBorder.Width() || aTwipBorder.Height()) &&
1357 Application::GetDefaultDevice() )
1359 Size aPixelBorder = SwHTMLWriter::ToPixel(aTwipBorder);
1361 if( aPixelBorder.Width() )
1362 aPixelBorder.setHeight( 0 );
1364 nBorderWidth =
1365 o3tl::narrowing<sal_uInt16>(aPixelBorder.Width() + aPixelBorder.Height());
1368 if( pColBorderLine )
1370 rHtml.start(OOO_STRING_SVTOOLS_HTML_font);
1371 HtmlWriterHelper::applyColor(rHtml, OOO_STRING_SVTOOLS_HTML_O_color, pColBorderLine->GetColor());
1375 OString aTag(OOO_STRING_SVTOOLS_HTML_image);
1376 if (bReplacement)
1377 // Write replacement graphic of OLE object as <object>.
1378 aTag = OOO_STRING_SVTOOLS_HTML_object;
1379 rHtml.start(aTag);
1381 if(rWrt.mbEmbedImages)
1383 OUString aGraphicInBase64;
1384 if (XOutBitmap::GraphicToBase64(rGraphic, aGraphicInBase64))
1386 OString sBuffer(OString::Concat(OOO_STRING_SVTOOLS_HTML_O_data)
1387 + ":"
1388 + OUStringToOString(aGraphicInBase64, RTL_TEXTENCODING_UTF8));
1389 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_src, sBuffer);
1391 else
1392 rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
1394 else
1396 OString sBuffer(OUStringToOString(aGraphicURL, RTL_TEXTENCODING_UTF8));
1397 OString aAttribute(OOO_STRING_SVTOOLS_HTML_O_src);
1398 if (bReplacement)
1399 aAttribute = OOO_STRING_SVTOOLS_HTML_O_data;
1400 rHtml.attribute(aAttribute, sBuffer);
1403 if (bReplacement)
1405 // Handle XHTML type attribute for OLE replacement images.
1406 if (!rMimeType.empty())
1407 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_type, rMimeType);
1410 // Events
1411 if (const SvxMacroItem* pMacroItem = rItemSet.GetItemIfSet(RES_FRMMACRO))
1413 const SvxMacroTableDtor& rMacTable = pMacroItem->GetMacroTable();
1414 if (!rMacTable.empty())
1416 HtmlWriterHelper::applyEvents(rHtml, rMacTable, aImageEventTable, rWrt.m_bCfgStarBasic);
1420 // alt, align, width, height, hspace, vspace
1421 rWrt.writeFrameFormatOptions(rHtml, rFrameFormat, rAlternateText, nFrameOpts);
1422 if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) )
1423 rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameOpts );
1425 if ((nFrameOpts & HtmlFrmOpts::Border) && !bReplacement)
1427 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_border, nBorderWidth);
1430 if( pURLItem && pURLItem->IsServerMap() )
1432 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_ismap);
1435 if( !aIMapName.isEmpty() )
1437 rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_usemap, Concat2View("#" + aIMapName));
1440 if (bReplacement)
1442 OUString aAltText = rAlternateText;
1443 // In ReqIF mode, output text from the frame instead
1444 if (rWrt.mbReqIF)
1445 if (OUString aFrameText = getFrameFormatText(rFrameFormat); !aFrameText.isEmpty())
1446 aAltText = aFrameText;
1448 // XHTML object replacement image's alternate text doesn't use the
1449 // "alt" attribute.
1450 if (aAltText.isEmpty())
1451 // Empty alternate text is not valid.
1452 rHtml.characters(" ");
1453 else
1454 rHtml.characters(aAltText.toUtf8());
1457 return rWrt;
1460 SwHTMLWriter& OutHTML_ImageEnd( HtmlWriter& rHtml, SwHTMLWriter& rWrt )
1462 rHtml.flushStack();
1464 if( !rWrt.m_aINetFormats.empty() )
1466 // There is still an attribute on the stack that has to be reopened
1467 SwFormatINetFormat *pINetFormat = rWrt.m_aINetFormats.back();
1468 OutHTML_INetFormat( rWrt, *pINetFormat, true );
1471 return rWrt;
1474 SwHTMLWriter& OutHTML_BulletImage( SwHTMLWriter& rWrt,
1475 const char *pTag,
1476 const SvxBrushItem* pBrush,
1477 const OUString &rGraphicURL)
1479 OUString aGraphicInBase64;
1480 OUString aLink;
1481 if( pBrush )
1483 aLink = pBrush->GetGraphicLink();
1484 if(rWrt.mbEmbedImages || aLink.isEmpty())
1486 const Graphic* pGrf = pBrush->GetGraphic();
1487 if( pGrf )
1489 if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) )
1491 rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
1495 else if(!aLink.isEmpty())
1497 if( rWrt.m_bCfgCpyLinkedGrfs )
1499 rWrt.CopyLocalFileToINet( aLink );
1504 else if(!rWrt.mbEmbedImages)
1506 aLink = rGraphicURL;
1508 if(!aLink.isEmpty())
1510 if( !HTMLOutFuncs::PrivateURLToInternalImg(aLink) )
1511 aLink = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), aLink);
1514 OStringBuffer sOut;
1515 if( pTag )
1516 sOut.append(OString::Concat("<") + pTag);
1518 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_style "=\"");
1519 if(!aLink.isEmpty())
1521 sOut.append(OOO_STRING_SVTOOLS_HTML_O_src "=\"");
1522 rWrt.Strm().WriteOString( sOut );
1523 sOut.setLength(0);
1524 HTMLOutFuncs::Out_String( rWrt.Strm(), aLink );
1526 else
1528 sOut.append("list-style-image: url("
1529 OOO_STRING_SVTOOLS_HTML_O_data ":");
1530 rWrt.Strm().WriteOString( sOut );
1531 sOut.setLength(0);
1532 HTMLOutFuncs::Out_String( rWrt.Strm(), aGraphicInBase64 );
1533 sOut.append(");");
1535 sOut.append('\"');
1537 if (pTag)
1538 sOut.append('>');
1539 rWrt.Strm().WriteOString( sOut );
1541 return rWrt;
1544 static SwHTMLWriter& OutHTML_FrameFormatTableNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat )
1546 const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
1547 SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
1548 SwNodeOffset nEnd = rWrt.m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex();
1550 OUString aCaption;
1551 bool bTopCaption = false;
1553 // Not const, because GetTable won't be const sometime later
1554 SwNode *pNd = rWrt.m_pDoc->GetNodes()[ nStt ];
1555 SwTableNode *pTableNd = pNd->GetTableNode();
1556 const SwTextNode *pTextNd = pNd->GetTextNode();
1557 if( !pTableNd && pTextNd )
1559 // Table with heading
1560 bTopCaption = true;
1561 pTableNd = rWrt.m_pDoc->GetNodes()[nStt+1]->GetTableNode();
1563 OSL_ENSURE( pTableNd, "Frame does not contain a table" );
1564 if( pTableNd )
1566 SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex();
1567 OSL_ENSURE( nTableEnd == nEnd - 1 ||
1568 (nTableEnd == nEnd - 2 && !bTopCaption),
1569 "Invalid frame content for a table" );
1571 if( nTableEnd == nEnd - 2 )
1572 pTextNd = rWrt.m_pDoc->GetNodes()[nTableEnd+1]->GetTextNode();
1574 if( pTextNd )
1575 aCaption = pTextNd->GetText();
1577 if( pTableNd )
1579 HTMLSaveData aSaveData( rWrt, pTableNd->GetIndex()+1,
1580 pTableNd->EndOfSectionIndex(),
1581 true, &rFrameFormat );
1582 rWrt.m_bOutFlyFrame = true;
1583 OutHTML_SwTableNode( rWrt, *pTableNd, &rFrameFormat, &aCaption,
1584 bTopCaption );
1587 return rWrt;
1590 static SwHTMLWriter & OutHTML_FrameFormatAsMulticol( SwHTMLWriter& rWrt,
1591 const SwFrameFormat& rFrameFormat,
1592 bool bInCntnr )
1594 rWrt.ChangeParaToken( HtmlTokenId::NONE );
1596 // Close the current <DL>!
1597 rWrt.OutAndSetDefList( 0 );
1599 // output as Multicol
1600 if( rWrt.m_bLFPossible )
1601 rWrt.OutNewLine();
1603 OStringBuffer sOut("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol);
1605 const SwFormatCol& rFormatCol = rFrameFormat.GetCol();
1607 // output the number of columns as COLS
1608 sal_uInt16 nCols = rFormatCol.GetNumCols();
1609 if( nCols )
1611 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cols
1612 "=\"" + OString::number(nCols) + "\"");
1615 // the Gutter width (minimum value) as GUTTER
1616 sal_uInt16 nGutter = rFormatCol.GetGutterWidth( true );
1617 if( nGutter!=USHRT_MAX )
1619 nGutter = SwHTMLWriter::ToPixel(nGutter);
1620 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_gutter
1621 "=\"" + OString::number(nGutter) + "\"");
1624 rWrt.Strm().WriteOString( sOut );
1625 sOut.setLength(0);
1627 // WIDTH
1628 HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_MULTICOL;
1629 if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
1630 nFrameFlags |= HTML_FRMOPTS_MULTICOL_CSS1;
1631 rWrt.OutFrameFormatOptions(rFrameFormat, OUString(), nFrameFlags);
1632 if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
1633 rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags );
1635 rWrt.Strm().WriteChar( '>' );
1637 rWrt.m_bLFPossible = true;
1638 rWrt.IncIndentLevel(); // indent the content of Multicol
1640 const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
1641 SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
1642 const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
1643 OSL_ENSURE( pSttNd, "Where is the start node" );
1646 // in a block, so that the old state can be restored in time
1647 // before the end
1648 HTMLSaveData aSaveData( rWrt, nStt+1,
1649 pSttNd->EndOfSectionIndex(),
1650 true, &rFrameFormat );
1651 rWrt.m_bOutFlyFrame = true;
1652 rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
1655 rWrt.DecIndentLevel(); // indent the content of Multicol;
1656 if( rWrt.m_bLFPossible )
1657 rWrt.OutNewLine();
1658 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol), false );
1659 rWrt.m_bLFPossible = true;
1661 return rWrt;
1664 static SwHTMLWriter& OutHTML_FrameFormatAsSpacer( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat )
1666 // if possible, output a line break before the graphic
1667 if( rWrt.m_bLFPossible )
1668 rWrt.OutNewLine( true );
1670 OString sOut =
1671 "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_spacer " "
1672 OOO_STRING_SVTOOLS_HTML_O_type "=\""
1673 OOO_STRING_SVTOOLS_HTML_SPTYPE_block "\"";
1674 rWrt.Strm().WriteOString( sOut );
1676 // ALIGN, WIDTH, HEIGHT
1677 OString aEndTags = rWrt.OutFrameFormatOptions(rFrameFormat, OUString(), HTML_FRMOPTS_SPACER);
1679 rWrt.Strm().WriteChar( '>' );
1680 if( !aEndTags.isEmpty() )
1681 rWrt.Strm().WriteOString( aEndTags );
1683 return rWrt;
1686 static SwHTMLWriter& OutHTML_FrameFormatAsDivOrSpan( SwHTMLWriter& rWrt,
1687 const SwFrameFormat& rFrameFormat, bool bSpan)
1689 OString aTag;
1690 if( !bSpan )
1692 rWrt.ChangeParaToken( HtmlTokenId::NONE );
1694 // Close the current <DL>!
1695 rWrt.OutAndSetDefList( 0 );
1696 aTag = OOO_STRING_SVTOOLS_HTML_division;
1698 else
1699 aTag = OOO_STRING_SVTOOLS_HTML_span;
1701 // output as DIV
1702 if( rWrt.m_bLFPossible )
1703 rWrt.OutNewLine();
1705 OStringBuffer sOut("<" + rWrt.GetNamespace() + aTag);
1707 rWrt.Strm().WriteOString( sOut );
1708 sOut.setLength(0);
1709 HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_DIV;
1710 if( rWrt.IsHTMLMode( HTMLMODE_BORDER_NONE ) )
1711 nFrameFlags |= HtmlFrmOpts::SNoBorder;
1712 OString aEndTags = rWrt.OutFrameFormatOptions(rFrameFormat, OUString(), nFrameFlags);
1713 rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags );
1714 rWrt.Strm().WriteChar( '>' );
1716 rWrt.IncIndentLevel(); // indent the content
1717 rWrt.m_bLFPossible = true;
1719 const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
1720 SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
1722 // Output frame-anchored frames that are anchored to the start node
1723 rWrt.OutFlyFrame( nStt, 0, HtmlPosition::Any );
1725 const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
1726 OSL_ENSURE( pSttNd, "Where is the start node" );
1729 // in a block, so that the old state can be restored in time
1730 // before the end
1731 HTMLSaveData aSaveData( rWrt, nStt+1,
1732 pSttNd->EndOfSectionIndex(),
1733 true, &rFrameFormat );
1734 rWrt.m_bOutFlyFrame = true;
1735 rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
1738 rWrt.DecIndentLevel(); // indent the content of Multicol;
1739 if( rWrt.m_bLFPossible )
1740 rWrt.OutNewLine();
1741 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false );
1743 if( !aEndTags.isEmpty() )
1744 rWrt.Strm().WriteOString( aEndTags );
1746 return rWrt;
1749 /// Starts the OLE version of an image in the ReqIF + OLE case.
1750 static void OutHTML_ImageOLEStart(SwHTMLWriter& rWrt, const Graphic& rGraphic,
1751 const SwFrameFormat& rFrameFormat)
1753 if (!rWrt.mbReqIF || !rWrt.m_bExportImagesAsOLE)
1754 return;
1756 // Write the original image as an RTF fragment.
1757 OUString aFileName;
1758 if (rWrt.GetOrigFileName())
1759 aFileName = *rWrt.GetOrigFileName();
1760 INetURLObject aURL(aFileName);
1761 OUString aName = aURL.getBase() + "_" + aURL.getExtension() + "_"
1762 + OUString::number(rGraphic.GetChecksum(), 16);
1763 aURL.setBase(aName);
1764 aURL.setExtension(u"ole");
1765 aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1767 SvFileStream aOutStream(aFileName, StreamMode::WRITE);
1768 if (!SwReqIfReader::WrapGraphicInRtf(rGraphic, rFrameFormat, aOutStream))
1769 SAL_WARN("sw.html", "SwReqIfReader::WrapGraphicInRtf() failed");
1771 // Refer to this data.
1772 aFileName = URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), aFileName);
1773 rWrt.Strm().WriteOString(
1774 Concat2View("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object));
1775 rWrt.Strm().WriteOString(Concat2View(" data=\"" + aFileName.toUtf8() + "\""));
1776 rWrt.Strm().WriteOString(" type=\"text/rtf\"");
1777 rWrt.Strm().WriteOString(">");
1778 rWrt.OutNewLine();
1781 /// Ends the OLE version of an image in the ReqIF + OLE case.
1782 static void OutHTML_ImageOLEEnd(SwHTMLWriter& rWrt)
1784 if (rWrt.mbReqIF && rWrt.m_bExportImagesAsOLE)
1786 rWrt.Strm().WriteOString(
1787 Concat2View("</" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">"));
1791 static SwHTMLWriter & OutHTML_FrameFormatAsImage( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat, bool bPNGFallback)
1793 bool bWritePNGFallback = rWrt.mbReqIF && !rWrt.m_bExportImagesAsOLE && bPNGFallback;
1795 if (rWrt.mbSkipImages)
1796 return rWrt;
1798 ImageMap aIMap;
1799 std::optional<Size> aDPI;
1800 if (rWrt.m_nShapeDPI.has_value())
1802 aDPI.emplace(*rWrt.m_nShapeDPI, *rWrt.m_nShapeDPI);
1804 Graphic aGraphic( const_cast<SwFrameFormat &>(rFrameFormat).MakeGraphic( &aIMap, /*nMaximumQuadraticPixels=*/2100000, aDPI ) );
1806 if (rWrt.mbReqIF)
1808 // ImageMap doesn't seem to be allowed in reqif.
1809 if (auto pGrafObj = dynamic_cast<const SdrGrafObj*>(rFrameFormat.FindSdrObject()))
1811 aGraphic = pGrafObj->GetGraphic();
1813 else
1815 // We only have a bitmap, write that as PNG without any fallback.
1816 bWritePNGFallback = false;
1820 Size aSz( 0, 0 );
1821 OUString GraphicURL;
1822 OUString aMimeType("image/jpeg");
1823 if(!rWrt.mbEmbedImages)
1825 if( rWrt.GetOrigFileName() )
1826 GraphicURL = *rWrt.GetOrigFileName();
1828 OUString aFilterName("JPG");
1829 XOutFlags nFlags = XOutFlags::UseGifIfPossible | XOutFlags::UseNativeIfPossible;
1831 if (rWrt.mbReqIF && !bWritePNGFallback)
1833 // Writing image without fallback PNG in ReqIF mode: force PNG output.
1834 aFilterName = "PNG";
1835 nFlags = XOutFlags::NONE;
1836 aMimeType = "image/png";
1838 else if (rWrt.mbReqIF)
1840 // Original format is wanted, don't force JPG.
1841 aFilterName.clear();
1842 aMimeType.clear();
1845 if( aGraphic.GetType() == GraphicType::NONE ||
1846 XOutBitmap::WriteGraphic( aGraphic, GraphicURL,
1847 aFilterName,
1848 nFlags ) != ERRCODE_NONE )
1850 // empty or incorrect, because there is nothing to output
1851 rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
1852 return rWrt;
1855 GraphicURL = URIHelper::SmartRel2Abs(
1856 INetURLObject(rWrt.GetBaseURL()), GraphicURL,
1857 URIHelper::GetMaybeFileHdl() );
1860 uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY);
1861 if (xGraphic.is() && aMimeType.isEmpty())
1862 xGraphic->getPropertyValue("MimeType") >>= aMimeType;
1864 OutHTML_ImageOLEStart(rWrt, aGraphic, rFrameFormat);
1866 HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
1867 OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz,
1868 HtmlFrmOpts::GenImgMask, "frame",
1869 aIMap.GetIMapObjectCount() ? &aIMap : nullptr, aMimeType );
1871 GfxLink aLink = aGraphic.GetGfxLink();
1872 if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
1874 OutHTML_FrameFormatAsImage( rWrt, rFrameFormat, /*bPNGFallback=*/false);
1877 OutHTML_ImageEnd(aHtml, rWrt);
1879 OutHTML_ImageOLEEnd(rWrt);
1881 return rWrt;
1884 static SwHTMLWriter& OutHTML_FrameFormatGrfNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
1885 bool bInCntnr, bool bPNGFallback )
1887 bool bWritePNGFallback = rWrt.mbReqIF && !rWrt.m_bExportImagesAsOLE && bPNGFallback;
1889 if (rWrt.mbSkipImages)
1890 return rWrt;
1892 const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
1893 SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
1894 SwGrfNode *pGrfNd = rWrt.m_pDoc->GetNodes()[ nStt ]->GetGrfNode();
1895 OSL_ENSURE( pGrfNd, "Grf node expected" );
1896 if( !pGrfNd )
1897 return rWrt;
1899 HtmlFrmOpts nFrameFlags = bInCntnr ? HTML_FRMOPTS_IMG_CNTNR : HTML_FRMOPTS_IMG;
1900 if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
1901 nFrameFlags |= HTML_FRMOPTS_IMG_CSS1;
1903 Graphic aGraphic = pGrfNd->GetGraphic();
1905 if (aGraphic.GetType() == GraphicType::GdiMetafile)
1907 // We only have a metafile, write that as PNG without any fallback.
1908 bWritePNGFallback = false;
1911 OUString aGraphicURL;
1912 OUString aMimeType;
1913 if(!rWrt.mbEmbedImages)
1915 const SwMirrorGrf& rMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf();
1917 if( !pGrfNd->IsLinkedFile() || MirrorGraph::Dont != rMirror.GetValue() )
1919 // create a (mirrored) jpeg file
1920 if( rWrt.GetOrigFileName() )
1921 aGraphicURL = *rWrt.GetOrigFileName();
1922 else
1923 aGraphicURL = rWrt.GetBaseURL();
1924 pGrfNd->GetGrf( true );
1926 XOutFlags nFlags = XOutFlags::UseGifIfSensible |
1927 XOutFlags::UseNativeIfPossible;
1928 switch( rMirror.GetValue() )
1930 case MirrorGraph::Vertical: nFlags = XOutFlags::MirrorHorz; break;
1931 case MirrorGraph::Horizontal: nFlags = XOutFlags::MirrorVert; break;
1932 case MirrorGraph::Both:
1933 nFlags = XOutFlags::MirrorVert | XOutFlags::MirrorHorz;
1934 break;
1935 default: break;
1938 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
1939 Size aMM100Size = o3tl::convert( rSize.GetSize(),
1940 o3tl::Length::twip, o3tl::Length::mm100 );
1942 OUString aFilterName;
1944 if (rWrt.mbReqIF)
1946 // In ReqIF mode, do not try to write GIF for other image types
1947 nFlags &= ~XOutFlags::UseGifIfSensible;
1948 if (!bWritePNGFallback)
1950 // Writing image without fallback PNG in ReqIF mode: force PNG
1951 // output.
1952 // But don't force it when writing the original format and we'll write PNG inside
1953 // that.
1954 aFilterName = "PNG";
1955 nFlags &= ~XOutFlags::UseNativeIfPossible;
1959 const Graphic& rGraphic = pGrfNd->GetGrf();
1961 // So that Graphic::IsTransparent() can report true.
1962 if (!rGraphic.isAvailable())
1963 const_cast<Graphic&>(rGraphic).makeAvailable();
1965 if (rWrt.mbReqIF && bWritePNGFallback)
1967 // ReqIF: force native data if possible.
1968 const std::shared_ptr<VectorGraphicData>& pVectorGraphicData = rGraphic.getVectorGraphicData();
1969 if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Svg)
1971 aFilterName = "svg";
1973 else if (rGraphic.GetGfxLink().IsEMF())
1975 aFilterName = "emf";
1977 else if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Wmf)
1979 aFilterName = "wmf";
1981 else if (rGraphic.GetGfxLink().GetType() == GfxLinkType::NativeTif)
1983 aFilterName = "tif";
1987 ErrCode nErr = XOutBitmap::WriteGraphic( rGraphic, aGraphicURL,
1988 aFilterName, nFlags, &aMM100Size, nullptr, &aMimeType );
1989 if( nErr )
1991 rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
1992 return rWrt;
1994 aGraphicURL = URIHelper::SmartRel2Abs(
1995 INetURLObject(rWrt.GetBaseURL()), aGraphicURL,
1996 URIHelper::GetMaybeFileHdl() );
1998 else
2000 pGrfNd->GetFileFilterNms( &aGraphicURL, nullptr );
2001 if( rWrt.m_bCfgCpyLinkedGrfs )
2002 rWrt.CopyLocalFileToINet( aGraphicURL );
2006 uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY);
2007 if (xGraphic.is() && aMimeType.isEmpty())
2008 xGraphic->getPropertyValue("MimeType") >>= aMimeType;
2010 OutHTML_ImageOLEStart(rWrt, aGraphic, rFrameFormat);
2012 HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
2013 OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, aGraphicURL, aGraphic, pGrfNd->GetTitle(),
2014 pGrfNd->GetTwipSize(), nFrameFlags, "graphic", nullptr, aMimeType );
2016 GfxLink aLink = aGraphic.GetGfxLink();
2017 if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
2019 // Not OLE mode, outer format is not PNG: write inner PNG.
2020 OutHTML_FrameFormatGrfNode( rWrt, rFrameFormat,
2021 bInCntnr, /*bPNGFallback=*/false );
2024 OutHTML_ImageEnd(aHtml, rWrt);
2026 OutHTML_ImageOLEEnd(rWrt);
2028 return rWrt;
2031 static SwHTMLWriter& OutHTML_FrameFormatAsMarquee( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
2032 const SdrObject& rSdrObj )
2034 // get the edit engine attributes of the object as SW attributes and
2035 // sort them as Hints
2036 const SfxItemSet& rFormatItemSet = rFrameFormat.GetAttrSet();
2037 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aItemSet( *rFormatItemSet.GetPool() );
2038 SwHTMLWriter::GetEEAttrsFromDrwObj( aItemSet, &rSdrObj );
2039 bool bCfgOutStylesOld = rWrt.m_bCfgOutStyles;
2040 rWrt.m_bCfgOutStyles = false;
2041 rWrt.m_bTextAttr = true;
2042 rWrt.m_bTagOn = true;
2043 Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false );
2044 rWrt.m_bTextAttr = false;
2046 OutHTML_DrawFrameFormatAsMarquee( rWrt,
2047 static_cast<const SwDrawFrameFormat &>(rFrameFormat),
2048 rSdrObj );
2049 rWrt.m_bTextAttr = true;
2050 rWrt.m_bTagOn = false;
2051 Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false );
2052 rWrt.m_bTextAttr = false;
2053 rWrt.m_bCfgOutStyles = bCfgOutStylesOld;
2055 return rWrt;
2058 SwHTMLWriter& OutHTML_HeaderFooter( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
2059 bool bHeader )
2061 // output as Multicol
2062 rWrt.OutNewLine();
2063 OStringBuffer sOut;
2064 sOut.append(OOO_STRING_SVTOOLS_HTML_division " "
2065 OOO_STRING_SVTOOLS_HTML_O_title "=\"")
2066 .append( bHeader ? "header" : "footer" ).append("\"");
2067 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOut) );
2069 rWrt.IncIndentLevel(); // indent the content of Multicol;
2071 // Piece a spacer for the spacing together. Because the
2072 // <DL> or </DL> always produces a space between paragraphs, it is
2073 // subtracted if necessary.
2074 const SvxULSpaceItem& rULSpace = rFrameFormat.GetULSpace();
2075 sal_uInt16 nSize = bHeader ? rULSpace.GetLower() : rULSpace.GetUpper();
2076 rWrt.m_nHeaderFooterSpace = nSize;
2078 OString aSpacer;
2079 if( rWrt.IsHTMLMode(HTMLMODE_VERT_SPACER) &&
2080 nSize > HTML_PARSPACE )
2082 nSize -= HTML_PARSPACE;
2083 nSize = SwHTMLWriter::ToPixel(nSize);
2085 aSpacer = OOO_STRING_SVTOOLS_HTML_spacer
2086 " " OOO_STRING_SVTOOLS_HTML_O_type
2087 "=\"" OOO_STRING_SVTOOLS_HTML_SPTYPE_vertical "\""
2088 " " OOO_STRING_SVTOOLS_HTML_O_size
2089 "=\"" + OString::number(nSize) + "\"";
2092 const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
2093 SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
2094 const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
2095 OSL_ENSURE( pSttNd, "Where is the start node" );
2097 if( !bHeader && !aSpacer.isEmpty() )
2099 rWrt.OutNewLine();
2100 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aSpacer) );
2104 // in a block, so that the old state can be restored in time
2105 // before the end. pFlyFormat doesn't need to be set here, because
2106 // PageDesc attributes cannot occur here
2107 HTMLSaveData aSaveData( rWrt, nStt+1,
2108 pSttNd->EndOfSectionIndex() );
2110 if( bHeader )
2111 rWrt.m_bOutHeader = true;
2112 else
2113 rWrt.m_bOutFooter = true;
2115 rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
2118 if( bHeader && !aSpacer.isEmpty() )
2120 rWrt.OutNewLine();
2121 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aSpacer) );
2124 rWrt.DecIndentLevel(); // indent the content of Multicol;
2125 rWrt.OutNewLine();
2126 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
2128 rWrt.m_nHeaderFooterSpace = 0;
2130 return rWrt;
2133 void SwHTMLWriter::AddLinkTarget( std::u16string_view aURL )
2135 if( aURL.empty() || aURL[0] != '#' )
2136 return;
2138 // There might be a '|' as delimiter (if the link has been inserted
2139 // freshly) or a '%7c' or a '%7C' if the document has been saved and
2140 // loaded already.
2141 sal_Int32 nPos = aURL.size();
2142 bool bFound = false, bEncoded = false;
2143 while( !bFound && nPos > 0 )
2145 sal_Unicode c = aURL[ --nPos ];
2146 switch( c )
2148 case cMarkSeparator:
2149 bFound = true;
2150 break;
2151 case '%':
2152 bFound = (aURL.size() - nPos) >=3 && aURL[ nPos+1 ] == '7';
2153 if(bFound)
2155 c = aURL[ nPos+2 ];
2156 bFound = (c == 'C' || c == 'c');
2158 if( bFound )
2159 bEncoded = true;
2162 if( !bFound || nPos < 2 ) // at least "#a|..."
2163 return;
2165 aURL = aURL.substr( 1 );
2167 // nPos-1+1/3 (-1 because of Erase)
2168 OUString sCmp = OUString(aURL.substr(bEncoded ? nPos+2 : nPos)).replaceAll(" ","");
2169 if( sCmp.isEmpty() )
2170 return;
2172 sCmp = sCmp.toAsciiLowerCase();
2174 if( sCmp == "region" ||
2175 sCmp == "frame" ||
2176 sCmp == "graphic" ||
2177 sCmp == "ole" ||
2178 sCmp == "table" )
2180 // Just remember it in a sorted array
2181 OUString aURL2(aURL);
2182 if( bEncoded )
2184 aURL2 = aURL2.replaceAt( nPos - 1, 3, rtl::OUStringChar(cMarkSeparator) );
2186 m_aImplicitMarks.insert( aURL2 );
2188 else if( sCmp == "outline" )
2190 // Here, we need position and name. That's why we sort a
2191 // sal_uInt16 and a string array ourselves.
2192 OUString aOutline( aURL.substr( 0, nPos-1 ) );
2193 SwPosition aPos( *m_pCurrentPam->GetPoint() );
2194 if( m_pDoc->GotoOutline( aPos, aOutline ) )
2196 SwNodeOffset nIdx = aPos.GetNodeIndex();
2198 decltype(m_aOutlineMarkPoss)::size_type nIns=0;
2199 while( nIns < m_aOutlineMarkPoss.size() &&
2200 m_aOutlineMarkPoss[nIns] < nIdx )
2201 nIns++;
2203 m_aOutlineMarkPoss.insert( m_aOutlineMarkPoss.begin()+nIns, nIdx );
2204 OUString aURL2(aURL);
2205 if( bEncoded )
2207 aURL2 = aURL2.replaceAt( nPos - 1, 3, rtl::OUStringChar(cMarkSeparator) );
2209 m_aOutlineMarks.insert( m_aOutlineMarks.begin()+nIns, aURL2 );
2214 void SwHTMLWriter::CollectLinkTargets()
2216 const SwTextINetFormat* pTextAttr;
2218 for (const SfxPoolItem* pItem : m_pDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
2220 auto pINetFormat = dynamic_cast<const SwFormatINetFormat*>(pItem);
2221 const SwTextNode* pTextNd;
2223 if( pINetFormat &&
2224 nullptr != ( pTextAttr = pINetFormat->GetTextINetFormat()) &&
2225 nullptr != ( pTextNd = pTextAttr->GetpTextNode() ) &&
2226 pTextNd->GetNodes().IsDocNodes() )
2228 AddLinkTarget( pINetFormat->GetValue() );
2232 for (const SfxPoolItem* pItem : m_pDoc->GetAttrPool().GetItemSurrogates(RES_URL))
2234 auto pURL = dynamic_cast<const SwFormatURL*>(pItem);
2235 if( pURL )
2237 AddLinkTarget( pURL->GetURL() );
2238 const ImageMap *pIMap = pURL->GetMap();
2239 if( pIMap )
2241 for( size_t i=0; i<pIMap->GetIMapObjectCount(); ++i )
2243 const IMapObject* pObj = pIMap->GetIMapObject( i );
2244 if( pObj )
2246 AddLinkTarget( pObj->GetURL() );
2254 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */