1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
21 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
22 #include <com/sun/star/frame/XModel.hpp>
24 #include <sal/log.hxx>
25 #include <rtl/tencinfo.h>
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/propertyvalue.hxx>
28 #include <comphelper/xmlencode.hxx>
29 #include <o3tl/safeint.hxx>
30 #include <osl/file.hxx>
31 #include <unotools/ucbstreamhelper.hxx>
32 #include <sfx2/frmhtmlw.hxx>
33 #include <sfx2/progress.hxx>
35 #include <svx/svditer.hxx>
36 #include <editeng/eeitem.hxx>
37 #include <editeng/outlobj.hxx>
38 #include <svtools/htmlout.hxx>
39 #include <editeng/editeng.hxx>
40 #include <editeng/wghtitem.hxx>
41 #include <editeng/udlnitem.hxx>
42 #include <editeng/postitem.hxx>
43 #include <editeng/crossedoutitem.hxx>
44 #include <editeng/flditem.hxx>
45 #include <editeng/frmdiritem.hxx>
46 #include <svx/svdoutl.hxx>
47 #include <svx/svdogrp.hxx>
48 #include <svx/svdotable.hxx>
49 #include <tools/urlobj.hxx>
50 #include <svtools/sfxecode.hxx>
51 #include <tools/debug.hxx>
53 #include <drawdoc.hxx>
54 #include <DrawDocShell.hxx>
55 #include <Outliner.hxx>
57 #include <strings.hrc>
58 #include <strings.hxx>
59 #include <sdresid.hxx>
61 using namespace ::com::sun::star
;
62 using namespace ::com::sun::star::uno
;
63 using namespace ::com::sun::star::document
;
65 using namespace sdr::table
;
69 // Helper class for the simple creation of files local/remote
73 std::unique_ptr
<SvStream
> pOStm
;
81 ErrCode
createStream( const OUString
& rUrl
, SvStream
*& rpStr
);
82 void createFileName( const OUString
& rUrl
, OUString
& rFileName
);
86 // create area for a circle; we expect pixel coordinates
87 OUString
ColorToHTMLString( Color aColor
)
89 static const char hex
[] = "0123456789ABCDEF";
90 OUStringBuffer
aStr( "#xxxxxx" );
91 aStr
[1] = hex
[(aColor
.GetRed() >> 4) & 0xf];
92 aStr
[2] = hex
[aColor
.GetRed() & 0xf];
93 aStr
[3] = hex
[(aColor
.GetGreen() >> 4) & 0xf];
94 aStr
[4] = hex
[aColor
.GetGreen() & 0xf];
95 aStr
[5] = hex
[(aColor
.GetBlue() >> 4) & 0xf];
96 aStr
[6] = hex
[aColor
.GetBlue() & 0xf];
98 return aStr
.makeStringAndClear();
103 // Helper class for the embedding of text attributes into the html output
119 explicit HtmlState( Color aDefColor
);
121 OUString
SetWeight( bool bWeight
);
122 OUString
SetItalic( bool bItalic
);
123 OUString
SetUnderline( bool bUnderline
);
124 OUString
SetColor( Color aColor
);
125 OUString
SetStrikeout( bool bStrike
);
126 OUString
SetLink( const OUString
& aLink
, const OUString
& aTarget
);
130 // close all still open tags
131 OUString
HtmlState::Flush()
133 OUString aStr
= SetWeight(false)
135 + SetUnderline(false)
136 + SetStrikeout(false)
137 + SetColor(maDefColor
)
138 + SetLink(u
""_ustr
,u
""_ustr
);
143 // c'tor with default color for the page
144 HtmlState::HtmlState( Color aDefColor
)
151 maDefColor(aDefColor
)
155 // enables/disables bold print
156 OUString
HtmlState::SetWeight( bool bWeight
)
160 if(bWeight
&& !mbWeight
)
162 else if(!bWeight
&& mbWeight
)
169 // enables/disables italic
171 OUString
HtmlState::SetItalic( bool bItalic
)
175 if(bItalic
&& !mbItalic
)
177 else if(!bItalic
&& mbItalic
)
184 // enables/disables underlines
186 OUString
HtmlState::SetUnderline( bool bUnderline
)
190 if(bUnderline
&& !mbUnderline
)
192 else if(!bUnderline
&& mbUnderline
)
195 mbUnderline
= bUnderline
;
199 // enables/disables strike through
200 OUString
HtmlState::SetStrikeout( bool bStrike
)
204 if(bStrike
&& !mbStrike
)
206 else if(!bStrike
&& mbStrike
)
213 // Sets the specified text color
214 OUString
HtmlState::SetColor( Color aColor
)
218 if(mbColor
&& aColor
== maColor
)
227 if(aColor
!= maDefColor
)
230 aStr
+= "<font color=\"" + ColorToHTMLString(aColor
) + "\">";
237 // enables/disables a hyperlink
238 OUString
HtmlState::SetLink( const OUString
& aLink
, const OUString
& aTarget
)
242 if(mbLink
&&maLink
== aLink
&&maTarget
==aTarget
)
251 if (!aLink
.isEmpty())
253 aStr
+= "<a href=\"" + comphelper::string::encodeForXml(aLink
);
254 if (!aTarget
.isEmpty())
256 aStr
+= "\" target=\"" + comphelper::string::encodeForXml(aTarget
);
270 OUString
getParagraphStyle( const SdrOutliner
* pOutliner
, sal_Int32 nPara
)
272 SfxItemSet
aParaSet( pOutliner
->GetParaAttribs( nPara
) );
276 if( aParaSet
.GetItem
<SvxFrameDirectionItem
>( EE_PARA_WRITINGDIR
)->GetValue() == SvxFrameDirection::Horizontal_RL_TB
)
279 sStyle
= "direction: rtl;";
283 // This is the default so don't write it out
284 // sStyle += "direction: ltr;";
289 void lclAppendStyle(OUStringBuffer
& aBuffer
, std::u16string_view aTag
, std::u16string_view aStyle
)
292 aBuffer
.append(OUString::Concat("<") + aTag
+ ">");
294 aBuffer
.append(OUString::Concat("<") + aTag
+ " style=\"" + aStyle
+ "\">");
297 // Depending on the attributes of the specified set and the specified
298 // HtmlState, it creates the needed html tags in order to get the
300 OUString
TextAttribToHTMLString( SfxItemSet
const * pSet
, HtmlState
* pState
)
307 OUString aLink
, aTarget
;
308 if ( pSet
->GetItemState( EE_FEATURE_FIELD
) == SfxItemState::SET
)
310 const SvxFieldItem
* pItem
= pSet
->GetItem
<SvxFieldItem
>( EE_FEATURE_FIELD
);
313 const SvxURLField
* pURL
= dynamic_cast<const SvxURLField
*>( pItem
->GetField() );
316 aLink
= pURL
->GetURL();
317 aTarget
= pURL
->GetTargetFrame();
325 if ( pSet
->GetItemState( EE_CHAR_WEIGHT
) == SfxItemState::SET
)
327 bTemp
= pSet
->Get( EE_CHAR_WEIGHT
).GetWeight() == WEIGHT_BOLD
;
328 aTemp
= pState
->SetWeight( bTemp
);
330 aStr
.insert(0, aTemp
);
335 if ( pSet
->GetItemState( EE_CHAR_UNDERLINE
) == SfxItemState::SET
)
337 bTemp
= pSet
->Get( EE_CHAR_UNDERLINE
).GetLineStyle() != LINESTYLE_NONE
;
338 aTemp
= pState
->SetUnderline( bTemp
);
340 aStr
.insert(0, aTemp
);
345 if ( pSet
->GetItemState( EE_CHAR_STRIKEOUT
) == SfxItemState::SET
)
347 bTemp
= pSet
->Get( EE_CHAR_STRIKEOUT
).GetStrikeout() != STRIKEOUT_NONE
;
348 aTemp
= pState
->SetStrikeout( bTemp
);
350 aStr
.insert(0, aTemp
);
355 if ( pSet
->GetItemState( EE_CHAR_ITALIC
) == SfxItemState::SET
)
357 bTemp
= pSet
->Get( EE_CHAR_ITALIC
).GetPosture() != ITALIC_NONE
;
358 aTemp
= pState
->SetItalic( bTemp
);
360 aStr
.insert(0, aTemp
);
365 if (!aLink
.isEmpty())
366 aStr
.insert(0, pState
->SetLink(aLink
, aTarget
));
368 aStr
.append(pState
->SetLink(aLink
, aTarget
));
370 return aStr
.makeStringAndClear();
373 // escapes a string for html
374 OUString
StringToHTMLString( std::u16string_view rString
)
376 SvMemoryStream aMemStm
;
377 HTMLOutFuncs::Out_String( aMemStm
, rString
);
378 return OUString( static_cast<char const *>(aMemStm
.GetData()), aMemStm
.GetSize(), RTL_TEXTENCODING_UTF8
);
381 // converts a paragraph of the outliner to html
382 OUString
ParagraphToHTMLString( SdrOutliner
const * pOutliner
, sal_Int32 nPara
)
386 if(nullptr == pOutliner
)
390 EditEngine
& rEditEngine
= *const_cast<EditEngine
*>(&pOutliner
->GetEditEngine());
391 bool bOldUpdateMode
= rEditEngine
.SetUpdateLayout(true);
393 Paragraph
* pPara
= pOutliner
->GetParagraph(nPara
);
397 HtmlState
aState( COL_BLACK
);
398 std::vector
<sal_Int32
> aPortionList
;
399 rEditEngine
.GetPortions( nPara
, aPortionList
);
402 for( sal_Int32 nPos2
: aPortionList
)
404 ESelection
aSelection( nPara
, nPos1
, nPara
, nPos2
);
406 SfxItemSet
aSet( rEditEngine
.GetAttribs( aSelection
) );
408 aStr
.append(TextAttribToHTMLString( &aSet
, &aState
) +
409 StringToHTMLString(rEditEngine
.GetText( aSelection
)));
413 aStr
.append(aState
.Flush());
414 rEditEngine
.SetUpdateLayout(bOldUpdateMode
);
416 return aStr
.makeStringAndClear();
419 void WriteOutlinerParagraph(OUStringBuffer
& aStr
, SdrOutliner
* pOutliner
,
420 OutlinerParaObject
const * pOutlinerParagraphObject
,
423 if (pOutlinerParagraphObject
== nullptr)
426 pOutliner
->SetText(*pOutlinerParagraphObject
);
428 sal_Int32 nCount
= pOutliner
->GetParagraphCount();
431 sal_Int16 nCurrentDepth
= -1;
433 for (sal_Int32 nIndex
= 0; nIndex
< nCount
; nIndex
++)
435 Paragraph
* pParagraph
= pOutliner
->GetParagraph(nIndex
);
436 if(pParagraph
== nullptr)
439 const sal_Int16 nDepth
= static_cast<sal_uInt16
>(pOutliner
->GetDepth(nIndex
));
440 OUString aParaText
= ParagraphToHTMLString(pOutliner
, nIndex
);
442 if (aParaText
.isEmpty())
447 OUString aTag
= bHeadLine
? u
"h2"_ustr
: u
"p"_ustr
;
448 lclAppendStyle(aStr
, aTag
, getParagraphStyle(pOutliner
, nIndex
));
450 aStr
.append(aParaText
);
451 aStr
.append("</" + aTag
+ ">\r\n");
455 while(nCurrentDepth
< nDepth
)
457 aStr
.append("<ul>\r\n");
460 while(nCurrentDepth
> nDepth
)
462 aStr
.append("</ul>\r\n");
465 lclAppendStyle(aStr
, u
"li", getParagraphStyle(pOutliner
, nIndex
));
466 aStr
.append(aParaText
);
467 aStr
.append("</li>\r\n");
470 while(nCurrentDepth
>= 0)
472 aStr
.append("</ul>\r\n");
478 void WriteTable(OUStringBuffer
& aStr
, SdrTableObj
const * pTableObject
, SdrOutliner
* pOutliner
)
480 CellPos aStart
, aEnd
;
482 aStart
= SdrTableObj::getFirstCell();
483 aEnd
= pTableObject
->getLastCell();
485 sal_Int32 nColCount
= pTableObject
->getColumnCount();
486 aStr
.append("<table>\r\n");
487 for (sal_Int32 nRow
= aStart
.mnRow
; nRow
<= aEnd
.mnRow
; nRow
++)
489 aStr
.append(" <tr>\r\n");
490 for (sal_Int32 nCol
= aStart
.mnCol
; nCol
<= aEnd
.mnCol
; nCol
++)
492 aStr
.append(" <td>\r\n");
493 sal_Int32 nCellIndex
= nRow
* nColCount
+ nCol
;
494 SdrText
* pText
= pTableObject
->getText(nCellIndex
);
496 if (pText
== nullptr)
498 WriteOutlinerParagraph(aStr
, pOutliner
, pText
->GetOutlinerParaObject(), false);
499 aStr
.append(" </td>\r\n");
501 aStr
.append(" </tr>\r\n");
503 aStr
.append("</table>\r\n");
506 void WriteObjectGroup(OUStringBuffer
& aStr
, SdrObjGroup
const * pObjectGroup
, SdrOutliner
* pOutliner
,
509 SdrObjListIter
aGroupIterator(pObjectGroup
->GetSubList(), SdrIterMode::DeepNoGroups
);
510 while (aGroupIterator
.IsMore())
512 SdrObject
* pCurrentObject
= aGroupIterator
.Next();
513 if (pCurrentObject
->GetObjIdentifier() == SdrObjKind::Group
)
515 SdrObjGroup
* pCurrentGroupObject
= static_cast<SdrObjGroup
*>(pCurrentObject
);
516 WriteObjectGroup(aStr
, pCurrentGroupObject
, pOutliner
, bHeadLine
);
520 OutlinerParaObject
* pOutlinerParagraphObject
= pCurrentObject
->GetOutlinerParaObject();
521 if (pOutlinerParagraphObject
!= nullptr)
523 WriteOutlinerParagraph(aStr
, pOutliner
, pOutlinerParagraphObject
, bHeadLine
);
529 // get SdrTextObject with layout text of this page
530 SdrTextObj
* GetLayoutTextObject(SdrPage
const * pPage
)
532 SdrTextObj
* pResult
= nullptr;
534 for (const rtl::Reference
<SdrObject
>& pObject
: *pPage
)
536 if (pObject
->GetObjInventor() == SdrInventor::Default
&&
537 pObject
->GetObjIdentifier() == SdrObjKind::OutlineText
)
539 pResult
= static_cast<SdrTextObj
*>(pObject
.get());
547 /** creates an outliner text for the title objects of a page
549 OUString
CreateTextForTitle( SdrOutliner
* pOutliner
, SdPage
* pPage
)
551 SdrTextObj
* pTO
= static_cast<SdrTextObj
*>(pPage
->GetPresObj(PresObjKind::Title
));
553 pTO
= GetLayoutTextObject(pPage
);
555 if (pTO
&& !pTO
->IsEmptyPresObj())
557 OutlinerParaObject
* pOPO
= pTO
->GetOutlinerParaObject();
558 if(pOPO
&& pOutliner
->GetParagraphCount() != 0)
561 pOutliner
->SetText(*pOPO
);
562 return ParagraphToHTMLString(pOutliner
, 0);
569 // creates an outliner text for a page
570 OUString
CreateTextForPage(SdrOutliner
* pOutliner
, SdPage
const * pPage
,
575 for (const rtl::Reference
<SdrObject
>& pObject
: *pPage
)
577 PresObjKind eKind
= pPage
->GetPresObjKind(pObject
.get());
581 case PresObjKind::NONE
:
583 if (pObject
->GetObjIdentifier() == SdrObjKind::Group
)
585 SdrObjGroup
* pObjectGroup
= static_cast<SdrObjGroup
*>(pObject
.get());
586 WriteObjectGroup(aStr
, pObjectGroup
, pOutliner
, false);
588 else if (pObject
->GetObjIdentifier() == SdrObjKind::Table
)
590 SdrTableObj
* pTableObject
= static_cast<SdrTableObj
*>(pObject
.get());
591 WriteTable(aStr
, pTableObject
, pOutliner
);
595 if (pObject
->GetOutlinerParaObject())
597 WriteOutlinerParagraph(aStr
, pOutliner
, pObject
->GetOutlinerParaObject(), false);
603 case PresObjKind::Table
:
605 SdrTableObj
* pTableObject
= static_cast<SdrTableObj
*>(pObject
.get());
606 WriteTable(aStr
, pTableObject
, pOutliner
);
610 case PresObjKind::Text
:
611 case PresObjKind::Outline
:
613 SdrTextObj
* pTextObject
= static_cast<SdrTextObj
*>(pObject
.get());
614 if (pTextObject
->IsEmptyPresObj())
616 WriteOutlinerParagraph(aStr
, pOutliner
, pTextObject
->GetOutlinerParaObject(), bHeadLine
);
624 return aStr
.makeStringAndClear();
629 constexpr OUStringLiteral
gaHTMLHeader(
630 u
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
631 " \"http://www.w3.org/TR/html4/transitional.dtd\">\r\n"
632 "<html>\r\n<head>\r\n" );
634 // constructor for the html export helper classes
635 HtmlExport::HtmlExport(
637 SdDrawDocument
* pExpDoc
,
638 sd::DrawDocShell
* pDocShell
)
639 : maPath(std::move( aPath
)),
643 bool bChange
= mpDoc
->IsChanged();
647 ExportSingleDocument();
649 mpDoc
->SetChanged(bChange
);
652 HtmlExport::~HtmlExport()
656 void HtmlExport::Init()
658 SdPage
* pPage
= mpDoc
->GetSdPage(0, PageKind::Standard
);
660 // we come up with a destination...
661 INetURLObject
aINetURLObj( maPath
);
662 DBG_ASSERT( aINetURLObj
.GetProtocol() != INetProtocol::NotValid
, "invalid URL" );
664 maExportPath
= aINetURLObj
.GetPartBeforeLastName(); // with trailing '/'
665 maIndex
= aINetURLObj
.GetLastName();
667 mnSdPageCount
= mpDoc
->GetSdPageCount( PageKind::Standard
);
668 for( sal_uInt16 nPage
= 0; nPage
< mnSdPageCount
; nPage
++ )
670 pPage
= mpDoc
->GetSdPage( nPage
, PageKind::Standard
);
672 maPages
.push_back( pPage
);
674 mnSdPageCount
= maPages
.size();
676 maDocFileName
= maIndex
;
679 void HtmlExport::ExportSingleDocument()
681 SdrOutliner
* pOutliner
= mpDoc
->GetInternalOutliner();
684 InitProgress(mnSdPageCount
);
686 OUStringBuffer
aStr(gaHTMLHeader
692 for(sal_uInt16 nSdPage
= 0; nSdPage
< mnSdPageCount
; ++nSdPage
)
694 SdPage
* pPage
= maPages
[nSdPage
];
697 OUString
sTitleText(CreateTextForTitle(pOutliner
, pPage
));
700 if (nSdPage
!= 0) // First page - no need for a page break here
701 sStyle
+= "page-break-before:always; ";
702 sStyle
+= getParagraphStyle(pOutliner
, 0);
704 lclAppendStyle(aStr
, u
"h1", sStyle
);
706 aStr
.append(sTitleText
+ "</h1>\r\n");
708 // write outline text
709 aStr
.append(CreateTextForPage( pOutliner
, pPage
, true));
712 mpProgress
->SetState(++mnPagesWritten
);
717 aStr
.append("</body>\r\n</html>");
719 WriteHtml(maDocFileName
, aStr
);
725 void HtmlExport::InitProgress( sal_uInt16 nProgrCount
)
727 mpProgress
.reset(new SfxProgress( mpDocSh
, SdResId(STR_CREATE_PAGES
), nProgrCount
));
730 void HtmlExport::ResetProgress()
735 OUString
HtmlExport::DocumentMetadata() const
737 SvMemoryStream aStream
;
739 uno::Reference
<document::XDocumentProperties
> xDocProps
;
742 uno::Reference
<document::XDocumentPropertiesSupplier
> xDPS(
743 mpDocSh
->GetModel(), uno::UNO_QUERY_THROW
);
744 xDocProps
.set(xDPS
->getDocumentProperties());
747 SfxFrameHTMLWriter::Out_DocInfo(aStream
, maDocFileName
, xDocProps
,
750 const sal_uInt64 nLen
= aStream
.GetSize();
751 OSL_ENSURE(nLen
< o3tl::make_unsigned(SAL_MAX_INT32
), "Stream can't fit in OString");
752 std::string_view
aData(static_cast<const char*>(aStream
.GetData()), static_cast<sal_Int32
>(nLen
));
754 return OStringToOUString(aData
, RTL_TEXTENCODING_UTF8
);
757 /** exports the given html data into a non unicode file in the current export path with
758 the given filename */
759 bool HtmlExport::WriteHtml( std::u16string_view rFileName
, std::u16string_view rHtmlData
)
761 ErrCode nErr
= ERRCODE_NONE
;
765 OUString
aFull(maExportPath
+ rFileName
);
766 nErr
= aFile
.createStream(aFull
, pStr
);
767 if(nErr
== ERRCODE_NONE
)
769 OString
aStr(OUStringToOString(rHtmlData
, RTL_TEXTENCODING_UTF8
));
770 pStr
->WriteOString( aStr
);
774 if( nErr
!= ERRCODE_NONE
)
775 ErrorHandler::HandleError(nErr
);
777 return nErr
== ERRCODE_NONE
;
780 EasyFile::EasyFile() : bOpen(false)
784 EasyFile::~EasyFile()
790 ErrCode
EasyFile::createStream( const OUString
& rUrl
, SvStream
* &rpStr
)
796 createFileName( rUrl
, aFileName
);
798 ErrCode nErr
= ERRCODE_NONE
;
799 pOStm
= ::utl::UcbStreamHelper::CreateStream( aFileName
, StreamMode::WRITE
| StreamMode::TRUNC
);
803 nErr
= pOStm
->GetError();
807 nErr
= ERRCODE_SFX_CANTCREATECONTENT
;
810 if( nErr
!= ERRCODE_NONE
)
821 void EasyFile::createFileName( const OUString
& rURL
, OUString
& rFileName
)
826 INetURLObject
aURL( rURL
);
828 if( aURL
.GetProtocol() == INetProtocol::NotValid
)
831 osl::FileBase::getFileURLFromSystemPath( rURL
, aURLStr
);
832 aURL
= INetURLObject( aURLStr
);
834 DBG_ASSERT( aURL
.GetProtocol() != INetProtocol::NotValid
, "invalid URL" );
835 rFileName
= aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
838 void EasyFile::close()
844 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */