cid#1636693 COPY_INSTEAD_OF_MOVE
[LibreOffice.git] / sc / source / filter / html / htmlexp.cxx
blob07591c2ad2aa89ff9dfa418bca3b2e8bfae8a490
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 <sal/config.h>
22 #include <string_view>
24 #include <scitems.hxx>
25 #include <editeng/eeitem.hxx>
27 #include <utility>
28 #include <vcl/svapp.hxx>
29 #include <editeng/boxitem.hxx>
30 #include <editeng/brushitem.hxx>
31 #include <editeng/colritem.hxx>
32 #include <editeng/crossedoutitem.hxx>
33 #include <editeng/fhgtitem.hxx>
34 #include <editeng/fontitem.hxx>
35 #include <editeng/postitem.hxx>
36 #include <editeng/udlnitem.hxx>
37 #include <editeng/wghtitem.hxx>
38 #include <editeng/justifyitem.hxx>
39 #include <svx/xoutbmp.hxx>
40 #include <editeng/editeng.hxx>
41 #include <svtools/htmlcfg.hxx>
42 #include <sfx2/docfile.hxx>
43 #include <sfx2/frmhtmlw.hxx>
44 #include <sfx2/objsh.hxx>
45 #include <svl/urihelper.hxx>
46 #include <svtools/htmlkywd.hxx>
47 #include <svtools/htmlout.hxx>
48 #include <svtools/parhtml.hxx>
49 #include <vcl/outdev.hxx>
50 #include <stdio.h>
51 #include <osl/diagnose.h>
52 #include <o3tl/unit_conversion.hxx>
53 #include <o3tl/string_view.hxx>
55 #include <htmlexp.hxx>
56 #include <global.hxx>
57 #include <postit.hxx>
58 #include <document.hxx>
59 #include <docsh.hxx>
60 #include <attrib.hxx>
61 #include <patattr.hxx>
62 #include <stlpool.hxx>
63 #include <scresid.hxx>
64 #include <formulacell.hxx>
65 #include <cellform.hxx>
66 #include <docoptio.hxx>
67 #include <editutil.hxx>
68 #include <ftools.hxx>
69 #include <cellvalue.hxx>
70 #include <conditio.hxx>
71 #include <colorscale.hxx>
72 #include <mtvelements.hxx>
74 #include <editeng/flditem.hxx>
75 #include <editeng/borderline.hxx>
77 // Without strings.hrc: error C2679: binary '=' : no operator defined which takes a
78 // right-hand operand of type 'const class String (__stdcall *)(class ScResId)'
79 // at
80 // const String aStrTable( ScResId( SCSTR_TABLE ) ); aStrOut = aStrTable;
81 // ?!???
82 #include <strings.hrc>
83 #include <globstr.hrc>
85 #include <com/sun/star/frame/XModel.hpp>
86 #include <com/sun/star/uno/Reference.h>
87 #include <com/sun/star/document/XDocumentProperties.hpp>
88 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
89 #include <rtl/strbuf.hxx>
90 #include <officecfg/Office/Common.hxx>
91 #include <tools/json_writer.hxx>
92 #include <svl/numformat.hxx>
93 #include <svl/zformat.hxx>
95 using ::editeng::SvxBorderLine;
96 using namespace ::com::sun::star;
98 const char sMyBegComment[] = "<!-- ";
99 const char sMyEndComment[] = " -->";
100 const char sDisplay[] = "display:";
101 const char sBorder[] = "border:";
102 const char sBackground[] = "background:";
104 const sal_uInt16 ScHTMLExport::nDefaultFontSize[SC_HTML_FONTSIZES] =
106 HTMLFONTSZ1_DFLT, HTMLFONTSZ2_DFLT, HTMLFONTSZ3_DFLT, HTMLFONTSZ4_DFLT,
107 HTMLFONTSZ5_DFLT, HTMLFONTSZ6_DFLT, HTMLFONTSZ7_DFLT
110 sal_uInt16 ScHTMLExport::nFontSize[SC_HTML_FONTSIZES] = { 0 };
112 const char* ScHTMLExport::pFontSizeCss[SC_HTML_FONTSIZES] =
114 "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"
117 const sal_uInt16 ScHTMLExport::nCellSpacing = 0;
118 const char ScHTMLExport::sIndentSource[nIndentMax+1] =
119 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
121 // Macros for HTML export
123 #define TAG_ON( tag ) HTMLOutFuncs::Out_AsciiTag( rStrm, tag )
124 #define TAG_OFF( tag ) HTMLOutFuncs::Out_AsciiTag( rStrm, tag, false )
125 #define OUT_STR( str ) HTMLOutFuncs::Out_String( rStrm, str, &aNonConvertibleChars )
126 #define OUT_LF() rStrm.WriteOString( SAL_NEWLINE_STRING ).WriteOString( GetIndentStr() )
127 #define TAG_ON_LF( tag ) (TAG_ON( tag ).WriteOString( SAL_NEWLINE_STRING ).WriteOString( GetIndentStr() ))
128 #define TAG_OFF_LF( tag ) (TAG_OFF( tag ).WriteOString( SAL_NEWLINE_STRING ).WriteOString( GetIndentStr() ))
129 #define OUT_HR() TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_horzrule )
130 #define OUT_COMMENT( comment ) (rStrm.WriteOString( sMyBegComment ), OUT_STR( comment ) \
131 .WriteOString( sMyEndComment ).WriteOString( SAL_NEWLINE_STRING ) \
132 .WriteOString( GetIndentStr() ))
134 #define OUT_SP_CSTR_ASS( s ) rStrm.WriteChar(' ').WriteOString( s ).WriteChar( '=' )
136 #define GLOBSTR(id) ScResId( id )
138 void ScFormatFilterPluginImpl::ScExportHTML( SvStream& rStrm, const OUString& rBaseURL, ScDocument* pDoc,
139 const ScRange& rRange, const rtl_TextEncoding /*eNach*/, bool bAll,
140 const OUString& rStreamPath, OUString& rNonConvertibleChars, const OUString& rFilterOptions )
142 ScHTMLExport aEx( rStrm, rBaseURL, pDoc, rRange, bAll, rStreamPath, rFilterOptions );
143 aEx.Write();
144 rNonConvertibleChars = aEx.GetNonConvertibleChars();
147 static OString lcl_getColGroupString(sal_Int32 nSpan, sal_Int32 nWidth)
149 OStringBuffer aByteStr(OString::Concat(OOO_STRING_SVTOOLS_HTML_colgroup)
150 + " ");
151 if( nSpan > 1 )
153 aByteStr.append(OString::Concat(OOO_STRING_SVTOOLS_HTML_O_span)
154 + "=\""
155 + OString::number(nSpan)
156 + "\" ");
158 aByteStr.append(OString::Concat(OOO_STRING_SVTOOLS_HTML_O_width)
159 + "=\""
160 + OString::number(nWidth)
161 + "\"");
162 return aByteStr.makeStringAndClear();
165 static void lcl_AddStamp( OUString& rStr, std::u16string_view rName,
166 const css::util::DateTime& rDateTime,
167 const LocaleDataWrapper& rLoc )
169 Date aD(rDateTime.Day, rDateTime.Month, rDateTime.Year);
170 tools::Time aT(rDateTime.Hours, rDateTime.Minutes, rDateTime.Seconds,
171 rDateTime.NanoSeconds);
172 DateTime aDateTime(aD,aT);
174 OUString aStrDate = rLoc.getDate( aDateTime );
175 OUString aStrTime = rLoc.getTime( aDateTime );
177 rStr += GLOBSTR( STR_BY ) + " ";
178 if (!rName.empty())
179 rStr += rName;
180 else
181 rStr += "???";
182 rStr += " " + GLOBSTR( STR_ON ) + " ";
183 if (!aStrDate.isEmpty())
184 rStr += aStrDate;
185 else
186 rStr += "???";
187 rStr += ", ";
188 if (!aStrTime.isEmpty())
189 rStr += aStrTime;
190 else
191 rStr += "???";
194 static OString lcl_makeHTMLColorTriplet(const Color& rColor)
196 char buf[24];
198 // <font COLOR="#00FF40">hello</font>
199 snprintf( buf, 24, "\"#%02X%02X%02X\"", rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
201 return buf;
204 ScHTMLExport::ScHTMLExport( SvStream& rStrmP, OUString _aBaseURL, ScDocument* pDocP,
205 const ScRange& rRangeP, bool bAllP,
206 OUString aStreamPathP, std::u16string_view rFilterOptions ) :
207 ScExportBase( rStrmP, pDocP, rRangeP ),
208 aBaseURL(std::move( _aBaseURL )),
209 aStreamPath(std::move( aStreamPathP )),
210 pAppWin( Application::GetDefaultDevice() ),
211 nUsedTables( 0 ),
212 nIndent( 0 ),
213 bAll( bAllP ),
214 bTabHasGraphics( false ),
215 bTabAlignedLeft( false ),
216 bCalcAsShown( pDocP->GetDocOptions().IsCalcAsShown() ),
217 bTableDataHeight( true ),
218 mbSkipImages ( false ),
219 mbSkipHeaderFooter( false )
221 strcpy( sIndent, sIndentSource );
222 sIndent[0] = 0;
224 // set HTML configuration
225 bCopyLocalFileToINet = officecfg::Office::Common::Filter::HTML::Export::LocalGraphic::get();
227 if (rFilterOptions == u"SkipImages")
229 mbSkipImages = true;
231 else if (rFilterOptions == u"SkipHeaderFooter")
233 mbSkipHeaderFooter = true;
236 for ( sal_uInt16 j=0; j < SC_HTML_FONTSIZES; j++ )
238 sal_uInt16 nSize = SvxHtmlOptions::GetFontSize( j );
239 // remember in Twips, like our SvxFontHeightItem
240 if ( nSize )
241 nFontSize[j] = nSize * 20;
242 else
243 nFontSize[j] = nDefaultFontSize[j] * 20;
246 const SCTAB nCount = pDoc->GetTableCount();
247 for ( SCTAB nTab = 0; nTab < nCount; nTab++ )
249 if ( !IsEmptyTable( nTab ) )
250 nUsedTables++;
254 ScHTMLExport::~ScHTMLExport()
256 aGraphList.clear();
259 sal_uInt16 ScHTMLExport::GetFontSizeNumber( sal_uInt16 nHeight )
261 sal_uInt16 nSize = 1;
262 for ( sal_uInt16 j=SC_HTML_FONTSIZES-1; j>0; j-- )
264 if( nHeight > (nFontSize[j] + nFontSize[j-1]) / 2 )
265 { // The one next to it
266 nSize = j+1;
267 break;
270 return nSize;
273 const char* ScHTMLExport::GetFontSizeCss( sal_uInt16 nHeight )
275 sal_uInt16 nSize = GetFontSizeNumber( nHeight );
276 return pFontSizeCss[ nSize-1 ];
279 sal_uInt16 ScHTMLExport::ToPixel( sal_uInt16 nVal )
281 if( nVal )
283 nVal = static_cast<sal_uInt16>(pAppWin->LogicToPixel(
284 Size( nVal, nVal ), MapMode( MapUnit::MapTwip ) ).Width());
285 if( !nVal ) // If there's a Twip there should also be a Pixel
286 nVal = 1;
288 return nVal;
291 Size ScHTMLExport::MMToPixel( const Size& rSize )
293 Size aSize = pAppWin->LogicToPixel( rSize, MapMode( MapUnit::Map100thMM ) );
294 // If there's something there should also be a Pixel
295 if ( !aSize.Width() && rSize.Width() )
296 aSize.setWidth( 1 );
297 if ( !aSize.Height() && rSize.Height() )
298 aSize.setHeight( 1 );
299 return aSize;
302 void ScHTMLExport::Write()
304 if (!mbSkipHeaderFooter)
306 rStrm.WriteChar( '<' ).WriteOString( OOO_STRING_SVTOOLS_HTML_doctype ).WriteChar( ' ' ).WriteOString( OOO_STRING_SVTOOLS_HTML_doctype5 ).WriteChar( '>' )
307 .WriteOString( SAL_NEWLINE_STRING ).WriteOString( SAL_NEWLINE_STRING );
308 TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_html );
309 WriteHeader();
310 OUT_LF();
312 WriteBody();
313 OUT_LF();
314 if (!mbSkipHeaderFooter)
315 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_html );
318 void ScHTMLExport::WriteHeader()
320 IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_head );
322 if ( pDoc->IsClipOrUndo() )
323 { // no real DocInfo available, but some META information like charset needed
324 SfxFrameHTMLWriter::Out_DocInfo( rStrm, aBaseURL, nullptr, sIndent, &aNonConvertibleChars );
326 else
328 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
329 static_cast<cppu::OWeakObject*>(pDoc->GetDocumentShell()->GetModel()), uno::UNO_QUERY_THROW);
330 uno::Reference<document::XDocumentProperties> xDocProps
331 = xDPS->getDocumentProperties();
332 SfxFrameHTMLWriter::Out_DocInfo( rStrm, aBaseURL, xDocProps,
333 sIndent, &aNonConvertibleChars );
334 OUT_LF();
336 if (!xDocProps->getPrintedBy().isEmpty())
338 OUT_COMMENT( GLOBSTR( STR_DOC_INFO ) );
339 OUString aStrOut = GLOBSTR( STR_DOC_PRINTED ) + ": ";
340 lcl_AddStamp( aStrOut, xDocProps->getPrintedBy(),
341 xDocProps->getPrintDate(), ScGlobal::getLocaleData() );
342 OUT_COMMENT( aStrOut );
346 OUT_LF();
348 // CSS1 StyleSheet
349 PageDefaults( bAll ? 0 : aRange.aStart.Tab() );
350 IncIndent(1);
351 rStrm.WriteOString( "<" ).WriteOString( OOO_STRING_SVTOOLS_HTML_style ).WriteOString( " " ).WriteOString( OOO_STRING_SVTOOLS_HTML_O_type ).WriteOString( "=\"text/css\">" );
353 OUT_LF();
354 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_body);
355 rStrm.WriteOString(",");
356 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_division);
357 rStrm.WriteOString(",");
358 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_table);
359 rStrm.WriteOString(",");
360 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_thead);
361 rStrm.WriteOString(",");
362 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tbody);
363 rStrm.WriteOString(",");
364 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tfoot);
365 rStrm.WriteOString(",");
366 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tablerow);
367 rStrm.WriteOString(",");
368 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tableheader);
369 rStrm.WriteOString(",");
370 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tabledata);
371 rStrm.WriteOString(",");
372 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_parabreak);
373 rStrm.WriteOString(" { ");
374 rStrm.WriteOString("font-family:");
376 if (!aHTMLStyle.aFontFamilyName.isEmpty())
378 const OUString& rList = aHTMLStyle.aFontFamilyName;
379 for(sal_Int32 nPos {0};;)
381 rStrm.WriteChar( '\"' );
382 OUT_STR( o3tl::getToken( rList, 0, ';', nPos ) );
383 rStrm.WriteChar( '\"' );
384 if (nPos<0)
385 break;
386 rStrm.WriteOString( ", " );
389 rStrm.WriteOString("; ");
390 rStrm.WriteOString("font-size:");
391 rStrm.WriteOString(GetFontSizeCss(static_cast<sal_uInt16>(aHTMLStyle.nFontHeight)));
392 rStrm.WriteOString(" }");
394 OUT_LF();
396 // write the style for the comments to make them stand out from normal cell content
397 // this is done through only showing the cell contents when the custom indicator is hovered
398 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_anchor);
399 rStrm.WriteOString(".comment-indicator:hover");
400 rStrm.WriteOString(" + ");
401 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_comment2);
402 rStrm.WriteOString(" { ");
403 rStrm.WriteOString(sBackground);
404 rStrm.WriteOString("#ffd");
405 rStrm.WriteOString("; ");
406 rStrm.WriteOString("position:");
407 rStrm.WriteOString("absolute");
408 rStrm.WriteOString("; ");
409 rStrm.WriteOString(sDisplay);
410 rStrm.WriteOString("block");
411 rStrm.WriteOString("; ");
412 rStrm.WriteOString(sBorder);
413 rStrm.WriteOString("1px solid black");
414 rStrm.WriteOString("; ");
415 rStrm.WriteOString("padding:");
416 rStrm.WriteOString("0.5em");
417 rStrm.WriteOString("; ");
418 rStrm.WriteOString(" } ");
420 OUT_LF();
422 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_anchor);
423 rStrm.WriteOString(".comment-indicator");
424 rStrm.WriteOString(" { ");
425 rStrm.WriteOString(sBackground);
426 rStrm.WriteOString("red");
427 rStrm.WriteOString("; ");
428 rStrm.WriteOString(sDisplay);
429 rStrm.WriteOString("inline-block");
430 rStrm.WriteOString("; ");
431 rStrm.WriteOString(sBorder);
432 rStrm.WriteOString("1px solid black");
433 rStrm.WriteOString("; ");
434 rStrm.WriteOString("width:");
435 rStrm.WriteOString("0.5em");
436 rStrm.WriteOString("; ");
437 rStrm.WriteOString("height:");
438 rStrm.WriteOString("0.5em");
439 rStrm.WriteOString("; ");
440 rStrm.WriteOString(" } ");
442 OUT_LF();
444 rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_comment2);
445 rStrm.WriteOString(" { ");
446 rStrm.WriteOString(sDisplay);
447 rStrm.WriteOString("none");
448 rStrm.WriteOString("; ");
449 rStrm.WriteOString(" } ");
452 IncIndent(-1);
453 OUT_LF();
454 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_style );
456 IncIndent(-1);
457 OUT_LF();
458 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_head );
461 void ScHTMLExport::WriteOverview()
463 if ( nUsedTables <= 1 )
464 return;
466 IncIndent(1);
467 OUT_HR();
468 IncIndent(1); TAG_ON( OOO_STRING_SVTOOLS_HTML_parabreak ); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_center );
469 TAG_ON( OOO_STRING_SVTOOLS_HTML_head1 );
470 OUT_STR( ScResId( STR_OVERVIEW ) );
471 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_head1 );
473 OUString aStr;
475 const SCTAB nCount = pDoc->GetTableCount();
476 for ( SCTAB nTab = 0; nTab < nCount; nTab++ )
478 if ( !IsEmptyTable( nTab ) )
480 pDoc->GetName( nTab, aStr );
481 rStrm.WriteOString( "<A HREF=\"#table" )
482 .WriteOString( OString::number(nTab) )
483 .WriteOString( "\">" );
484 OUT_STR( aStr );
485 rStrm.WriteOString( "</A>" );
486 TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_linebreak );
490 IncIndent(-1); OUT_LF();
491 IncIndent(-1); TAG_OFF( OOO_STRING_SVTOOLS_HTML_center ); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_parabreak );
494 const SfxItemSet& ScHTMLExport::PageDefaults( SCTAB nTab )
496 SfxStyleSheetBasePool* pStylePool = pDoc->GetStyleSheetPool();
497 SfxStyleSheetBase* pStyleSheet = nullptr;
498 OSL_ENSURE( pStylePool, "StylePool not found! :-(" );
500 // remember defaults for compare in WriteCell
501 if ( !aHTMLStyle.bInitialized )
503 pStyleSheet = pStylePool->Find(
504 ScResId(STR_STYLENAME_STANDARD),
505 SfxStyleFamily::Para );
506 OSL_ENSURE( pStyleSheet, "ParaStyle not found! :-(" );
507 if (!pStyleSheet)
508 pStyleSheet = pStylePool->First(SfxStyleFamily::Para);
509 const SfxItemSet& rSetPara = pStyleSheet->GetItemSet();
511 aHTMLStyle.nDefaultScriptType = ScGlobal::GetDefaultScriptType();
512 aHTMLStyle.aFontFamilyName = static_cast<const SvxFontItem&>((rSetPara.Get(
513 ScGlobal::GetScriptedWhichID(
514 aHTMLStyle.nDefaultScriptType, ATTR_FONT
515 )))).GetFamilyName();
516 aHTMLStyle.nFontHeight = static_cast<const SvxFontHeightItem&>((rSetPara.Get(
517 ScGlobal::GetScriptedWhichID(
518 aHTMLStyle.nDefaultScriptType, ATTR_FONT_HEIGHT
519 )))).GetHeight();
520 aHTMLStyle.nFontSizeNumber = GetFontSizeNumber( static_cast< sal_uInt16 >( aHTMLStyle.nFontHeight ) );
523 // Page style sheet printer settings, e.g. for background graphics.
524 // There's only one background graphic in HTML!
525 pStyleSheet = pStylePool->Find( pDoc->GetPageStyle( nTab ), SfxStyleFamily::Page );
526 OSL_ENSURE( pStyleSheet, "PageStyle not found! :-(" );
527 if (!pStyleSheet)
528 pStyleSheet = pStylePool->First(SfxStyleFamily::Page);
529 const SfxItemSet& rSet = pStyleSheet->GetItemSet();
530 if ( !aHTMLStyle.bInitialized )
532 const SvxBrushItem* pBrushItem = &rSet.Get( ATTR_BACKGROUND );
533 aHTMLStyle.aBackgroundColor = pBrushItem->GetColor();
534 aHTMLStyle.bInitialized = true;
536 return rSet;
539 OString ScHTMLExport::BorderToStyle(const char* pBorderName,
540 const SvxBorderLine* pLine, bool& bInsertSemicolon)
542 OStringBuffer aOut;
544 if ( pLine )
546 if ( bInsertSemicolon )
547 aOut.append("; ");
549 // which border
550 aOut.append(OString::Concat("border-") + pBorderName + ": ");
552 // thickness
553 int nWidth = pLine->GetWidth();
554 int nPxWidth = (nWidth > 0) ?
555 std::max(o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::px), sal_Int64(1)) : 0;
556 aOut.append(OString::number(nPxWidth) + "px ");
557 switch (pLine->GetBorderLineStyle())
559 case SvxBorderLineStyle::SOLID:
560 aOut.append("solid");
561 break;
562 case SvxBorderLineStyle::DOTTED:
563 aOut.append("dotted");
564 break;
565 case SvxBorderLineStyle::DASHED:
566 case SvxBorderLineStyle::DASH_DOT:
567 case SvxBorderLineStyle::DASH_DOT_DOT:
568 aOut.append("dashed");
569 break;
570 case SvxBorderLineStyle::DOUBLE:
571 case SvxBorderLineStyle::DOUBLE_THIN:
572 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
573 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
574 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
575 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
576 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
577 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
578 aOut.append("double");
579 break;
580 case SvxBorderLineStyle::EMBOSSED:
581 aOut.append("ridge");
582 break;
583 case SvxBorderLineStyle::ENGRAVED:
584 aOut.append("groove");
585 break;
586 case SvxBorderLineStyle::OUTSET:
587 aOut.append("outset");
588 break;
589 case SvxBorderLineStyle::INSET:
590 aOut.append("inset");
591 break;
592 default:
593 aOut.append("hidden");
595 aOut.append(" #");
597 // color
598 char hex[7];
599 snprintf( hex, 7, "%06" SAL_PRIxUINT32, static_cast<sal_uInt32>( pLine->GetColor().GetRGBColor() ) );
600 hex[6] = 0;
602 aOut.append(hex);
604 bInsertSemicolon = true;
607 return aOut.makeStringAndClear();
610 void ScHTMLExport::WriteBody()
612 const SfxItemSet& rSet = PageDefaults( bAll ? 0 : aRange.aStart.Tab() );
613 const SvxBrushItem* pBrushItem = &rSet.Get( ATTR_BACKGROUND );
615 // default text color black
616 if (!mbSkipHeaderFooter)
618 rStrm.WriteChar( '<' ).WriteOString( OOO_STRING_SVTOOLS_HTML_body );
620 if (!mbSkipImages)
622 if ( bAll && GPOS_NONE != pBrushItem->GetGraphicPos() )
624 OUString aLink = pBrushItem->GetGraphicLink();
625 OUString aGrfNm;
627 // Embedded graphic -> write using WriteGraphic
628 if( aLink.isEmpty() )
630 const Graphic* pGrf = pBrushItem->GetGraphic();
631 if( pGrf )
633 // Save graphic as (JPG) file
634 aGrfNm = aStreamPath;
635 ErrCode nErr = XOutBitmap::WriteGraphic( *pGrf, aGrfNm,
636 u"JPG"_ustr, XOutFlags::UseNativeIfPossible );
637 if( !nErr ) // Contains errors, as we have nothing to output
639 aGrfNm = URIHelper::SmartRel2Abs(
640 INetURLObject(aBaseURL),
641 aGrfNm, URIHelper::GetMaybeFileHdl());
642 aLink = aGrfNm;
646 else
648 aGrfNm = aLink;
649 if( bCopyLocalFileToINet )
651 CopyLocalFileToINet( aGrfNm, aStreamPath );
653 else
654 aGrfNm = URIHelper::SmartRel2Abs(
655 INetURLObject(aBaseURL),
656 aGrfNm, URIHelper::GetMaybeFileHdl());
657 aLink = aGrfNm;
659 if( !aLink.isEmpty() )
661 rStrm.WriteChar( ' ' ).WriteOString( OOO_STRING_SVTOOLS_HTML_O_background ).WriteOString( "=\"" );
662 OUT_STR( URIHelper::simpleNormalizedMakeRelative(
663 aBaseURL,
664 aLink ) ).WriteChar( '\"' );
668 if ( !aHTMLStyle.aBackgroundColor.IsTransparent() )
669 { // A transparent background color should always result in default
670 // background of the browser. Also, HTMLOutFuncs::Out_Color() writes
671 // black #000000 for COL_AUTO which is the same as white #ffffff with
672 // transparency set to 0xff, our default background.
673 OUT_SP_CSTR_ASS( OOO_STRING_SVTOOLS_HTML_O_bgcolor );
674 HTMLOutFuncs::Out_Color( rStrm, aHTMLStyle.aBackgroundColor );
677 rStrm.WriteChar( '>' ); OUT_LF();
679 // A marker right after <body> can be used, so that data-sheets-* attributes are considered
680 // at all. This is disabled by default.
681 OString aMarker;
682 char* pEnv = getenv("SC_DEBUG_HTML_MARKER");
683 if (pEnv)
685 aMarker = pEnv;
687 else if (comphelper::LibreOfficeKit::isActive())
689 aMarker = "<google-sheets-html-origin/>"_ostr;
691 rStrm.WriteOString(aMarker);
694 if ( bAll )
695 WriteOverview();
697 WriteTables();
699 if (!mbSkipHeaderFooter)
700 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_body );
703 void ScHTMLExport::WriteTables()
705 const SCTAB nTabCount = pDoc->GetTableCount();
706 const OUString aStrTable( ScResId( SCSTR_TABLE ) );
707 OUString aStr;
708 OUString aStrOut;
709 SCCOL nStartCol;
710 SCROW nStartRow;
711 SCTAB nStartTab;
712 SCCOL nEndCol;
713 SCROW nEndRow;
714 SCTAB nEndTab;
715 SCCOL nStartColFix = 0;
716 SCROW nStartRowFix = 0;
717 SCCOL nEndColFix = 0;
718 SCROW nEndRowFix = 0;
719 ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
720 if ( bAll )
722 nStartTab = 0;
723 nEndTab = nTabCount - 1;
725 else
727 nStartCol = nStartColFix = aRange.aStart.Col();
728 nStartRow = nStartRowFix = aRange.aStart.Row();
729 nStartTab = aRange.aStart.Tab();
730 nEndCol = nEndColFix = aRange.aEnd.Col();
731 nEndRow = nEndRowFix = aRange.aEnd.Row();
732 nEndTab = aRange.aEnd.Tab();
734 SCTAB nTableStrNum = 1;
735 for ( SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++ )
737 if ( !pDoc->IsVisible( nTab ) )
738 continue; // for
740 if ( bAll )
742 if ( !GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ) )
743 continue; // for
745 if ( nUsedTables > 1 )
747 aStrOut = aStrTable + " " + OUString::number( nTableStrNum++ ) + ": ";
749 OUT_HR();
751 // Write anchor
752 rStrm.WriteOString( "<A NAME=\"table" )
753 .WriteOString( OString::number(nTab) )
754 .WriteOString( "\">" );
755 TAG_ON( OOO_STRING_SVTOOLS_HTML_head1 );
756 OUT_STR( aStrOut );
757 TAG_ON( OOO_STRING_SVTOOLS_HTML_emphasis );
759 pDoc->GetName( nTab, aStr );
760 OUT_STR( aStr );
762 TAG_OFF( OOO_STRING_SVTOOLS_HTML_emphasis );
763 TAG_OFF( OOO_STRING_SVTOOLS_HTML_head1 );
764 rStrm.WriteOString( "</A>" ); OUT_LF();
767 else
769 nStartCol = nStartColFix;
770 nStartRow = nStartRowFix;
771 nEndCol = nEndColFix;
772 nEndRow = nEndRowFix;
773 if ( !TrimDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ) )
774 continue; // for
777 // <TABLE ...>
778 OStringBuffer aByteStrOut(OOO_STRING_SVTOOLS_HTML_table);
780 bTabHasGraphics = bTabAlignedLeft = false;
781 if ( bAll && pDrawLayer )
782 PrepareGraphics( pDrawLayer, nTab, nStartCol, nStartRow,
783 nEndCol, nEndRow );
785 // more <TABLE ...>
786 if ( bTabAlignedLeft )
788 aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
789 "=\""
790 OOO_STRING_SVTOOLS_HTML_AL_left "\"");
792 // ALIGN=LEFT allow text and graphics to flow around
793 // CELLSPACING
794 aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellspacing
795 "=\"" +
796 OString::number(nCellSpacing) + "\"");
798 // BORDER=0, we do the styling of the cells in <TD>
799 aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_border "=\"0\"");
800 IncIndent(1); TAG_ON_LF( aByteStrOut.makeStringAndClear() );
802 // --- <COLGROUP> ----
804 SCCOL nCol = nStartCol;
805 sal_Int32 nWidth = 0;
806 sal_Int32 nSpan = 0;
807 while( nCol <= nEndCol )
809 if( pDoc->ColHidden(nCol, nTab) )
811 ++nCol;
812 continue;
815 if( nWidth != ToPixel( pDoc->GetColWidth( nCol, nTab ) ) )
817 if( nSpan != 0 )
819 TAG_ON(lcl_getColGroupString(nSpan, nWidth));
820 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_colgroup );
822 nWidth = ToPixel( pDoc->GetColWidth( nCol, nTab ) );
823 nSpan = 1;
825 else
826 nSpan++;
827 nCol++;
829 if( nSpan )
831 TAG_ON(lcl_getColGroupString(nSpan, nWidth));
832 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_colgroup );
836 // <TBODY> // Re-enable only when THEAD and TFOOT are exported
837 // IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_tbody );
838 // At least old (3.x, 4.x?) Netscape doesn't follow <TABLE COLS=n> and
839 // <COL WIDTH=x> specified, but needs a width at every column.
840 bool bHasHiddenRows = pDoc->HasHiddenRows(nStartRow, nEndRow, nTab);
841 // We need to cache sc::ColumnBlockPosition per each column.
842 std::vector< sc::ColumnBlockPosition > blockPos( nEndCol - nStartCol + 1 );
843 for( SCCOL i = nStartCol; i <= nEndCol; ++i )
844 pDoc->InitColumnBlockPosition( blockPos[ i - nStartCol ], nTab, i );
845 for ( SCROW nRow=nStartRow; nRow<=nEndRow; nRow++ )
847 if ( bHasHiddenRows && pDoc->RowHidden(nRow, nTab) )
849 nRow = pDoc->FirstVisibleRow(nRow+1, nEndRow, nTab);
850 --nRow;
851 continue; // for
854 IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_tablerow );
855 bTableDataHeight = true; // height at every first cell of each row
856 for ( SCCOL nCol2=nStartCol; nCol2<=nEndCol; nCol2++ )
858 if ( pDoc->ColHidden(nCol2, nTab) )
859 continue; // for
861 if ( nCol2 == nEndCol )
862 IncIndent(-1);
863 WriteCell( blockPos[ nCol2 - nStartCol ], nCol2, nRow, nTab );
864 bTableDataHeight = false;
867 if ( nRow == nEndRow )
868 IncIndent(-1);
869 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tablerow );
871 // TODO: Uncomment later
872 // IncIndent(-1); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tbody );
874 IncIndent(-1); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_table );
876 if ( bTabHasGraphics && !mbSkipImages )
878 // the rest that is not in a cell
879 size_t ListSize = aGraphList.size();
880 for ( size_t i = 0; i < ListSize; ++i )
882 ScHTMLGraphEntry* pE = &aGraphList[ i ];
883 if ( !pE->bWritten )
884 WriteGraphEntry( pE );
886 aGraphList.clear();
887 if ( bTabAlignedLeft )
889 // clear <TABLE ALIGN=LEFT> with <BR CLEAR=LEFT>
890 aByteStrOut.append(
891 OOO_STRING_SVTOOLS_HTML_linebreak
893 OOO_STRING_SVTOOLS_HTML_O_clear "="
894 OOO_STRING_SVTOOLS_HTML_AL_left);
895 TAG_ON_LF( aByteStrOut.makeStringAndClear() );
899 if ( bAll )
900 OUT_COMMENT( u"**************************************************************************" );
904 void ScHTMLExport::WriteCell( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow, SCTAB nTab )
906 std::optional<Color> aColorScale;
907 ScAddress aPos( nCol, nRow, nTab );
908 ScRefCellValue aCell(*pDoc, aPos, rBlockPos);
909 const ScPatternAttr* pAttr = pDoc->GetPattern( nCol, nRow, nTab );
910 const SfxItemSet* pCondItemSet = pDoc->GetCondResult( nCol, nRow, nTab, &aCell );
911 if (!pCondItemSet)
913 ScConditionalFormatList* pCondList = pDoc->GetCondFormList(nTab);
914 const ScCondFormatItem& rCondItem = pAttr->GetItem(ATTR_CONDITIONAL);
915 const ScCondFormatIndexes& rCondIndex = rCondItem.GetCondFormatData();
916 if (rCondIndex.size() > 0)
918 ScConditionalFormat* pCondFmt = pCondList->GetFormat(rCondIndex[0]);
919 if (pCondFmt)
921 const ScColorScaleFormat* pEntry = dynamic_cast<const ScColorScaleFormat*>(pCondFmt->GetEntry(0));
922 if (pEntry)
923 aColorScale = pEntry->GetColor(aPos);
928 const ScMergeFlagAttr& rMergeFlagAttr = pAttr->GetItem( ATTR_MERGE_FLAG, pCondItemSet );
929 if ( rMergeFlagAttr.IsOverlapped() )
930 return ;
932 ScHTMLGraphEntry* pGraphEntry = nullptr;
933 if ( bTabHasGraphics && !mbSkipImages )
935 size_t ListSize = aGraphList.size();
936 for ( size_t i = 0; i < ListSize; ++i )
938 ScHTMLGraphEntry* pE = &aGraphList[ i ];
939 if ( pE->bInCell && pE->aRange.Contains( aPos ) )
941 if ( pE->aRange.aStart == aPos )
943 pGraphEntry = pE;
944 break; // for
946 else
947 return ; // Is a Col/RowSpan, Overlapped
952 sal_uInt32 nFormat = pAttr->GetNumberFormat( pFormatter );
953 bool bValueData = aCell.hasNumeric();
954 SvtScriptType nScriptType = SvtScriptType::NONE;
955 if (!aCell.isEmpty())
956 nScriptType = pDoc->GetScriptType(nCol, nRow, nTab, &aCell);
958 if ( nScriptType == SvtScriptType::NONE )
959 nScriptType = aHTMLStyle.nDefaultScriptType;
961 OStringBuffer aStrTD(OOO_STRING_SVTOOLS_HTML_tabledata);
963 // border of the cells
964 const SvxBoxItem* pBorder = pDoc->GetAttr( nCol, nRow, nTab, ATTR_BORDER );
965 if ( pBorder && (pBorder->GetTop() || pBorder->GetBottom() || pBorder->GetLeft() || pBorder->GetRight()) )
967 aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_style "=\"");
969 bool bInsertSemicolon = false;
970 aStrTD.append(BorderToStyle("top", pBorder->GetTop(),
971 bInsertSemicolon));
972 aStrTD.append(BorderToStyle("bottom", pBorder->GetBottom(),
973 bInsertSemicolon));
974 aStrTD.append(BorderToStyle("left", pBorder->GetLeft(),
975 bInsertSemicolon));
976 aStrTD.append(BorderToStyle("right", pBorder->GetRight(),
977 bInsertSemicolon));
979 aStrTD.append('"');
982 const char* pChar;
983 sal_uInt16 nHeightPixel;
985 const ScMergeAttr& rMergeAttr = pAttr->GetItem( ATTR_MERGE, pCondItemSet );
986 if ( pGraphEntry || rMergeAttr.IsMerged() )
988 SCCOL nC, jC;
989 SCROW nR;
990 tools::Long v;
991 if ( pGraphEntry )
992 nC = std::max( SCCOL(pGraphEntry->aRange.aEnd.Col() - nCol + 1),
993 rMergeAttr.GetColMerge() );
994 else
995 nC = rMergeAttr.GetColMerge();
996 if ( nC > 1 )
998 aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_colspan
999 "=" + OString::number(static_cast<sal_Int32>(nC)));
1000 nC = nC + nCol;
1001 for ( jC=nCol, v=0; jC<nC; jC++ )
1002 v += pDoc->GetColWidth( jC, nTab );
1005 if ( pGraphEntry )
1006 nR = std::max( SCROW(pGraphEntry->aRange.aEnd.Row() - nRow + 1),
1007 rMergeAttr.GetRowMerge() );
1008 else
1009 nR = rMergeAttr.GetRowMerge();
1010 if ( nR > 1 )
1012 aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_rowspan
1013 "=" + OString::number(static_cast<sal_Int32>(nR)));
1014 nR += nRow;
1015 v = pDoc->GetRowHeight( nRow, nR-1, nTab );
1016 nHeightPixel = ToPixel( static_cast< sal_uInt16 >( v ) );
1018 else
1019 nHeightPixel = ToPixel( pDoc->GetRowHeight( nRow, nTab ) );
1021 else
1022 nHeightPixel = ToPixel( pDoc->GetRowHeight( nRow, nTab ) );
1024 if ( bTableDataHeight )
1026 aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_height "=\"" +
1027 OString::number(nHeightPixel) + "\"");
1030 const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>( pAttr->GetItem(
1031 ScGlobal::GetScriptedWhichID( nScriptType, ATTR_FONT),
1032 pCondItemSet) );
1034 const SvxFontHeightItem& rFontHeightItem = static_cast<const SvxFontHeightItem&>(
1035 pAttr->GetItem( ScGlobal::GetScriptedWhichID( nScriptType,
1036 ATTR_FONT_HEIGHT), pCondItemSet) );
1038 const SvxWeightItem& rWeightItem = static_cast<const SvxWeightItem&>( pAttr->GetItem(
1039 ScGlobal::GetScriptedWhichID( nScriptType, ATTR_FONT_WEIGHT),
1040 pCondItemSet) );
1042 const SvxPostureItem& rPostureItem = static_cast<const SvxPostureItem&>(
1043 pAttr->GetItem( ScGlobal::GetScriptedWhichID( nScriptType,
1044 ATTR_FONT_POSTURE), pCondItemSet) );
1046 const SvxUnderlineItem& rUnderlineItem =
1047 pAttr->GetItem( ATTR_FONT_UNDERLINE, pCondItemSet );
1049 const SvxCrossedOutItem& rCrossedOutItem =
1050 pAttr->GetItem( ATTR_FONT_CROSSEDOUT, pCondItemSet );
1052 const SvxColorItem& rColorItem = pAttr->GetItem(
1053 ATTR_FONT_COLOR, pCondItemSet );
1055 const SvxHorJustifyItem& rHorJustifyItem =
1056 pAttr->GetItem( ATTR_HOR_JUSTIFY, pCondItemSet );
1058 const SvxVerJustifyItem& rVerJustifyItem =
1059 pAttr->GetItem( ATTR_VER_JUSTIFY, pCondItemSet );
1061 const SvxBrushItem& rBrushItem = pAttr->GetItem(
1062 ATTR_BACKGROUND, pCondItemSet );
1064 Color aBgColor;
1065 if ( aColorScale )
1066 aBgColor = *aColorScale;
1067 else if ( rBrushItem.GetColor().GetAlpha() == 0 )
1068 aBgColor = aHTMLStyle.aBackgroundColor; // No unwanted background color
1069 else
1070 aBgColor = rBrushItem.GetColor();
1072 bool bBold = ( WEIGHT_BOLD <= rWeightItem.GetWeight() );
1073 bool bItalic = ( ITALIC_NONE != rPostureItem.GetPosture() );
1074 bool bUnderline = ( LINESTYLE_NONE != rUnderlineItem.GetLineStyle() );
1075 bool bCrossedOut = ( STRIKEOUT_SINGLE <= rCrossedOutItem.GetStrikeout() );
1076 bool bSetFontColor = ( COL_AUTO != rColorItem.GetValue() ); // default is AUTO now
1077 bool bSetFontName = ( aHTMLStyle.aFontFamilyName != rFontItem.GetFamilyName() );
1078 sal_uInt16 nSetFontSizeNumber = 0;
1079 sal_uInt32 nFontHeight = rFontHeightItem.GetHeight();
1080 if ( nFontHeight != aHTMLStyle.nFontHeight )
1082 nSetFontSizeNumber = GetFontSizeNumber( static_cast<sal_uInt16>(nFontHeight) );
1083 if ( nSetFontSizeNumber == aHTMLStyle.nFontSizeNumber )
1084 nSetFontSizeNumber = 0; // no difference, don't set
1087 bool bSetFont = (bSetFontColor || bSetFontName || nSetFontSizeNumber);
1089 //! TODO: we could entirely use CSS1 here instead, but that would exclude
1090 //! Netscape 3.0 and Netscape 4.x without JavaScript enabled.
1091 //! Do we want that?
1093 switch( rHorJustifyItem.GetValue() )
1095 case SvxCellHorJustify::Standard:
1096 pChar = (bValueData ? OOO_STRING_SVTOOLS_HTML_AL_right : OOO_STRING_SVTOOLS_HTML_AL_left);
1097 break;
1098 case SvxCellHorJustify::Center: pChar = OOO_STRING_SVTOOLS_HTML_AL_center; break;
1099 case SvxCellHorJustify::Block: pChar = OOO_STRING_SVTOOLS_HTML_AL_justify; break;
1100 case SvxCellHorJustify::Right: pChar = OOO_STRING_SVTOOLS_HTML_AL_right; break;
1101 case SvxCellHorJustify::Left:
1102 case SvxCellHorJustify::Repeat:
1103 default: pChar = OOO_STRING_SVTOOLS_HTML_AL_left; break;
1106 aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"" +
1107 OString::Concat(pChar) + "\"");
1109 switch( rVerJustifyItem.GetValue() )
1111 case SvxCellVerJustify::Top: pChar = OOO_STRING_SVTOOLS_HTML_VA_top; break;
1112 case SvxCellVerJustify::Center: pChar = OOO_STRING_SVTOOLS_HTML_VA_middle; break;
1113 case SvxCellVerJustify::Bottom: pChar = OOO_STRING_SVTOOLS_HTML_VA_bottom; break;
1114 case SvxCellVerJustify::Standard:
1115 default: pChar = nullptr;
1117 if ( pChar )
1119 aStrTD.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_valign "=") + pChar);
1122 if ( aHTMLStyle.aBackgroundColor != aBgColor )
1124 aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_bgcolor "="
1125 + lcl_makeHTMLColorTriplet(aBgColor));
1128 double fVal = 0.0;
1129 if ( bValueData )
1131 switch (aCell.getType())
1133 case CELLTYPE_VALUE:
1134 fVal = aCell.getDouble();
1135 if ( bCalcAsShown && fVal != 0.0 )
1136 fVal = pDoc->RoundValueAsShown( fVal, nFormat );
1137 break;
1138 case CELLTYPE_FORMULA:
1139 fVal = aCell.getFormula()->GetValue();
1140 break;
1141 default:
1142 OSL_FAIL( "value data with unsupported cell type" );
1146 aStrTD.append(HTMLOutFuncs::CreateTableDataOptionsValNum(bValueData, fVal,
1147 nFormat, *pFormatter, &aNonConvertibleChars));
1149 std::optional<tools::JsonWriter> oJson;
1150 const SvNumberformat* pNumberFormat = nullptr;
1151 if (bValueData)
1153 if (nFormat)
1155 const SvNumberformat* pFormatEntry = pFormatter->GetEntry(nFormat);
1156 if (pFormatEntry)
1158 OUString aNumStr = pFormatEntry->GetFormatstring();
1159 if (aNumStr == "BOOLEAN")
1161 // 4 is boolean.
1162 oJson.emplace();
1163 oJson->put("1", static_cast<sal_Int32>(4));
1164 oJson->put("4", static_cast<sal_Int32>(fVal));
1166 else
1168 // 3 is number.
1169 oJson.emplace();
1170 oJson->put("1", static_cast<sal_Int32>(3));
1171 oJson->put("3", static_cast<sal_Int32>(fVal));
1172 pNumberFormat = pFormatEntry;
1177 if (aCell.getType() == CELLTYPE_FORMULA)
1179 // If it's a formula, then also emit that, grammar is R1C1 reference style.
1180 OUString aFormula = aCell.getFormula()->GetFormula(
1181 formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1182 aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_DSformula "=\""
1183 + HTMLOutFuncs::ConvertStringToHTML(aFormula) + "\"");
1186 else
1188 // 2 is text.
1189 oJson.emplace();
1190 oJson->put("1", static_cast<sal_Int32>(2));
1191 oJson->put("2", pDoc->GetString(aPos));
1194 if (oJson)
1196 OUString aJsonString = OUString::fromUtf8(oJson->finishAndGetAsOString());
1197 aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_DSval "=\""
1198 + HTMLOutFuncs::ConvertStringToHTML(aJsonString) + "\"");
1201 if (pNumberFormat)
1203 // 2 is a number format.
1204 oJson.emplace();
1205 oJson->put("1", static_cast<sal_Int32>(2));
1206 oJson->put("2", pNumberFormat->GetFormatstring());
1207 // The number format is for a number.
1208 oJson->put("3", static_cast<sal_Int32>(1));
1209 OUString aJsonString = OUString::fromUtf8(oJson->finishAndGetAsOString());
1210 aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_DSnum "=\""
1211 + HTMLOutFuncs::ConvertStringToHTML(aJsonString) + "\"");
1214 TAG_ON(aStrTD.makeStringAndClear());
1216 //write the note for this as the first thing in the tag
1217 ScPostIt* pNote = pDoc->HasNote(aPos) ? pDoc->GetNote(aPos) : nullptr;
1218 if (pNote)
1220 //create the comment indicator
1221 OString aStr = OOO_STRING_SVTOOLS_HTML_anchor " "
1222 OOO_STRING_SVTOOLS_HTML_O_class "=\"comment-indicator\""_ostr;
1223 TAG_ON(aStr);
1224 TAG_OFF(OOO_STRING_SVTOOLS_HTML_anchor);
1225 OUT_LF();
1227 //create the element holding the contents
1228 //this is a bit naive, since it doesn't separate
1229 //lines into html breaklines yet
1230 TAG_ON(OOO_STRING_SVTOOLS_HTML_comment2);
1231 OUT_STR( pNote->GetText() );
1232 TAG_OFF(OOO_STRING_SVTOOLS_HTML_comment2);
1233 OUT_LF();
1236 if ( bBold ) TAG_ON( OOO_STRING_SVTOOLS_HTML_bold );
1237 if ( bItalic ) TAG_ON( OOO_STRING_SVTOOLS_HTML_italic );
1238 if ( bUnderline ) TAG_ON( OOO_STRING_SVTOOLS_HTML_underline );
1239 if ( bCrossedOut ) TAG_ON( OOO_STRING_SVTOOLS_HTML_strikethrough );
1241 if ( bSetFont )
1243 OStringBuffer aStr(OOO_STRING_SVTOOLS_HTML_font);
1244 if ( bSetFontName )
1246 aStr.append(" " OOO_STRING_SVTOOLS_HTML_O_face "=\"");
1248 if (!rFontItem.GetFamilyName().isEmpty())
1250 const OUString& rList = rFontItem.GetFamilyName();
1251 for (sal_Int32 nPos {0};;)
1253 OString aTmpStr = HTMLOutFuncs::ConvertStringToHTML(
1254 o3tl::getToken( rList, 0, ';', nPos ),
1255 &aNonConvertibleChars);
1256 aStr.append(aTmpStr);
1257 if (nPos<0)
1258 break;
1259 aStr.append(',');
1263 aStr.append('\"');
1265 if ( nSetFontSizeNumber )
1267 aStr.append(" " OOO_STRING_SVTOOLS_HTML_O_size "="
1268 + OString::number(static_cast<sal_Int32>(nSetFontSizeNumber)));
1270 if ( bSetFontColor )
1272 Color aColor = rColorItem.GetValue();
1274 // always export automatic text color as black
1275 if ( aColor == COL_AUTO )
1276 aColor = COL_BLACK;
1278 aStr.append(" " OOO_STRING_SVTOOLS_HTML_O_color "="
1279 + lcl_makeHTMLColorTriplet(aColor));
1281 TAG_ON(aStr.makeStringAndClear());
1284 OUString aURL;
1285 bool bWriteHyperLink(false);
1286 if (aCell.getType() == CELLTYPE_FORMULA)
1288 ScFormulaCell* pFCell = aCell.getFormula();
1289 if (pFCell->IsHyperLinkCell())
1291 OUString aCellText;
1292 pFCell->GetURLResult(aURL, aCellText);
1293 bWriteHyperLink = true;
1297 if (bWriteHyperLink)
1299 OString aURLStr = HTMLOutFuncs::ConvertStringToHTML(aURL, &aNonConvertibleChars);
1300 OString aStr = OOO_STRING_SVTOOLS_HTML_anchor " " OOO_STRING_SVTOOLS_HTML_O_href "=\"" + aURLStr + "\"";
1301 TAG_ON(aStr);
1304 OUString aStrOut;
1305 bool bFieldText = false;
1307 const Color* pColor;
1308 switch (aCell.getType())
1310 case CELLTYPE_EDIT :
1311 bFieldText = WriteFieldText(aCell.getEditText());
1312 if ( bFieldText )
1313 break;
1314 [[fallthrough]];
1315 default:
1316 aStrOut = ScCellFormat::GetString(aCell, nFormat, &pColor, nullptr, *pDoc);
1319 if ( !bFieldText )
1321 if ( aStrOut.isEmpty() )
1323 TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak ); // No completely empty line
1325 else
1327 sal_Int32 nPos = aStrOut.indexOf( '\n' );
1328 if ( nPos == -1 )
1330 OUT_STR( aStrOut );
1332 else
1334 sal_Int32 nStartPos = 0;
1337 OUString aSingleLine = aStrOut.copy( nStartPos, nPos - nStartPos );
1338 OUT_STR( aSingleLine );
1339 TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
1340 nStartPos = nPos + 1;
1342 while( ( nPos = aStrOut.indexOf( '\n', nStartPos ) ) != -1 );
1343 OUString aSingleLine = aStrOut.copy( nStartPos );
1344 OUT_STR( aSingleLine );
1348 if ( pGraphEntry )
1349 WriteGraphEntry( pGraphEntry );
1351 if (bWriteHyperLink) { TAG_OFF(OOO_STRING_SVTOOLS_HTML_anchor); }
1353 if ( bSetFont ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_font );
1354 if ( bCrossedOut ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_strikethrough );
1355 if ( bUnderline ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_underline );
1356 if ( bItalic ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_italic );
1357 if ( bBold ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_bold );
1359 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tabledata );
1362 bool ScHTMLExport::WriteFieldText( const EditTextObject* pData )
1364 bool bFields = false;
1365 // text and anchor of URL fields, Doc-Engine is a ScFieldEditEngine
1366 EditEngine& rEngine = pDoc->GetEditEngine();
1367 rEngine.SetText( *pData );
1368 sal_Int32 nParas = rEngine.GetParagraphCount();
1369 if ( nParas )
1371 ESelection aSel( 0, 0, nParas-1, rEngine.GetTextLen( nParas-1 ) );
1372 SfxItemSet aSet( rEngine.GetAttribs( aSel ) );
1373 SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
1374 if ( eFieldState == SfxItemState::INVALID || eFieldState == SfxItemState::SET )
1375 bFields = true;
1377 if ( bFields )
1379 bool bOldUpdateMode = rEngine.SetUpdateLayout( true ); // no portions if not formatted
1380 for ( sal_Int32 nPar=0; nPar < nParas; nPar++ )
1382 if ( nPar > 0 )
1383 TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
1384 std::vector<sal_Int32> aPortions;
1385 rEngine.GetPortions( nPar, aPortions );
1386 sal_Int32 nStart = 0;
1387 for ( const sal_Int32 nEnd : aPortions )
1389 ESelection aSel( nPar, nStart, nPar, nEnd );
1390 bool bUrl = false;
1391 // fields are single characters
1392 if ( nEnd == nStart+1 )
1394 SfxItemSet aSet = rEngine.GetAttribs( aSel );
1395 if ( const SvxFieldItem* pFieldItem = aSet.GetItemIfSet( EE_FEATURE_FIELD, false ) )
1397 const SvxFieldData* pField = pFieldItem->GetField();
1398 if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
1400 bUrl = true;
1401 rStrm.WriteChar( '<' ).WriteOString( OOO_STRING_SVTOOLS_HTML_anchor ).WriteChar( ' ' ).WriteOString( OOO_STRING_SVTOOLS_HTML_O_href ).WriteOString( "=\"" );
1402 OUT_STR( pURLField->GetURL() );
1403 rStrm.WriteOString( "\">" );
1404 OUT_STR( pURLField->GetRepresentation() );
1405 rStrm.WriteOString( "</" ).WriteOString( OOO_STRING_SVTOOLS_HTML_anchor ).WriteChar( '>' );
1409 if ( !bUrl )
1410 OUT_STR( rEngine.GetText( aSel ) );
1411 nStart = nEnd;
1414 rEngine.SetUpdateLayout( bOldUpdateMode );
1416 return bFields;
1419 void ScHTMLExport::CopyLocalFileToINet( OUString& rFileNm,
1420 std::u16string_view rTargetNm )
1422 INetURLObject aFileUrl, aTargetUrl;
1423 aFileUrl.SetSmartURL( rFileNm );
1424 aTargetUrl.SetSmartURL( rTargetNm );
1425 if (!(INetProtocol::File == aFileUrl.GetProtocol()
1426 && (INetProtocol::Http == aTargetUrl.GetProtocol()
1427 || INetProtocol::Https == aTargetUrl.GetProtocol()
1428 || INetProtocol::VndSunStarWebdav == aTargetUrl.GetProtocol()
1429 || INetProtocol::Smb == aTargetUrl.GetProtocol()
1430 || INetProtocol::Sftp == aTargetUrl.GetProtocol()
1431 || INetProtocol::Cmis == aTargetUrl.GetProtocol())))
1433 return;
1436 if( pFileNameMap )
1438 // Did we already move the file?
1439 std::map<OUString, OUString>::iterator it = pFileNameMap->find( rFileNm );
1440 if( it != pFileNameMap->end() )
1442 rFileNm = it->second;
1443 return;
1446 else
1448 pFileNameMap.reset( new std::map<OUString, OUString> );
1451 bool bRet = false;
1452 SvFileStream aTmp( aFileUrl.PathToFileName(), StreamMode::READ );
1454 OUString aSrc = rFileNm;
1455 OUString aDest = aTargetUrl.GetPartBeforeLastName() + aFileUrl.GetLastName();
1457 SfxMedium aMedium( aDest, StreamMode::WRITE | StreamMode::SHARE_DENYNONE );
1460 SvFileStream aCpy( aMedium.GetPhysicalName(), StreamMode::WRITE );
1461 aCpy.WriteStream( aTmp );
1464 // Take over
1465 aMedium.Close();
1466 aMedium.Commit();
1468 bRet = ERRCODE_NONE == aMedium.GetErrorIgnoreWarning();
1470 if( bRet )
1472 pFileNameMap->insert( std::make_pair( aSrc, aDest ) );
1473 rFileNm = aDest;
1477 void ScHTMLExport::IncIndent( short nVal )
1479 sIndent[nIndent] = '\t';
1480 nIndent = nIndent + nVal;
1481 if ( nIndent < 0 )
1482 nIndent = 0;
1483 else if ( nIndent > nIndentMax )
1484 nIndent = nIndentMax;
1485 sIndent[nIndent] = 0;
1488 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */