diff options
author | Xisco Fauli <xiscofauli@libreoffice.org> | 2023-09-27 10:58:34 +0200 |
---|---|---|
committer | Xisco Fauli <xiscofauli@libreoffice.org> | 2023-09-28 15:39:03 +0200 |
commit | 608c35665bee5990bd7e2799854e233d1454b6a4 (patch) | |
tree | b7b429f09884c514e9cad5ed237d1ba1f278c2f3 | |
parent | 9169202e7217d24a108d97d1af7b4ef2d2dc4ac5 (diff) |
tdf#105303: re-introduce single-document html export filter
which was deleted in 28b6480c6bdd179f3943f768926b7f196226c768
"tdf#105303: Drop html export wizard"
With this commit, exporting to HTML from File - Export dialog
behaves the same way as using --convert-to html:"impress_html_Export"
from the commandline. The slides are exported as a single document
For more information see the discussion in tdf#105303
Change-Id: Ib96b3d855ae807e79f276099422903c86ea2628a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157331
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
-rw-r--r-- | desktop/source/lib/init.cxx | 2 | ||||
-rw-r--r-- | filter/Configuration_filter.mk | 2 | ||||
-rw-r--r-- | filter/source/config/fragments/filters/draw_html_Export.xcu | 30 | ||||
-rw-r--r-- | filter/source/config/fragments/filters/impress_html_Export.xcu | 30 | ||||
-rw-r--r-- | officecfg/registry/data/org/openoffice/TypeDetection/UISort.xcu | 2 | ||||
-rw-r--r-- | sd/Library_sd.mk | 2 | ||||
-rw-r--r-- | sd/inc/sdhtmlfilter.hxx | 34 | ||||
-rw-r--r-- | sd/qa/unit/HtmlExportTest.cxx | 29 | ||||
-rw-r--r-- | sd/qa/unit/dialogs-test.cxx | 1 | ||||
-rw-r--r-- | sd/source/filter/html/htmlex.cxx | 858 | ||||
-rw-r--r-- | sd/source/filter/html/htmlex.hxx | 84 | ||||
-rw-r--r-- | sd/source/filter/html/sdhtmlfilter.cxx | 44 | ||||
-rw-r--r-- | sd/source/ui/docshell/docshel4.cxx | 7 |
13 files changed, 1118 insertions, 7 deletions
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 777d1030d075..434b65319776 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -335,6 +335,7 @@ const ExtensionMap aCalcExtensionMap[] = const ExtensionMap aImpressExtensionMap[] = { { "fodp", "OpenDocument Presentation Flat XML" }, + { "html", "impress_html_Export" }, { "odg", "impress8_draw" }, { "odp", "impress8" }, { "otp", "impress8_template" }, @@ -354,6 +355,7 @@ const ExtensionMap aImpressExtensionMap[] = const ExtensionMap aDrawExtensionMap[] = { { "fodg", "draw_ODG_FlatXML" }, + { "html", "draw_html_Export" }, { "odg", "draw8" }, { "pdf", "draw_pdf_Export" }, { "svg", "draw_svg_Export" }, diff --git a/filter/Configuration_filter.mk b/filter/Configuration_filter.mk index 921189c4de25..bd3d3486234e 100644 --- a/filter/Configuration_filter.mk +++ b/filter/Configuration_filter.mk @@ -764,6 +764,7 @@ $(eval $(call filter_Configuration_add_filters,fcfg_langpack,fcfg_drawgraphics_f draw_emz_Export \ draw_eps_Export \ draw_gif_Export \ + draw_html_Export \ draw_jpg_Export \ draw_png_Export \ draw_svg_Export \ @@ -808,6 +809,7 @@ $(eval $(call filter_Configuration_add_filters,fcfg_langpack,fcfg_impressgraphic impress_emf_Export \ impress_eps_Export \ impress_gif_Export \ + impress_html_Export \ impress_jpg_Export \ impress_png_Export \ impress_svg_Export \ diff --git a/filter/source/config/fragments/filters/draw_html_Export.xcu b/filter/source/config/fragments/filters/draw_html_Export.xcu new file mode 100644 index 000000000000..ebb14b75b19f --- /dev/null +++ b/filter/source/config/fragments/filters/draw_html_Export.xcu @@ -0,0 +1,30 @@ +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . +--> + <node oor:name="draw_html_Export" oor:op="replace"> + <prop oor:name="Flags"><value>EXPORT ALIEN</value></prop> + <prop oor:name="UIComponent"/> + <prop oor:name="FilterService"></prop> + <prop oor:name="UserData"><value></value></prop> + <prop oor:name="FileFormatVersion"><value>0</value></prop> + <prop oor:name="Type"><value>graphic_HTML</value></prop> + <prop oor:name="TemplateName"/> + <prop oor:name="DocumentService"><value>com.sun.star.drawing.DrawingDocument</value></prop> + <prop oor:name="UIName"> + <value xml:lang="en-US">HTML Document (Draw)</value> + </prop> + </node> diff --git a/filter/source/config/fragments/filters/impress_html_Export.xcu b/filter/source/config/fragments/filters/impress_html_Export.xcu new file mode 100644 index 000000000000..1e0fb435e01e --- /dev/null +++ b/filter/source/config/fragments/filters/impress_html_Export.xcu @@ -0,0 +1,30 @@ +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . +--> + <node oor:name="impress_html_Export" oor:op="replace"> + <prop oor:name="Flags"><value>EXPORT ALIEN</value></prop> + <prop oor:name="UIComponent"/> + <prop oor:name="FilterService"></prop> + <prop oor:name="UserData"><value></value></prop> + <prop oor:name="FileFormatVersion"><value>0</value></prop> + <prop oor:name="Type"><value>graphic_HTML</value></prop> + <prop oor:name="TemplateName"/> + <prop oor:name="DocumentService"><value>com.sun.star.presentation.PresentationDocument</value></prop> + <prop oor:name="UIName"> + <value xml:lang="en-US">HTML Document (Impress)</value> + </prop> + </node> diff --git a/officecfg/registry/data/org/openoffice/TypeDetection/UISort.xcu b/officecfg/registry/data/org/openoffice/TypeDetection/UISort.xcu index 3dc6272ab7b2..5dcce7b1e688 100644 --- a/officecfg/registry/data/org/openoffice/TypeDetection/UISort.xcu +++ b/officecfg/registry/data/org/openoffice/TypeDetection/UISort.xcu @@ -26,7 +26,7 @@ </node> <node oor:name="com.sun.star.drawing.DrawingDocument" oor:op="replace" install:module="draw"> <prop oor:name="SortedFilterList"> - <value oor:separator=";">draw8;draw8_template;StarOffice XML (Draw);draw_StarOffice_XML_Draw_Template;OpenDocument Drawing Flat XML;draw_pdf_Export;draw_flash_Export;WordPerfect Graphics;DXF - AutoCAD Interchange;EMF - MS Windows Metafile;EPS - Encapsulated PostScript;MET - OS/2 Metafile;PCT - Mac Pict;SVM - StarView Metafile;WMF - MS Windows Metafile</value> + <value oor:separator=";">draw8;draw8_template;StarOffice XML (Draw);draw_StarOffice_XML_Draw_Template;OpenDocument Drawing Flat XML;draw_html_Export;draw_pdf_Export;draw_flash_Export;WordPerfect Graphics;DXF - AutoCAD Interchange;EMF - MS Windows Metafile;EPS - Encapsulated PostScript;MET - OS/2 Metafile;PCT - Mac Pict;SVM - StarView Metafile;WMF - MS Windows Metafile</value> </prop> </node> <node oor:name="com.sun.star.presentation.PresentationDocument" oor:op="replace" install:module="impress"> diff --git a/sd/Library_sd.mk b/sd/Library_sd.mk index 8b56e8b55c33..01c546ad3dca 100644 --- a/sd/Library_sd.mk +++ b/sd/Library_sd.mk @@ -200,6 +200,8 @@ $(eval $(call gb_Library_add_exception_objects,sd,\ sd/source/filter/ppt/pptinanimations \ sd/source/filter/ppt/propread \ sd/source/filter/grf/sdgrffilter \ + sd/source/filter/html/htmlex \ + sd/source/filter/html/sdhtmlfilter \ sd/source/filter/pdf/sdpdffilter \ sd/source/filter/sdfilter \ sd/source/filter/sdpptwrp \ diff --git a/sd/inc/sdhtmlfilter.hxx b/sd/inc/sdhtmlfilter.hxx new file mode 100644 index 000000000000..84b9c5d48e0f --- /dev/null +++ b/sd/inc/sdhtmlfilter.hxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "sdfilter.hxx" + +// SdHTMLFilter +class SdHTMLFilter final : public SdFilter +{ +public: + SdHTMLFilter(SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell); + virtual ~SdHTMLFilter() override; + + virtual bool Export() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/qa/unit/HtmlExportTest.cxx b/sd/qa/unit/HtmlExportTest.cxx index be7884d51156..93c1a21339d1 100644 --- a/sd/qa/unit/HtmlExportTest.cxx +++ b/sd/qa/unit/HtmlExportTest.cxx @@ -12,14 +12,34 @@ using namespace css; -class SdXHTMLFilterTest : public UnoApiXmlTest, public HtmlTestTools +class SdHTMLFilterTest : public UnoApiXmlTest, public HtmlTestTools { public: - SdXHTMLFilterTest() + SdHTMLFilterTest() : UnoApiXmlTest("/sd/qa/unit/data/") { } + void testHTMLExport() + { + loadFromURL(u"HtmlExportTestDocument.odp"); + save("impress_html_Export"); + htmlDocUniquePtr htmlDoc = parseHtml(maTempFile); + + assertXPath(htmlDoc, "/html", 1); + assertXPath(htmlDoc, "/html/body", 1); + assertXPath(htmlDoc, "/html/body/h1", 4); + assertXPath(htmlDoc, "/html/body/table", 1); + assertXPath(htmlDoc, "/html/body/table/tr", 5); + assertXPath(htmlDoc, "/html/body/ul", 1); + assertXPath(htmlDoc, "/html/body/ul/li", 2); + + assertXPath(htmlDoc, "/html/head/meta[1]", "content", "text/html; charset=utf-8"); + assertXPath(htmlDoc, "/html/head/meta[2]", "name", "generator"); + assertXPath(htmlDoc, "/html/head/meta[3]", "name", "created"); + assertXPath(htmlDoc, "/html/head/meta[3]", "content", "2014-04-09T17:05:41.987922038"); + } + void testTdf154989() { loadFromURL(u"tdf154989.odg"); @@ -43,12 +63,13 @@ public: pXmlDoc, "/xhtml:html/xhtml:body/xhtml:div[1]/xhtml:div[4]/xhtml:div/xhtml:p", "below"); } - CPPUNIT_TEST_SUITE(SdXHTMLFilterTest); + CPPUNIT_TEST_SUITE(SdHTMLFilterTest); + CPPUNIT_TEST(testHTMLExport); CPPUNIT_TEST(testTdf154989); CPPUNIT_TEST_SUITE_END(); }; -CPPUNIT_TEST_SUITE_REGISTRATION(SdXHTMLFilterTest); +CPPUNIT_TEST_SUITE_REGISTRATION(SdHTMLFilterTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sd/qa/unit/dialogs-test.cxx b/sd/qa/unit/dialogs-test.cxx index 6cb34354b84c..5a80d5ac4f72 100644 --- a/sd/qa/unit/dialogs-test.cxx +++ b/sd/qa/unit/dialogs-test.cxx @@ -580,7 +580,6 @@ void SdDialogsTest::openAnyDialog() { // example for SfxTabDialog: 5 -> "modules/sdraw/ui/drawpagedialog.ui" // example for TabDialog: 22 -> "modules/simpress/ui/headerfooterdialog.ui" - // example for self-adapted wizard: 0 -> "modules/simpress/ui/publishingdialog.ui" ScopedVclPtr<VclAbstractDialog> pDlg(createDialogByID(5)); if (pDlg) diff --git a/sd/source/filter/html/htmlex.cxx b/sd/source/filter/html/htmlex.cxx new file mode 100644 index 000000000000..f1f9682f542d --- /dev/null +++ b/sd/source/filter/html/htmlex.cxx @@ -0,0 +1,858 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "htmlex.hxx" +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <sal/log.hxx> +#include <rtl/tencinfo.h> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/xmlencode.hxx> +#include <o3tl/safeint.hxx> +#include <osl/file.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <sfx2/frmhtmlw.hxx> +#include <sfx2/progress.hxx> +#include <utility> +#include <svx/svditer.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/outlobj.hxx> +#include <svtools/htmlout.hxx> +#include <editeng/editeng.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdotable.hxx> +#include <tools/urlobj.hxx> +#include <svtools/sfxecode.hxx> +#include <tools/debug.hxx> + +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <Outliner.hxx> +#include <sdpage.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <sdresid.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::document; + +using namespace sdr::table; + +namespace { + +// Helper class for the simple creation of files local/remote +class EasyFile +{ +private: + std::unique_ptr<SvStream> pOStm; + bool bOpen; + +public: + + EasyFile(); + ~EasyFile(); + + ErrCode createStream( const OUString& rUrl, SvStream*& rpStr ); + void createFileName( const OUString& rUrl, OUString& rFileName ); + void close(); +}; + +// create area for a circle; we expect pixel coordinates +OUString ColorToHTMLString( Color aColor ) +{ + static const char hex[] = "0123456789ABCDEF"; + OUStringBuffer aStr( "#xxxxxx" ); + aStr[1] = hex[(aColor.GetRed() >> 4) & 0xf]; + aStr[2] = hex[aColor.GetRed() & 0xf]; + aStr[3] = hex[(aColor.GetGreen() >> 4) & 0xf]; + aStr[4] = hex[aColor.GetGreen() & 0xf]; + aStr[5] = hex[(aColor.GetBlue() >> 4) & 0xf]; + aStr[6] = hex[aColor.GetBlue() & 0xf]; + + return aStr.makeStringAndClear(); +} + +} //namespace + +// Helper class for the embedding of text attributes into the html output +class HtmlState +{ +private: + bool mbColor; + bool mbWeight; + bool mbItalic; + bool mbUnderline; + bool mbStrike; + bool mbLink; + Color maColor; + Color maDefColor; + OUString maLink; + OUString maTarget; + +public: + explicit HtmlState( Color aDefColor ); + + OUString SetWeight( bool bWeight ); + OUString SetItalic( bool bItalic ); + OUString SetUnderline( bool bUnderline ); + OUString SetColor( Color aColor ); + OUString SetStrikeout( bool bStrike ); + OUString SetLink( const OUString& aLink, const OUString& aTarget ); + OUString Flush(); +}; + +// close all still open tags +OUString HtmlState::Flush() +{ + OUString aStr = SetWeight(false) + + SetItalic(false) + + SetUnderline(false) + + SetStrikeout(false) + + SetColor(maDefColor) + + SetLink("",""); + + return aStr; +} + +// c'tor with default color for the page +HtmlState::HtmlState( Color aDefColor ) + : mbColor(false), + mbWeight(false), + mbItalic(false), + mbUnderline(false), + mbStrike(false), + mbLink(false), + maDefColor(aDefColor) +{ +} + +// enables/disables bold print +OUString HtmlState::SetWeight( bool bWeight ) +{ + OUString aStr; + + if(bWeight && !mbWeight) + aStr = "<b>"; + else if(!bWeight && mbWeight) + aStr = "</b>"; + + mbWeight = bWeight; + return aStr; +} + +// enables/disables italic + +OUString HtmlState::SetItalic( bool bItalic ) +{ + OUString aStr; + + if(bItalic && !mbItalic) + aStr = "<i>"; + else if(!bItalic && mbItalic) + aStr = "</i>"; + + mbItalic = bItalic; + return aStr; +} + +// enables/disables underlines + +OUString HtmlState::SetUnderline( bool bUnderline ) +{ + OUString aStr; + + if(bUnderline && !mbUnderline) + aStr = "<u>"; + else if(!bUnderline && mbUnderline) + aStr = "</u>"; + + mbUnderline = bUnderline; + return aStr; +} + +// enables/disables strike through +OUString HtmlState::SetStrikeout( bool bStrike ) +{ + OUString aStr; + + if(bStrike && !mbStrike) + aStr = "<strike>"; + else if(!bStrike && mbStrike) + aStr = "</strike>"; + + mbStrike = bStrike; + return aStr; +} + +// Sets the specified text color +OUString HtmlState::SetColor( Color aColor ) +{ + OUString aStr; + + if(mbColor && aColor == maColor) + return aStr; + + if(mbColor) + { + aStr = "</font>"; + mbColor = false; + } + + if(aColor != maDefColor) + { + maColor = aColor; + aStr += "<font color=\"" + ColorToHTMLString(aColor) + "\">"; + mbColor = true; + } + + return aStr; +} + +// enables/disables a hyperlink +OUString HtmlState::SetLink( const OUString& aLink, const OUString& aTarget ) +{ + OUString aStr; + + if(mbLink&&maLink == aLink&&maTarget==aTarget) + return aStr; + + if(mbLink) + { + aStr = "</a>"; + mbLink = false; + } + + if (!aLink.isEmpty()) + { + aStr += "<a href=\"" + comphelper::string::encodeForXml(aLink); + if (!aTarget.isEmpty()) + { + aStr += "\" target=\"" + comphelper::string::encodeForXml(aTarget); + } + aStr += "\">"; + mbLink = true; + maLink = aLink; + maTarget = aTarget; + } + + return aStr; +} + +namespace +{ + +OUString getParagraphStyle( const SdrOutliner* pOutliner, sal_Int32 nPara ) +{ + SfxItemSet aParaSet( pOutliner->GetParaAttribs( nPara ) ); + + OUString sStyle; + + if( aParaSet.GetItem<SvxFrameDirectionItem>( EE_PARA_WRITINGDIR )->GetValue() == SvxFrameDirection::Horizontal_RL_TB ) + { + + sStyle = "direction: rtl;"; + } + else + { + // This is the default so don't write it out + // sStyle += "direction: ltr;"; + } + return sStyle; +} + +void lclAppendStyle(OUStringBuffer& aBuffer, std::u16string_view aTag, std::u16string_view aStyle) +{ + if (aStyle.empty()) + aBuffer.append(OUString::Concat("<") + aTag + ">"); + else + aBuffer.append(OUString::Concat("<") + aTag + " style=\"" + aStyle + "\">"); +} + +// Depending on the attributes of the specified set and the specified +// HtmlState, it creates the needed html tags in order to get the +// attributes +OUString TextAttribToHTMLString( SfxItemSet const * pSet, HtmlState* pState ) +{ + OUStringBuffer aStr; + + if(nullptr == pSet) + return OUString(); + + OUString aLink, aTarget; + if ( pSet->GetItemState( EE_FEATURE_FIELD ) == SfxItemState::SET ) + { + const SvxFieldItem* pItem = pSet->GetItem<SvxFieldItem>( EE_FEATURE_FIELD ); + if(pItem) + { + const SvxURLField* pURL = dynamic_cast<const SvxURLField*>( pItem->GetField() ); + if(pURL) + { + aLink = pURL->GetURL(); + aTarget = pURL->GetTargetFrame(); + } + } + } + + bool bTemp; + OUString aTemp; + + if ( pSet->GetItemState( EE_CHAR_WEIGHT ) == SfxItemState::SET ) + { + bTemp = pSet->Get( EE_CHAR_WEIGHT ).GetWeight() == WEIGHT_BOLD; + aTemp = pState->SetWeight( bTemp ); + if( bTemp ) + aStr.insert(0, aTemp); + else + aStr.append(aTemp); + } + + if ( pSet->GetItemState( EE_CHAR_UNDERLINE ) == SfxItemState::SET ) + { + bTemp = pSet->Get( EE_CHAR_UNDERLINE ).GetLineStyle() != LINESTYLE_NONE; + aTemp = pState->SetUnderline( bTemp ); + if( bTemp ) + aStr.insert(0, aTemp); + else + aStr.append(aTemp); + } + + if ( pSet->GetItemState( EE_CHAR_STRIKEOUT ) == SfxItemState::SET ) + { + bTemp = pSet->Get( EE_CHAR_STRIKEOUT ).GetStrikeout() != STRIKEOUT_NONE; + aTemp = pState->SetStrikeout( bTemp ); + if( bTemp ) + aStr.insert(0, aTemp); + else + aStr.append(aTemp); + } + + if ( pSet->GetItemState( EE_CHAR_ITALIC ) == SfxItemState::SET ) + { + bTemp = pSet->Get( EE_CHAR_ITALIC ).GetPosture() != ITALIC_NONE; + aTemp = pState->SetItalic( bTemp ); + if( bTemp ) + aStr.insert(0, aTemp); + else + aStr.append(aTemp); + } + + if (!aLink.isEmpty()) + aStr.insert(0, pState->SetLink(aLink, aTarget)); + else + aStr.append(pState->SetLink(aLink, aTarget)); + + return aStr.makeStringAndClear(); +} + +// escapes a string for html +OUString StringToHTMLString( const OUString& rString ) +{ + SvMemoryStream aMemStm; + HTMLOutFuncs::Out_String( aMemStm, rString ); + aMemStm.WriteChar( char(0) ); + sal_Int32 nLength = strlen(static_cast<char const *>(aMemStm.GetData())); + return OUString( static_cast<char const *>(aMemStm.GetData()), nLength, RTL_TEXTENCODING_UTF8 ); +} + +// converts a paragraph of the outliner to html +OUString ParagraphToHTMLString( SdrOutliner const * pOutliner, sal_Int32 nPara ) +{ + OUStringBuffer aStr; + + if(nullptr == pOutliner) + return OUString(); + + // TODO: MALTE!!! + EditEngine& rEditEngine = *const_cast<EditEngine*>(&pOutliner->GetEditEngine()); + bool bOldUpdateMode = rEditEngine.SetUpdateLayout(true); + + Paragraph* pPara = pOutliner->GetParagraph(nPara); + if(nullptr == pPara) + return OUString(); + + HtmlState aState( COL_BLACK ); + std::vector<sal_Int32> aPortionList; + rEditEngine.GetPortions( nPara, aPortionList ); + + sal_Int32 nPos1 = 0; + for( sal_Int32 nPos2 : aPortionList ) + { + ESelection aSelection( nPara, nPos1, nPara, nPos2); + + SfxItemSet aSet( rEditEngine.GetAttribs( aSelection ) ); + + aStr.append(TextAttribToHTMLString( &aSet, &aState ) + + StringToHTMLString(rEditEngine.GetText( aSelection ))); + + nPos1 = nPos2; + } + aStr.append(aState.Flush()); + rEditEngine.SetUpdateLayout(bOldUpdateMode); + + return aStr.makeStringAndClear(); +} + +void WriteOutlinerParagraph(OUStringBuffer& aStr, SdrOutliner* pOutliner, + OutlinerParaObject const * pOutlinerParagraphObject, + bool bHeadLine) +{ + if (pOutlinerParagraphObject == nullptr) + return; + + pOutliner->SetText(*pOutlinerParagraphObject); + + sal_Int32 nCount = pOutliner->GetParagraphCount(); + + + sal_Int16 nCurrentDepth = -1; + + for (sal_Int32 nIndex = 0; nIndex < nCount; nIndex++) + { + Paragraph* pParagraph = pOutliner->GetParagraph(nIndex); + if(pParagraph == nullptr) + continue; + + const sal_Int16 nDepth = static_cast<sal_uInt16>(pOutliner->GetDepth(nIndex)); + OUString aParaText = ParagraphToHTMLString(pOutliner, nIndex); + + if (aParaText.isEmpty()) + continue; + + if (nDepth < 0) + { + OUString aTag = bHeadLine ? OUString("h2") : OUString("p"); + lclAppendStyle(aStr, aTag, getParagraphStyle(pOutliner, nIndex)); + + aStr.append(aParaText); + aStr.append("</" + aTag + ">\r\n"); + } + else + { + while(nCurrentDepth < nDepth) + { + aStr.append("<ul>\r\n"); + nCurrentDepth++; + } + while(nCurrentDepth > nDepth) + { + aStr.append("</ul>\r\n"); + nCurrentDepth--; + } + lclAppendStyle(aStr, u"li", getParagraphStyle(pOutliner, nIndex)); + aStr.append(aParaText); + aStr.append("</li>\r\n"); + } + } + while(nCurrentDepth >= 0) + { + aStr.append("</ul>\r\n"); + nCurrentDepth--; + } + pOutliner->Clear(); +} + +void WriteTable(OUStringBuffer& aStr, SdrTableObj const * pTableObject, SdrOutliner* pOutliner) +{ + CellPos aStart, aEnd; + + aStart = SdrTableObj::getFirstCell(); + aEnd = pTableObject->getLastCell(); + + sal_Int32 nColCount = pTableObject->getColumnCount(); + aStr.append("<table>\r\n"); + for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++) + { + aStr.append(" <tr>\r\n"); + for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++) + { + aStr.append(" <td>\r\n"); + sal_Int32 nCellIndex = nRow * nColCount + nCol; + SdrText* pText = pTableObject->getText(nCellIndex); + + if (pText == nullptr) + continue; + WriteOutlinerParagraph(aStr, pOutliner, pText->GetOutlinerParaObject(), false); + aStr.append(" </td>\r\n"); + } + aStr.append(" </tr>\r\n"); + } + aStr.append("</table>\r\n"); +} + +void WriteObjectGroup(OUStringBuffer& aStr, SdrObjGroup const * pObjectGroup, SdrOutliner* pOutliner, + bool bHeadLine) +{ + SdrObjListIter aGroupIterator(pObjectGroup->GetSubList(), SdrIterMode::DeepNoGroups); + while (aGroupIterator.IsMore()) + { + SdrObject* pCurrentObject = aGroupIterator.Next(); + if (pCurrentObject->GetObjIdentifier() == SdrObjKind::Group) + { + SdrObjGroup* pCurrentGroupObject = static_cast<SdrObjGroup*>(pCurrentObject); + WriteObjectGroup(aStr, pCurrentGroupObject, pOutliner, bHeadLine); + } + else + { + OutlinerParaObject* pOutlinerParagraphObject = pCurrentObject->GetOutlinerParaObject(); + if (pOutlinerParagraphObject != nullptr) + { + WriteOutlinerParagraph(aStr, pOutliner, pOutlinerParagraphObject, bHeadLine); + } + } + } +} + +// get SdrTextObject with layout text of this page +SdrTextObj* GetLayoutTextObject(SdrPage const * pPage) +{ + const size_t nObjectCount = pPage->GetObjCount(); + SdrTextObj* pResult = nullptr; + + for (size_t nObject = 0; nObject < nObjectCount; ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + if (pObject->GetObjInventor() == SdrInventor::Default && + pObject->GetObjIdentifier() == SdrObjKind::OutlineText) + { + pResult = static_cast<SdrTextObj*>(pObject); + break; + } + } + return pResult; +} + + +/** creates an outliner text for the title objects of a page + */ +OUString CreateTextForTitle( SdrOutliner* pOutliner, SdPage* pPage ) +{ + SdrTextObj* pTO = static_cast<SdrTextObj*>(pPage->GetPresObj(PresObjKind::Title)); + if(!pTO) + pTO = GetLayoutTextObject(pPage); + + if (pTO && !pTO->IsEmptyPresObj()) + { + OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject(); + if(pOPO && pOutliner->GetParagraphCount() != 0) + { + pOutliner->Clear(); + pOutliner->SetText(*pOPO); + return ParagraphToHTMLString(pOutliner, 0); + } + } + + return OUString(); +} + +// creates an outliner text for a page +OUString CreateTextForPage(SdrOutliner* pOutliner, SdPage const * pPage, + bool bHeadLine) +{ + OUStringBuffer aStr; + + for (size_t i = 0; i <pPage->GetObjCount(); ++i ) + { + SdrObject* pObject = pPage->GetObj(i); + PresObjKind eKind = pPage->GetPresObjKind(pObject); + + switch (eKind) + { + case PresObjKind::NONE: + { + if (pObject->GetObjIdentifier() == SdrObjKind::Group) + { + SdrObjGroup* pObjectGroup = static_cast<SdrObjGroup*>(pObject); + WriteObjectGroup(aStr, pObjectGroup, pOutliner, false); + } + else if (pObject->GetObjIdentifier() == SdrObjKind::Table) + { + SdrTableObj* pTableObject = static_cast<SdrTableObj*>(pObject); + WriteTable(aStr, pTableObject, pOutliner); + } + else + { + if (pObject->GetOutlinerParaObject()) + { + WriteOutlinerParagraph(aStr, pOutliner, pObject->GetOutlinerParaObject(), false); + } + } + } + break; + + case PresObjKind::Table: + { + SdrTableObj* pTableObject = static_cast<SdrTableObj*>(pObject); + WriteTable(aStr, pTableObject, pOutliner); + } + break; + + case PresObjKind::Text: + case PresObjKind::Outline: + { + SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject); + if (pTextObject->IsEmptyPresObj()) + continue; + WriteOutlinerParagraph(aStr, pOutliner, pTextObject->GetOutlinerParaObject(), bHeadLine); + } + break; + + default: + break; + } + } + return aStr.makeStringAndClear(); +} + +} // namespace + +constexpr OUStringLiteral gaHTMLHeader( + u"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n" + " \"http://www.w3.org/TR/html4/transitional.dtd\">\r\n" + "<html>\r\n<head>\r\n" ); + +constexpr OUStringLiteral gaHTMLExtension = u"" STR_HTMLEXP_DEFAULT_EXTENSION; + +// constructor for the html export helper classes +HtmlExport::HtmlExport( + OUString aPath, + SdDrawDocument* pExpDoc, + sd::DrawDocShell* pDocShell ) + : maPath(std::move( aPath )), + mpDoc(pExpDoc), + mpDocSh( pDocShell ) +{ + bool bChange = mpDoc->IsChanged(); + + Init(); + + ExportSingleDocument(); + + mpDoc->SetChanged(bChange); +} + +HtmlExport::~HtmlExport() +{ +} + +void HtmlExport::Init() +{ + SdPage* pPage = mpDoc->GetSdPage(0, PageKind::Standard); + + // we come up with a destination... + INetURLObject aINetURLObj( maPath ); + DBG_ASSERT( aINetURLObj.GetProtocol() != INetProtocol::NotValid, "invalid URL" ); + + maExportPath = aINetURLObj.GetPartBeforeLastName(); // with trailing '/' + maIndex = aINetURLObj.GetLastName(); + + mnSdPageCount = mpDoc->GetSdPageCount( PageKind::Standard ); + for( sal_uInt16 nPage = 0; nPage < mnSdPageCount; nPage++ ) + { + pPage = mpDoc->GetSdPage( nPage, PageKind::Standard ); + + maPages.push_back( pPage ); + } + mnSdPageCount = maPages.size(); + + maDocFileName = maIndex; +} + +void HtmlExport::ExportSingleDocument() +{ + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + + mnPagesWritten = 0; + InitProgress(mnSdPageCount); + + OUStringBuffer aStr(gaHTMLHeader + + DocumentMetadata() + + "\r\n" + "</head>\r\n" + "<body>\r\n"); + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; ++nSdPage) + { + SdPage* pPage = maPages[nSdPage]; + + // page title + OUString sTitleText(CreateTextForTitle(pOutliner, pPage)); + OUString sStyle; + + if (nSdPage != 0) // First page - no need for a page break here + sStyle += "page-break-before:always; "; + sStyle += getParagraphStyle(pOutliner, 0); + + lclAppendStyle(aStr, u"h1", sStyle); + + aStr.append(sTitleText + "</h1>\r\n"); + + // write outline text + aStr.append(CreateTextForPage( pOutliner, pPage, true)); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + } + + // close page + aStr.append("</body>\r\n</html>"); + + WriteHtml(maDocFileName, false, aStr); + + pOutliner->Clear(); + ResetProgress(); +} + +void HtmlExport::InitProgress( sal_uInt16 nProgrCount ) +{ + mpProgress.reset(new SfxProgress( mpDocSh, SdResId(STR_CREATE_PAGES), nProgrCount )); +} + +void HtmlExport::ResetProgress() +{ + mpProgress.reset(); +} + +OUString HtmlExport::DocumentMetadata() const +{ + SvMemoryStream aStream; + + uno::Reference<document::XDocumentProperties> xDocProps; + if (mpDocSh) + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + mpDocSh->GetModel(), uno::UNO_QUERY_THROW); + xDocProps.set(xDPS->getDocumentProperties()); + } + + SfxFrameHTMLWriter::Out_DocInfo(aStream, maDocFileName, xDocProps, + " "); + + const sal_uInt64 nLen = aStream.GetSize(); + OSL_ENSURE(nLen < o3tl::make_unsigned(SAL_MAX_INT32), "Stream can't fit in OString"); + std::string_view aData(static_cast<const char*>(aStream.GetData()), static_cast<sal_Int32>(nLen)); + + return OStringToOUString(aData, RTL_TEXTENCODING_UTF8); +} + +/** exports the given html data into a non unicode file in the current export path with + the given filename */ +bool HtmlExport::WriteHtml( const OUString& rFileName, bool bAddExtension, std::u16string_view rHtmlData ) +{ + ErrCode nErr = ERRCODE_NONE; + + OUString aFileName( rFileName ); + if( bAddExtension ) + aFileName += gaHTMLExtension; + + EasyFile aFile; + SvStream* pStr; + OUString aFull(maExportPath + aFileName); + nErr = aFile.createStream(aFull , pStr); + if(nErr == ERRCODE_NONE) + { + OString aStr(OUStringToOString(rHtmlData, RTL_TEXTENCODING_UTF8)); + pStr->WriteOString( aStr ); + aFile.close(); + } + + if( nErr != ERRCODE_NONE ) + ErrorHandler::HandleError(nErr); + + return nErr == ERRCODE_NONE; +} + +EasyFile::EasyFile() : bOpen(false) +{ +} + +EasyFile::~EasyFile() +{ + if( bOpen ) + close(); +} + +ErrCode EasyFile::createStream( const OUString& rUrl, SvStream* &rpStr ) +{ + if(bOpen) + close(); + + OUString aFileName; + createFileName( rUrl, aFileName ); + + ErrCode nErr = ERRCODE_NONE; + pOStm = ::utl::UcbStreamHelper::CreateStream( aFileName, StreamMode::WRITE | StreamMode::TRUNC ); + if( pOStm ) + { + bOpen = true; + nErr = pOStm->GetError(); + } + else + { + nErr = ERRCODE_SFX_CANTCREATECONTENT; + } + + if( nErr != ERRCODE_NONE ) + { + bOpen = false; + pOStm.reset(); + } + + rpStr = pOStm.get(); + + return nErr; +} + +void EasyFile::createFileName( const OUString& rURL, OUString& rFileName ) +{ + if( bOpen ) + close(); + + INetURLObject aURL( rURL ); + + if( aURL.GetProtocol() == INetProtocol::NotValid ) + { + OUString aURLStr; + osl::FileBase::getFileURLFromSystemPath( rURL, aURLStr ); + aURL = INetURLObject( aURLStr ); + } + DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" ); + rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); +} + +void EasyFile::close() +{ + pOStm.reset(); + bOpen = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlex.hxx b/sd/source/filter/html/htmlex.hxx new file mode 100644 index 000000000000..719c8cf27350 --- /dev/null +++ b/sd/source/filter/html/htmlex.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <resltn.hxx> +#include <rtl/ustrbuf.hxx> +#include <tools/color.hxx> +#include <tools/solar.h> +#include <vcl/errinf.hxx> +#include <unotools/resmgr.hxx> + +#include <memory> +#include <string_view> +#include <vector> + +namespace sd { class DrawDocShell; } + +class OutlinerParaObject; +class SfxItemSet; +class SfxProgress; +class SdrOutliner; +class SdPage; +class HtmlState; +class SdrTextObj; +class SdrObjGroup; +namespace sdr::table { class SdrTableObj; } +class SdrPage; +class SdDrawDocument; + +/// this class exports an Impress Document as a HTML Presentation. +class HtmlExport final +{ + std::vector< SdPage* > maPages; + + OUString maPath; + + SdDrawDocument* mpDoc; + ::sd::DrawDocShell* mpDocSh; + + std::unique_ptr<SfxProgress> mpProgress; + sal_uInt16 mnSdPageCount; + sal_uInt16 mnPagesWritten; + OUString maIndex; + OUString maDocFileName; + + OUString maExportPath; ///< output directory or URL. + + void InitProgress( sal_uInt16 nProgrCount ); + void ResetProgress(); + + /// Output document metadata. + OUString DocumentMetadata() const; + + void Init(); + void ExportSingleDocument(); + + bool WriteHtml( const OUString& rFileName, bool bAddExtension, std::u16string_view rHtmlData ); + + public: + HtmlExport(OUString aPath, + SdDrawDocument* pExpDoc, + sd::DrawDocShell* pDocShell); + + ~HtmlExport(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/sdhtmlfilter.cxx b/sd/source/filter/html/sdhtmlfilter.cxx new file mode 100644 index 000000000000..57053e6b8b23 --- /dev/null +++ b/sd/source/filter/html/sdhtmlfilter.cxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sfx2/docfile.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/sfxsids.hrc> + +#include "htmlex.hxx" +#include <sdhtmlfilter.hxx> + +SdHTMLFilter::SdHTMLFilter(SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell) + : SdFilter(rMedium, rDocShell) +{ +} + +SdHTMLFilter::~SdHTMLFilter() {} + +bool SdHTMLFilter::Export() +{ + mrMedium.Close(); + mrMedium.Commit(); + + HtmlExport aExport(mrMedium.GetName(), &mrDocument, &mrDocShell); + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshel4.cxx b/sd/source/ui/docshell/docshel4.cxx index 57a9be9393d8..6b9d8993ada9 100644 --- a/sd/source/ui/docshell/docshel4.cxx +++ b/sd/source/ui/docshell/docshel4.cxx @@ -70,6 +70,7 @@ #include <sdpptwrp.hxx> #include <sdcgmfilter.hxx> #include <sdgrffilter.hxx> +#include <sdhtmlfilter.hxx> #include <sdpdffilter.hxx> #include <framework/FrameworkHelper.hxx> #include <o3tl/string_view.hxx> @@ -601,7 +602,11 @@ bool DrawDocShell::ConvertTo( SfxMedium& rMedium ) const OUString aTypeName( pMediumFilter->GetTypeName() ); std::unique_ptr<SdFilter> xFilter; - if( aTypeName.indexOf( "MS_PowerPoint_97" ) >= 0 ) + if( aTypeName.indexOf( "graphic_HTML" ) >= 0 ) + { + xFilter = std::make_unique<SdHTMLFilter>(rMedium, *this); + } + else if( aTypeName.indexOf( "MS_PowerPoint_97" ) >= 0 ) { xFilter = std::make_unique<SdPPTFilter>(rMedium, *this); static_cast<SdPPTFilter*>(xFilter.get())->PreSaveBasic(); |