1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 
24 #include <scitems.hxx>
25 #include <editeng/eeitem.hxx>
26 
27 #include <vcl/svapp.hxx>
28 #include <editeng/boxitem.hxx>
29 #include <editeng/brushitem.hxx>
30 #include <editeng/colritem.hxx>
31 #include <editeng/crossedoutitem.hxx>
32 #include <editeng/fhgtitem.hxx>
33 #include <editeng/fontitem.hxx>
34 #include <editeng/postitem.hxx>
35 #include <editeng/udlnitem.hxx>
36 #include <editeng/wghtitem.hxx>
37 #include <editeng/justifyitem.hxx>
38 #include <svx/xoutbmp.hxx>
39 #include <editeng/editeng.hxx>
40 #include <svtools/htmlcfg.hxx>
41 #include <sfx2/docfile.hxx>
42 #include <sfx2/frmhtmlw.hxx>
43 #include <sfx2/objsh.hxx>
44 #include <svl/urihelper.hxx>
45 #include <svtools/htmlkywd.hxx>
46 #include <svtools/htmlout.hxx>
47 #include <svtools/parhtml.hxx>
48 #include <vcl/outdev.hxx>
49 #include <stdio.h>
50 #include <osl/diagnose.h>
51 #include <o3tl/unit_conversion.hxx>
52 
53 #include <htmlexp.hxx>
54 #include <global.hxx>
55 #include <postit.hxx>
56 #include <document.hxx>
57 #include <attrib.hxx>
58 #include <patattr.hxx>
59 #include <stlpool.hxx>
60 #include <scresid.hxx>
61 #include <formulacell.hxx>
62 #include <cellform.hxx>
63 #include <docoptio.hxx>
64 #include <editutil.hxx>
65 #include <ftools.hxx>
66 #include <cellvalue.hxx>
67 #include <mtvelements.hxx>
68 
69 #include <editeng/flditem.hxx>
70 #include <editeng/borderline.hxx>
71 
72 // Without strings.hrc: error C2679: binary '=' : no operator defined which takes a
73 // right-hand operand of type 'const class String (__stdcall *)(class ScResId)'
74 // at
75 // const String aStrTable( ScResId( SCSTR_TABLE ) ); aStrOut = aStrTable;
76 // ?!???
77 #include <strings.hrc>
78 #include <globstr.hrc>
79 
80 #include <com/sun/star/frame/XModel.hpp>
81 #include <com/sun/star/uno/Reference.h>
82 #include <com/sun/star/document/XDocumentProperties.hpp>
83 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
84 #include <rtl/strbuf.hxx>
85 
86 using ::editeng::SvxBorderLine;
87 using namespace ::com::sun::star;
88 
89 const char sMyBegComment[]   = "<!-- ";
90 const char sMyEndComment[]   = " -->";
91 const char sDisplay[]        = "display:";
92 const char sBorder[]         = "border:";
93 const char sBackground[]     = "background:";
94 
95 const sal_uInt16 ScHTMLExport::nDefaultFontSize[SC_HTML_FONTSIZES] =
96 {
97     HTMLFONTSZ1_DFLT, HTMLFONTSZ2_DFLT, HTMLFONTSZ3_DFLT, HTMLFONTSZ4_DFLT,
98     HTMLFONTSZ5_DFLT, HTMLFONTSZ6_DFLT, HTMLFONTSZ7_DFLT
99 };
100 
101 sal_uInt16 ScHTMLExport::nFontSize[SC_HTML_FONTSIZES] = { 0 };
102 
103 const char* ScHTMLExport::pFontSizeCss[SC_HTML_FONTSIZES] =
104 {
105     "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"
106 };
107 
108 const sal_uInt16 ScHTMLExport::nCellSpacing = 0;
109 const char ScHTMLExport::sIndentSource[nIndentMax+1] =
110     "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
111 
112 // Macros for HTML export
113 
114 #define TAG_ON( tag )       HTMLOutFuncs::Out_AsciiTag( rStrm, tag )
115 #define TAG_OFF( tag )      HTMLOutFuncs::Out_AsciiTag( rStrm, tag, false )
116 #define OUT_STR( str )      HTMLOutFuncs::Out_String( rStrm, str, eDestEnc, &aNonConvertibleChars )
117 #define OUT_LF()            rStrm.WriteCharPtr( SAL_NEWLINE_STRING ).WriteCharPtr( GetIndentStr() )
118 #define TAG_ON_LF( tag )    (TAG_ON( tag ).WriteCharPtr( SAL_NEWLINE_STRING ).WriteCharPtr( GetIndentStr() ))
119 #define TAG_OFF_LF( tag )   (TAG_OFF( tag ).WriteCharPtr( SAL_NEWLINE_STRING ).WriteCharPtr( GetIndentStr() ))
120 #define OUT_HR()            TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_horzrule )
121 #define OUT_COMMENT( comment )  (rStrm.WriteCharPtr( sMyBegComment ), OUT_STR( comment ) \
122                                 .WriteCharPtr( sMyEndComment ).WriteCharPtr( SAL_NEWLINE_STRING ) \
123                                 .WriteCharPtr( GetIndentStr() ))
124 
125 #define OUT_SP_CSTR_ASS( s )    rStrm.WriteChar( ' ').WriteCharPtr( s ).WriteChar( '=' )
126 
127 #define GLOBSTR(id) ScResId( id )
128 
ScExportHTML(SvStream & rStrm,const OUString & rBaseURL,ScDocument * pDoc,const ScRange & rRange,const rtl_TextEncoding,bool bAll,const OUString & rStreamPath,OUString & rNonConvertibleChars,const OUString & rFilterOptions)129 void ScFormatFilterPluginImpl::ScExportHTML( SvStream& rStrm, const OUString& rBaseURL, ScDocument* pDoc,
130         const ScRange& rRange, const rtl_TextEncoding /*eNach*/, bool bAll,
131         const OUString& rStreamPath, OUString& rNonConvertibleChars, const OUString& rFilterOptions )
132 {
133     ScHTMLExport aEx( rStrm, rBaseURL, pDoc, rRange, bAll, rStreamPath, rFilterOptions );
134     aEx.Write();
135     rNonConvertibleChars = aEx.GetNonConvertibleChars();
136 }
137 
lcl_getColGroupString(sal_Int32 nSpan,sal_Int32 nWidth)138 static OString lcl_getColGroupString(sal_Int32 nSpan, sal_Int32 nWidth)
139 {
140     OStringBuffer aByteStr(OOO_STRING_SVTOOLS_HTML_colgroup);
141     aByteStr.append(' ');
142     if( nSpan > 1 )
143     {
144         aByteStr.append(OOO_STRING_SVTOOLS_HTML_O_span);
145         aByteStr.append("=\"");
146         aByteStr.append(nSpan);
147         aByteStr.append("\" ");
148     }
149     aByteStr.append(OOO_STRING_SVTOOLS_HTML_O_width);
150     aByteStr.append("=\"");
151     aByteStr.append(nWidth);
152     aByteStr.append('"');
153     return aByteStr.makeStringAndClear();
154 }
155 
lcl_AddStamp(OUString & rStr,std::u16string_view rName,const css::util::DateTime & rDateTime,const LocaleDataWrapper & rLoc)156 static void lcl_AddStamp( OUString& rStr, std::u16string_view rName,
157     const css::util::DateTime& rDateTime,
158     const LocaleDataWrapper& rLoc )
159 {
160     Date aD(rDateTime.Day, rDateTime.Month, rDateTime.Year);
161     tools::Time aT(rDateTime.Hours, rDateTime.Minutes, rDateTime.Seconds,
162             rDateTime.NanoSeconds);
163     DateTime aDateTime(aD,aT);
164 
165     OUString        aStrDate    = rLoc.getDate( aDateTime );
166     OUString        aStrTime    = rLoc.getTime( aDateTime );
167 
168     rStr += GLOBSTR( STR_BY ) + " ";
169     if (!rName.empty())
170         rStr += rName;
171     else
172         rStr += "???";
173     rStr += " " + GLOBSTR( STR_ON ) + " ";
174     if (!aStrDate.isEmpty())
175         rStr += aStrDate;
176     else
177         rStr += "???";
178     rStr += ", ";
179     if (!aStrTime.isEmpty())
180         rStr += aStrTime;
181     else
182         rStr += "???";
183 }
184 
lcl_makeHTMLColorTriplet(const Color & rColor)185 static OString lcl_makeHTMLColorTriplet(const Color& rColor)
186 {
187     char    buf[24];
188 
189     // <font COLOR="#00FF40">hello</font>
190     snprintf( buf, 24, "\"#%02X%02X%02X\"", rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
191 
192     return buf;
193 }
194 
ScHTMLExport(SvStream & rStrmP,const OUString & rBaseURL,ScDocument * pDocP,const ScRange & rRangeP,bool bAllP,const OUString & rStreamPathP,std::u16string_view rFilterOptions)195 ScHTMLExport::ScHTMLExport( SvStream& rStrmP, const OUString& rBaseURL, ScDocument* pDocP,
196                             const ScRange& rRangeP, bool bAllP,
197                             const OUString& rStreamPathP, std::u16string_view rFilterOptions ) :
198     ScExportBase( rStrmP, pDocP, rRangeP ),
199     aBaseURL( rBaseURL ),
200     aStreamPath( rStreamPathP ),
201     pAppWin( Application::GetDefaultDevice() ),
202     nUsedTables( 0 ),
203     nIndent( 0 ),
204     bAll( bAllP ),
205     bTabHasGraphics( false ),
206     bTabAlignedLeft( false ),
207     bCalcAsShown( pDocP->GetDocOptions().IsCalcAsShown() ),
208     bTableDataHeight( true ),
209     mbSkipImages ( false ),
210     mbSkipHeaderFooter( false )
211 {
212     strcpy( sIndent, sIndentSource );
213     sIndent[0] = 0;
214 
215     // set HTML configuration
216     SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get();
217     eDestEnc = (pDoc->IsClipOrUndo() ? RTL_TEXTENCODING_UTF8 : rHtmlOptions.GetTextEncoding());
218     bCopyLocalFileToINet = rHtmlOptions.IsSaveGraphicsLocal();
219 
220     if (rFilterOptions == u"SkipImages")
221     {
222         mbSkipImages = true;
223     }
224     else if (rFilterOptions == u"SkipHeaderFooter")
225     {
226         mbSkipHeaderFooter = true;
227     }
228 
229     for ( sal_uInt16 j=0; j < SC_HTML_FONTSIZES; j++ )
230     {
231         sal_uInt16 nSize = rHtmlOptions.GetFontSize( j );
232         // remember in Twips, like our SvxFontHeightItem
233         if ( nSize )
234             nFontSize[j] = nSize * 20;
235         else
236             nFontSize[j] = nDefaultFontSize[j] * 20;
237     }
238 
239     const SCTAB nCount = pDoc->GetTableCount();
240     for ( SCTAB nTab = 0; nTab < nCount; nTab++ )
241     {
242         if ( !IsEmptyTable( nTab ) )
243             nUsedTables++;
244     }
245 }
246 
~ScHTMLExport()247 ScHTMLExport::~ScHTMLExport()
248 {
249     aGraphList.clear();
250 }
251 
GetFontSizeNumber(sal_uInt16 nHeight)252 sal_uInt16 ScHTMLExport::GetFontSizeNumber( sal_uInt16 nHeight )
253 {
254     sal_uInt16 nSize = 1;
255     for ( sal_uInt16 j=SC_HTML_FONTSIZES-1; j>0; j-- )
256     {
257         if( nHeight > (nFontSize[j] + nFontSize[j-1]) / 2 )
258         {   // The one next to it
259             nSize = j+1;
260             break;
261         }
262     }
263     return nSize;
264 }
265 
GetFontSizeCss(sal_uInt16 nHeight)266 const char* ScHTMLExport::GetFontSizeCss( sal_uInt16 nHeight )
267 {
268     sal_uInt16 nSize = GetFontSizeNumber( nHeight );
269     return pFontSizeCss[ nSize-1 ];
270 }
271 
ToPixel(sal_uInt16 nVal)272 sal_uInt16 ScHTMLExport::ToPixel( sal_uInt16 nVal )
273 {
274     if( nVal )
275     {
276         nVal = static_cast<sal_uInt16>(pAppWin->LogicToPixel(
277                     Size( nVal, nVal ), MapMode( MapUnit::MapTwip ) ).Width());
278         if( !nVal ) // If there's a Twip there should also be a Pixel
279             nVal = 1;
280     }
281     return nVal;
282 }
283 
MMToPixel(const Size & rSize)284 Size ScHTMLExport::MMToPixel( const Size& rSize )
285 {
286     Size aSize = pAppWin->LogicToPixel( rSize, MapMode( MapUnit::Map100thMM ) );
287     // If there's something there should also be a Pixel
288     if ( !aSize.Width() && rSize.Width() )
289         aSize.setWidth( 1 );
290     if ( !aSize.Height() && rSize.Height() )
291         aSize.setHeight( 1 );
292     return aSize;
293 }
294 
Write()295 void ScHTMLExport::Write()
296 {
297     if (!mbSkipHeaderFooter)
298     {
299         rStrm.WriteChar( '<' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_doctype ).WriteChar( ' ' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_doctype40 ).WriteChar( '>' )
300            .WriteCharPtr( SAL_NEWLINE_STRING ).WriteCharPtr( SAL_NEWLINE_STRING );
301         TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_html );
302         WriteHeader();
303         OUT_LF();
304     }
305     WriteBody();
306     OUT_LF();
307     if (!mbSkipHeaderFooter)
308         TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_html );
309 }
310 
WriteHeader()311 void ScHTMLExport::WriteHeader()
312 {
313     IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_head );
314 
315     if ( pDoc->IsClipOrUndo() )
316     {   // no real DocInfo available, but some META information like charset needed
317         SfxFrameHTMLWriter::Out_DocInfo( rStrm, aBaseURL, nullptr, sIndent, eDestEnc, &aNonConvertibleChars );
318     }
319     else
320     {
321         uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
322             pDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
323         uno::Reference<document::XDocumentProperties> xDocProps
324             = xDPS->getDocumentProperties();
325         SfxFrameHTMLWriter::Out_DocInfo( rStrm, aBaseURL, xDocProps,
326             sIndent, eDestEnc, &aNonConvertibleChars );
327         OUT_LF();
328 
329         if (!xDocProps->getPrintedBy().isEmpty())
330         {
331             OUT_COMMENT( GLOBSTR( STR_DOC_INFO ) );
332             OUString aStrOut = GLOBSTR( STR_DOC_PRINTED ) + ": ";
333             lcl_AddStamp( aStrOut, xDocProps->getPrintedBy(),
334                 xDocProps->getPrintDate(), *ScGlobal::getLocaleDataPtr() );
335             OUT_COMMENT( aStrOut );
336         }
337 
338     }
339     OUT_LF();
340 
341     // CSS1 StyleSheet
342     PageDefaults( bAll ? 0 : aRange.aStart.Tab() );
343     IncIndent(1);
344     rStrm.WriteCharPtr( "<" ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_style ).WriteCharPtr( " " ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_O_type ).WriteCharPtr( "=\"text/css\">" );
345 
346     OUT_LF();
347     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_body);
348     rStrm.WriteCharPtr(",");
349     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_division);
350     rStrm.WriteCharPtr(",");
351     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_table);
352     rStrm.WriteCharPtr(",");
353     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_thead);
354     rStrm.WriteCharPtr(",");
355     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tbody);
356     rStrm.WriteCharPtr(",");
357     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tfoot);
358     rStrm.WriteCharPtr(",");
359     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tablerow);
360     rStrm.WriteCharPtr(",");
361     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tableheader);
362     rStrm.WriteCharPtr(",");
363     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tabledata);
364     rStrm.WriteCharPtr(",");
365     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_parabreak);
366     rStrm.WriteCharPtr(" { ");
367     rStrm.WriteCharPtr("font-family:");
368 
369     if (!aHTMLStyle.aFontFamilyName.isEmpty())
370     {
371         const OUString& rList = aHTMLStyle.aFontFamilyName;
372         for(sal_Int32 nPos {0};;)
373         {
374             rStrm.WriteChar( '\"' );
375             OUT_STR( rList.getToken( 0, ';', nPos ) );
376             rStrm.WriteChar( '\"' );
377             if (nPos<0)
378                 break;
379             rStrm.WriteCharPtr( ", " );
380         }
381     }
382     rStrm.WriteCharPtr("; ");
383     rStrm.WriteCharPtr("font-size:");
384     rStrm.WriteCharPtr(GetFontSizeCss(static_cast<sal_uInt16>(aHTMLStyle.nFontHeight)));
385     rStrm.WriteCharPtr(" }");
386 
387     OUT_LF();
388 
389     // write the style for the comments to make them stand out from normal cell content
390     // this is done through only showing the cell contents when the custom indicator is hovered
391     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_anchor);
392     rStrm.WriteCharPtr(".comment-indicator:hover");
393     rStrm.WriteCharPtr(" + ");
394     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_comment2);
395     rStrm.WriteCharPtr(" { ");
396     rStrm.WriteCharPtr(sBackground);
397     rStrm.WriteCharPtr("#ffd");
398     rStrm.WriteCharPtr("; ");
399     rStrm.WriteCharPtr("position:");
400     rStrm.WriteCharPtr("absolute");
401     rStrm.WriteCharPtr("; ");
402     rStrm.WriteCharPtr(sDisplay);
403     rStrm.WriteCharPtr("block");
404     rStrm.WriteCharPtr("; ");
405     rStrm.WriteCharPtr(sBorder);
406     rStrm.WriteCharPtr("1px solid black");
407     rStrm.WriteCharPtr("; ");
408     rStrm.WriteCharPtr("padding:");
409     rStrm.WriteCharPtr("0.5em");
410     rStrm.WriteCharPtr("; ");
411     rStrm.WriteCharPtr(" } ");
412 
413     OUT_LF();
414 
415     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_anchor);
416     rStrm.WriteCharPtr(".comment-indicator");
417     rStrm.WriteCharPtr(" { ");
418     rStrm.WriteCharPtr(sBackground);
419     rStrm.WriteCharPtr("red");
420     rStrm.WriteCharPtr("; ");
421     rStrm.WriteCharPtr(sDisplay);
422     rStrm.WriteCharPtr("inline-block");
423     rStrm.WriteCharPtr("; ");
424     rStrm.WriteCharPtr(sBorder);
425     rStrm.WriteCharPtr("1px solid black");
426     rStrm.WriteCharPtr("; ");
427     rStrm.WriteCharPtr("width:");
428     rStrm.WriteCharPtr("0.5em");
429     rStrm.WriteCharPtr("; ");
430     rStrm.WriteCharPtr("height:");
431     rStrm.WriteCharPtr("0.5em");
432     rStrm.WriteCharPtr("; ");
433     rStrm.WriteCharPtr(" } ");
434 
435     OUT_LF();
436 
437     rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_comment2);
438     rStrm.WriteCharPtr(" { ");
439     rStrm.WriteCharPtr(sDisplay);
440     rStrm.WriteCharPtr("none");
441     rStrm.WriteCharPtr("; ");
442     rStrm.WriteCharPtr(" } ");
443 
444 
445     IncIndent(-1);
446     OUT_LF();
447     TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_style );
448 
449     IncIndent(-1);
450     OUT_LF();
451     TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_head );
452 }
453 
WriteOverview()454 void ScHTMLExport::WriteOverview()
455 {
456     if ( nUsedTables <= 1 )
457         return;
458 
459     IncIndent(1);
460     OUT_HR();
461     IncIndent(1); TAG_ON( OOO_STRING_SVTOOLS_HTML_parabreak ); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_center );
462     TAG_ON( OOO_STRING_SVTOOLS_HTML_head1 );
463     OUT_STR( ScResId( STR_OVERVIEW ) );
464     TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_head1 );
465 
466     OUString aStr;
467 
468     const SCTAB nCount = pDoc->GetTableCount();
469     for ( SCTAB nTab = 0; nTab < nCount; nTab++ )
470     {
471         if ( !IsEmptyTable( nTab ) )
472         {
473             pDoc->GetName( nTab, aStr );
474             rStrm.WriteCharPtr( "<A HREF=\"#table" )
475                .WriteOString( OString::number(nTab) )
476                .WriteCharPtr( "\">" );
477             OUT_STR( aStr );
478             rStrm.WriteCharPtr( "</A>" );
479             TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_linebreak );
480         }
481     }
482 
483     IncIndent(-1); OUT_LF();
484     IncIndent(-1); TAG_OFF( OOO_STRING_SVTOOLS_HTML_center ); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_parabreak );
485 }
486 
PageDefaults(SCTAB nTab)487 const SfxItemSet& ScHTMLExport::PageDefaults( SCTAB nTab )
488 {
489     SfxStyleSheetBasePool*  pStylePool  = pDoc->GetStyleSheetPool();
490     SfxStyleSheetBase*      pStyleSheet = nullptr;
491     OSL_ENSURE( pStylePool, "StylePool not found! :-(" );
492 
493     // remember defaults for compare in WriteCell
494     if ( !aHTMLStyle.bInitialized )
495     {
496         pStyleSheet = pStylePool->Find(
497                 ScResId(STR_STYLENAME_STANDARD),
498                 SfxStyleFamily::Para );
499         OSL_ENSURE( pStyleSheet, "ParaStyle not found! :-(" );
500         if (!pStyleSheet)
501             pStyleSheet = pStylePool->First(SfxStyleFamily::Para);
502         const SfxItemSet& rSetPara = pStyleSheet->GetItemSet();
503 
504         aHTMLStyle.nDefaultScriptType = ScGlobal::GetDefaultScriptType();
505         aHTMLStyle.aFontFamilyName = static_cast<const SvxFontItem&>((rSetPara.Get(
506                         ScGlobal::GetScriptedWhichID(
507                             aHTMLStyle.nDefaultScriptType, ATTR_FONT
508                             )))).GetFamilyName();
509         aHTMLStyle.nFontHeight = static_cast<const SvxFontHeightItem&>((rSetPara.Get(
510                         ScGlobal::GetScriptedWhichID(
511                             aHTMLStyle.nDefaultScriptType, ATTR_FONT_HEIGHT
512                             )))).GetHeight();
513         aHTMLStyle.nFontSizeNumber = GetFontSizeNumber( static_cast< sal_uInt16 >( aHTMLStyle.nFontHeight ) );
514     }
515 
516     // Page style sheet printer settings, e.g. for background graphics.
517     // There's only one background graphic in HTML!
518     pStyleSheet = pStylePool->Find( pDoc->GetPageStyle( nTab ), SfxStyleFamily::Page );
519     OSL_ENSURE( pStyleSheet, "PageStyle not found! :-(" );
520     if (!pStyleSheet)
521         pStyleSheet = pStylePool->First(SfxStyleFamily::Page);
522     const SfxItemSet& rSet = pStyleSheet->GetItemSet();
523     if ( !aHTMLStyle.bInitialized )
524     {
525         const SvxBrushItem* pBrushItem = &rSet.Get( ATTR_BACKGROUND );
526         aHTMLStyle.aBackgroundColor = pBrushItem->GetColor();
527         aHTMLStyle.bInitialized = true;
528     }
529     return rSet;
530 }
531 
BorderToStyle(const char * pBorderName,const SvxBorderLine * pLine,bool & bInsertSemicolon)532 OString ScHTMLExport::BorderToStyle(const char* pBorderName,
533         const SvxBorderLine* pLine, bool& bInsertSemicolon)
534 {
535     OStringBuffer aOut;
536 
537     if ( pLine )
538     {
539         if ( bInsertSemicolon )
540             aOut.append("; ");
541 
542         // which border
543         aOut.append(OString::Concat("border-") + pBorderName + ": ");
544 
545         // thickness
546         int nWidth = pLine->GetWidth();
547         int nPxWidth = (nWidth > 0) ?
548             std::max(o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::px), sal_Int64(1)) : 0;
549         aOut.append(OString::number(nPxWidth) + "px ");
550         switch (pLine->GetBorderLineStyle())
551         {
552             case SvxBorderLineStyle::SOLID:
553                 aOut.append("solid");
554                 break;
555             case SvxBorderLineStyle::DOTTED:
556                 aOut.append("dotted");
557                 break;
558             case SvxBorderLineStyle::DASHED:
559             case SvxBorderLineStyle::DASH_DOT:
560             case SvxBorderLineStyle::DASH_DOT_DOT:
561                 aOut.append("dashed");
562                 break;
563             case SvxBorderLineStyle::DOUBLE:
564             case SvxBorderLineStyle::DOUBLE_THIN:
565             case SvxBorderLineStyle::THINTHICK_SMALLGAP:
566             case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
567             case SvxBorderLineStyle::THINTHICK_LARGEGAP:
568             case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
569             case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
570             case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
571                 aOut.append("double");
572                 break;
573             case SvxBorderLineStyle::EMBOSSED:
574                 aOut.append("ridge");
575                 break;
576             case SvxBorderLineStyle::ENGRAVED:
577                 aOut.append("groove");
578                 break;
579             case SvxBorderLineStyle::OUTSET:
580                 aOut.append("outset");
581                 break;
582             case SvxBorderLineStyle::INSET:
583                 aOut.append("inset");
584                 break;
585             default:
586                 aOut.append("hidden");
587         }
588         aOut.append(" #");
589 
590         // color
591         char hex[7];
592         snprintf( hex, 7, "%06" SAL_PRIxUINT32, static_cast<sal_uInt32>( pLine->GetColor().GetRGBColor() ) );
593         hex[6] = 0;
594 
595         aOut.append(hex);
596 
597         bInsertSemicolon = true;
598     }
599 
600     return aOut.makeStringAndClear();
601 }
602 
WriteBody()603 void ScHTMLExport::WriteBody()
604 {
605     const SfxItemSet& rSet = PageDefaults( bAll ? 0 : aRange.aStart.Tab() );
606     const SvxBrushItem* pBrushItem = &rSet.Get( ATTR_BACKGROUND );
607 
608     // default text color black
609     if (!mbSkipHeaderFooter)
610     {
611         rStrm.WriteChar( '<' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_body );
612 
613         if (!mbSkipImages)
614         {
615             if ( bAll && GPOS_NONE != pBrushItem->GetGraphicPos() )
616             {
617                 OUString aLink = pBrushItem->GetGraphicLink();
618                 OUString aGrfNm;
619 
620                 // Embedded graphic -> write using WriteGraphic
621                 if( aLink.isEmpty() )
622                 {
623                     const Graphic* pGrf = pBrushItem->GetGraphic();
624                     if( pGrf )
625                     {
626                         // Save graphic as (JPG) file
627                         aGrfNm = aStreamPath;
628                         ErrCode nErr = XOutBitmap::WriteGraphic( *pGrf, aGrfNm,
629                             "JPG", XOutFlags::UseNativeIfPossible );
630                         if( !nErr ) // Contains errors, as we have nothing to output
631                         {
632                             aGrfNm = URIHelper::SmartRel2Abs(
633                                     INetURLObject(aBaseURL),
634                                     aGrfNm, URIHelper::GetMaybeFileHdl());
635                             aLink = aGrfNm;
636                         }
637                     }
638                 }
639                 else
640                 {
641                     aGrfNm = aLink;
642                     if( bCopyLocalFileToINet )
643                     {
644                         CopyLocalFileToINet( aGrfNm, aStreamPath );
645                     }
646                     else
647                         aGrfNm = URIHelper::SmartRel2Abs(
648                                 INetURLObject(aBaseURL),
649                                 aGrfNm, URIHelper::GetMaybeFileHdl());
650                     aLink = aGrfNm;
651                 }
652                 if( !aLink.isEmpty() )
653                 {
654                     rStrm.WriteChar( ' ' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_O_background ).WriteCharPtr( "=\"" );
655                     OUT_STR( URIHelper::simpleNormalizedMakeRelative(
656                                 aBaseURL,
657                                 aLink ) ).WriteChar( '\"' );
658                 }
659             }
660         }
661         if ( !aHTMLStyle.aBackgroundColor.IsTransparent() )
662         {   // A transparent background color should always result in default
663             // background of the browser. Also, HTMLOutFuncs::Out_Color() writes
664             // black #000000 for COL_AUTO which is the same as white #ffffff with
665             // transparency set to 0xff, our default background.
666             OUT_SP_CSTR_ASS( OOO_STRING_SVTOOLS_HTML_O_bgcolor );
667             HTMLOutFuncs::Out_Color( rStrm, aHTMLStyle.aBackgroundColor );
668         }
669 
670         rStrm.WriteChar( '>' ); OUT_LF();
671     }
672 
673     if ( bAll )
674         WriteOverview();
675 
676     WriteTables();
677 
678     if (!mbSkipHeaderFooter)
679         TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_body );
680 }
681 
WriteTables()682 void ScHTMLExport::WriteTables()
683 {
684     const SCTAB nTabCount = pDoc->GetTableCount();
685     const OUString  aStrTable( ScResId( SCSTR_TABLE ) );
686     OUString   aStr;
687     OUString        aStrOut;
688     SCCOL           nStartCol;
689     SCROW           nStartRow;
690     SCTAB           nStartTab;
691     SCCOL           nEndCol;
692     SCROW           nEndRow;
693     SCTAB           nEndTab;
694     SCCOL           nStartColFix = 0;
695     SCROW           nStartRowFix = 0;
696     SCCOL           nEndColFix = 0;
697     SCROW           nEndRowFix = 0;
698     ScDrawLayer*    pDrawLayer = pDoc->GetDrawLayer();
699     if ( bAll )
700     {
701         nStartTab = 0;
702         nEndTab = nTabCount - 1;
703     }
704     else
705     {
706         nStartCol = nStartColFix = aRange.aStart.Col();
707         nStartRow = nStartRowFix = aRange.aStart.Row();
708         nStartTab = aRange.aStart.Tab();
709         nEndCol = nEndColFix = aRange.aEnd.Col();
710         nEndRow = nEndRowFix = aRange.aEnd.Row();
711         nEndTab = aRange.aEnd.Tab();
712     }
713     SCTAB nTableStrNum = 1;
714     for ( SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++ )
715     {
716         if ( !pDoc->IsVisible( nTab ) )
717             continue;   // for
718 
719         if ( bAll )
720         {
721             if ( !GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ) )
722                 continue;   // for
723 
724             if ( nUsedTables > 1 )
725             {
726                 aStrOut = aStrTable + " "  + OUString::number( nTableStrNum++ ) + ": ";
727 
728                 OUT_HR();
729 
730                 // Write anchor
731                 rStrm.WriteCharPtr( "<A NAME=\"table" )
732                    .WriteOString( OString::number(nTab) )
733                    .WriteCharPtr( "\">" );
734                 TAG_ON( OOO_STRING_SVTOOLS_HTML_head1 );
735                 OUT_STR( aStrOut );
736                 TAG_ON( OOO_STRING_SVTOOLS_HTML_emphasis );
737 
738                 pDoc->GetName( nTab, aStr );
739                 OUT_STR( aStr );
740 
741                 TAG_OFF( OOO_STRING_SVTOOLS_HTML_emphasis );
742                 TAG_OFF( OOO_STRING_SVTOOLS_HTML_head1 );
743                 rStrm.WriteCharPtr( "</A>" ); OUT_LF();
744             }
745         }
746         else
747         {
748             nStartCol = nStartColFix;
749             nStartRow = nStartRowFix;
750             nEndCol = nEndColFix;
751             nEndRow = nEndRowFix;
752             if ( !TrimDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ) )
753                 continue;   // for
754         }
755 
756         // <TABLE ...>
757         OStringBuffer aByteStrOut(OOO_STRING_SVTOOLS_HTML_table);
758 
759         bTabHasGraphics = bTabAlignedLeft = false;
760         if ( bAll && pDrawLayer )
761             PrepareGraphics( pDrawLayer, nTab, nStartCol, nStartRow,
762                 nEndCol, nEndRow );
763 
764         // more <TABLE ...>
765         if ( bTabAlignedLeft )
766         {
767             aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
768                     "=\""
769                     OOO_STRING_SVTOOLS_HTML_AL_left "\"");
770         }
771             // ALIGN=LEFT allow text and graphics to flow around
772         // CELLSPACING
773         aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellspacing
774                 "=\"" +
775                 OString::number(nCellSpacing) + "\"");
776 
777         // BORDER=0, we do the styling of the cells in <TD>
778         aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_border "=\"0\"");
779         IncIndent(1); TAG_ON_LF( aByteStrOut.makeStringAndClear().getStr() );
780 
781         // --- <COLGROUP> ----
782         {
783             SCCOL nCol = nStartCol;
784             sal_Int32 nWidth = 0;
785             sal_Int32 nSpan = 0;
786             while( nCol <= nEndCol )
787             {
788                 if( pDoc->ColHidden(nCol, nTab) )
789                 {
790                     ++nCol;
791                     continue;
792                 }
793 
794                 if( nWidth != ToPixel( pDoc->GetColWidth( nCol, nTab ) ) )
795                 {
796                     if( nSpan != 0 )
797                     {
798                         TAG_ON(lcl_getColGroupString(nSpan, nWidth).getStr());
799                         TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_colgroup );
800                     }
801                     nWidth = ToPixel( pDoc->GetColWidth( nCol, nTab ) );
802                     nSpan = 1;
803                 }
804                 else
805                     nSpan++;
806                 nCol++;
807             }
808             if( nSpan )
809             {
810                 TAG_ON(lcl_getColGroupString(nSpan, nWidth).getStr());
811                 TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_colgroup );
812             }
813         }
814 
815         // <TBODY> // Re-enable only when THEAD and TFOOT are exported
816         // IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_tbody );
817         // At least old (3.x, 4.x?) Netscape doesn't follow <TABLE COLS=n> and
818         // <COL WIDTH=x> specified, but needs a width at every column.
819         bool bHasHiddenRows = pDoc->HasHiddenRows(nStartRow, nEndRow, nTab);
820         // We need to cache sc::ColumnBlockPosition per each column.
821         std::vector< sc::ColumnBlockPosition > blockPos( nEndCol - nStartCol + 1 );
822         for( SCCOL i = nStartCol; i <= nEndCol; ++i )
823             pDoc->InitColumnBlockPosition( blockPos[ i - nStartCol ], nTab, i );
824         for ( SCROW nRow=nStartRow; nRow<=nEndRow; nRow++ )
825         {
826             if ( bHasHiddenRows && pDoc->RowHidden(nRow, nTab) )
827             {
828                 nRow = pDoc->FirstVisibleRow(nRow+1, nEndRow, nTab);
829                 --nRow;
830                 continue;   // for
831             }
832 
833             IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_tablerow );
834             bTableDataHeight = true;  // height at every first cell of each row
835             for ( SCCOL nCol2=nStartCol; nCol2<=nEndCol; nCol2++ )
836             {
837                 if ( pDoc->ColHidden(nCol2, nTab) )
838                     continue;   // for
839 
840                 if ( nCol2 == nEndCol )
841                     IncIndent(-1);
842                 WriteCell( blockPos[ nCol2 - nStartCol ], nCol2, nRow, nTab );
843                 bTableDataHeight = false;
844             }
845 
846             if ( nRow == nEndRow )
847                 IncIndent(-1);
848             TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tablerow );
849         }
850         // TODO: Uncomment later
851         // IncIndent(-1); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tbody );
852 
853         IncIndent(-1); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_table );
854 
855         if ( bTabHasGraphics && !mbSkipImages )
856         {
857             // the rest that is not in a cell
858             size_t ListSize = aGraphList.size();
859             for ( size_t i = 0; i < ListSize; ++i )
860             {
861                 ScHTMLGraphEntry* pE = &aGraphList[ i ];
862                 if ( !pE->bWritten )
863                     WriteGraphEntry( pE );
864             }
865             aGraphList.clear();
866             if ( bTabAlignedLeft )
867             {
868                 // clear <TABLE ALIGN=LEFT> with <BR CLEAR=LEFT>
869                 aByteStrOut.append(OOO_STRING_SVTOOLS_HTML_linebreak);
870                 aByteStrOut.append(' ').
871                     append(OOO_STRING_SVTOOLS_HTML_O_clear).append('=').
872                     append(OOO_STRING_SVTOOLS_HTML_AL_left);
873                 TAG_ON_LF( aByteStrOut.makeStringAndClear().getStr() );
874             }
875         }
876 
877         if ( bAll )
878             OUT_COMMENT( "**************************************************************************" );
879     }
880 }
881 
WriteCell(sc::ColumnBlockPosition & rBlockPos,SCCOL nCol,SCROW nRow,SCTAB nTab)882 void ScHTMLExport::WriteCell( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow, SCTAB nTab )
883 {
884     ScAddress aPos( nCol, nRow, nTab );
885     ScRefCellValue aCell(*pDoc, aPos, rBlockPos);
886     const ScPatternAttr* pAttr = pDoc->GetPattern( nCol, nRow, nTab );
887     const SfxItemSet* pCondItemSet = pDoc->GetCondResult( nCol, nRow, nTab, &aCell );
888 
889     const ScMergeFlagAttr& rMergeFlagAttr = pAttr->GetItem( ATTR_MERGE_FLAG, pCondItemSet );
890     if ( rMergeFlagAttr.IsOverlapped() )
891         return ;
892 
893     ScHTMLGraphEntry* pGraphEntry = nullptr;
894     if ( bTabHasGraphics && !mbSkipImages )
895     {
896         size_t ListSize = aGraphList.size();
897         for ( size_t i = 0; i < ListSize; ++i )
898         {
899             ScHTMLGraphEntry* pE = &aGraphList[ i ];
900             if ( pE->bInCell && pE->aRange.In( aPos ) )
901             {
902                 if ( pE->aRange.aStart == aPos )
903                 {
904                     pGraphEntry = pE;
905                     break;  // for
906                 }
907                 else
908                     return ; // Is a Col/RowSpan, Overlapped
909             }
910         }
911     }
912 
913     sal_uInt32 nFormat = pAttr->GetNumberFormat( pFormatter );
914     bool bValueData = aCell.hasNumeric();
915     SvtScriptType nScriptType = SvtScriptType::NONE;
916     if (!aCell.isEmpty())
917         nScriptType = pDoc->GetScriptType(nCol, nRow, nTab, &aCell);
918 
919     if ( nScriptType == SvtScriptType::NONE )
920         nScriptType = aHTMLStyle.nDefaultScriptType;
921 
922     OStringBuffer aStrTD(OOO_STRING_SVTOOLS_HTML_tabledata);
923 
924     // border of the cells
925     const SvxBoxItem* pBorder = pDoc->GetAttr( nCol, nRow, nTab, ATTR_BORDER );
926     if ( pBorder && (pBorder->GetTop() || pBorder->GetBottom() || pBorder->GetLeft() || pBorder->GetRight()) )
927     {
928         aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_style "=\"");
929 
930         bool bInsertSemicolon = false;
931         aStrTD.append(BorderToStyle("top", pBorder->GetTop(),
932             bInsertSemicolon));
933         aStrTD.append(BorderToStyle("bottom", pBorder->GetBottom(),
934             bInsertSemicolon));
935         aStrTD.append(BorderToStyle("left", pBorder->GetLeft(),
936             bInsertSemicolon));
937         aStrTD.append(BorderToStyle("right", pBorder->GetRight(),
938             bInsertSemicolon));
939 
940         aStrTD.append('"');
941     }
942 
943     const char* pChar;
944     sal_uInt16 nHeightPixel;
945 
946     const ScMergeAttr& rMergeAttr = pAttr->GetItem( ATTR_MERGE, pCondItemSet );
947     if ( pGraphEntry || rMergeAttr.IsMerged() )
948     {
949         SCCOL nC, jC;
950         SCROW nR;
951         sal_uLong v;
952         if ( pGraphEntry )
953             nC = std::max( SCCOL(pGraphEntry->aRange.aEnd.Col() - nCol + 1),
954                            rMergeAttr.GetColMerge() );
955         else
956             nC = rMergeAttr.GetColMerge();
957         if ( nC > 1 )
958         {
959             aStrTD.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_colspan).
960                 append('=').append(static_cast<sal_Int32>(nC));
961             nC = nC + nCol;
962             for ( jC=nCol, v=0; jC<nC; jC++ )
963                 v += pDoc->GetColWidth( jC, nTab );
964         }
965 
966         if ( pGraphEntry )
967             nR = std::max( SCROW(pGraphEntry->aRange.aEnd.Row() - nRow + 1),
968                            rMergeAttr.GetRowMerge() );
969         else
970             nR = rMergeAttr.GetRowMerge();
971         if ( nR > 1 )
972         {
973             aStrTD.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_rowspan).
974                 append('=').append(static_cast<sal_Int32>(nR));
975             nR += nRow;
976             v = pDoc->GetRowHeight( nRow, nR-1, nTab );
977             nHeightPixel = ToPixel( static_cast< sal_uInt16 >( v ) );
978         }
979         else
980             nHeightPixel = ToPixel( pDoc->GetRowHeight( nRow, nTab ) );
981     }
982     else
983         nHeightPixel = ToPixel( pDoc->GetRowHeight( nRow, nTab ) );
984 
985     if ( bTableDataHeight )
986     {
987         aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_height "=\"" +
988                 OString::number(nHeightPixel) + "\"");
989     }
990 
991     const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>( pAttr->GetItem(
992             ScGlobal::GetScriptedWhichID( nScriptType, ATTR_FONT),
993             pCondItemSet) );
994 
995     const SvxFontHeightItem& rFontHeightItem = static_cast<const SvxFontHeightItem&>(
996         pAttr->GetItem( ScGlobal::GetScriptedWhichID( nScriptType,
997                     ATTR_FONT_HEIGHT), pCondItemSet) );
998 
999     const SvxWeightItem& rWeightItem = static_cast<const SvxWeightItem&>( pAttr->GetItem(
1000             ScGlobal::GetScriptedWhichID( nScriptType, ATTR_FONT_WEIGHT),
1001             pCondItemSet) );
1002 
1003     const SvxPostureItem& rPostureItem = static_cast<const SvxPostureItem&>(
1004         pAttr->GetItem( ScGlobal::GetScriptedWhichID( nScriptType,
1005                     ATTR_FONT_POSTURE), pCondItemSet) );
1006 
1007     const SvxUnderlineItem& rUnderlineItem =
1008         pAttr->GetItem( ATTR_FONT_UNDERLINE, pCondItemSet );
1009 
1010     const SvxCrossedOutItem& rCrossedOutItem =
1011         pAttr->GetItem( ATTR_FONT_CROSSEDOUT, pCondItemSet );
1012 
1013     const SvxColorItem& rColorItem = pAttr->GetItem(
1014             ATTR_FONT_COLOR, pCondItemSet );
1015 
1016     const SvxHorJustifyItem& rHorJustifyItem =
1017         pAttr->GetItem( ATTR_HOR_JUSTIFY, pCondItemSet );
1018 
1019     const SvxVerJustifyItem& rVerJustifyItem =
1020         pAttr->GetItem( ATTR_VER_JUSTIFY, pCondItemSet );
1021 
1022     const SvxBrushItem& rBrushItem = pAttr->GetItem(
1023             ATTR_BACKGROUND, pCondItemSet );
1024 
1025     Color aBgColor;
1026     if ( rBrushItem.GetColor().GetAlpha() == 0 )
1027         aBgColor = aHTMLStyle.aBackgroundColor; // No unwanted background color
1028     else
1029         aBgColor = rBrushItem.GetColor();
1030 
1031     bool bBold          = ( WEIGHT_BOLD      <= rWeightItem.GetWeight() );
1032     bool bItalic        = ( ITALIC_NONE      != rPostureItem.GetPosture() );
1033     bool bUnderline     = ( LINESTYLE_NONE   != rUnderlineItem.GetLineStyle() );
1034     bool bCrossedOut    = ( STRIKEOUT_SINGLE <= rCrossedOutItem.GetStrikeout() );
1035     bool bSetFontColor  = ( COL_AUTO         != rColorItem.GetValue() );  // default is AUTO now
1036     bool bSetFontName   = ( aHTMLStyle.aFontFamilyName  != rFontItem.GetFamilyName() );
1037     sal_uInt16 nSetFontSizeNumber = 0;
1038     sal_uInt32 nFontHeight = rFontHeightItem.GetHeight();
1039     if ( nFontHeight != aHTMLStyle.nFontHeight )
1040     {
1041         nSetFontSizeNumber = GetFontSizeNumber( static_cast<sal_uInt16>(nFontHeight) );
1042         if ( nSetFontSizeNumber == aHTMLStyle.nFontSizeNumber )
1043             nSetFontSizeNumber = 0;   // no difference, don't set
1044     }
1045 
1046     bool bSetFont = (bSetFontColor || bSetFontName || nSetFontSizeNumber);
1047 
1048     //! TODO: we could entirely use CSS1 here instead, but that would exclude
1049     //! Netscape 3.0 and Netscape 4.x without JavaScript enabled.
1050     //! Do we want that?
1051 
1052     switch( rHorJustifyItem.GetValue() )
1053     {
1054         case SvxCellHorJustify::Standard:
1055             pChar = (bValueData ? OOO_STRING_SVTOOLS_HTML_AL_right : OOO_STRING_SVTOOLS_HTML_AL_left);
1056             break;
1057         case SvxCellHorJustify::Center:    pChar = OOO_STRING_SVTOOLS_HTML_AL_center;  break;
1058         case SvxCellHorJustify::Block:     pChar = OOO_STRING_SVTOOLS_HTML_AL_justify; break;
1059         case SvxCellHorJustify::Right:     pChar = OOO_STRING_SVTOOLS_HTML_AL_right;   break;
1060         case SvxCellHorJustify::Left:
1061         case SvxCellHorJustify::Repeat:
1062         default:                        pChar = OOO_STRING_SVTOOLS_HTML_AL_left;    break;
1063     }
1064 
1065     aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"" +
1066         OString::Concat(pChar) + "\"");
1067 
1068     switch( rVerJustifyItem.GetValue() )
1069     {
1070         case SvxCellVerJustify::Top:       pChar = OOO_STRING_SVTOOLS_HTML_VA_top;     break;
1071         case SvxCellVerJustify::Center:    pChar = OOO_STRING_SVTOOLS_HTML_VA_middle;  break;
1072         case SvxCellVerJustify::Bottom:    pChar = OOO_STRING_SVTOOLS_HTML_VA_bottom;  break;
1073         case SvxCellVerJustify::Standard:
1074         default:                        pChar = nullptr;
1075     }
1076     if ( pChar )
1077     {
1078         aStrTD.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_valign).
1079             append('=').append(pChar);
1080     }
1081 
1082     if ( aHTMLStyle.aBackgroundColor != aBgColor )
1083     {
1084         aStrTD.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_bgcolor).
1085             append('=');
1086         aStrTD.append(lcl_makeHTMLColorTriplet(aBgColor));
1087     }
1088 
1089     double fVal = 0.0;
1090     if ( bValueData )
1091     {
1092         switch (aCell.meType)
1093         {
1094             case CELLTYPE_VALUE:
1095                 fVal = aCell.mfValue;
1096                 if ( bCalcAsShown && fVal != 0.0 )
1097                     fVal = pDoc->RoundValueAsShown( fVal, nFormat );
1098                 break;
1099             case CELLTYPE_FORMULA:
1100                 fVal = aCell.mpFormula->GetValue();
1101                 break;
1102             default:
1103                 OSL_FAIL( "value data with unsupported cell type" );
1104         }
1105     }
1106 
1107     aStrTD.append(HTMLOutFuncs::CreateTableDataOptionsValNum(bValueData, fVal,
1108         nFormat, *pFormatter, eDestEnc, &aNonConvertibleChars));
1109 
1110     TAG_ON(aStrTD.makeStringAndClear().getStr());
1111 
1112     //write the note for this as the first thing in the tag
1113     ScPostIt* pNote = pDoc->HasNote(aPos) ? pDoc->GetNote(aPos) : nullptr;
1114     if (pNote)
1115     {
1116         //create the comment indicator
1117         OString aStr = OOO_STRING_SVTOOLS_HTML_anchor " "
1118             OOO_STRING_SVTOOLS_HTML_O_class "=\"comment-indicator\"";
1119         TAG_ON(aStr.getStr());
1120         TAG_OFF(OOO_STRING_SVTOOLS_HTML_anchor);
1121         OUT_LF();
1122 
1123         //create the element holding the contents
1124         //this is a bit naive, since it doesn't separate
1125         //lines into html breaklines yet
1126         TAG_ON(OOO_STRING_SVTOOLS_HTML_comment2);
1127         OUT_STR( pNote->GetText() );
1128         TAG_OFF(OOO_STRING_SVTOOLS_HTML_comment2);
1129         OUT_LF();
1130     }
1131 
1132     if ( bBold )        TAG_ON( OOO_STRING_SVTOOLS_HTML_bold );
1133     if ( bItalic )      TAG_ON( OOO_STRING_SVTOOLS_HTML_italic );
1134     if ( bUnderline )   TAG_ON( OOO_STRING_SVTOOLS_HTML_underline );
1135     if ( bCrossedOut )  TAG_ON( OOO_STRING_SVTOOLS_HTML_strikethrough );
1136 
1137     if ( bSetFont )
1138     {
1139         OStringBuffer aStr(OOO_STRING_SVTOOLS_HTML_font);
1140         if ( bSetFontName )
1141         {
1142             aStr.append(" " OOO_STRING_SVTOOLS_HTML_O_face "=\"");
1143 
1144             if (!rFontItem.GetFamilyName().isEmpty())
1145             {
1146                 const OUString& rList = rFontItem.GetFamilyName();
1147                 for (sal_Int32 nPos {0};;)
1148                 {
1149                     OString aTmpStr = HTMLOutFuncs::ConvertStringToHTML(
1150                         rList.getToken( 0, ';', nPos ), eDestEnc,
1151                         &aNonConvertibleChars);
1152                     aStr.append(aTmpStr);
1153                     if (nPos<0)
1154                         break;
1155                     aStr.append(',');
1156                 }
1157             }
1158 
1159             aStr.append('\"');
1160         }
1161         if ( nSetFontSizeNumber )
1162         {
1163             aStr.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_size).
1164                 append('=').append(static_cast<sal_Int32>(nSetFontSizeNumber));
1165         }
1166         if ( bSetFontColor )
1167         {
1168             Color   aColor = rColorItem.GetValue();
1169 
1170             //  always export automatic text color as black
1171             if ( aColor == COL_AUTO )
1172                 aColor = COL_BLACK;
1173 
1174             aStr.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_color).
1175                 append('=').append(lcl_makeHTMLColorTriplet(aColor));
1176         }
1177         TAG_ON(aStr.makeStringAndClear().getStr());
1178     }
1179 
1180     OUString aURL;
1181     bool bWriteHyperLink(false);
1182     if (aCell.meType == CELLTYPE_FORMULA)
1183     {
1184         ScFormulaCell* pFCell = aCell.mpFormula;
1185         if (pFCell->IsHyperLinkCell())
1186         {
1187             OUString aCellText;
1188             pFCell->GetURLResult(aURL, aCellText);
1189             bWriteHyperLink = true;
1190         }
1191     }
1192 
1193     if (bWriteHyperLink)
1194     {
1195         OString aURLStr = HTMLOutFuncs::ConvertStringToHTML(aURL, eDestEnc, &aNonConvertibleChars);
1196         OString aStr = OOO_STRING_SVTOOLS_HTML_anchor " " OOO_STRING_SVTOOLS_HTML_O_href "=\"" + aURLStr + "\"";
1197         TAG_ON(aStr.getStr());
1198     }
1199 
1200     OUString aStrOut;
1201     bool bFieldText = false;
1202 
1203     const Color* pColor;
1204     switch (aCell.meType)
1205     {
1206         case CELLTYPE_EDIT :
1207             bFieldText = WriteFieldText(aCell.mpEditText);
1208             if ( bFieldText )
1209                 break;
1210             [[fallthrough]];
1211         default:
1212             ScCellFormat::GetString(aCell, nFormat, aStrOut, &pColor, *pFormatter, *pDoc);
1213     }
1214 
1215     if ( !bFieldText )
1216     {
1217         if ( aStrOut.isEmpty() )
1218         {
1219             TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak ); // No completely empty line
1220         }
1221         else
1222         {
1223             sal_Int32 nPos = aStrOut.indexOf( '\n' );
1224             if ( nPos == -1 )
1225             {
1226                 OUT_STR( aStrOut );
1227             }
1228             else
1229             {
1230                 sal_Int32 nStartPos = 0;
1231                 do
1232                 {
1233                     OUString aSingleLine = aStrOut.copy( nStartPos, nPos - nStartPos );
1234                     OUT_STR( aSingleLine );
1235                     TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
1236                     nStartPos = nPos + 1;
1237                 }
1238                 while( ( nPos = aStrOut.indexOf( '\n', nStartPos ) ) != -1 );
1239                 OUString aSingleLine = aStrOut.copy( nStartPos );
1240                 OUT_STR( aSingleLine );
1241             }
1242         }
1243     }
1244     if ( pGraphEntry )
1245         WriteGraphEntry( pGraphEntry );
1246 
1247     if (bWriteHyperLink) { TAG_OFF(OOO_STRING_SVTOOLS_HTML_anchor); }
1248 
1249     if ( bSetFont )     TAG_OFF( OOO_STRING_SVTOOLS_HTML_font );
1250     if ( bCrossedOut )  TAG_OFF( OOO_STRING_SVTOOLS_HTML_strikethrough );
1251     if ( bUnderline )   TAG_OFF( OOO_STRING_SVTOOLS_HTML_underline );
1252     if ( bItalic )      TAG_OFF( OOO_STRING_SVTOOLS_HTML_italic );
1253     if ( bBold )        TAG_OFF( OOO_STRING_SVTOOLS_HTML_bold );
1254 
1255     TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tabledata );
1256 }
1257 
WriteFieldText(const EditTextObject * pData)1258 bool ScHTMLExport::WriteFieldText( const EditTextObject* pData )
1259 {
1260     bool bFields = false;
1261     // text and anchor of URL fields, Doc-Engine is a ScFieldEditEngine
1262     EditEngine& rEngine = pDoc->GetEditEngine();
1263     rEngine.SetText( *pData );
1264     sal_Int32 nParas = rEngine.GetParagraphCount();
1265     if ( nParas )
1266     {
1267         ESelection aSel( 0, 0, nParas-1, rEngine.GetTextLen( nParas-1 ) );
1268         SfxItemSet aSet( rEngine.GetAttribs( aSel ) );
1269         SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
1270         if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
1271             bFields = true;
1272     }
1273     if ( bFields )
1274     {
1275         bool bOldUpdateMode = rEngine.GetUpdateMode();
1276         rEngine.SetUpdateMode( true );      // no portions if not formatted
1277         for ( sal_Int32 nPar=0; nPar < nParas; nPar++ )
1278         {
1279             if ( nPar > 0 )
1280                 TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
1281             std::vector<sal_Int32> aPortions;
1282             rEngine.GetPortions( nPar, aPortions );
1283             sal_Int32 nStart = 0;
1284             for ( const sal_Int32 nEnd : aPortions )
1285             {
1286                 ESelection aSel( nPar, nStart, nPar, nEnd );
1287                 bool bUrl = false;
1288                 // fields are single characters
1289                 if ( nEnd == nStart+1 )
1290                 {
1291                     const SfxPoolItem* pItem;
1292                     SfxItemSet aSet = rEngine.GetAttribs( aSel );
1293                     if ( aSet.GetItemState( EE_FEATURE_FIELD, false, &pItem ) == SfxItemState::SET )
1294                     {
1295                         const SvxFieldData* pField = static_cast<const SvxFieldItem*>(pItem)->GetField();
1296                         if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
1297                         {
1298                             bUrl = true;
1299                             rStrm.WriteChar( '<' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_anchor ).WriteChar( ' ' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_O_href ).WriteCharPtr( "=\"" );
1300                             OUT_STR( pURLField->GetURL() );
1301                             rStrm.WriteCharPtr( "\">" );
1302                             OUT_STR( pURLField->GetRepresentation() );
1303                             rStrm.WriteCharPtr( "</" ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_anchor ).WriteChar( '>' );
1304                         }
1305                     }
1306                 }
1307                 if ( !bUrl )
1308                     OUT_STR( rEngine.GetText( aSel ) );
1309                 nStart = nEnd;
1310             }
1311         }
1312         rEngine.SetUpdateMode( bOldUpdateMode );
1313     }
1314     return bFields;
1315 }
1316 
CopyLocalFileToINet(OUString & rFileNm,const OUString & rTargetNm)1317 void ScHTMLExport::CopyLocalFileToINet( OUString& rFileNm,
1318         const OUString& rTargetNm )
1319 {
1320     INetURLObject aFileUrl, aTargetUrl;
1321     aFileUrl.SetSmartURL( rFileNm );
1322     aTargetUrl.SetSmartURL( rTargetNm );
1323     if( !(INetProtocol::File == aFileUrl.GetProtocol() &&
1324         ( INetProtocol::File != aTargetUrl.GetProtocol() &&
1325           INetProtocol::Ftp <= aTargetUrl.GetProtocol() &&
1326           INetProtocol::Javascript >= aTargetUrl.GetProtocol()))  )
1327         return;
1328 
1329     if( pFileNameMap )
1330     {
1331         // Did we already move the file?
1332         std::map<OUString, OUString>::iterator it = pFileNameMap->find( rFileNm );
1333         if( it != pFileNameMap->end() )
1334         {
1335             rFileNm = it->second;
1336             return;
1337         }
1338     }
1339     else
1340     {
1341         pFileNameMap.reset( new std::map<OUString, OUString> );
1342     }
1343 
1344     bool bRet = false;
1345     SvFileStream aTmp( aFileUrl.PathToFileName(), StreamMode::READ );
1346 
1347     OUString aSrc = rFileNm;
1348     OUString aDest = aTargetUrl.GetPartBeforeLastName() + aFileUrl.GetLastName();
1349 
1350     SfxMedium aMedium( aDest, StreamMode::WRITE | StreamMode::SHARE_DENYNONE );
1351 
1352     {
1353         SvFileStream aCpy( aMedium.GetPhysicalName(), StreamMode::WRITE );
1354         aCpy.WriteStream( aTmp );
1355     }
1356 
1357     // Take over
1358     aMedium.Close();
1359     aMedium.Commit();
1360 
1361     bRet = ERRCODE_NONE == aMedium.GetError();
1362 
1363     if( bRet )
1364     {
1365         pFileNameMap->insert( std::make_pair( aSrc, aDest ) );
1366         rFileNm = aDest;
1367     }
1368 }
1369 
IncIndent(short nVal)1370 void ScHTMLExport::IncIndent( short nVal )
1371 {
1372     sIndent[nIndent] = '\t';
1373     nIndent = nIndent + nVal;
1374     if ( nIndent < 0 )
1375         nIndent = 0;
1376     else if ( nIndent > nIndentMax )
1377         nIndent = nIndentMax;
1378     sIndent[nIndent] = 0;
1379 }
1380 
1381 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1382