diff options
Diffstat (limited to 'sd/source/filter')
50 files changed, 32775 insertions, 0 deletions
diff --git a/sd/source/filter/cgm/sdcgmfilter.cxx b/sd/source/filter/cgm/sdcgmfilter.cxx new file mode 100644 index 000000000..80b11519d --- /dev/null +++ b/sd/source/filter/cgm/sdcgmfilter.cxx @@ -0,0 +1,137 @@ +/* -*- 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 <memory> +#include <tools/urlobj.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <sfx2/docfile.hxx> +#include <svx/xflclit.hxx> +#include <svx/xfillit0.hxx> + +#include <sddll.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <sdcgmfilter.hxx> + +#include <DrawDocShell.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::frame; + +typedef sal_uInt32 ( *ImportCGMPointer )(SvStream&, Reference< XModel > const &, Reference< XStatusIndicator > const &); + +#ifdef DISABLE_DYNLOADING + +extern "C" sal_uInt32 ImportCGM(SvStream&, Reference< XModel > const &, Reference< XStatusIndicator > const &); + +#endif + +SdCGMFilter::SdCGMFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell ) : + SdFilter( rMedium, rDocShell ) +{ +} + +SdCGMFilter::~SdCGMFilter() +{ +} + +namespace +{ + class CGMPointer + { + ImportCGMPointer m_pPointer; + public: + CGMPointer() + { +#ifdef DISABLE_DYNLOADING + m_pPointer = ImportCGM; +#else + m_pPointer = reinterpret_cast<ImportCGMPointer>( + SdFilter::GetLibrarySymbol("icg", "ImportCGM")); +#endif + } + ImportCGMPointer get() { return m_pPointer; } + }; +} + +bool SdCGMFilter::Import() +{ + bool bRet = false; + + CGMPointer aPointer; + ImportCGMPointer FncImportCGM = aPointer.get(); + if (FncImportCGM && mxModel.is()) + { + OUString aFileURL( mrMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + sal_uInt32 nRetValue; + + if( mrDocument.GetPageCount() == 0 ) + mrDocument.CreateFirstPages(); + + CreateStatusIndicator(); + std::unique_ptr<SvStream> xIn(::utl::UcbStreamHelper::CreateStream(aFileURL, StreamMode::READ)); + nRetValue = xIn ? FncImportCGM(*xIn, mxModel, mxStatusIndicator) : 0; + + if( nRetValue ) + { + bRet = true; + + if( ( nRetValue &~0xff000000 ) != 0xffffff ) // maybe the backgroundcolor is already white + { // so we must not set a master page + mrDocument.StopWorkStartupDelay(); + SdPage* pSdPage = mrDocument.GetMasterSdPage(0, PageKind::Standard); + + if(pSdPage) + { + // set PageFill to given color + const Color aColor(static_cast<sal_uInt8>(nRetValue >> 16), static_cast<sal_uInt8>(nRetValue >> 8), static_cast<sal_uInt8>(nRetValue >> 16)); + pSdPage->getSdrPageProperties().PutItem(XFillColorItem(OUString(), aColor)); + pSdPage->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_SOLID)); + } + } + } + } + return bRet; +} + +bool SdCGMFilter::Export() +{ + // No ExportCGM function exists(!) + return false; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportCGM(SvStream &rStream) +{ + SdDLL::Init(); + + ::sd::DrawDocShellRef xDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::EMBEDDED, false, DocumentType::Impress); + + CGMPointer aPointer; + + xDocShRef->GetDoc()->EnableUndo(false); + bool bRet = aPointer.get()(rStream, xDocShRef->GetModel(), css::uno::Reference<css::task::XStatusIndicator>()) == 0; + + xDocShRef->DoClose(); + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/eppt.cxx b/sd/source/filter/eppt/eppt.cxx new file mode 100644 index 000000000..6f58d919e --- /dev/null +++ b/sd/source/filter/eppt/eppt.cxx @@ -0,0 +1,1464 @@ +/* -*- 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 "eppt.hxx" +#include "epptdef.hxx" +#include "pptexanimations.hxx" +#include <o3tl/any.hxx> +#include <tools/globname.hxx> +#include <rtl/ustring.hxx> +#include <tools/stream.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdoole2.hxx> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/office/XAnnotation.hpp> +#include <com/sun/star/office/XAnnotationAccess.hpp> +#include <com/sun/star/office/XAnnotationEnumeration.hpp> +#include <com/sun/star/presentation/AnimationSpeed.hpp> +#include <com/sun/star/presentation/XPresentationSupplier.hpp> +#include <com/sun/star/presentation/XCustomPresentationSupplier.hpp> +#include <com/sun/star/geometry/RealPoint2D.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <comphelper/sequence.hxx> +#include <tools/zcodec.hxx> +#include <filter/msfilter/classids.hxx> +#include <filter/msfilter/msoleexp.hxx> +#include <filter/msfilter/msdffimp.hxx> +#include <filter/msfilter/svxmsbas.hxx> +#include <editeng/flditem.hxx> +#include <sfx2/docinf.hxx> +#include <oox/export/utils.hxx> +#include <oox/ole/olehelper.hxx> +#include <memory> + +class SfxObjectShell; + // complete SfxObjectShell for SaveVBA under -fsanitize=function + +using namespace com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::beans::XPropertySet; + +//============================ PPTWriter ================================== + +PPTWriter::PPTWriter( tools::SvRef<SotStorage> const & rSvStorage, + css::uno::Reference< css::frame::XModel > const & rXModel, + css::uno::Reference< css::task::XStatusIndicator > const & rXStatInd, + SvMemoryStream* pVBA, sal_uInt32 nCnvrtFlags ) : + PPTWriterBase ( rXModel, rXStatInd ), + mnCnvrtFlags ( nCnvrtFlags ), + mbStatus ( false ), + mnStatMaxValue ( 0 ), + mnLatestStatValue ( 0 ), + mnTextStyle( 0 ), + mbFontIndependentLineSpacing( false ), + mnTextSize( 0 ), + mrStg ( rSvStorage ), + mnVBAOleOfs ( 0 ), + mpVBA ( pVBA ), + mnExEmbed ( 0 ), + mpExEmbed ( new SvMemoryStream ), + mnPagesWritten ( 0 ), + mnTxId ( 0x7a2f64 ), + mnDiaMode ( 0 ), + mnShapeMasterTitle ( 0 ), + mnShapeMasterBody ( 0 ) +{ +} + +void PPTWriter::exportPPTPre( const std::vector< css::beans::PropertyValue >& rMediaData ) +{ + if ( !mrStg.is() ) + return; + + if ( mXStatusIndicator.is() ) + { + mbStatusIndicator = true; + mnStatMaxValue = ( mnPages + mnMasterPages ) * 5; + mXStatusIndicator->start( "PowerPoint Export", mnStatMaxValue + ( mnStatMaxValue >> 3 ) ); + } + + SvGlobalName aGName(MSO_PPT8_CLASSID); + mrStg->SetClass( aGName, SotClipboardFormatId::NONE, "MS PowerPoint 97" ); + + if ( !ImplCreateCurrentUserStream() ) + return; + + mpStrm = mrStg->OpenSotStream( "PowerPoint Document" ); + if ( !mpStrm ) + return; + + if ( !mpPicStrm ) + mpPicStrm = mrStg->OpenSotStream( "Pictures" ); + + auto aIter = std::find_if(rMediaData.begin(), rMediaData.end(), + [](const css::beans::PropertyValue& rProp) { return rProp.Name == "BaseURI"; }); + if (aIter != rMediaData.end()) + (*aIter).Value >>= maBaseURI; + mpPptEscherEx.reset( new PptEscherEx( *mpStrm, maBaseURI ) ); +} + +void PPTWriter::exportPPTPost( ) +{ + if ( !ImplCloseDocument() ) + return; + + if ( mbStatusIndicator ) + { + mXStatusIndicator->setText( "PowerPoint Export" ); + sal_uInt32 nValue = mnStatMaxValue + ( mnStatMaxValue >> 3 ); + if ( nValue > mnLatestStatValue ) + { + mXStatusIndicator->setValue( nValue ); + mnLatestStatValue = nValue; + } + } + + ImplWriteOLE(); + + ImplWriteVBA(); + + ImplWriteAtomEnding(); + + ImplCreateDocumentSummaryInformation(); + + mbStatus = true; +}; + +static void ImplExportComments( const uno::Reference< drawing::XDrawPage >& xPage, SvMemoryStream& rBinaryTagData10Atom ); + +void PPTWriter::ImplWriteSlide( sal_uInt32 nPageNum, sal_uInt32 nMasterNum, sal_uInt16 nMode, + bool bHasBackground, Reference< XPropertySet > const & aXBackgroundPropSet ) +{ + Any aAny; + + const PHLayout& rLayout = GetLayout( mXPagePropSet ); + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_Slide | nPageNum, mpStrm->Tell() ); + mpPptEscherEx->OpenContainer( EPP_Slide ); + mpPptEscherEx->AddAtom( 24, EPP_SlideAtom, 2 ); + mpStrm->WriteInt32( static_cast<sal_Int32>(rLayout.nLayout) ); + mpStrm->WriteBytes(rLayout.nPlaceHolder, 8); // placeholderIDs (8 parts) + mpStrm->WriteUInt32( nMasterNum | 0x80000000 ) // master ID (equals 0x80000000 on a master page) + .WriteUInt32( nPageNum + 0x100 ) // notes ID (equals null if no notes are present) + .WriteUInt16( nMode ) + .WriteUInt16( 0 ); // padword + + mnDiaMode = 0; + bool bVisible = true; + css::presentation::FadeEffect eFe = css::presentation::FadeEffect_NONE; + + if ( GetPropertyValue( aAny, mXPagePropSet, "Visible" ) ) + aAny >>= bVisible; + if ( GetPropertyValue( aAny, mXPagePropSet, "Change" ) ) + { + switch ( *o3tl::doAccess<sal_Int32>(aAny) ) + { + case 1 : // automatic + mnDiaMode++; + [[fallthrough]]; + case 2 : // semi-automatic + mnDiaMode++; + break; + default : + case 0 : // manual + break; + } + } + if ( GetPropertyValue( aAny, mXPagePropSet, "Effect" ) ) + aAny >>= eFe; + + sal_uInt32 nSoundRef = 0; + bool bIsSound = false; + bool bStopSound = false; + bool bLoopSound = false; + + if ( GetPropertyValue( aAny, mXPagePropSet, "Sound" ) ) + { + OUString aSoundURL; + if ( aAny >>= aSoundURL ) + { + nSoundRef = maSoundCollection.GetId( aSoundURL ); + bIsSound = true; + } + else + aAny >>= bStopSound; + } + if ( GetPropertyValue( aAny, mXPagePropSet, "LoopSound" ) ) + aAny >>= bLoopSound; + + bool bNeedsSSSlideInfoAtom = !bVisible + || ( mnDiaMode == 2 ) + || bIsSound + || bStopSound + || ( eFe != css::presentation::FadeEffect_NONE ); + if ( bNeedsSSSlideInfoAtom ) + { + sal_uInt8 nDirection = 0; + sal_uInt8 nTransitionType = 0; + sal_uInt16 nBuildFlags = 1; // advance by mouseclick + sal_Int32 nSlideTime = 0; // still has to !!! + sal_uInt8 nSpeed = 1; + + if ( GetPropertyValue( aAny, mXPagePropSet, "TransitionDuration" ) ) + { + css::presentation::AnimationSpeed aAs; + double fTransitionDuration = -1.0; + aAny >>= fTransitionDuration; + + if (fTransitionDuration >= 0) + { + if (fTransitionDuration <= 0.5) + { + aAs = css::presentation::AnimationSpeed::AnimationSpeed_FAST; + } + else if (fTransitionDuration >= 1.0) + { + aAs = css::presentation::AnimationSpeed::AnimationSpeed_SLOW; + } + else + { + aAs = css::presentation::AnimationSpeed::AnimationSpeed_MEDIUM; + } + } + else + aAs = css::presentation::AnimationSpeed::AnimationSpeed_MEDIUM; + + nSpeed = static_cast<sal_uInt8>(aAs); + } + sal_Int16 nTT = 0; + if ( GetPropertyValue( aAny, mXPagePropSet, "TransitionType" ) + && ( aAny >>= nTT ) ) + { + sal_Int16 nTST = 0; + if ( GetPropertyValue( aAny, mXPagePropSet, "TransitionSubtype" ) + && ( aAny >>= nTST ) ) + nTransitionType = GetTransition( nTT, nTST, eFe, 0, nDirection ); + + } + if ( !nTransitionType ) + nTransitionType = GetTransition( eFe, nDirection ); + if ( mnDiaMode == 2 ) // automatic ? + nBuildFlags |= 0x400; + if ( !bVisible ) + nBuildFlags |= 4; + if ( bIsSound ) + nBuildFlags |= 16; + if ( bLoopSound ) + nBuildFlags |= 64; + if ( bStopSound ) + nBuildFlags |= 256; + + if ( GetPropertyValue( aAny, mXPagePropSet, "Duration" ) )// duration of this slide + nSlideTime = *o3tl::doAccess<sal_Int32>(aAny) << 10; // in ticks + + mpPptEscherEx->AddAtom( 16, EPP_SSSlideInfoAtom ); + mpStrm->WriteInt32( nSlideTime ) // standtime in ticks + .WriteUInt32( nSoundRef ) + .WriteUChar( nDirection ) + .WriteUChar( nTransitionType ) + .WriteUInt16( nBuildFlags ) + .WriteUChar( nSpeed ) + .WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ); + } + + ImplCreateHeaderFooters( mXPagePropSet ); + + EscherSolverContainer aSolverContainer; + mpPptEscherEx->OpenContainer( EPP_PPDrawing ); + mpPptEscherEx->OpenContainer( ESCHER_DgContainer ); + mpPptEscherEx->EnterGroup(nullptr,nullptr); + ImplWritePage( rLayout, aSolverContainer, NORMAL, false, nPageNum ); // the shapes of the pages are created in the PPT document + mpPptEscherEx->LeaveGroup(); + + if ( bHasBackground ) + ImplWriteBackground( aXBackgroundPropSet ); + else + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::Background | ShapeFlag::HaveShapeProperty ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_fillRectRight, PPTtoEMU( maDestPageSize.Width ) ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectBottom, PPTtoEMU( maDestPageSize.Width ) ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x120012 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + aPropOpt.AddOpt( ESCHER_Prop_bWMode, ESCHER_wDontShow ); + aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 ); // if true, this is the background shape + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + + aSolverContainer.WriteSolver( *mpStrm ); + + mpPptEscherEx->CloseContainer(); // ESCHER_DgContainer + mpPptEscherEx->CloseContainer(); // EPP_Drawing + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 1 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + + SvMemoryStream aBinaryTagData10Atom; + ImplExportComments( mXDrawPage, aBinaryTagData10Atom ); + SvMemoryStream amsofbtAnimGroup; + ppt::AnimationExporter aExporter( aSolverContainer, maSoundCollection ); + aExporter.doexport( mXDrawPage, amsofbtAnimGroup ); + sal_uInt32 nmsofbtAnimGroupSize = amsofbtAnimGroup.Tell(); + if ( nmsofbtAnimGroupSize ) + { + { + EscherExAtom aMagic2( aBinaryTagData10Atom, 0x2eeb ); + aBinaryTagData10Atom.WriteUInt32( 0x01c45df9 ) + .WriteUInt32( 0xe1471b30 ); + } + { + EscherExAtom aMagic( aBinaryTagData10Atom, 0x2b00 ); + aBinaryTagData10Atom.WriteUInt32( 0 ); + } + aBinaryTagData10Atom.WriteBytes(amsofbtAnimGroup.GetData(), amsofbtAnimGroup.Tell()); + { + EscherExContainer aMagic2( aBinaryTagData10Atom, 0x2b02 ); + } + } + if ( aBinaryTagData10Atom.Tell() ) + { + EscherExContainer aProgTags ( *mpStrm, EPP_ProgTags ); + EscherExContainer aProgBinaryTag( *mpStrm, EPP_ProgBinaryTag ); + { + EscherExAtom aCString( *mpStrm, EPP_CString ); + mpStrm->WriteUInt32( 0x5f005f ) + .WriteUInt32( 0x50005f ) + .WriteUInt32( 0x540050 ) + .WriteUInt16( 0x31 ) + .WriteUInt16( 0x30 ); + } + { + EscherExAtom aBinaryTagData( *mpStrm, EPP_BinaryTagData ); + mpStrm->WriteBytes(aBinaryTagData10Atom.GetData(), aBinaryTagData10Atom.Tell()); + } + } + mpPptEscherEx->CloseContainer(); // EPP_Slide +} + +void PPTWriter::ImplWriteSlideMaster( sal_uInt32 nPageNum, Reference< XPropertySet > const & aXBackgroundPropSet ) +{ + if (!aXBackgroundPropSet) + return; + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_MainMaster | nPageNum, mpStrm->Tell() ); + mpPptEscherEx->OpenContainer( EPP_MainMaster ); + mpPptEscherEx->AddAtom( 24, EPP_SlideAtom, 2 ); + mpStrm->WriteInt32( static_cast<sal_Int32>(EppLayout::TITLEANDBODYSLIDE) ) // slide layout -> title and body slide + .WriteUChar( 1 ).WriteUChar( 2 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ) // placeholderID + .WriteUInt32( 0 ) // master ID (equals null at a master page) + .WriteUInt32( 0 ) // notes ID (equals null if no notes are present) + .WriteUInt16( 0 ) // Bit 1: Follow master objects, Bit 2: Follow master scheme, Bit 3: Follow master background + .WriteUInt16( 0 ); // padword + + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xff0000 ).WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x00ffff ).WriteUInt32( 0x0099ff ).WriteUInt32( 0xffff00 ).WriteUInt32( 0x0000ff ).WriteUInt32( 0x969696 ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xccffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x336666 ).WriteUInt32( 0x008080 ).WriteUInt32( 0x339933 ).WriteUInt32( 0x000080 ).WriteUInt32( 0xcc3300 ).WriteUInt32( 0x66ccff ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x333333 ).WriteUInt32( 0x000000 ).WriteUInt32( 0xdddddd ).WriteUInt32( 0x808080 ).WriteUInt32( 0x4d4d4d ).WriteUInt32( 0xeaeaea ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x66ccff ).WriteUInt32( 0xff0000 ).WriteUInt32( 0xcc00cc ).WriteUInt32( 0xc0c0c0 ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0xc0c0c0 ).WriteUInt32( 0xff6600 ).WriteUInt32( 0x0000ff ).WriteUInt32( 0x009900 ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0xff9933 ).WriteUInt32( 0xccff99 ).WriteUInt32( 0xcc00cc ).WriteUInt32( 0xb2b2b2 ); + + for ( int nInstance = EPP_TEXTTYPE_Title; nInstance <= EPP_TEXTTYPE_QuarterBody; nInstance++ ) + { + if ( nInstance == EPP_TEXTTYPE_notUsed ) + continue; + + // the auto color is dependent to the page background,so we have to set a page that is in the right context + if ( nInstance == EPP_TEXTTYPE_Notes ) + (void)GetPageByIndex(0, NOTICE); + else + (void)GetPageByIndex(0, MASTER); + + mpPptEscherEx->BeginAtom(); + + bool bSimpleText = false; + + mpStrm->WriteUInt16( 5 ); // paragraph count + + for ( sal_uInt16 nLev = 0; nLev < 5; nLev++ ) + { + if ( nInstance >= EPP_TEXTTYPE_CenterBody ) + { + bSimpleText = true; + mpStrm->WriteUInt16( nLev ); + } + mpStyleSheet->mpParaSheet[ nInstance ]->Write( *mpStrm, nLev, bSimpleText, mXPagePropSet ); + mpStyleSheet->mpCharSheet[ nInstance ]->Write( *mpStrm, nLev, bSimpleText, mXPagePropSet ); + } + mpPptEscherEx->EndAtom( EPP_TxMasterStyleAtom, 0, nInstance ); + } + GetPageByIndex( nPageNum, MASTER ); + + EscherSolverContainer aSolverContainer; + + mpPptEscherEx->OpenContainer( EPP_PPDrawing ); + mpPptEscherEx->OpenContainer( ESCHER_DgContainer ); + + mpPptEscherEx->EnterGroup(nullptr,nullptr); + ImplWritePage( GetLayout( 0 ), aSolverContainer, MASTER, true ); // the shapes of the pages are created in the PPT document + mpPptEscherEx->LeaveGroup(); + + ImplWriteBackground( aXBackgroundPropSet ); + + aSolverContainer.WriteSolver( *mpStrm ); + + mpPptEscherEx->CloseContainer(); // ESCHER_DgContainer + mpPptEscherEx->CloseContainer(); // EPP_Drawing + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 1 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + + if ( aBuExMasterStream.Tell() ) + { + ImplProgTagContainer( mpStrm.get(), &aBuExMasterStream ); + } + mpPptEscherEx->CloseContainer(); // EPP_MainMaster +}; + +PPTWriter::~PPTWriter() +{ + mpExEmbed.reset(); + mpPptEscherEx.reset(); + mpCurUserStrm.clear(); + mpPicStrm.clear(); + mpStrm.clear(); + maStyleSheetList.clear(); + maExOleObj.clear(); + if ( mbStatusIndicator ) + mXStatusIndicator->end(); +} + +bool PPTWriter::ImplCreateCurrentUserStream() +{ + mpCurUserStrm = mrStg->OpenSotStream( "Current User" ); + if ( !mpCurUserStrm ) + return false; + char pUserName[] = "Current User"; + sal_uInt32 nLenOfUserName = strlen( pUserName ); + sal_uInt32 nSizeOfRecord = 0x14 + ( ( nLenOfUserName + 4 ) & ~ 3 ); + + mpCurUserStrm->WriteUInt16( 0 ).WriteUInt16( EPP_CurrentUserAtom ).WriteUInt32( nSizeOfRecord ); + mpCurUserStrm->WriteUInt32( 0x14 ) // Len + .WriteUInt32( 0xe391c05f ); // Magic + + sal_uInt32 nEditPos = mpCurUserStrm->Tell(); + mpCurUserStrm->WriteUInt32( 0x0 ) // OffsetToCurrentEdit; + .WriteUInt16( nLenOfUserName ) + .WriteUInt16( 0x3f4 ) // DocFileVersion + .WriteUChar( 3 ) // MajorVersion + .WriteUChar( 0 ) // MinorVersion + .WriteUInt16( 0 ); // Pad Word + pUserName[ nLenOfUserName ] = 8; + mpCurUserStrm->WriteBytes(pUserName, nLenOfUserName + 1); + for ( sal_uInt32 i = 0x15 + nLenOfUserName; i < nSizeOfRecord; i++ ) + { + mpCurUserStrm->WriteUChar( 0 ); // pad bytes + } + mpCurUserStrm->Seek( nEditPos ); + return true; +}; + +void PPTWriter::ImplCreateDocumentSummaryInformation() +{ + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + mXModel, uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps( + xDPS->getDocumentProperties()); + + if (!xDocProps.is()) + return; + + // no idea what this is... + static const sal_Int8 aGuid[ 0x52 ] = + { + 0x4e, 0x00, 0x00, 0x00, + '{',0,'D',0,'B',0,'1',0,'A',0,'C',0,'9',0,'6',0,'4',0,'-',0, + 'E',0,'3',0,'9',0,'C',0,'-',0,'1',0,'1',0,'D',0,'2',0,'-',0, + 'A',0,'1',0,'E',0,'F',0,'-',0,'0',0,'0',0,'6',0,'0',0,'9',0, + '7',0,'D',0,'A',0,'5',0,'6',0,'8',0,'9',0,'}',0 + }; + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + uno::Sequence<sal_Int8> aGuidSeq(aGuid, 0x52); + + SvMemoryStream aHyperBlob; + ImplCreateHyperBlob( aHyperBlob ); + + auto nHyperLength = static_cast<sal_Int32>(aHyperBlob.Tell()); + const sal_Int8* pBlob( + static_cast<const sal_Int8*>(aHyperBlob.GetData())); + auto aHyperSeq = comphelper::arrayToSequence<sal_Int8>(pBlob, nHyperLength); + + if ( mnCnvrtFlags & 0x8000 ) + { + uno::Sequence<sal_Int8> aThumbSeq; + if ( GetPageByIndex( 0, NORMAL ) && ImplGetPropertyValue( mXPagePropSet, "PreviewBitmap" ) ) + { + aThumbSeq = *o3tl::doAccess<uno::Sequence<sal_Int8>>(mAny); + } + sfx2::SaveOlePropertySet( xDocProps, mrStg.get(), + &aThumbSeq, &aGuidSeq, &aHyperSeq); + } + else + { + sfx2::SaveOlePropertySet( xDocProps, mrStg.get(), + nullptr, &aGuidSeq, &aHyperSeq ); + } +} + +void PPTWriter::ImplWriteExtParaHeader( SvMemoryStream& rSt, sal_uInt32 nRef, sal_uInt32 nInstance, sal_uInt32 nSlideId ) +{ + if ( rSt.Tell() ) + { + aBuExOutlineStream.WriteUInt32( ( EPP_PST_ExtendedParagraphHeaderAtom << 16 ) + | ( nRef << 4 ) ) + .WriteUInt32( 8 ) + .WriteUInt32( nSlideId ) + .WriteUInt32( nInstance ); + aBuExOutlineStream.WriteBytes(rSt.GetData(), rSt.Tell()); + } +} + +void PPTWriter::ImplCreateHeaderFooterStrings( SvStream& rStrm, css::uno::Reference< css::beans::XPropertySet > const & rXPagePropSet ) +{ + if ( !rXPagePropSet.is() ) + return; + + OUString aString; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "HeaderText", true ) ) + { + if ( aAny >>= aString ) + PPTWriter::WriteCString( rStrm, aString, 1 ); + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "FooterText", true ) ) + { + if ( aAny >>= aString ) + PPTWriter::WriteCString( rStrm, aString, 2 ); + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "DateTimeText", true ) ) + { + if ( aAny >>= aString ) + PPTWriter::WriteCString( rStrm, aString ); + } +} + +void PPTWriter::ImplCreateHeaderFooters( css::uno::Reference< css::beans::XPropertySet > const & rXPagePropSet ) +{ + if ( !rXPagePropSet.is() ) + return; + + bool bVal = false; + sal_uInt32 nVal = 0; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsHeaderVisible", true ) ) + { + if ( ( aAny >>= bVal ) && bVal ) + nVal |= 0x100000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsFooterVisible", true ) ) + { + if ( ( aAny >>= bVal ) && bVal ) + nVal |= 0x200000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsDateTimeVisible", true ) ) + { + if ( ( aAny >>= bVal ) && bVal ) + nVal |= 0x010000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsPageNumberVisible", true ) ) + { + if ( ( aAny >>= bVal ) && bVal ) + nVal |= 0x080000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsDateTimeFixed", true ) ) + { + if ( ( aAny >>= bVal ) && !bVal ) + nVal |= 0x20000; + else + nVal |= 0x40000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "DateTimeFormat", true ) ) + { + sal_Int32 nFormat = *o3tl::doAccess<sal_Int32>(aAny); + SvxDateFormat eDateFormat = static_cast<SvxDateFormat>( nFormat & 0xf ); + SvxTimeFormat eTimeFormat = static_cast<SvxTimeFormat>( ( nFormat >> 4 ) & 0xf ); + switch( eDateFormat ) + { + case SvxDateFormat::F : + nFormat = 1; + break; + case SvxDateFormat::D : + nFormat = 2; + break; + case SvxDateFormat::C : + nFormat = 4; + break; + default: + case SvxDateFormat::A : + nFormat = 0; + } + switch( eTimeFormat ) + { + case SvxTimeFormat::HH24_MM : + nFormat = 9; + break; + case SvxTimeFormat::HH12_MM : + nFormat = 11; + break; + case SvxTimeFormat::HH24_MM_SS : + nFormat = 10; + break; + case SvxTimeFormat::HH12_MM_SS : + nFormat = 12; + break; + default: + break; + } + nVal |= nFormat; + } + + mpPptEscherEx->OpenContainer( EPP_HeadersFooters ); + mpPptEscherEx->AddAtom( 4, EPP_HeadersFootersAtom ); + mpStrm->WriteUInt32( nVal ); + ImplCreateHeaderFooterStrings( *mpStrm, rXPagePropSet ); + mpPptEscherEx->CloseContainer(); +} + +bool PPTWriter::ImplCreateDocument() +{ + sal_uInt32 i; + sal_uInt16 nSlideType = EPP_SLIDESIZE_TYPECUSTOM; + + sal_uInt32 nWidth = maDestPageSize.Width; + sal_uInt32 nHeight = maDestPageSize.Height; + + if ( ( nWidth == 0x1680 ) && ( nHeight == 0x10e0 ) ) + nSlideType = EPP_SLIDESIZE_TYPEONSCREEN; + else if ( ( nWidth == 0x1200 ) && ( nHeight == 0x240 ) ) + nSlideType = EPP_SLIDESIZE_TYPEBANNER; + else if ( ( nWidth == 0x1950 ) && ( nHeight == 0x10e0 ) ) + nSlideType = EPP_SLIDESIZE_TYPE35MM; + else if ( ( nWidth == 0x1860 ) && ( nHeight == 0x10e0 ) ) + nSlideType = EPP_SLIDESIZE_TYPEA4PAPER; + + mpPptEscherEx->OpenContainer( EPP_Document ); + // CREATE DOCUMENT ATOM + mpPptEscherEx->AddAtom( 40, EPP_DocumentAtom, 1 ); + mpStrm->WriteUInt32( nWidth ) // Slide Size in Master coordinates X + .WriteUInt32( nHeight ) // " " " " " Y + .WriteInt32( maNotesPageSize.Width ) // Notes Page Size X + .WriteInt32( maNotesPageSize.Height ) // " " " Y + .WriteInt32( 1 ).WriteInt32( 2 ); // the scale used when the Powerpoint document is embedded. the default is 1:2 + mpPptEscherEx->InsertPersistOffset( EPP_MAINNOTESMASTER_PERSIST_KEY, mpStrm->Tell() ); + mpStrm->WriteUInt32( 0 ) // Reference to NotesMaster ( 0 if none ); + .WriteUInt32( 0 ) // Reference to HandoutMaster ( 0 if none ); + .WriteInt16( 1 ) // Number of the first slide; + .WriteUInt16( nSlideType ) // Size of the document slides ( default: EPP_SLIDESIZETYPEONSCREEN ) + .WriteUChar( 0 ) // bool1 indicates if document was saved with embedded true type fonts + .WriteUChar( 0 ) // bool1 indicates if the placeholders on the title slide are omitted + .WriteUChar( 0 ) // bool1 right to left ( flag for Bidi version ) + .WriteUChar( 1 ); // bool1 visibility of comments shapes + + mpPptEscherEx->PtInsert( EPP_Persist_Document, mpStrm->Tell() ); + + mpPptEscherEx->OpenContainer( EPP_HeadersFooters, 3 ); //Master footer (default) + mpPptEscherEx->AddAtom( 4, EPP_HeadersFootersAtom ); + mpStrm->WriteUInt32( 0x25000d ); + if ( GetPageByIndex( 0, MASTER ) ) + ImplCreateHeaderFooterStrings( *mpStrm, mXPagePropSet ); + mpPptEscherEx->CloseContainer(); + mpPptEscherEx->OpenContainer( EPP_HeadersFooters, 4 ); //NotesMaster footer (default) + mpPptEscherEx->AddAtom( 4, EPP_HeadersFootersAtom ); + mpStrm->WriteUInt32( 0x3d000d ); + if ( GetPageByIndex( 0, NOTICE ) ) + ImplCreateHeaderFooterStrings( *mpStrm, mXPagePropSet ); + mpPptEscherEx->CloseContainer(); + + mpPptEscherEx->OpenContainer( EPP_SlideListWithText ); // animation information for the slides + + for ( i = 0; i < mnPages; i++ ) + { + mpPptEscherEx->AddAtom( 20, EPP_SlidePersistAtom ); + mpPptEscherEx->InsertPersistOffset( EPP_MAINSLIDE_PERSIST_KEY | i, mpStrm->Tell() ); + mpStrm->WriteUInt32( 0 ) // psrReference - logical reference to the slide persist object ( EPP_MAINSLIDE_PERSIST_KEY ) + .WriteUInt32( 4 ) // flags - only bit 3 used, if set then slide contains shapes other than placeholders + .WriteInt32( 0 ) // numberTexts - number of placeholder texts stored with the persist object. Allows to display outline view without loading the slide persist objects + .WriteInt32( i + 0x100 ) // slideId - Unique slide identifier, used for OLE link monikers for example + .WriteUInt32( 0 ); // reserved, usually 0 + + if ( !GetPageByIndex( i, NORMAL ) ) // very exciting: once again through all pages + return false; + SetCurrentStyleSheet( GetMasterIndex( NORMAL ) ); + + css::uno::Reference< css::container::XNamed > + aXName( mXDrawPage, css::uno::UNO_QUERY ); + + if ( aXName.is() ) + maSlideNameList.push_back( aXName->getName() ); + else + maSlideNameList.emplace_back( ); + } + mpPptEscherEx->CloseContainer(); // EPP_SlideListWithText + + mpPptEscherEx->OpenContainer( EPP_SlideListWithText, 2 ); // animation information for the notes + for( i = 0; i < mnPages; i++ ) + { + mpPptEscherEx->AddAtom( 20, EPP_SlidePersistAtom ); + mpPptEscherEx->InsertPersistOffset( EPP_MAINNOTES_PERSIST_KEY | i, mpStrm->Tell() ); + mpStrm->WriteUInt32( 0 ) + .WriteUInt32( 4 ) + .WriteInt32( 0 ) + .WriteInt32( i + 0x100 ) + .WriteUInt32( 0 ); + } + mpPptEscherEx->CloseContainer(); // EPP_SlideListWithText + + css::uno::Reference< css::presentation::XPresentationSupplier > + aXPresSupplier( mXModel, css::uno::UNO_QUERY ); + if ( aXPresSupplier.is() ) + { + css::uno::Reference< css::presentation::XPresentation > aXPresentation( aXPresSupplier->getPresentation() ); + if ( aXPresentation.is() ) + { + mXPropSet.set( aXPresentation, css::uno::UNO_QUERY ); + if ( mXPropSet.is() ) + { + OUString aCustomShow; + sal_uInt32 const nPenColor = 0x1000000; + sal_Int32 const nRestartTime = 0x7fffffff; + sal_Int16 nStartSlide = 0; + sal_Int16 nEndSlide = 0; + sal_uInt32 nFlags = 0; // Bit 0: Auto advance + // Bit 1 Skip builds ( do not allow slide effects ) + // Bit 2 Use slide range + // Bit 3 Use named show + // Bit 4 Browse mode on + // Bit 5 Kiosk mode on + // Bit 6 Skip narration + // Bit 7 loop continuously + // Bit 8 show scrollbar + + if ( ImplGetPropertyValue( "CustomShow" ) ) + { + aCustomShow = *o3tl::doAccess<OUString>(mAny); + if ( !aCustomShow.isEmpty() ) + { + nFlags |= 8; + } + } + if ( ( nFlags & 8 ) == 0 ) + { + if ( ImplGetPropertyValue( "FirstPage" ) ) + { + auto aSlideName = o3tl::doAccess<OUString>(mAny); + + std::vector<OUString>::const_iterator pIter = std::find( + maSlideNameList.begin(),maSlideNameList.end(), *aSlideName); + + if (pIter != maSlideNameList.end()) + { + nStartSlide = pIter - maSlideNameList.begin() + 1; + nFlags |= 4; + nEndSlide = static_cast<sal_uInt16>(mnPages); + } + } + } + + if ( ImplGetPropertyValue( "IsAutomatic" ) ) + { + bool bBool = false; + mAny >>= bBool; + if ( !bBool ) + nFlags |= 1; + } + + if ( ImplGetPropertyValue( "IsEndless" ) ) + { + bool bBool = false; + mAny >>= bBool; + if ( bBool ) + nFlags |= 0x80; + } + if ( ImplGetPropertyValue( "IsFullScreen" ) ) + { + bool bBool = false; + mAny >>= bBool; + if ( !bBool ) + nFlags |= 0x11; + } + + mpPptEscherEx->AddAtom( 80, EPP_SSDocInfoAtom, 1 ); + mpStrm->WriteUInt32( nPenColor ).WriteInt32( nRestartTime ).WriteInt16( nStartSlide ).WriteInt16( nEndSlide ); + + sal_uInt32 nCustomShowNameLen = aCustomShow.getLength(); + if ( nCustomShowNameLen > 31 ) + nCustomShowNameLen = 31; + if ( nCustomShowNameLen ) // named show identifier + { + const sal_Unicode* pCustomShow = aCustomShow.getStr(); + for ( i = 0; i < nCustomShowNameLen; i++ ) + { + mpStrm->WriteUInt16( pCustomShow[ i ] ); + } + } + for ( i = nCustomShowNameLen; i < 32; i++, mpStrm->WriteUInt16( 0 ) ) ; + + mpStrm->WriteUInt32( nFlags ); + css::uno::Reference< css::presentation::XCustomPresentationSupplier > aXCPSup( mXModel, css::uno::UNO_QUERY ); + if ( aXCPSup.is() ) + { + css::uno::Reference< css::container::XNameContainer > aXCont( aXCPSup->getCustomPresentations() ); + if ( aXCont.is() ) + { + const css::uno::Sequence< OUString> aNameSeq( aXCont->getElementNames() ); + if ( aNameSeq.hasElements() ) + { + mpPptEscherEx->OpenContainer( EPP_NamedShows ); + sal_uInt32 nCustomShowIndex = 0; + for( OUString const & customShowName : aNameSeq ) + { + if ( !customShowName.isEmpty() ) + { + mpPptEscherEx->OpenContainer( EPP_NamedShow, nCustomShowIndex++ ); + + sal_uInt32 nNamedShowLen = customShowName.getLength(); + if ( nNamedShowLen > 31 ) + nNamedShowLen = 31; + mpPptEscherEx->AddAtom( nNamedShowLen << 1, EPP_CString ); + const sal_Unicode* pCustomShowName = customShowName.getStr(); + for ( sal_uInt32 k = 0; k < nNamedShowLen; ++k ) + mpStrm->WriteUInt16( pCustomShowName[ k ] ); + mAny = aXCont->getByName( customShowName ); + css::uno::Reference< css::container::XIndexContainer > aXIC; + if ( mAny >>= aXIC ) + { + mpPptEscherEx->BeginAtom(); + + sal_Int32 nSlideCount = aXIC->getCount(); + for ( sal_Int32 j = 0; j < nSlideCount; j++ ) // number of slides + { + mAny = aXIC->getByIndex( j ); + css::uno::Reference< css::drawing::XDrawPage > aXDrawPage; + if ( mAny >>= aXDrawPage ) + { + css::uno::Reference< css::container::XNamed > aXName( aXDrawPage, css::uno::UNO_QUERY ); + if ( aXName.is() ) + { + OUString aSlideName( aXName->getName() ); + std::vector<OUString>::const_iterator pIter = std::find( + maSlideNameList.begin(),maSlideNameList.end(),aSlideName); + + if (pIter != maSlideNameList.end()) + { + sal_uInt32 nPageNumber = pIter - maSlideNameList.begin(); + mpStrm->WriteUInt32( nPageNumber + 0x100 ); // unique slide id + } + } + } + } + mpPptEscherEx->EndAtom( EPP_NamedShowSlides ); + } + mpPptEscherEx->CloseContainer(); // EPP_NamedShow + } + } + mpPptEscherEx->CloseContainer(); // EPP_NamedShows + } + } + } + } + } + } + mpPptEscherEx->AddAtom( 0, EPP_EndDocument ); + mpPptEscherEx->CloseContainer(); // EPP_Document + return true; +}; + +void PPTWriter::ImplCreateHyperBlob( SvMemoryStream& rStrm ) +{ + sal_uInt32 nCurrentOfs, nParaOfs, nParaCount = 0; + + nParaOfs = rStrm.Tell(); + rStrm.WriteUInt32( 0 ); // property size + rStrm.WriteUInt32( 0 ); // property count + + for ( const auto& rHyperlink : maHyperlink ) + { + nParaCount += 6; + rStrm .WriteUInt32( 3 ) // Type VT_I4 + .WriteUInt32( 7 ) // (VTI4 - Private1) + .WriteUInt32( 3 ) // Type VT_I4 + .WriteUInt32( 6 ) // (VTI4 - Private2) + .WriteUInt32( 3 ) // Type VT_I4 + .WriteUInt32( 0 ); // (VTI4 - Private3) + + // INFO + // HIWORD: = 0 : do not change anything + // = 1 : replace the hyperlink with the target and subaddress in the following two VTLPWSTR + // = 2 : delete the hyperlink + // LOWORD: = 0 : graphic shown as background (link) + // = 1 : graphic shown as shape (link) + // = 2 : graphic is used to fill a shape + // = 3 : graphic used to fill a shape outline (future use) + // = 4 : hyperlink attached to a shape + // = 5 : " " " " (Word) field + // = 6 : " " " " (Excel) range + // = 7 : " " " " (PPT) text range + // = 8 : " " " " (Project) task + + sal_Int32 nUrlLen = rHyperlink.aURL.getLength(); + const OUString& rUrl = rHyperlink.aURL; + + sal_uInt32 const nInfo = 7; + + rStrm .WriteUInt32( 3 ) // Type VT_I4 + .WriteUInt32( nInfo ); // Info + + switch( rHyperlink.nType & 0xff ) + { + case 1 : // click action to slidenumber + { + rStrm.WriteUInt32( 0x1f ).WriteUInt32( 1 ).WriteUInt32( 0 ); // path + rStrm.WriteUInt32( 0x1f ).WriteUInt32( nUrlLen + 1 ); + for ( sal_Int32 i = 0; i < nUrlLen; i++ ) + { + rStrm.WriteUInt16( rUrl[ i ] ); + } + rStrm.WriteUInt16( 0 ); + } + break; + case 2 : + { + sal_Int32 i; + + rStrm .WriteUInt32( 0x1f ) + .WriteUInt32( nUrlLen + 1 ); + for ( i = 0; i < nUrlLen; i++ ) + { + rStrm.WriteUInt16( rUrl[ i ] ); + } + if ( ! ( i & 1 ) ) + rStrm.WriteUInt16( 0 ); + rStrm .WriteUInt16( 0 ) + .WriteUInt32( 0x1f ) + .WriteUInt32( 1 ) + .WriteUInt32( 0 ); + } + break; + } + } + nCurrentOfs = rStrm.Tell(); + rStrm.Seek( nParaOfs ); + rStrm.WriteUInt32( nCurrentOfs - ( nParaOfs + 4 ) ); + rStrm.WriteUInt32( nParaCount ); + rStrm.Seek( nCurrentOfs ); +} + +bool PPTWriter::ImplCreateMainNotes() +{ + EscherSolverContainer aSolverContainer; + + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_MainNotes, mpStrm->Tell() ); + mpPptEscherEx->OpenContainer( EPP_Notes ); + mpPptEscherEx->AddAtom( 8, EPP_NotesAtom, 1 ); + mpStrm->WriteUInt32( 0x80000001 ) // Number that identifies this slide + .WriteUInt32( 0 ); // follow nothing + mpPptEscherEx->OpenContainer( EPP_PPDrawing ); + mpPptEscherEx->OpenContainer( ESCHER_DgContainer ); + mpPptEscherEx->EnterGroup(nullptr,nullptr); + + ImplWritePage( GetLayout( 20 ), aSolverContainer, NOTICE, true ); + + mpPptEscherEx->LeaveGroup(); + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, ShapeFlag::Background | ShapeFlag::HaveShapeProperty ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0xffffff ); // stock valued fill color + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectRight, 0x68bdde ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectBottom, 0x8b9f8e ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x120012 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_bWMode, ESCHER_wDontShow ); + aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 ); // if true, this is the background shape + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + + aSolverContainer.WriteSolver( *mpStrm ); + + mpPptEscherEx->CloseContainer(); // ESCHER_DgContainer + mpPptEscherEx->CloseContainer(); // EPP_Drawing + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 1 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + mpPptEscherEx->CloseContainer(); // EPP_Notes + return true; +} + +static OUString getInitials( const OUString& rName ) +{ + OUStringBuffer sInitials; + + const sal_Unicode * pStr = rName.getStr(); + sal_Int32 nLength = rName.getLength(); + + while( nLength ) + { + // skip whitespace + while( nLength && (*pStr <= ' ') ) + { + nLength--; pStr++; + } + + // take letter + if( nLength ) + { + sInitials.append( *pStr ); + nLength--; pStr++; + } + + // skip letters until whitespace + while( nLength && (*pStr > ' ') ) + { + nLength--; pStr++; + } + } + + return sInitials.makeStringAndClear(); +} + +void ImplExportComments( const uno::Reference< drawing::XDrawPage >& xPage, SvMemoryStream& rBinaryTagData10Atom ) +{ + try + { + uno::Reference< office::XAnnotationAccess > xAnnotationAccess( xPage, uno::UNO_QUERY_THROW ); + uno::Reference< office::XAnnotationEnumeration > xAnnotationEnumeration( xAnnotationAccess->createAnnotationEnumeration() ); + + sal_Int32 nIndex = 1; + + while( xAnnotationEnumeration->hasMoreElements() ) + { + EscherExContainer aComment10( rBinaryTagData10Atom, EPP_Comment10 ); + { + uno::Reference< office::XAnnotation > xAnnotation( xAnnotationEnumeration->nextElement() ); + + geometry::RealPoint2D aRealPoint2D( xAnnotation->getPosition() ); + Point aPoint(o3tl::convert(aRealPoint2D.X, o3tl::Length::mm, o3tl::Length::master), + o3tl::convert(aRealPoint2D.Y, o3tl::Length::mm, o3tl::Length::master)); + + OUString sAuthor( xAnnotation->getAuthor() ); + uno::Reference< text::XText > xText( xAnnotation->getTextRange() ); + OUString sText( xText->getString() ); + OUString sInitials( getInitials( sAuthor ) ); + util::DateTime aDateTime( xAnnotation->getDateTime() ); + if ( !sAuthor.isEmpty() ) + PPTWriter::WriteCString( rBinaryTagData10Atom, sAuthor ); + if ( !sText.isEmpty() ) + PPTWriter::WriteCString( rBinaryTagData10Atom, sText, 1 ); + if ( !sInitials.isEmpty() ) + PPTWriter::WriteCString( rBinaryTagData10Atom, sInitials, 2 ); + + sal_Int16 nMilliSeconds = static_cast<sal_Int16>(::rtl::math::round(static_cast<double>(aDateTime.NanoSeconds) / 1000000000.0)); + EscherExAtom aCommentAtom10( rBinaryTagData10Atom, EPP_CommentAtom10 ); + rBinaryTagData10Atom.WriteInt32( nIndex++ ) + .WriteInt16( aDateTime.Year ) + .WriteUInt16( aDateTime.Month ) + .WriteUInt16( aDateTime.Day ) // todo: day of week + .WriteUInt16( aDateTime.Day ) + .WriteUInt16( aDateTime.Hours ) + .WriteUInt16( aDateTime.Minutes ) + .WriteUInt16( aDateTime.Seconds ) + .WriteInt16( nMilliSeconds ) + .WriteInt32( aPoint.X() ) + .WriteInt32( aPoint.Y() ); + } + } + } + catch ( uno::Exception& ) + { + } +} + +void PPTWriter::ImplWriteNotes( sal_uInt32 nPageNum ) +{ + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_Notes | nPageNum, mpStrm->Tell() ); + mpPptEscherEx->OpenContainer( EPP_Notes ); + mpPptEscherEx->AddAtom( 8, EPP_NotesAtom, 1 ); + mpStrm->WriteUInt32( nPageNum + 0x100 ) + .WriteUInt16( 3 ) // follow master... + .WriteUInt16( 0 ); + + ImplCreateHeaderFooters( mXPagePropSet ); + + EscherSolverContainer aSolverContainer; + + mpPptEscherEx->OpenContainer( EPP_PPDrawing ); + mpPptEscherEx->OpenContainer( ESCHER_DgContainer ); + mpPptEscherEx->EnterGroup(nullptr,nullptr); + + ImplWritePage( GetLayout( 20 ), aSolverContainer, NOTICE, false ); // the shapes of the pages are created in the PPT document + + mpPptEscherEx->LeaveGroup(); + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, ShapeFlag::Background | ShapeFlag::HaveShapeProperty ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0xffffff ); // stock valued fill color + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectRight, 0x8b9f8e ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectBottom, 0x68bdde ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x120012 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + aPropOpt.AddOpt( ESCHER_Prop_bWMode, ESCHER_wDontShow ); + aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 ); + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + + aSolverContainer.WriteSolver( *mpStrm ); + + mpPptEscherEx->CloseContainer(); // ESCHER_DgContainer + mpPptEscherEx->CloseContainer(); // EPP_Drawing + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 1 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + mpPptEscherEx->CloseContainer(); // EPP_Notes +}; + +void PPTWriter::ImplWriteBackground( css::uno::Reference< css::beans::XPropertySet > const & rXPropSet ) +{ + //************************ ****** + //** DEFAULT BACKGROUND SHAPE ** + + sal_uInt32 nFillColor = 0xffffff; + sal_uInt32 nFillBackColor = 0; + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, ShapeFlag::Background | ShapeFlag::HaveShapeProperty ); + + // #i121183# Use real PageSize in 100th mm + ::tools::Rectangle aRect(Point(0, 0), Size(maPageSize.Width, maPageSize.Height)); + + EscherPropertyContainer aPropOpt( mpPptEscherEx->GetGraphicProvider(), mpPicStrm.get(), aRect ); + aPropOpt.AddOpt( ESCHER_Prop_fillType, ESCHER_FillSolid ); + css::drawing::FillStyle aFS( css::drawing::FillStyle_NONE ); + if ( ImplGetPropertyValue( rXPropSet, "FillStyle" ) ) + mAny >>= aFS; + + switch( aFS ) + { + case css::drawing::FillStyle_GRADIENT : + { + aPropOpt.CreateGradientProperties( rXPropSet ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x1f001e ); + aPropOpt.GetOpt( ESCHER_Prop_fillColor, nFillColor ); + aPropOpt.GetOpt( ESCHER_Prop_fillBackColor, nFillBackColor ); + } + break; + + case css::drawing::FillStyle_BITMAP : + aPropOpt.CreateGraphicProperties( rXPropSet, "FillBitmap", true ); + break; + + case css::drawing::FillStyle_HATCH : + aPropOpt.CreateGraphicProperties( rXPropSet, "FillHatch", true ); + break; + + case css::drawing::FillStyle_SOLID : + { + if ( ImplGetPropertyValue( rXPropSet, "FillColor" ) ) + { + nFillColor = EscherEx::GetColor( *o3tl::doAccess<sal_uInt32>(mAny) ); + nFillBackColor = nFillColor ^ 0xffffff; + } + [[fallthrough]]; + } + case css::drawing::FillStyle_NONE : + default: + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x120012 ); + break; + } + aPropOpt.AddOpt( ESCHER_Prop_fillColor, nFillColor ); + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, nFillBackColor ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectRight, PPTtoEMU( maDestPageSize.Width ) ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectBottom, PPTtoEMU( maDestPageSize.Height ) ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + aPropOpt.AddOpt( ESCHER_Prop_bWMode, ESCHER_bwWhite ); + aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 ); + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer +} + +void PPTWriter::ImplWriteVBA() +{ + if ( mpVBA ) + { + sal_uInt32 nLen = mpVBA->TellEnd(); + if ( nLen > 8 ) + { + nLen -= 8; + mnVBAOleOfs = mpStrm->Tell(); + mpPptEscherEx->BeginAtom(); + mpStrm->WriteBytes(static_cast<sal_Int8 const *>(mpVBA->GetData()) + 8, nLen); + mpPptEscherEx->EndAtom( EPP_ExOleObjStg, 0, 1 ); + } + } +} + +void PPTWriter::ImplWriteOLE( ) +{ + + SvxMSExportOLEObjects aOleExport( mnCnvrtFlags ); + + for ( const auto& rxExOleObjEntry : maExOleObj ) + { + PPTExOleObjEntry* pPtr = rxExOleObjEntry.get(); + std::unique_ptr<SvMemoryStream> pStrm; + pPtr->nOfsB = mpStrm->Tell(); + switch ( pPtr->eType ) + { + case NORMAL_OLE_OBJECT : + { + SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(pPtr->xShape); + if ( auto pSdrOle2Obj = dynamic_cast< SdrOle2Obj* >(pSdrObj) ) + { + const ::uno::Reference < embed::XEmbeddedObject >& xObj( pSdrOle2Obj->GetObjRef() ); + if( xObj.is() ) + { + tools::SvRef<SotStorage> xTempStorage( new SotStorage( new SvMemoryStream(), true ) ); + aOleExport.ExportOLEObject( xObj, *xTempStorage ); + + //TODO/MBA: testing + SvMemoryStream aStream; + tools::SvRef<SotStorage> xCleanStorage( new SotStorage( false, aStream ) ); + xTempStorage->CopyTo( xCleanStorage.get() ); + // create a dummy content stream, the dummy content is necessary for ppt, but not for + // doc files, so we can't share code. + tools::SvRef<SotStorageStream> xStm = xCleanStorage->OpenSotStream( SVEXT_PERSIST_STREAM ); + xStm->WriteUInt32( 0 ) // no ClipboardId + .WriteUInt32( 4 ) // no target device + .WriteUInt32( 1 ) // aspect ratio + .WriteInt32( -1 ) // L-Index + .WriteUInt32( 0 ) // Advanced Flags + .WriteUInt32( 0 ) // compression + .WriteUInt32( 0 ) // Size + .WriteUInt32( 0 ) // " + .WriteUInt32( 0 ); + pStrm = xCleanStorage->CreateMemoryStream(); + } + } + } + break; + + case OCX_CONTROL : + { + if ( pPtr->xControlModel.is() ) + { + OUString aName; + //Initialize the graphic size which will be used on export + css::awt::Size aSize( pPtr->xShape->getSize() ); + tools::SvRef<SotStorage> xDest( new SotStorage( new SvMemoryStream(), true ) ); + bool bOk = oox::ole::MSConvertOCXControls::WriteOCXStream( mXModel, xDest, pPtr->xControlModel, aSize, aName ); + if ( bOk ) + pStrm = xDest->CreateMemoryStream(); + } + } + } + if ( pStrm ) + { + mpPptEscherEx->BeginAtom(); + pStrm->Seek( STREAM_SEEK_TO_END ); + sal_uInt32 npStrmSize = pStrm->Tell(); + mpStrm->WriteUInt32( npStrmSize ); // uncompressed size + + pStrm->Seek( 0 ); + ZCodec aZCodec( 0x8000, 0x8000 ); + aZCodec.BeginCompression(); + aZCodec.Compress( *pStrm, *mpStrm ); + aZCodec.EndCompression(); + pStrm.reset(); + mpPptEscherEx->EndAtom( EPP_ExOleObjStg, 0, 1 ); + } + } +} + +// write PersistantTable and UserEditAtom + +void PPTWriter::ImplWriteAtomEnding() +{ + +#define EPP_LastViewTypeSlideView 1 + + sal_uInt32 i, nPos, nOfs, nPersistOfs = mpStrm->Tell(); + sal_uInt32 nPersistEntrys = 0; + mpStrm->WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ); // skip record header and first entry + + // write document persist + nPersistEntrys++; + mpStrm->WriteUInt32( 0 ); + // write MasterPages persists + for ( i = 0; i < mnMasterPages; i++ ) + { + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_MainMaster | i ); + if ( nOfs ) + { + mpStrm->WriteUInt32( nOfs ); + mpPptEscherEx->InsertAtPersistOffset( EPP_MAINMASTER_PERSIST_KEY | i, ++nPersistEntrys ); + } + } + // write MainNotesMaster persist + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_MainNotes ); + if ( nOfs ) + { + mpStrm->WriteUInt32( nOfs ); + mpPptEscherEx->InsertAtPersistOffset( EPP_MAINNOTESMASTER_PERSIST_KEY, ++nPersistEntrys ); + } + // write slide persists -> we have to write a valid value into EPP_SlidePersistAtome too + for ( i = 0; i < mnPages; i++ ) + { + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_Slide | i ); + if ( nOfs ) + { + mpStrm->WriteUInt32( nOfs ); + mpPptEscherEx->InsertAtPersistOffset( EPP_MAINSLIDE_PERSIST_KEY | i, ++nPersistEntrys ); + } + } + // write Notes persists + for ( i = 0; i < mnPages; i++ ) + { + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_Notes | i ); + if ( nOfs ) + { + mpStrm->WriteUInt32( nOfs ); + mpPptEscherEx->InsertAtPersistOffset( EPP_MAINNOTES_PERSIST_KEY | i, ++nPersistEntrys ); + } + } + // Ole persists + for ( const auto& rxExOleObjEntry : maExOleObj ) + { + PPTExOleObjEntry* pPtr = rxExOleObjEntry.get(); + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_ExObj ); + if ( nOfs ) + { + nPersistEntrys++; + mpStrm->WriteUInt32( pPtr->nOfsB ); + sal_uInt32 nOldPos, nPersOfs = nOfs + pPtr->nOfsA + 16 + 8; // 8 bytes atom header, +16 to the persist entry + nOldPos = mpStrm->Tell(); + mpStrm->Seek( nPersOfs ); + mpStrm->WriteUInt32( nPersistEntrys ); + mpStrm->Seek( nOldPos ); + } + } + // VB persist + if ( mnVBAOleOfs && mpVBA ) + { + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_VBAInfoAtom ); + if ( nOfs ) + { + nPersistEntrys++; + sal_uInt32 n1, n2; + + mpVBA->Seek( 0 ); + mpVBA->ReadUInt32( n1 ) + .ReadUInt32( n2 ); + + mpStrm->WriteUInt32( mnVBAOleOfs ); + sal_uInt32 nOldPos = mpStrm->Tell(); + mpStrm->Seek( nOfs ); // Fill the VBAInfoAtom with the correct index to the persisttable + mpStrm->WriteUInt32( nPersistEntrys ) + .WriteUInt32( n1 ) + .WriteInt32( 2 ); + mpStrm->Seek( nOldPos ); + + } + } + nPos = mpStrm->Tell(); + mpStrm->Seek( nPersistOfs ); + mpPptEscherEx->AddAtom( ( nPersistEntrys + 1 ) << 2, EPP_PersistPtrIncrementalBlock ); // insert Record Header + mpStrm->WriteUInt32( ( nPersistEntrys << 20 ) | 1 ); + mpStrm->Seek( nPos ); + + mpCurUserStrm->WriteUInt32( nPos ); // set offset to current edit + mpPptEscherEx->AddAtom( 28, EPP_UserEditAtom ); + mpStrm->WriteInt32( 0x100 ) // last slide ID + .WriteUInt32( 0x03000dbc ) // minor and major app version that did the save + .WriteUInt32( 0 ) // offset last save, 0 after a full save + .WriteUInt32( nPersistOfs ) // File offset to persist pointers for this save operation + .WriteUInt32( 1 ) // Persist reference to the document persist object + .WriteUInt32( nPersistEntrys ) // max persists written, Seed value for persist object id management + .WriteInt16( EPP_LastViewTypeSlideView ) // last view type + .WriteInt16( 0x12 ); // padword +} + +// - exported function - + +extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool ExportPPT( const std::vector< css::beans::PropertyValue >& rMediaData, + tools::SvRef<SotStorage> const & rSvStorage, + css::uno::Reference< css::frame::XModel > const & rXModel, + css::uno::Reference< css::task::XStatusIndicator > const & rXStatInd, + SvMemoryStream* pVBA, + sal_uInt32 nCnvrtFlags ) +{ + PPTWriter aPPTWriter( rSvStorage, rXModel, rXStatInd, pVBA, nCnvrtFlags ); + aPPTWriter.exportPPT(rMediaData); + bool bStatus = aPPTWriter.IsValid(); + return bStatus; +} + +extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool SaveVBA( SfxObjectShell& rDocShell, SvMemoryStream*& pBas ) +{ + tools::SvRef<SotStorage> xDest( new SotStorage( new SvMemoryStream(), true ) ); + SvxImportMSVBasic aMSVBas( rDocShell, *xDest ); + aMSVBas.SaveOrDelMSVBAStorage( true, "_MS_VBA_Overhead" ); + + tools::SvRef<SotStorage> xOverhead = xDest->OpenSotStorage( "_MS_VBA_Overhead" ); + if ( xOverhead.is() && ( xOverhead->GetError() == ERRCODE_NONE ) ) + { + tools::SvRef<SotStorage> xOverhead2 = xOverhead->OpenSotStorage( "_MS_VBA_Overhead" ); + if ( xOverhead2.is() && ( xOverhead2->GetError() == ERRCODE_NONE ) ) + { + tools::SvRef<SotStorageStream> xTemp = xOverhead2->OpenSotStream( "_MS_VBA_Overhead2" ); + if ( xTemp.is() && ( xTemp->GetError() == ERRCODE_NONE ) ) + { + sal_uInt32 nLen = xTemp->GetSize(); + if ( nLen ) + { + char* pTemp = new char[ nLen ]; + xTemp->Seek( STREAM_SEEK_TO_BEGIN ); + xTemp->ReadBytes(pTemp, nLen); + pBas = new SvMemoryStream( pTemp, nLen, StreamMode::READ ); + pBas->ObjectOwnsMemory( true ); + return true; + } + } + } + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/eppt.hxx b/sd/source/filter/eppt/eppt.hxx new file mode 100644 index 000000000..eec839290 --- /dev/null +++ b/sd/source/filter/eppt/eppt.hxx @@ -0,0 +1,232 @@ +/* -*- 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 <memory> +#include <vector> +#include "escherex.hxx" +#include <sal/types.h> +#include <sot/storage.hxx> +#include "pptexsoundcollection.hxx" + +#include "text.hxx" + +#include <com/sun/star/presentation/AnimationEffect.hpp> +#include <com/sun/star/presentation/ClickAction.hpp> + +#include "epptbase.hxx" + +namespace com::sun::star::awt { class XControlModel; } +namespace com::sun::star::beans { class XPropertySet; } +namespace com::sun::star::beans { struct PropertyValue; } +namespace com::sun::star::drawing { class XShape; } +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::task { class XStatusIndicator; } +namespace com::sun::star::text { class XSimpleText; } + +class SotStorage; + +#define EPP_MAINMASTER_PERSIST_KEY 0x80010000 +#define EPP_MAINNOTESMASTER_PERSIST_KEY 0x80020000 +#define EPP_MAINSLIDE_PERSIST_KEY 0x80030000 +#define EPP_MAINNOTES_PERSIST_KEY 0x80040000 + +#define EPP_Persist_Document 0x80080000 +#define EPP_Persist_MainMaster 0x80100000 +#define EPP_Persist_MainNotes 0x80200000 +#define EPP_Persist_Slide 0x80400000 +#define EPP_Persist_Notes 0x80800000 +#define EPP_Persist_CurrentPos 0x81000000 +#define EPP_Persist_VBAInfoAtom 0x84000000 +#define EPP_Persist_ExObj 0x88000000 + +#define EPP_TEXTSTYLE_NORMAL 0x00000001 +#define EPP_TEXTSTYLE_TITLE 0x00000010 +#define EPP_TEXTSTYLE_BODY 0x00000100 +#define EPP_TEXTSTYLE_TEXT 0x00001000 + +struct EPPTHyperlink +{ + OUString aURL; + sal_uInt32 nType; // bit 0-7 : type ( 1: click action to a slide ) + // ( 2: hyperlink url ) + // bit 8-23: index + // bit 31 : hyperlink is attached to a shape + + EPPTHyperlink( const OUString& rURL, sal_uInt32 nT ) : + aURL ( rURL ), + nType ( nT ){}; +}; + +enum PPTExOleObjEntryType +{ + NORMAL_OLE_OBJECT, OCX_CONTROL +}; + +struct PPTExOleObjEntry +{ + PPTExOleObjEntryType eType; + sal_uInt32 nOfsA; ///< offset to the EPP_ExOleObjAtom in mpExEmbed (set at creation) + sal_uInt32 nOfsB; ///< offset to the EPP_ExOleObjStg + + css::uno::Reference< css::awt::XControlModel > xControlModel; + css::uno::Reference< css::drawing::XShape > xShape; + + PPTExOleObjEntry(PPTExOleObjEntryType eT, sal_uInt32 nOfs) + : eType(eT) + , nOfsA(nOfs) + , nOfsB(0) + {} +}; + +struct TextRuleEntry +{ + std::unique_ptr<SvMemoryStream> pOut; +}; + +class TextObjBinary : public TextObj +{ +public: + TextObjBinary( css::uno::Reference< css::text::XSimpleText > const & rXText, + int nInstance, FontCollection& rFontCollection, PPTExBulletProvider& rBuProv ) : TextObj( rXText, nInstance, rFontCollection, rBuProv ) {} + void Write( SvStream* pStrm ); + void WriteTextSpecInfo( SvStream* pStrm ); +}; + +struct CellBorder; +class PPTWriter final : public PPTWriterBase, public PPTExBulletProvider +{ + sal_uInt32 mnCnvrtFlags; + bool mbStatus; + sal_uInt32 mnStatMaxValue; + sal_uInt32 mnLatestStatValue; + + std::vector<OUString> maSlideNameList; + OUString maBaseURI; + + css::uno::Reference< css::text::XSimpleText > mXText; // TextRef of the global text + sal_uInt32 mnTextStyle; + + bool mbFontIndependentLineSpacing; + sal_uInt32 mnTextSize; + + tools::SvRef<SotStorage> mrStg; + tools::SvRef<SotStorageStream> mpCurUserStrm; + tools::SvRef<SotStorageStream> mpStrm; + tools::SvRef<SotStorageStream> mpPicStrm; + std::unique_ptr<PptEscherEx> mpPptEscherEx; + + std::vector<std::unique_ptr<PPTExOleObjEntry>> maExOleObj; + sal_uInt32 mnVBAOleOfs; + SvMemoryStream* mpVBA; + sal_uInt32 mnExEmbed; + std::unique_ptr<SvMemoryStream> mpExEmbed; + + sal_uInt32 mnPagesWritten; + sal_uInt32 mnTxId; // Identifier determined by the HOST (PP) ???? + sal_uInt32 mnDiaMode; // 0 -> manual + // 1 -> semi-automatic + // 2 -> automatic + + sal_uInt32 mnShapeMasterTitle; + sal_uInt32 mnShapeMasterBody; + + std::vector<EPPTHyperlink> maHyperlink; + + ppt::ExSoundCollection maSoundCollection; + + void ImplWriteExtParaHeader( SvMemoryStream& rSt, sal_uInt32 nRef, sal_uInt32 nInstance, sal_uInt32 nSlideId ); + + sal_uInt32 ImplProgBinaryTag( SvStream* pOutStrm ); + sal_uInt32 ImplProgBinaryTagContainer( SvStream* pOutStrm, SvMemoryStream* pBinTag ); + sal_uInt32 ImplProgTagContainer( SvStream* pOutStrm, SvMemoryStream* pBinTag = nullptr ); + static sal_uInt32 ImplOutlineViewInfoContainer( SvStream* pOutStrm ); + static sal_uInt32 ImplSlideViewInfoContainer( sal_uInt32 nInstance, SvStream* pOutStrm ); + sal_uInt32 ImplVBAInfoContainer( SvStream* pOutStrm ); + sal_uInt32 ImplDocumentListContainer( SvStream* pOutStrm ); + sal_uInt32 ImplMasterSlideListContainer( SvStream* pOutStrm ); + + public: + static void WriteCString( SvStream&, const OUString&, sal_uInt32 nInstance = 0 ); + + private: + + void ImplCreateDocumentSummaryInformation(); + bool ImplCreateCurrentUserStream(); + static void ImplCreateHeaderFooterStrings( SvStream& rOut, + css::uno::Reference< css::beans::XPropertySet > const & rXPagePropSet ); + void ImplCreateHeaderFooters( css::uno::Reference< css::beans::XPropertySet > const & rXPagePropSet ); + virtual bool ImplCreateDocument() override; + void ImplCreateHyperBlob( SvMemoryStream& rStream ); + sal_uInt32 ImplInsertBookmarkURL( const OUString& rBookmark, const sal_uInt32 nType, + const OUString& rStringVer0, const OUString& rStringVer1, const OUString& rStringVer2, const OUString& rStringVer3 ); + virtual bool ImplCreateMainNotes() override; + void ImplWriteBackground( css::uno::Reference< css::beans::XPropertySet > const & rXBackgroundPropSet ); + void ImplWriteVBA(); + void ImplWriteOLE(); + void ImplWriteAtomEnding(); + + void ImplFlipBoundingBox( EscherPropertyContainer& rPropOpt ); + bool ImplGetText(); + bool ImplCreatePresentationPlaceholder( const bool bMaster, + const sal_uInt32 StyleInstance, const sal_uInt8 PlaceHolderId ); + static bool ImplGetEffect( const css::uno::Reference< css::beans::XPropertySet > &, + css::presentation::AnimationEffect& eEffect, + css::presentation::AnimationEffect& eTextEffect, + bool& bHasSound ); + void ImplWriteClickAction( SvStream& rSt, css::presentation::ClickAction eAction, bool bMediaClickAction ); + void ImplWriteParagraphs( SvStream& rOutStrm, TextObj& rTextObj ); + void ImplWritePortions( SvStream& rOutStrm, TextObj& rTextObj ); + void ImplWriteTextStyleAtom( SvStream& rOut, int nTextInstance, sal_uInt32 nAtomInstance, + TextRuleEntry* pTextRule, SvStream& rExtBu, EscherPropertyContainer* ); + void ImplAdjustFirstLineLineSpacing( TextObj& rTextObj, EscherPropertyContainer& rPropOpt ); + void ImplCreateShape( sal_uInt32 nType, ShapeFlag nFlags, EscherSolverContainer& ); + void ImplCreateTextShape( EscherPropertyContainer&, EscherSolverContainer&, bool bFill ); + + void ImplWritePage( const PHLayout& rLayout, + EscherSolverContainer& rSolver, + PageType ePageType, + bool bMaster, + int nPageNumber = 0 ); + bool ImplCreateCellBorder( const CellBorder* pCellBorder, sal_Int32 nX1, sal_Int32 nY1, sal_Int32 nX2, sal_Int32 nY2 ); + void ImplCreateTable( css::uno::Reference< css::drawing::XShape > const & rXShape, EscherSolverContainer& aSolverContainer, + EscherPropertyContainer& aPropOpt ); + + bool ImplCloseDocument(); // we write the font, hyper and sound list + + virtual void ImplWriteSlide( sal_uInt32 nPageNum, sal_uInt32 nMasterID, sal_uInt16 nMode, + bool bHasBackground, css::uno::Reference< css::beans::XPropertySet > const & aXBackgroundPropSet ) override; + virtual void ImplWriteNotes( sal_uInt32 nPageNum ) override; + virtual void ImplWriteSlideMaster( sal_uInt32 nPageNum, css::uno::Reference< css::beans::XPropertySet > const & aXBackgroundPropSet ) override; + + public: + PPTWriter( tools::SvRef<SotStorage> const & rSvStorage, + css::uno::Reference< css::frame::XModel > const & rModel, + css::uno::Reference< css::task::XStatusIndicator > const & rStatInd, + SvMemoryStream* pVBA, sal_uInt32 nCnvrtFlags ); + + virtual ~PPTWriter() override; + + bool IsValid() const { return mbStatus; }; + + virtual void exportPPTPre( const std::vector< css::beans::PropertyValue >& ) override; + virtual void exportPPTPost( ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/epptbase.hxx b/sd/source/filter/eppt/epptbase.hxx new file mode 100644 index 000000000..e8ac992e3 --- /dev/null +++ b/sd/source/filter/eppt/epptbase.hxx @@ -0,0 +1,412 @@ +/* -*- 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 <memory> +#include <vector> + +#include <vcl/mapmod.hxx> +#include <tools/stream.hxx> +#include <tools/fract.hxx> +#include <tools/gen.hxx> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/presentation/FadeEffect.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <vcl/vclptr.hxx> +#include <vcl/graph.hxx> + +#include "grouptable.hxx" + +namespace com::sun::star::task { class XStatusIndicator; } +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::awt { struct Rectangle; } +namespace com::sun::star::drawing { class XMasterPagesSupplier; } +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XDrawPages; } +namespace com::sun::star::drawing { class XDrawPagesSupplier; } +namespace com::sun::star::beans { struct PropertyValue; } +namespace com::sun::star::beans { class XPropertySet; } +namespace com::sun::star::drawing { class XShape; } +namespace com::sun::star::drawing { class XShapes; } + +class VirtualDevice; + +// PLACEMENT_ID +enum class EppLayout +{ + TITLESLIDE = 0, /* The slide is a title slide */ + TITLEANDBODYSLIDE = 1, /* Title and body slide */ + TITLEMASTERSLIDE = 2, /* Title master slide */ + MASTERSLIDE = 3, /* Master slide layout */ + MASTERNOTES = 4, /* Master notes layout */ + NOTESTITLEBODY = 5, /* Notes title/body layout */ + HANDOUTLAYOUT = 6, /* Handout layout, therefore it doesn't have placeholders except header, footer, and date */ + ONLYTITLE = 7, /* Only title placeholder */ + TWOCOLUMNSANDTITLE = 8, /* Body of the slide has 2 columns and a title */ + TWOROWSANDTITLE = 9, /* Slide's body has 2 rows and a title */ + RIGHTCOLUMN2ROWS = 10, /* Body contains 2 columns, right column has 2 rows */ + LEFTCOLUMN2ROWS = 11, /* Body contains 2 columns, left column has 2 rows */ + BOTTOMROW2COLUMNS = 12, /* Body contains 2 rows, bottom row has 2 columns */ + TOPROW2COLUMN = 13, /* Body contains 2 rows, top row has 2 columns */ + FOUROBJECTS = 14, /* 4 objects */ + BIGOBJECT = 15, /* Big object */ + BLANKSLIDE = 16, /* Blank slide */ + TITLERIGHTBODYLEFT = 17, /* Vertical title on the right, body on the left */ + TITLERIGHT2BODIESLEFT = 18 /* Vertical title on the right, body on the left split into 2 rows */ +}; + +#define EPP_LAYOUT_SIZE 25 + +struct PHLayout +{ + EppLayout nLayout; + sal_uInt8 nPlaceHolder[ 8 ]; + + sal_uInt8 nUsedObjectPlaceHolder; + sal_uInt8 nTypeOfTitle; + sal_uInt8 nTypeOfOutliner; + + bool bTitlePossible; + bool bOutlinerPossible; + bool bSecOutlinerPossible; +}; + +enum PageType { NORMAL = 0, MASTER = 1, NOTICE = 2, UNDEFINED = 3, LAYOUT = 4 }; + +class PropValue +{ + protected: + + css::uno::Any mAny; + css::uno::Reference< css::beans::XPropertySet > mXPropSet; + + bool ImplGetPropertyValue( const OUString& rString ); + bool ImplGetPropertyValue( const css::uno::Reference + < css::beans::XPropertySet > &, const OUString& ); + + public: + + PropValue() {} + + static bool GetPropertyValue( + css::uno::Any& rAny, + const css::uno::Reference< css::beans::XPropertySet > &, + const OUString& rPropertyName, + bool bTestPropertyAvailability = false ); + + static css::beans::PropertyState GetPropertyState( + const css::uno::Reference < css::beans::XPropertySet > &, + const OUString& rPropertyName ); +}; + +class EscherGraphicProvider; +class PPTExBulletProvider +{ + friend struct PPTExParaSheet; + + protected: + + SvMemoryStream aBuExPictureStream; + SvMemoryStream aBuExOutlineStream; + SvMemoryStream aBuExMasterStream; + + std::unique_ptr<EscherGraphicProvider> + pGraphicProv; + + public: + + sal_uInt16 GetId(Graphic const & rGraphic, Size& rGraphicSize); + + PPTExBulletProvider(); + ~PPTExBulletProvider(); +}; + +struct FontCollectionEntry +{ + OUString Name; + double Scaling; + sal_Int16 Family; + sal_Int16 Pitch; + sal_Int16 CharSet; + + OUString Original; + + FontCollectionEntry( const OUString& rName, sal_Int16 nFamily, sal_Int16 nPitch, sal_Int16 nCharSet ) : + Scaling ( 1.0 ), + Family ( nFamily ), + Pitch ( nPitch ), + CharSet ( nCharSet ), + Original( rName ) + { + ImplInit( rName ); + }; + + explicit FontCollectionEntry( const OUString& rName ) : + Scaling ( 1.0 ), + Family ( 0 ), + Pitch ( 0 ), + CharSet ( 0 ), + Original( rName ) + { + ImplInit( rName ); + }; + + private: + + void ImplInit( const OUString& rName ); +}; + +class FontCollection +{ +public: + + FontCollection(); + + ~FontCollection(); + + static short GetScriptDirection( std::u16string_view rText ); + + sal_uInt32 GetId( FontCollectionEntry& rFontDescriptor ); + + sal_uInt32 GetCount() const { return maFonts.size(); }; + + const FontCollectionEntry* GetById( sal_uInt32 nId ); + + FontCollectionEntry& GetLast() { return *(maFonts.rbegin()); }; + +private: + + VclPtr<VirtualDevice> pVDev; + std::vector<FontCollectionEntry> maFonts; +}; + +#define PPTEX_STYLESHEETENTRIES 9 + +enum PPTExTextAttr +{ + ParaAttr_BulletOn, + ParaAttr_BuHardFont, + ParaAttr_BuHardColor, + ParaAttr_BuHardHeight, + ParaAttr_BulletChar, + ParaAttr_BulletFont, + ParaAttr_BulletHeight, + ParaAttr_BulletColor, + ParaAttr_Adjust, + ParaAttr_LineFeed, + ParaAttr_UpperDist, + ParaAttr_LowerDist, + ParaAttr_TextOfs, + ParaAttr_BulletOfs, + ParaAttr_DefaultTab, + ParaAttr_BiDi, + CharAttr_Bold, + CharAttr_Italic, + CharAttr_Underline, + CharAttr_Shadow, + CharAttr_Strikeout, + CharAttr_Embossed, + CharAttr_Font, + CharAttr_AsianOrComplexFont, + CharAttr_Symbol, + CharAttr_FontHeight, + CharAttr_FontColor, + CharAttr_Escapement +}; + +struct PPTExCharLevel +{ + sal_uInt16 mnFlags; + sal_uInt16 mnFont; + sal_uInt16 mnAsianOrComplexFont; + sal_uInt16 mnFontHeight; + sal_uInt16 mnEscapement; + Color mnFontColor; +}; + +struct PPTExCharSheet +{ + PPTExCharLevel maCharLevel[ 5 ]; + + explicit PPTExCharSheet( int nInstance ); + + void SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > &, + FontCollection& rFontCollection, int nLevel ); + void Write( SvStream& rSt, sal_uInt16 nLev, bool bSimpleText, + const css::uno::Reference< css::beans::XPropertySet > & rPagePropSet ); + +}; + +struct PPTExParaLevel +{ + bool mbIsBullet; + sal_uInt16 mnBulletChar; + sal_uInt16 mnBulletFont; + sal_uInt16 mnBulletHeight; + sal_uInt32 mnBulletColor; + + sal_uInt16 mnAdjust; + sal_uInt16 mnLineFeed; + sal_uInt16 mnUpperDist; + sal_uInt16 mnLowerDist; + sal_uInt16 mnTextOfs; + sal_uInt16 mnBulletOfs; + sal_uInt16 mnDefaultTab; + + bool mbExtendedBulletsUsed; + sal_uInt16 mnBulletId; + sal_uInt16 mnBulletStart; + sal_uInt32 mnMappedNumType; + sal_uInt32 mnNumberingType; + sal_uInt16 mnAsianSettings; + sal_uInt16 mnBiDi; +}; + +struct PPTExParaSheet +{ + PPTExBulletProvider* pBuProv; + + sal_uInt32 mnInstance; + + PPTExParaLevel maParaLevel[ 5 ]; + PPTExParaSheet( int nInstance, sal_uInt16 nDefaultTab, PPTExBulletProvider* pProv ); + + void SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > &, + FontCollection& rFontCollection, int nLevel, const PPTExCharLevel& rCharLevel ); + void Write( SvStream& rSt, sal_uInt16 nLev, bool bSimpleText, + const css::uno::Reference< css::beans::XPropertySet > & rPagePropSet ); +}; + +class PPTExStyleSheet +{ + + public: + + std::unique_ptr<PPTExCharSheet> mpCharSheet[ PPTEX_STYLESHEETENTRIES ]; + std::unique_ptr<PPTExParaSheet> mpParaSheet[ PPTEX_STYLESHEETENTRIES ]; + + PPTExStyleSheet( sal_uInt16 nDefaultTab, PPTExBulletProvider* pBuProv ); + ~PPTExStyleSheet(); + + PPTExParaSheet& GetParaSheet( int nInstance ) { return *mpParaSheet[ nInstance ]; }; + + void SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > &, + FontCollection& rFontCollection, int nInstance, int nLevel ); + bool IsHardAttribute( sal_uInt32 nInstance, sal_uInt32 nLevel, PPTExTextAttr eAttr, sal_uInt32 nValue ); + + static sal_uInt32 SizeOfTxCFStyleAtom() { return 24; } + void WriteTxCFStyleAtom( SvStream& rSt ); +}; + +class PPTWriterBase : public PropValue, public GroupTable +{ +protected: + css::uno::Reference< css::frame::XModel > mXModel; + css::uno::Reference< css::task::XStatusIndicator > mXStatusIndicator; + + bool mbStatusIndicator; + + css::uno::Reference< css::drawing::XDrawPagesSupplier > mXDrawPagesSupplier; + css::uno::Reference< css::drawing::XMasterPagesSupplier > mXMasterPagesSupplier; + css::uno::Reference< css::drawing::XDrawPages > mXDrawPages; + css::uno::Reference< css::drawing::XDrawPage > mXDrawPage; + css::uno::Reference< css::beans::XPropertySet > mXPagePropSet; + css::uno::Reference< css::beans::XPropertySet > mXBackgroundPropSet; + css::uno::Reference< css::drawing::XShapes > mXShapes; + css::uno::Reference< css::drawing::XShape > mXShape; + css::awt::Size maSize; + css::awt::Point maPosition; + ::tools::Rectangle maRect; + OString mType; + bool mbPresObj; + bool mbEmptyPresObj; + bool mbIsBackgroundDark; + sal_Int32 mnAngle; + + sal_uInt32 mnPages; ///< number of Slides ( w/o master pages & notes & handout ) + sal_uInt32 mnMasterPages; + + Fraction maFraction; + MapMode maMapModeSrc; + MapMode maMapModeDest; + css::awt::Size maDestPageSize; + css::awt::Size maPageSize; // #i121183# Keep size in logic coordinates (100th mm) + css::awt::Size maNotesPageSize; + + PageType meLatestPageType; + std::vector< std::unique_ptr<PPTExStyleSheet> > maStyleSheetList; + PPTExStyleSheet* mpStyleSheet; + + FontCollection maFontCollection; + + virtual void ImplWriteSlide( sal_uInt32 /* nPageNum */, sal_uInt32 /* nMasterNum */, sal_uInt16 /* nMode */, + bool /* bHasBackground */, css::uno::Reference< css::beans::XPropertySet > const & /* aXBackgroundPropSet */ ) {} + virtual void ImplWriteNotes( sal_uInt32 nPageNum ) = 0; + virtual void ImplWriteSlideMaster( sal_uInt32 /* nPageNum */, css::uno::Reference< css::beans::XPropertySet > const & /* aXBackgroundPropSet */ ) {} + + virtual void exportPPTPre( const std::vector< css::beans::PropertyValue >& ) {} + virtual void exportPPTPost() {} + + virtual bool ImplCreateDocument()=0; + virtual bool ImplCreateMainNotes()=0; + + bool GetStyleSheets(); + bool GetShapeByIndex( sal_uInt32 nIndex, bool bGroup ); + + bool CreateMainNotes(); + + css::awt::Size MapSize( const css::awt::Size& ); + css::awt::Point MapPoint( const css::awt::Point& ); + ::tools::Rectangle MapRectangle( const css::awt::Rectangle& ); + + bool ContainsOtherShapeThanPlaceholders(); + +public: + PPTWriterBase(); + PPTWriterBase( const css::uno::Reference< css::frame::XModel > & rModel, + const css::uno::Reference< css::task::XStatusIndicator > & rStatInd ); + + virtual ~PPTWriterBase(); + + void exportPPT(const std::vector< css::beans::PropertyValue >&); + + bool InitSOIface(); + bool GetPageByIndex( sal_uInt32 nIndex, PageType ); + sal_uInt32 GetMasterIndex( PageType ePageType ); + void SetCurrentStyleSheet( sal_uInt32 nPageNum ); + + bool GetPresObj() const { return mbPresObj; } + + static PHLayout const & GetLayout( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ); + static PHLayout const & GetLayout( sal_Int32 nOffset ); + static sal_Int32 GetLayoutOffset( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ); + static sal_Int32 GetLayoutOffsetFixed( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ); + + bool CreateSlide( sal_uInt32 nPageNum ); + bool CreateSlideMaster( sal_uInt32 nPageNum ); + bool CreateNotes( sal_uInt32 nPageNum ); + + static sal_Int8 GetTransition( sal_Int16 nTransitionType, sal_Int16 nTransitionSubtype, css::presentation::FadeEffect eEffect, + sal_Int32 nTransitionFadeColor, sal_uInt8& nDirection ); + static sal_Int8 GetTransition( css::presentation::FadeEffect eEffect, sal_uInt8& nDirection ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/epptdef.hxx b/sd/source/filter/eppt/epptdef.hxx new file mode 100644 index 000000000..f5059681c --- /dev/null +++ b/sd/source/filter/eppt/epptdef.hxx @@ -0,0 +1,145 @@ +/* -*- 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 + +#define EPP_Document 1000 +#define EPP_DocumentAtom 1001 +#define EPP_EndDocument 1002 +#define EPP_Slide 1006 +#define EPP_SlideAtom 1007 +#define EPP_Notes 1008 +#define EPP_NotesAtom 1009 +#define EPP_Environment 1010 +#define EPP_SlidePersistAtom 1011 //0x03F3 +#define EPP_MainMaster 1016 +#define EPP_SSSlideInfoAtom 1017 +#define EPP_SlideViewInfo 1018 +#define EPP_GuideAtom 1019 +#define EPP_ViewInfoAtom 1021 +#define EPP_SlideViewInfoAtom 1022 +#define EPP_VBAInfo 1023 +#define EPP_VBAInfoAtom 1024 +#define EPP_SSDocInfoAtom 1025 +#define EPP_OutlineViewInfo 1031 +#define EPP_ExObjList 1033 +#define EPP_ExObjListAtom 1034 +#define EPP_PPDrawingGroup 1035 +#define EPP_PPDrawing 1036 +#define EPP_NamedShows 1040 +#define EPP_NamedShow 1041 +#define EPP_NamedShowSlides 1042 +#define EPP_List 2000 +#define EPP_FontCollection 2005 +#define EPP_SoundCollection 2020 +#define EPP_SoundCollAtom 2021 +#define EPP_Sound 2022 +#define EPP_SoundData 2023 +#define EPP_ColorSchemeAtom 2032 + +// these atoms first was seen in ppt2000 in a private Tag atom +#define EPP_PST_ExtendedBuGraContainer 2040 // consist of 4041 + +#define EPP_ExObjRefAtom 3009 +#define EPP_OEPlaceholderAtom 3011 +#define EPP_TextHeaderAtom 3999 +#define EPP_TextCharsAtom 4000 +#define EPP_StyleTextPropAtom 4001 +#define EPP_BaseTextPropAtom 4002 +#define EPP_TxMasterStyleAtom 4003 +#define EPP_TxCFStyleAtom 4004 +#define EPP_TextRulerAtom 4006 +#define EPP_TxSIStyleAtom 4009 +#define EPP_TextSpecInfoAtom 4010 + +// these atoms first was seen in ppt2000 in a private Tag atom +#define EPP_PST_ExtendedParagraphAtom 4012 +#define EPP_PST_ExtendedParagraphMasterAtom 4013 +#define EPP_PST_ExtendedPresRuleContainer 4014 // consist of 4012, 4015, +#define EPP_PST_ExtendedParagraphHeaderAtom 4015 // the instance of this atom indices the current presobj + // the first sal_uInt32 in this atom indices the current slideId + +#define EPP_FontEnityAtom 4023 +#define EPP_CString 4026 +#define EPP_ExOleObjAtom 4035 +#define EPP_SrKinsoku 4040 +#define EPP_ExEmbed 4044 +#define EPP_ExEmbedAtom 4045 +#define EPP_SrKinsokuAtom 4050 +#define EPP_ExHyperlinkAtom 4051 +#define EPP_ExHyperlink 4055 +#define EPP_SlideNumberMCAtom 4056 +#define EPP_HeadersFooters 4057 +#define EPP_HeadersFootersAtom 4058 +#define EPP_TxInteractiveInfoAtom 4063 +#define EPP_ExControl 4078 +#define EPP_ExControlAtom 4091 +#define EPP_SlideListWithText 4080 // 0x0FF0 +#define EPP_AnimationInfoAtom 4081 +#define EPP_InteractiveInfo 4082 +#define EPP_InteractiveInfoAtom 4083 +#define EPP_UserEditAtom 4085 +#define EPP_CurrentUserAtom 4086 +#define EPP_DateTimeMCAtom 4087 +#define EPP_GenericDateMCAtom 4088 +#define EPP_HeaderMCAtom 4089 +#define EPP_FooterMCAtom 4090 +#define EPP_ExMediaAtom 4100 +#define EPP_ExVideo 4101 +#define EPP_ExMCIMovie 4103 +#define EPP_ExOleObjStg 4113 +#define EPP_AnimationInfo 4116 +#define EPP_ProgTags 5000 +#define EPP_ProgBinaryTag 5002 +#define EPP_BinaryTagData 5003 +#define EPP_PersistPtrIncrementalBlock 6002 +#define EPP_Comment10 12000 +#define EPP_CommentAtom10 12001 + +#define EPP_PLACEHOLDER_NONE 0 // 0 None +#define EPP_PLACEHOLDER_MASTERTITLE 1 // 1 Master title +#define EPP_PLACEHOLDER_MASTERBODY 2 // 2 Master body +#define EPP_PLACEHOLDER_MASTERSUBTITLE 4 // 10 Master subtitle +#define EPP_PLACEHOLDER_MASTERNOTESSLIDEIMAGE 5 // 4 Master notes slide image +#define EPP_PLACEHOLDER_MASTERNOTESBODYIMAGE 6 // 5 Master notes body image +#define EPP_PLACEHOLDER_MASTERDATE 7 // 6 Master date +#define EPP_PLACEHOLDER_MASTERSLIDENUMBER 8 // 7 Master slide number +#define EPP_PLACEHOLDER_MASTERFOOTER 9 // 8 Master footer +#define EPP_PLACEHOLDER_MASTERHEADER 10 // 9 Master header +#define EPP_PLACEHOLDER_GENERICTEXTOBJECT // 11 Generic text object +#define EPP_PLACEHOLDER_NOTESBODY 12 // 14 Notes body +#define EPP_PLACEHOLDER_NOTESSLIDEIMAGE 11 // 19 Notes slide image + +#define EPP_TEXTTYPE_Title 0 +#define EPP_TEXTTYPE_Body 1 +#define EPP_TEXTTYPE_Notes 2 +#define EPP_TEXTTYPE_notUsed 3 +#define EPP_TEXTTYPE_Other 4 // ( Text in a shape ) +#define EPP_TEXTTYPE_CenterBody 5 // ( subtitle in title slide ) +#define EPP_TEXTTYPE_CenterTitle 6 // ( title in title slide ) +#define EPP_TEXTTYPE_HalfBody 7 // ( body in two-column slide ) +#define EPP_TEXTTYPE_QuarterBody 8 // ( body in four-body slide ) + +#define EPP_SLIDESIZE_TYPEONSCREEN 0 +#define EPP_SLIDESIZE_TYPEA4PAPER 2 +#define EPP_SLIDESIZE_TYPE35MM 3 +#define EPP_SLIDESIZE_TYPEBANNER 5 +#define EPP_SLIDESIZE_TYPECUSTOM 6 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/epptooxml.hxx b/sd/source/filter/eppt/epptooxml.hxx new file mode 100644 index 000000000..5ee3248ec --- /dev/null +++ b/sd/source/filter/eppt/epptooxml.hxx @@ -0,0 +1,189 @@ +/* -*- 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 <oox/core/xmlfilterbase.hxx> +#include <oox/vml/vmldrawing.hxx> +#include <oox/export/shapes.hxx> +#include "epptbase.hxx" + +using ::sax_fastparser::FSHelperPtr; + +namespace svx +{ +class Theme; +} + +namespace oox::core { + +struct LayoutInfo +{ + std::vector< sal_Int32 > mnFileIdArray; +}; + +enum PlaceholderType +{ + None, + SlideImage, + Notes, + Header, + Footer, + SlideNumber, + DateAndTime, + Outliner, + Title, + Subtitle +}; + +class PowerPointShapeExport; + +class PowerPointExport final : public XmlFilterBase, public PPTWriterBase +{ + friend class PowerPointShapeExport; +public: + + PowerPointExport(const css::uno::Reference<css::uno::XComponentContext> & rContext, const css::uno::Sequence<css::uno::Any>& rArguments); + + virtual ~PowerPointExport() override; + + // from FilterBase + virtual bool importDocument() noexcept override; + virtual bool exportDocument() override; + + // only needed for import, leave them empty, refactor later XmlFilterBase to export and import base? + virtual oox::vml::Drawing* getVmlDrawing() override { return nullptr; } + virtual const oox::drawingml::Theme* getCurrentTheme() const override { return nullptr; } + virtual oox::drawingml::table::TableStyleListPtr getTableStyles() override { return oox::drawingml::table::TableStyleListPtr(); } + virtual oox::drawingml::chart::ChartConverter* getChartConverter() override { return nullptr; } + + static const char* GetSideDirection( sal_uInt8 nDirection ); + static const char* GetCornerDirection( sal_uInt8 nDirection ); + static const char* Get8Direction( sal_uInt8 nDirection ); + static int GetPPTXLayoutId( int nOffset ); + + sal_Int32 GetShapeID(const css::uno::Reference<css::drawing::XShape>& rXShape); + sal_Int32 GetNextAnimationNodeID(); + + void embedEffectAudio(const FSHelperPtr& pFS, const OUString& sUrl, OUString& sRelId, OUString& sName); + +private: + + virtual void ImplWriteSlide( sal_uInt32 nPageNum, sal_uInt32 nMasterNum, sal_uInt16 nMode, + bool bHasBackground, css::uno::Reference< css::beans::XPropertySet > const & aXBackgroundPropSet ) override; + virtual void ImplWriteNotes( sal_uInt32 nPageNum ) override; + virtual void ImplWriteSlideMaster( sal_uInt32 nPageNum, css::uno::Reference< css::beans::XPropertySet > const & aXBackgroundPropSet ) override; + void ImplWritePPTXLayout( sal_Int32 nOffset, sal_uInt32 nMasterNum ); + + /// Export the color set part of a theme. + static bool WriteColorSets(const FSHelperPtr& pFS, svx::Theme* pTheme); + + /// Same as WriteColorSets(), but works from a grab-bag. + bool WriteColorSchemes(const FSHelperPtr& pFS, const OUString& rThemePath); + + static void WriteDefaultColorSchemes(const FSHelperPtr& pFS); + void WriteTheme( sal_Int32 nThemeNum, svx::Theme* pTheme ); + + virtual bool ImplCreateDocument() override; + virtual bool ImplCreateMainNotes() override; + virtual ::oox::ole::VbaProject* implCreateVbaProject() const override; + void WriteNotesMaster(); + + bool WriteComments( sal_uInt32 nPageNum ); + void ImplWriteBackground( const ::sax_fastparser::FSHelperPtr& pFS, const css::uno::Reference< css::beans::XPropertySet >& aXBackgroundPropSet ); + void WriteTransition( const ::sax_fastparser::FSHelperPtr& pFS ); + + sal_Int32 GetLayoutFileId( sal_Int32 nOffset, sal_uInt32 nMasterNum ); + + // shapes + void WriteShapeTree( const ::sax_fastparser::FSHelperPtr& pFS, PageType ePageType, bool bMaster ); + + sal_uInt32 GetNewSlideId() { return mnSlideIdMax ++; } + sal_uInt32 GetNewSlideMasterId() { return mnSlideMasterIdMax ++; } + sal_Int32 GetAuthorIdAndLastIndex( const OUString& sAuthor, sal_Int32& nLastIndex ); + + // Write docProps/core.xml and docprops/custom.xml and docprops/app.xml + void writeDocumentProperties(); + + void WriteCustomSlideShow(); + + void AddLayoutIdAndRelation( const ::sax_fastparser::FSHelperPtr& pFS, sal_Int32 nLayoutFileId ); + + virtual OUString SAL_CALL getImplementationName() override; + + static void WriteDiagram(const FSHelperPtr& pFS, PowerPointShapeExport& rDML, const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId); + + /** Create a new placeholder index for a master placeholder shape + + @param rXShape Master placeholder shape + @returns Placeholder index + */ + sal_Int32 CreateNewPlaceholderIndex(const css::uno::Reference<css::drawing::XShape>& rXShape); + css::uno::Reference<css::drawing::XShape> GetReferencedPlaceholderXShape(const PlaceholderType eType, PageType ePageType) const; + void WritePlaceholderReferenceShapes(PowerPointShapeExport& rDML, PageType ePageType); + + /// Should we export as .pptm, ie. do we contain macros? + bool mbPptm; + + // Export as a template + bool mbExportTemplate; + + ::sax_fastparser::FSHelperPtr mPresentationFS; + + LayoutInfo mLayoutInfo[EPP_LAYOUT_SIZE]; + std::vector< ::sax_fastparser::FSHelperPtr > mpSlidesFSArray; + sal_Int32 mnLayoutFileIdMax; + + sal_uInt32 mnSlideIdMax; + sal_uInt32 mnSlideMasterIdMax; + sal_uInt32 mnAnimationNodeIdMax; + + sal_uInt32 mnDiagramId; + + std::vector<OUString> maRelId; + + bool mbCreateNotes; + + ::oox::drawingml::ShapeExport::ShapeHashMap maShapeMap; + + sal_Int32 mnPlaceholderIndexMax; ///< Last used placeholder index + /// Map of placeholder indexes for Master placeholders + std::unordered_map< css::uno::Reference<css::drawing::XShape>, sal_Int32 > maPlaceholderShapeToIndexMap; + + struct AuthorComments { + sal_Int32 nId; + sal_Int32 nLastIndex; + }; + typedef std::unordered_map< OUString, struct AuthorComments > AuthorsMap; + AuthorsMap maAuthors; + + void WriteAuthors(); + + void WritePresentationProps(); + + /// If this is PPTM, output the VBA stream. + void WriteVBA(); + + void WriteModifyVerifier(); +}; + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/epptso.cxx b/sd/source/filter/eppt/epptso.cxx new file mode 100644 index 000000000..41126aedf --- /dev/null +++ b/sd/source/filter/eppt/epptso.cxx @@ -0,0 +1,3361 @@ +/* -*- 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 <memory> +#include <sal/config.h> + +#include <basegfx/numeric/ftools.hxx> +#include <o3tl/any.hxx> +#include "eppt.hxx" +#include "text.hxx" +#include "epptdef.hxx" +#include "escherex.hxx" +#include <tools/poly.hxx> +#include <tools/stream.hxx> +#include <tools/fontenum.hxx> +#include <tools/UnitConversion.hxx> +#include <sot/storage.hxx> +#include <vcl/graph.hxx> +#include <editeng/svxenum.hxx> +#include <svx/svdobj.hxx> +#include <com/sun/star/awt/FontFamily.hpp> +#include <com/sun/star/awt/FontPitch.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/style/TabStop.hpp> +#include <com/sun/star/drawing/CircleKind.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <tools/urlobj.hxx> +#include <com/sun/star/text/XSimpleText.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/table/XTable.hpp> +#include <com/sun/star/table/XMergeableCell.hpp> +#include <com/sun/star/table/BorderLine.hpp> +#include <com/sun/star/table/XColumnRowRange.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <oox/ole/olehelper.hxx> +#include <i18nlangtag/languagetag.hxx> + +using namespace ::com::sun::star; + +#define ANSI_CHARSET 0 +#define SYMBOL_CHARSET 2 + +/* Font Families */ +#define FF_ROMAN 0x10 +#define FF_SWISS 0x20 +#define FF_MODERN 0x30 +#define FF_SCRIPT 0x40 +#define FF_DECORATIVE 0x50 + +#define DEFAULT_PITCH 0x00 +#define FIXED_PITCH 0x01 + +PPTExBulletProvider::PPTExBulletProvider() + : pGraphicProv( new EscherGraphicProvider( EscherGraphicProviderFlags::UseInstances ) ) +{ +} + +PPTExBulletProvider::~PPTExBulletProvider() +{ +} + +sal_uInt16 PPTExBulletProvider::GetId(Graphic const & rGraphic, Size& rGraphicSize ) +{ + sal_uInt16 nRetValue = 0xffff; + + if (!rGraphic.IsNone()) + { + Graphic aMappedGraphic, aGraphic(rGraphic); + GraphicObject aGraphicObject(aGraphic); + Size aPrefSize( aGraphic.GetPrefSize() ); + BitmapEx aBmpEx( aGraphic.GetBitmapEx() ); + + if ( rGraphicSize.Width() && rGraphicSize.Height() ) + { + if (aPrefSize.IsEmpty()) + { + aBmpEx.Scale(aPrefSize); + } + else + { + double fQ1 = static_cast<double>(aPrefSize.Width()) / static_cast<double>(aPrefSize.Height()); + double fQ2 = static_cast<double>(rGraphicSize.Width()) / static_cast<double>(rGraphicSize.Height()); + double fXScale = 1; + double fYScale = 1; + + if ( fQ1 > fQ2 ) + fYScale = fQ1 / fQ2; + else if ( fQ1 < fQ2 ) + fXScale = fQ2 / fQ1; + + if ( ( fXScale != 1.0 ) || ( fYScale != 1.0 ) ) + { + aBmpEx.Scale( fXScale, fYScale ); + rGraphicSize = Size( static_cast<sal_Int32>(static_cast<double>(rGraphicSize.Width()) / fXScale + 0.5 ), + static_cast<sal_Int32>(static_cast<double>(rGraphicSize.Height()) / fYScale + 0.5 ) ); + + aMappedGraphic = Graphic( aBmpEx ); + aGraphicObject.SetGraphic(aMappedGraphic); + } + } + } + sal_uInt32 nId = pGraphicProv->GetBlibID(aBuExPictureStream, aGraphicObject); + + if ( nId && ( nId < 0x10000 ) ) + nRetValue = static_cast<sal_uInt16>(nId) - 1; + } + return nRetValue; +} + +sal_uInt32 PPTWriter::ImplVBAInfoContainer( SvStream* pStrm ) +{ + sal_uInt32 nSize = 28; + if ( pStrm ) + { + pStrm->WriteUInt32( 0x1f | ( EPP_VBAInfo << 16 ) ) + .WriteUInt32( nSize - 8 ) + .WriteUInt32( 2 | ( EPP_VBAInfoAtom << 16 ) ) + .WriteUInt32( 12 ); + mpPptEscherEx->InsertPersistOffset( EPP_Persist_VBAInfoAtom, pStrm->Tell() ); + pStrm->WriteUInt32( 0 ) + .WriteUInt32( 0 ) + .WriteUInt32( 1 ); + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplSlideViewInfoContainer( sal_uInt32 nInstance, SvStream* pStrm ) +{ + sal_uInt32 nSize = 111; + if ( pStrm ) + { + sal_uInt8 bShowGuides = 0; + sal_uInt8 const bSnapToGrid = 1; + sal_uInt8 const bSnapToShape = 0; + + sal_Int32 nScaling = 85; + sal_Int32 nMasterCoordinate = 0xdda; + sal_Int32 nXOrigin = -780; + sal_Int32 nYOrigin = -84; + + sal_Int32 nPosition1 = 0x870; + sal_Int32 nPosition2 = 0xb40; + + if ( nInstance ) + { + bShowGuides = 1; + nScaling = 0x3b; + nMasterCoordinate = 0xf0c; + nXOrigin = -1752; + nYOrigin = -72; + nPosition1 = 0xb40; + nPosition2 = 0x870; + } + pStrm->WriteUInt32( 0xf | ( EPP_SlideViewInfo << 16 ) | ( nInstance << 4 ) ) + .WriteUInt32( nSize - 8 ) + .WriteUInt32( EPP_SlideViewInfoAtom << 16 ).WriteUInt32( 3 ) + .WriteUChar( bShowGuides ).WriteUChar( bSnapToGrid ).WriteUChar( bSnapToShape ) + .WriteUInt32( EPP_ViewInfoAtom << 16 ).WriteUInt32( 52 ) + .WriteInt32( nScaling ).WriteInt32( 100 ).WriteInt32( nScaling ).WriteInt32( 100 ) // scaling atom - Keeps the current scale + .WriteInt32( nScaling ).WriteInt32( 100 ).WriteInt32( nScaling ).WriteInt32( 100 ) // scaling atom - Keeps the previous scale + .WriteInt32( 0x17ac ).WriteInt32( nMasterCoordinate )// Origin - Keeps the origin in master coordinates + .WriteInt32( nXOrigin ).WriteInt32( nYOrigin ) // Origin + .WriteUChar( 1 ) // Bool1 varScale - Set if zoom to fit is set + .WriteUChar( 0 ) // bool1 draftMode - Not used + .WriteUInt16( 0 ) // padword + .WriteUInt32( ( 7 << 4 ) | ( EPP_GuideAtom << 16 ) ).WriteUInt32( 8 ) + .WriteUInt32( 0 ) // Type of the guide. If the guide is horizontal this value is zero. If it's vertical, it's one. + .WriteInt32( nPosition1 ) // Position of the guide in master coordinates. X coordinate if it's vertical, and Y coordinate if it's horizontal. + .WriteUInt32( ( 7 << 4 ) | ( EPP_GuideAtom << 16 ) ).WriteUInt32( 8 ) + .WriteInt32( 1 ) // Type of the guide. If the guide is horizontal this value is zero. If it's vertical, it's one. + .WriteInt32( nPosition2 ); // Position of the guide in master coordinates. X coordinate if it's vertical, and Y coordinate if it's horizontal. + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplOutlineViewInfoContainer( SvStream* pStrm ) +{ + sal_uInt32 nSize = 68; + if ( pStrm ) + { + pStrm->WriteUInt32( 0xf | ( EPP_OutlineViewInfo << 16 ) ).WriteUInt32( nSize - 8 ) + .WriteUInt32( EPP_ViewInfoAtom << 16 ).WriteUInt32( 52 ) + .WriteInt32( 170 ).WriteInt32( 200 ).WriteInt32( 170 ).WriteInt32( 200 ) // scaling atom - Keeps the current scale + .WriteInt32( 170 ).WriteInt32( 200 ).WriteInt32( 170 ).WriteInt32( 200 ) // scaling atom - Keeps the previous scale + .WriteInt32( 0x17ac ).WriteInt32( 0xdda ) // Origin - Keeps the origin in master coordinates + .WriteInt32( -780 ).WriteInt32( -84 ) // Origin + .WriteUChar( 1 ) // bool1 varScale - Set if zoom to fit is set + .WriteUChar( 0 ) // bool1 draftMode - Not used + .WriteUInt16( 0 ); // padword + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplProgBinaryTag( SvStream* pStrm ) +{ + sal_uInt32 nPictureStreamSize, nOutlineStreamSize, nSize = 8; + + nPictureStreamSize = aBuExPictureStream.Tell(); + if ( nPictureStreamSize ) + nSize += nPictureStreamSize + 8; + + nOutlineStreamSize = aBuExOutlineStream.Tell(); + if ( nOutlineStreamSize ) + nSize += nOutlineStreamSize + 8; + + if ( pStrm ) + { + pStrm->WriteUInt32( EPP_BinaryTagData << 16 ).WriteUInt32( nSize - 8 ); + if ( nPictureStreamSize ) + { + pStrm->WriteUInt32( 0xf | ( EPP_PST_ExtendedBuGraContainer << 16 ) ).WriteUInt32( nPictureStreamSize ); + pStrm->WriteBytes(aBuExPictureStream.GetData(), nPictureStreamSize); + } + if ( nOutlineStreamSize ) + { + pStrm->WriteUInt32( 0xf | ( EPP_PST_ExtendedPresRuleContainer << 16 ) ).WriteUInt32( nOutlineStreamSize ); + pStrm->WriteBytes(aBuExOutlineStream.GetData(), nOutlineStreamSize); + } + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplProgBinaryTagContainer( SvStream* pStrm, SvMemoryStream* pBinTagStrm ) +{ + sal_uInt32 nSize = 8 + 8 + 14; + if ( pStrm ) + { + pStrm->WriteUInt32( 0xf | ( EPP_ProgBinaryTag << 16 ) ).WriteUInt32( 0 ) + .WriteUInt32( EPP_CString << 16 ).WriteUInt32( 14 ) + .WriteUInt32( 0x5f005f ).WriteUInt32( 0x50005f ) + .WriteUInt32( 0x540050 ).WriteUInt16( 0x39 ); + } + if ( pStrm && pBinTagStrm ) + { + sal_uInt32 nLen = pBinTagStrm->Tell(); + nSize += nLen + 8; + pStrm->WriteUInt32( EPP_BinaryTagData << 16 ).WriteUInt32( nLen ); + pStrm->WriteBytes(pBinTagStrm->GetData(), nLen); + } + else + nSize += ImplProgBinaryTag( pStrm ); + + if ( pStrm ) + { + pStrm->SeekRel( - ( static_cast<sal_Int32>(nSize) - 4 ) ); + pStrm->WriteUInt32( nSize - 8 ); + pStrm->SeekRel( nSize - 8 ); + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplProgTagContainer( SvStream* pStrm, SvMemoryStream* pBinTagStrm ) +{ + sal_uInt32 nSize = 0; + if ( aBuExPictureStream.Tell() || aBuExOutlineStream.Tell() || pBinTagStrm ) + { + nSize = 8; + if ( pStrm ) + { + pStrm->WriteUInt32( 0xf | ( EPP_ProgTags << 16 ) ).WriteUInt32( 0 ); + } + nSize += ImplProgBinaryTagContainer( pStrm, pBinTagStrm ); + if ( pStrm ) + { + pStrm->SeekRel( - ( static_cast<sal_Int32>(nSize) - 4 ) ); + pStrm->WriteUInt32( nSize - 8 ); + pStrm->SeekRel( nSize - 8 ); + } + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplDocumentListContainer( SvStream* pStrm ) +{ + sal_uInt32 nSize = 8; + if ( pStrm ) + { + pStrm->WriteUInt32( ( EPP_List << 16 ) | 0xf ).WriteUInt32( 0 ); + } + + nSize += ImplVBAInfoContainer( pStrm ); + nSize += ImplSlideViewInfoContainer( 0, pStrm ); + nSize += ImplOutlineViewInfoContainer( pStrm ); + nSize += ImplSlideViewInfoContainer( 1, pStrm ); + nSize += ImplProgTagContainer( pStrm ); + + if ( pStrm ) + { + pStrm->SeekRel( - ( static_cast<sal_Int32>(nSize) - 4 ) ); + pStrm->WriteUInt32( nSize - 8 ); + pStrm->SeekRel( nSize - 8 ); + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplMasterSlideListContainer( SvStream* pStrm ) +{ + sal_uInt32 i, nSize = 28 * mnMasterPages + 8; + if ( pStrm ) + { + pStrm->WriteUInt32( 0x1f | ( EPP_SlideListWithText << 16 ) ).WriteUInt32( nSize - 8 ); + + for ( i = 0; i < mnMasterPages; i++ ) + { + pStrm->WriteUInt32( EPP_SlidePersistAtom << 16 ).WriteUInt32( 20 ); + mpPptEscherEx->InsertPersistOffset( EPP_MAINMASTER_PERSIST_KEY | i, pStrm->Tell() ); + pStrm->WriteUInt32( 0 ) // psrReference - logical reference to the slide persist object ( EPP_MAINMASTER_PERSIST_KEY ) + .WriteUInt32( 0 ) // flags - only bit 3 used, if set then slide contains shapes other than placeholders + .WriteInt32( 0 ) // numberTexts - number of placeholder texts stored with the persist object. Allows to display outline view without loading the slide persist objects + .WriteInt32( 0x80000000 | i ) // slideId - Unique slide identifier, used for OLE link monikers for example + .WriteUInt32( 0 ); // reserved, usually 0 + } + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplInsertBookmarkURL( const OUString& rBookmarkURL, const sal_uInt32 nType, + const OUString& rStringVer0, const OUString& rStringVer1, const OUString& rStringVer2, const OUString& rStringVer3 ) +{ + sal_uInt32 nHyperId = ++mnExEmbed; + + OUString sBookmarkURL( rBookmarkURL ); + INetURLObject aBaseURI( maBaseURI ); + INetURLObject aBookmarkURI( rBookmarkURL ); + if( aBaseURI.GetProtocol() == aBookmarkURI.GetProtocol() ) + { + OUString aRelUrl( INetURLObject::GetRelURL( maBaseURI, rBookmarkURL ) ); + if ( !aRelUrl.isEmpty() ) + sBookmarkURL = aRelUrl; + } + maHyperlink.emplace_back( sBookmarkURL, nType ); + + mpExEmbed->WriteUInt16( 0xf ) + .WriteUInt16( EPP_ExHyperlink ) + .WriteUInt32( 0 ); + sal_uInt32 nHyperSize, nHyperStart = mpExEmbed->Tell(); + mpExEmbed->WriteUInt16( 0 ) + .WriteUInt16( EPP_ExHyperlinkAtom ) + .WriteUInt32( 4 ) + .WriteUInt32( nHyperId ); + + PPTWriter::WriteCString( *mpExEmbed, rStringVer0 ); + PPTWriter::WriteCString( *mpExEmbed, rStringVer1, 1 ); + PPTWriter::WriteCString( *mpExEmbed, rStringVer2, 2 ); + PPTWriter::WriteCString( *mpExEmbed, rStringVer3, 3 ); + + nHyperSize = mpExEmbed->Tell() - nHyperStart; + mpExEmbed->SeekRel( - ( static_cast<sal_Int32>(nHyperSize) + 4 ) ); + mpExEmbed->WriteUInt32( nHyperSize ); + mpExEmbed->SeekRel( nHyperSize ); + return nHyperId; +} + +bool PPTWriter::ImplCloseDocument() +{ + sal_uInt32 nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_Document ); + if ( nOfs ) + { + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_CurrentPos, mpStrm->Tell() ); + mpStrm->Seek( nOfs ); + + // creating the TxMasterStyleAtom + SvMemoryStream aTxMasterStyleAtomStrm( 0x200, 0x200 ); + { + EscherExAtom aTxMasterStyleAtom( aTxMasterStyleAtomStrm, EPP_TxMasterStyleAtom, EPP_TEXTTYPE_Other ); + aTxMasterStyleAtomStrm.WriteUInt16( 5 ); // paragraph count + sal_uInt16 nLev; + for ( nLev = 0; nLev < 5; nLev++ ) + { + mpStyleSheet->mpParaSheet[ EPP_TEXTTYPE_Other ]->Write( aTxMasterStyleAtomStrm, nLev, false, mXPagePropSet ); + mpStyleSheet->mpCharSheet[ EPP_TEXTTYPE_Other ]->Write( aTxMasterStyleAtomStrm, nLev, false, mXPagePropSet ); + } + } + + sal_uInt32 nExEmbedSize = mpExEmbed->TellEnd(); + + // nEnvironment : whole size of the environment container + sal_uInt32 nEnvironment = maFontCollection.GetCount() * 76 // 68 bytes per Fontenityatom and 8 Bytes per header + + 8 // 1 FontCollection container + + 20 // SrKinsoku container + + 18 // 1 TxSiStyleAtom + + aTxMasterStyleAtomStrm.Tell() // 1 TxMasterStyleAtom; + + PPTExStyleSheet::SizeOfTxCFStyleAtom(); + + sal_uInt32 nBytesToInsert = nEnvironment + 8; + + if ( nExEmbedSize ) + nBytesToInsert += nExEmbedSize + 8 + 12; + + nBytesToInsert += maSoundCollection.GetSize(); + nBytesToInsert += mpPptEscherEx->DrawingGroupContainerSize(); + nBytesToInsert += ImplMasterSlideListContainer(nullptr); + nBytesToInsert += ImplDocumentListContainer(nullptr); + + // insert nBytes into stream and adjust depending container + mpPptEscherEx->InsertAtCurrentPos( nBytesToInsert ); + + // CREATE HYPERLINK CONTAINER + if ( nExEmbedSize ) + { + mpStrm->WriteUInt16( 0xf ) + .WriteUInt16( EPP_ExObjList ) + .WriteUInt32( nExEmbedSize + 12 ) + .WriteUInt16( 0 ) + .WriteUInt16( EPP_ExObjListAtom ) + .WriteUInt32( 4 ) + .WriteUInt32( mnExEmbed ); + mpPptEscherEx->InsertPersistOffset( EPP_Persist_ExObj, mpStrm->Tell() ); + mpStrm->WriteBytes(mpExEmbed->GetData(), nExEmbedSize); + } + + // CREATE ENVIRONMENT + mpStrm->WriteUInt16( 0xf ).WriteUInt16( EPP_Environment ).WriteUInt32( nEnvironment ); + + // Open Container ( EPP_SrKinsoku ) + mpStrm->WriteUInt16( 0x2f ).WriteUInt16( EPP_SrKinsoku ).WriteUInt32( 12 ); + mpPptEscherEx->AddAtom( 4, EPP_SrKinsokuAtom, 0, 3 ); + mpStrm->WriteInt32( 0 ); // SrKinsoku Level 0 + + // Open Container ( EPP_FontCollection ) + mpStrm->WriteUInt16( 0xf ).WriteUInt16( EPP_FontCollection ).WriteUInt32( maFontCollection.GetCount() * 76 ); + + for ( sal_uInt32 i = 0; i < maFontCollection.GetCount(); i++ ) + { + mpPptEscherEx->AddAtom( 68, EPP_FontEnityAtom, 0, i ); + const FontCollectionEntry* pDesc = maFontCollection.GetById( i ); + sal_Int32 nFontLen = pDesc->Name.getLength(); + if ( nFontLen > 31 ) + nFontLen = 31; + for ( sal_Int32 n = 0; n < 32; n++ ) + { + sal_Unicode nUniCode = 0; + if ( n < nFontLen ) + nUniCode = pDesc->Name[n]; + mpStrm->WriteUInt16( nUniCode ); + } + sal_uInt8 lfCharSet = ANSI_CHARSET; + sal_uInt8 const lfClipPrecision = 0; + sal_uInt8 const lfQuality = 6; + sal_uInt8 lfPitchAndFamily = 0; + + if ( pDesc->CharSet == RTL_TEXTENCODING_SYMBOL ) + lfCharSet = SYMBOL_CHARSET; + + switch( pDesc->Family ) + { + case css::awt::FontFamily::ROMAN : + lfPitchAndFamily |= FF_ROMAN; + break; + + case css::awt::FontFamily::SWISS : + lfPitchAndFamily |= FF_SWISS; + break; + + case css::awt::FontFamily::MODERN : + lfPitchAndFamily |= FF_MODERN; + break; + + case css::awt::FontFamily::SCRIPT: + lfPitchAndFamily |= FF_SCRIPT; + break; + + case css::awt::FontFamily::DECORATIVE: + lfPitchAndFamily |= FF_DECORATIVE; + break; + + default: + lfPitchAndFamily |= FAMILY_DONTKNOW; + break; + } + switch( pDesc->Pitch ) + { + case css::awt::FontPitch::FIXED: + lfPitchAndFamily |= FIXED_PITCH; + break; + + default: + lfPitchAndFamily |= DEFAULT_PITCH; + break; + } + mpStrm->WriteUChar( lfCharSet ) + .WriteUChar( lfClipPrecision ) + .WriteUChar( lfQuality ) + .WriteUChar( lfPitchAndFamily ); + } + mpStyleSheet->WriteTxCFStyleAtom( *mpStrm ); // create style that is used for new standard objects + mpPptEscherEx->AddAtom( 10, EPP_TxSIStyleAtom ); + mpStrm->WriteUInt32( 7 ) // ? + .WriteInt16( 2 ) // ? + .WriteUChar( 9 ) // ? + .WriteUChar( 8 ) // ? + .WriteInt16( 0 ); // ? + + mpStrm->WriteBytes(aTxMasterStyleAtomStrm.GetData(), aTxMasterStyleAtomStrm.Tell()); + maSoundCollection.Write( *mpStrm ); + mpPptEscherEx->WriteDrawingGroupContainer( *mpStrm ); + ImplMasterSlideListContainer( mpStrm.get() ); + ImplDocumentListContainer( mpStrm.get() ); + + sal_uInt32 nOldPos = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_CurrentPos ); + if ( nOldPos ) + { + mpStrm->Seek( nOldPos ); + return true; + } + } + return false; +} + +bool PropValue::GetPropertyValue( + css::uno::Any& rAny, + const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + const OUString& rString, + bool bTestPropertyAvailability ) +{ + bool bRetValue = true; + if ( bTestPropertyAvailability ) + { + bRetValue = false; + try + { + css::uno::Reference< css::beans::XPropertySetInfo > aXPropSetInfo( rXPropSet->getPropertySetInfo() ); + if ( aXPropSetInfo.is() ) + bRetValue = aXPropSetInfo->hasPropertyByName( rString ); + } + catch( css::uno::Exception& ) + { + bRetValue = false; + } + } + if ( bRetValue ) + { + try + { + rAny = rXPropSet->getPropertyValue( rString ); + if ( !rAny.hasValue() ) + bRetValue = false; + } + catch( css::uno::Exception& ) + { + bRetValue = false; + } + } + return bRetValue; +} + +css::beans::PropertyState PropValue::GetPropertyState( + const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + const OUString& rPropertyName ) +{ + css::beans::PropertyState eRetValue = css::beans::PropertyState_AMBIGUOUS_VALUE; + try + { + css::uno::Reference< css::beans::XPropertyState > aXPropState( rXPropSet, css::uno::UNO_QUERY ); + if ( aXPropState.is() ) + eRetValue = aXPropState->getPropertyState( rPropertyName ); + } + catch( css::uno::Exception& ) + { + + } + return eRetValue; +} + +bool PropValue::ImplGetPropertyValue( const OUString& rString ) +{ + return GetPropertyValue( mAny, mXPropSet, rString ); +} + +bool PropValue::ImplGetPropertyValue( const css::uno::Reference< css::beans::XPropertySet > & aXPropSet, const OUString& rString ) +{ + return GetPropertyValue( mAny, aXPropSet, rString ); +} + +bool PropStateValue::ImplGetPropertyValue( const OUString& rString, bool bGetPropertyState ) +{ + ePropState = css::beans::PropertyState_AMBIGUOUS_VALUE; + bool bRetValue = true; +#ifdef UNX + css::uno::Reference< css::beans::XPropertySetInfo > + aXPropSetInfo( mXPropSet->getPropertySetInfo() ); + if ( !aXPropSetInfo.is() ) + return false; +#endif + try + { + mAny = mXPropSet->getPropertyValue( rString ); + if ( !mAny.hasValue() ) + bRetValue = false; + else if ( bGetPropertyState ) + ePropState = mXPropState->getPropertyState( rString ); + else + ePropState = css::beans::PropertyState_DIRECT_VALUE; + } + catch( css::uno::Exception& ) + { + bRetValue = false; + } + return bRetValue; +} + +void PPTWriter::ImplWriteParagraphs( SvStream& rOut, TextObj& rTextObj ) +{ + bool bFirstParagraph = true; + sal_uInt32 nCharCount; + sal_uInt32 nPropertyFlags = 0; + sal_Int16 nLineSpacing; + int nInstance = rTextObj.GetInstance(); + + for ( sal_uInt32 i = 0; i < rTextObj.ParagraphCount(); ++i, bFirstParagraph = false ) + { + ParagraphObj* pPara = rTextObj.GetParagraph(i); + const PortionObj& rPortion = pPara->front(); + nCharCount = pPara->CharacterCount(); + + if ( ( pPara->meTextAdjust == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_Adjust, pPara->mnTextAdjust ) ) ) + nPropertyFlags |= 0x00000800; + nLineSpacing = pPara->mnLineSpacing; + + const FontCollectionEntry* pDesc = maFontCollection.GetById( rPortion.mnFont ); + sal_Int16 nNormalSpacing = 100; + if ( !mbFontIndependentLineSpacing && pDesc ) + { + double fN = 100.0; + fN *= pDesc->Scaling; + nNormalSpacing = static_cast<sal_Int16>( fN + 0.5 ); + } + if ( !mbFontIndependentLineSpacing && bFirstParagraph && ( nLineSpacing > nNormalSpacing ) ) // sj: i28747, no replacement for fixed linespacing + { + nLineSpacing = nNormalSpacing; + nPropertyFlags |= 0x00001000; + } + else + { + if ( nLineSpacing > 0 ) + { + if ( !mbFontIndependentLineSpacing && pDesc ) + nLineSpacing = static_cast<sal_Int16>( static_cast<double>(nLineSpacing) * pDesc->Scaling + 0.5 ); + } + else + { + if ( !pPara->mbFixedLineSpacing && rPortion.mnCharHeight > static_cast<sal_uInt16>( static_cast<double>(-nLineSpacing) * 0.001 * 72.0 / 2.54 ) ) // 1/100mm to point + nLineSpacing = nNormalSpacing; + else + nLineSpacing = static_cast<sal_Int16>( convertMm100ToMasterUnit(nLineSpacing) ); + } + if ( ( pPara->meLineSpacing == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_LineFeed, nLineSpacing ) ) ) + nPropertyFlags |= 0x00001000; + } + if ( ( pPara->meLineSpacingTop == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_UpperDist, pPara->mnLineSpacingTop ) ) ) + nPropertyFlags |= 0x00002000; + if ( ( pPara->meLineSpacingBottom == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_LowerDist, pPara->mnLineSpacingBottom ) ) ) + nPropertyFlags |= 0x00004000; + if ( ( pPara->meForbiddenRules == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_UpperDist, pPara->mbForbiddenRules ? 1 : 0 ) ) ) + nPropertyFlags |= 0x00020000; + if ( ( pPara->meParagraphPunctation == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_UpperDist, pPara->mbParagraphPunctation ? 1 : 0 ) ) ) + nPropertyFlags |= 0x00080000; + if ( ( pPara->meBiDi == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_BiDi, pPara->mnBiDi ) ) ) + nPropertyFlags |= 0x00200000; + + sal_Int32 nBuRealSize = pPara->nBulletRealSize; + sal_Int16 nBulletFlags = pPara->nBulletFlags; + + if ( pPara->bExtendedParameters ) + nPropertyFlags |= pPara->nParaFlags; + else + { + nPropertyFlags |= 1; // turn off bullet explicit + nBulletFlags = 0; + } + + // Write nTextOfs and nBullets + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_TextOfs, pPara->nTextOfs ) ) + nPropertyFlags |= 0x100; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_BulletOfs, pPara->nBulletOfs )) + nPropertyFlags |= 0x400; + + FontCollectionEntry aFontDescEntry( pPara->aFontDesc.Name, pPara->aFontDesc.Family, pPara->aFontDesc.Pitch, pPara->aFontDesc.CharSet ); + sal_uInt16 nFontId = static_cast<sal_uInt16>(maFontCollection.GetId( aFontDescEntry )); + + rOut.WriteUInt32( nCharCount ) + .WriteUInt16( pPara->nDepth ) // Level + .WriteUInt32( nPropertyFlags ); // Paragraph Attribute Set + + if ( nPropertyFlags & 0xf ) + rOut.WriteInt16( nBulletFlags ); + if ( nPropertyFlags & 0x80 ) + rOut.WriteUInt16( pPara->cBulletId ); + if ( nPropertyFlags & 0x10 ) + rOut.WriteUInt16( nFontId ); + if ( nPropertyFlags & 0x40 ) + rOut.WriteInt16( nBuRealSize ); + if ( nPropertyFlags & 0x20 ) + { + sal_uInt32 nBulletColor = pPara->nBulletColor; + if ( nBulletColor == sal_uInt32(COL_AUTO) ) + { + bool bIsDark = false; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, mXPagePropSet, "IsBackgroundDark", true ) ) + aAny >>= bIsDark; + nBulletColor = bIsDark ? 0xffffff : 0x000000; + } + nBulletColor &= 0xffffff; + nBulletColor |= 0xfe000000; + rOut.WriteUInt32( nBulletColor ); + } + if ( nPropertyFlags & 0x00000800 ) + rOut.WriteUInt16( pPara->mnTextAdjust ); + if ( nPropertyFlags & 0x00001000 ) + rOut.WriteUInt16( nLineSpacing ); + if ( nPropertyFlags & 0x00002000 ) + rOut.WriteUInt16( pPara->mnLineSpacingTop ); + if ( nPropertyFlags & 0x00004000 ) + rOut.WriteUInt16( pPara->mnLineSpacingBottom ); + if ( nPropertyFlags & 0x100 ) + rOut.WriteUInt16( pPara->nTextOfs ); + if ( nPropertyFlags & 0x400 ) + rOut.WriteUInt16( pPara->nBulletOfs ); + if ( nPropertyFlags & 0x000e0000 ) + { + sal_uInt16 nAsianSettings = 0; + if ( pPara->mbForbiddenRules ) + nAsianSettings |= 1; + if ( pPara->mbParagraphPunctation ) + nAsianSettings |= 4; + rOut.WriteUInt16( nAsianSettings ); + } + if ( nPropertyFlags & 0x200000 ) + rOut.WriteUInt16( pPara->mnBiDi ); + } +} + +void PPTWriter::ImplWritePortions( SvStream& rOut, TextObj& rTextObj ) +{ + sal_uInt32 nPropertyFlags; + int nInstance = rTextObj.GetInstance(); + + for ( sal_uInt32 i = 0; i < rTextObj.ParagraphCount(); ++i ) + { + ParagraphObj* pPara = rTextObj.GetParagraph(i); + for ( std::vector<std::unique_ptr<PortionObj> >::const_iterator it = pPara->begin(); it != pPara->end(); ++it ) + { + const PortionObj& rPortion = **it; + nPropertyFlags = 0; + sal_uInt32 nCharAttr = rPortion.mnCharAttr; + sal_uInt32 nCharColor = rPortion.mnCharColor; + + if ( nCharColor == sal_uInt32(COL_AUTO) ) // nCharColor depends to the background color + { + bool bIsDark = false; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, mXPagePropSet, "IsBackgroundDark", true ) ) + aAny >>= bIsDark; + nCharColor = bIsDark ? 0xffffff : 0x000000; + } + + nCharColor &= 0xffffff; + + /* the portion is using the embossed or engraved attribute, which we want to map to the relief feature of PPT. + Because the relief feature of PPT is dependent to the background color, such a mapping can not always be used. */ + if ( nCharAttr & 0x200 ) + { + sal_uInt32 nBackgroundColor = 0xffffff; + + if ( !nCharColor ) // special treatment for + nCharColor = 0xffffff; // black fontcolor + + css::uno::Any aAny; + css::drawing::FillStyle aFS( css::drawing::FillStyle_NONE ); + if ( PropValue::GetPropertyValue( aAny, mXPropSet, "FillStyle" ) ) + aAny >>= aFS; + switch( aFS ) + { + case css::drawing::FillStyle_GRADIENT : + { + ::tools::Rectangle aRect( Point(), Size( 28000, 21000 ) ); + EscherPropertyContainer aPropOpt( mpPptEscherEx->GetGraphicProvider(), mpPicStrm.get(), aRect ); + aPropOpt.CreateGradientProperties( mXPropSet ); + aPropOpt.GetOpt( ESCHER_Prop_fillColor, nBackgroundColor ); + } + break; + case css::drawing::FillStyle_SOLID : + { + if ( PropValue::GetPropertyValue( aAny, mXPropSet, "FillColor" ) ) + nBackgroundColor = EscherEx::GetColor( *o3tl::doAccess<sal_uInt32>(aAny) ); + } + break; + case css::drawing::FillStyle_NONE : + { + css::uno::Any aBackAny; + css::drawing::FillStyle aBackFS( css::drawing::FillStyle_NONE ); + if ( PropValue::GetPropertyValue( aBackAny, mXBackgroundPropSet, "FillStyle" ) ) + aBackAny >>= aBackFS; + switch( aBackFS ) + { + case css::drawing::FillStyle_GRADIENT : + { + ::tools::Rectangle aRect( Point(), Size( 28000, 21000 ) ); + EscherPropertyContainer aPropOpt( mpPptEscherEx->GetGraphicProvider(), mpPicStrm.get(), aRect ); + aPropOpt.CreateGradientProperties( mXBackgroundPropSet ); + aPropOpt.GetOpt( ESCHER_Prop_fillColor, nBackgroundColor ); + } + break; + case css::drawing::FillStyle_SOLID : + { + if ( PropValue::GetPropertyValue( aAny, mXBackgroundPropSet, "FillColor" ) ) + nBackgroundColor = EscherEx::GetColor( *o3tl::doAccess<sal_uInt32>(aAny) ); + } + break; + default: + break; + } + } + break; + default: + break; + } + + sal_Int32 nB = nBackgroundColor & 0xff; + nB += static_cast<sal_uInt8>( nBackgroundColor >> 8 ); + nB += static_cast<sal_uInt8>( nBackgroundColor >> 16 ); + // if the background color is nearly black, relief can't been used, because the text would not be visible + if ( nB < 0x60 || ( nBackgroundColor != nCharColor ) ) + { + nCharAttr &=~ 0x200; + + // now check if the text is part of a group, and if the previous object has the same color than the fontcolor + // ( and if fillcolor is not available the background color ), it is sometimes + // not possible to export the 'embossed' flag + if ( ( GetCurrentGroupLevel() > 0 ) && ( GetCurrentGroupIndex() >= 1 ) ) + { + css::uno::Reference< css::drawing::XShape > aGroupedShape( GetCurrentGroupAccess()->getByIndex( GetCurrentGroupIndex() - 1 ), uno::UNO_QUERY ); + if( aGroupedShape.is() ) + { + css::uno::Reference< css::beans::XPropertySet > aPropSetOfNextShape + ( aGroupedShape, css::uno::UNO_QUERY ); + if ( aPropSetOfNextShape.is() ) + { + if ( PropValue::GetPropertyValue( aAny, aPropSetOfNextShape, + "FillColor", true ) ) + { + if ( nCharColor == EscherEx::GetColor( *o3tl::doAccess<sal_uInt32>(aAny) ) ) + { + nCharAttr |= 0x200; + } + } + } + } + } + } + } + nCharColor |= 0xfe000000; + if ( nInstance == 4 ) // special handling for normal textobjects: + nPropertyFlags |= nCharAttr & 0x217; // not all attributes are inherited + else + { + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Bold, nCharAttr ) ) + nPropertyFlags |= 1; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Italic, nCharAttr ) ) + nPropertyFlags |= 2; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Underline, nCharAttr ) ) + nPropertyFlags |= 4; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Shadow, nCharAttr ) ) + nPropertyFlags |= 0x10; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Embossed, nCharAttr ) ) + nPropertyFlags |= 512; + } + if ( rTextObj.HasExtendedBullets() ) + { + nPropertyFlags |= ( i & 0x3f ) << 10 ; + nCharAttr |= ( i & 0x3f ) << 10; + } + if ( ( rPortion.meFontName == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Font, rPortion.mnFont ) ) ) + nPropertyFlags |= 0x00010000; + if ( ( rPortion.meAsianOrComplexFont == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_AsianOrComplexFont, rPortion.mnAsianOrComplexFont ) ) ) + nPropertyFlags |= 0x00200000; + if ( ( rPortion.meCharHeight == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_FontHeight, rPortion.mnCharHeight ) ) ) + nPropertyFlags |= 0x00020000; + if ( ( rPortion.meCharColor == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_FontColor, nCharColor & 0xffffff ) ) ) + nPropertyFlags |= 0x00040000; + if ( ( rPortion.meCharEscapement == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Escapement, rPortion.mnCharEscapement ) ) ) + nPropertyFlags |= 0x00080000; + + sal_uInt32 nCharCount = rPortion.Count(); + + rOut.WriteUInt32( nCharCount ) + .WriteUInt32( nPropertyFlags ); //PropertyFlags + + if ( nPropertyFlags & 0xffff ) + rOut.WriteUInt16( nCharAttr ); + if ( nPropertyFlags & 0x00010000 ) + rOut.WriteUInt16( rPortion.mnFont ); + if ( nPropertyFlags & 0x00200000 ) + rOut.WriteUInt16( rPortion.mnAsianOrComplexFont ); + if ( nPropertyFlags & 0x00020000 ) + rOut.WriteUInt16( rPortion.mnCharHeight ); + if ( nPropertyFlags & 0x00040000 ) + rOut.WriteUInt32( nCharColor ); + if ( nPropertyFlags & 0x00080000 ) + rOut.WriteInt16( rPortion.mnCharEscapement ); + } + } +} + +/** + * Loads and converts text from shape, value is stored in mnTextSize. + */ +bool PPTWriter::ImplGetText() +{ + mnTextSize = 0; + mbFontIndependentLineSpacing = false; + mXText.set( mXShape, css::uno::UNO_QUERY ); + + if ( mXText.is() ) + { + mnTextSize = mXText->getString().getLength(); + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "FontIndependentLineSpacing", true ) ) + aAny >>= mbFontIndependentLineSpacing; + } + return ( mnTextSize != 0 ); +} + +void PPTWriter::ImplFlipBoundingBox( EscherPropertyContainer& rPropOpt ) +{ + if ( mnAngle < 0 ) + mnAngle = ( 36000 + mnAngle ) % 36000; + else + mnAngle = ( 36000 - ( mnAngle % 36000 ) ); + + double fCos = cos( basegfx::deg2rad<100>(mnAngle) ); + double fSin = sin( basegfx::deg2rad<100>(mnAngle) ); + + double fWidthHalf = maRect.GetWidth() / 2.0; + double fHeightHalf = maRect.GetHeight() / 2.0; + + double fXDiff = fCos * fWidthHalf + fSin * (-fHeightHalf); + double fYDiff = - ( fSin * fWidthHalf - fCos * ( -fHeightHalf ) ); + + maRect.Move( static_cast<sal_Int32>( -( fWidthHalf - fXDiff ) ), static_cast<sal_Int32>( - ( fHeightHalf + fYDiff ) ) ); + mnAngle *= 655; + mnAngle += 0x8000; + mnAngle &=~0xffff; // round nAngle to full grads + rPropOpt.AddOpt( ESCHER_Prop_Rotation, mnAngle ); + + if ( ( mnAngle >= ( 45 << 16 ) && mnAngle < ( 135 << 16 ) ) || + ( mnAngle >= ( 225 << 16 ) && mnAngle < ( 315 << 16 ) ) ) + { + // Maddeningly, in those two areas of PPT is the BoundingBox already + // vertical. Therefore, we need to put down it BEFORE THE ROTATION. + css::awt::Point aTopLeft( static_cast<sal_Int32>( maRect.Left() + fWidthHalf - fHeightHalf ), static_cast<sal_Int32>( maRect.Top() + fHeightHalf - fWidthHalf ) ); + const tools::Long nRotatedWidth(maRect.GetHeight()); + const tools::Long nRotatedHeight(maRect.GetWidth()); + const Size aNewSize(nRotatedWidth, nRotatedHeight); + maRect = ::tools::Rectangle( Point( aTopLeft.X, aTopLeft.Y ), aNewSize ); + } +} + +void PPTWriter::ImplAdjustFirstLineLineSpacing( TextObj& rTextObj, EscherPropertyContainer& rPropOpt ) +{ + if ( mbFontIndependentLineSpacing ) + return; + + if ( !rTextObj.ParagraphCount() ) + return; + + ParagraphObj* pPara = rTextObj.GetParagraph(0); + if ( pPara->empty() ) + return; + + const PortionObj& rPortion = pPara->front(); + sal_Int16 nLineSpacing = pPara->mnLineSpacing; + const FontCollectionEntry* pDesc = maFontCollection.GetById( rPortion.mnFont ); + if ( pDesc ) + nLineSpacing = static_cast<sal_Int16>( static_cast<double>(nLineSpacing) * pDesc->Scaling + 0.5 ); + + if ( ( nLineSpacing > 0 ) && ( nLineSpacing < 100 ) ) + { + double fCharHeight = convertPointToMm100<double>(rPortion.mnCharHeight); + fCharHeight *= 100 - nLineSpacing; + fCharHeight /= 100; + + sal_uInt32 nUpperDistance = 0; + rPropOpt.GetOpt( ESCHER_Prop_dyTextTop, nUpperDistance ); + nUpperDistance += static_cast< sal_uInt32 >( fCharHeight * 360.0 ); + rPropOpt.AddOpt( ESCHER_Prop_dyTextTop, nUpperDistance ); + } +} + +void PPTWriter::ImplWriteTextStyleAtom( SvStream& rOut, int nTextInstance, sal_uInt32 nAtomInstance, + TextRuleEntry* pTextRule, SvStream& rExtBuStr, EscherPropertyContainer* pPropOpt ) +{ + PPTExParaSheet& rParaSheet = mpStyleSheet->GetParaSheet( nTextInstance ); + + rOut.WriteUInt32( ( EPP_TextHeaderAtom << 16 ) | ( nAtomInstance << 4 ) ).WriteUInt32( 4 ) + .WriteInt32( nTextInstance ); + + if ( mbEmptyPresObj ) + mnTextSize = 0; + if ( mbEmptyPresObj ) + return; + + ParagraphObj* pPara; + TextObjBinary aTextObj( mXText, nTextInstance, maFontCollection, static_cast<PPTExBulletProvider&>(*this) ); + + // leaving out EPP_TextCharsAtom w/o text - still write out + // attribute info though + if ( mnTextSize ) + aTextObj.Write( &rOut ); + + if ( pPropOpt && mType != "drawing.Table" ) + ImplAdjustFirstLineLineSpacing( aTextObj, *pPropOpt ); + + sal_uInt32 nSize, nPos = rOut.Tell(); + + rOut.WriteUInt32( EPP_StyleTextPropAtom << 16 ).WriteUInt32( 0 ); + ImplWriteParagraphs( rOut, aTextObj ); + ImplWritePortions( rOut, aTextObj ); + nSize = rOut.Tell() - nPos; + rOut.SeekRel( - ( static_cast<sal_Int32>(nSize) - 4 ) ); + rOut.WriteUInt32( nSize - 8 ); + rOut.SeekRel( nSize - 8 ); + + for ( sal_uInt32 i = 0; i < aTextObj.ParagraphCount(); ++i ) + { + pPara = aTextObj.GetParagraph(i); + for ( std::vector<std::unique_ptr<PortionObj> >::const_iterator it = pPara->begin(); it != pPara->end(); ++it ) + { + const PortionObj& rPortion = **it; + if ( rPortion.mpFieldEntry ) + { + const FieldEntry* pFieldEntry = rPortion.mpFieldEntry.get(); + + switch ( pFieldEntry->nFieldType >> 28 ) + { + case 1 : + case 2 : + { + rOut.WriteUInt32( EPP_DateTimeMCAtom << 16 ).WriteUInt32( 8 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ) // TxtOffset to TxtField; + .WriteUChar( pFieldEntry->nFieldType & 0xff ) // Type + .WriteUChar( 0 ).WriteUInt16( 0 ); // PadBytes + } + break; + case 3 : + { + rOut.WriteUInt32( EPP_SlideNumberMCAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ); + } + break; + case 4 : + { + sal_uInt32 nPageIndex = 0; + OUString aPageUrl; + OUString aFile( pFieldEntry->aFieldUrl ); + OUString aTarget( pFieldEntry->aFieldUrl ); + INetURLObject aUrl( pFieldEntry->aFieldUrl ); + if ( INetProtocol::File == aUrl.GetProtocol() ) + aFile = aUrl.PathToFileName(); + else if ( INetProtocol::Smb == aUrl.GetProtocol() ) + { + // Convert smb notation to '\\' and skip the 'smb:' part + aFile = aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE).copy(4); + aFile = aFile.replaceAll( "/", "\\" ); + aTarget = aFile; + } + else if ( pFieldEntry->aFieldUrl.startsWith("#") ) + { + OUString aPage( INetURLObject::decode( pFieldEntry->aFieldUrl, INetURLObject::DecodeMechanism::WithCharset ) ); + aPage = aPage.copy( 1 ); + + std::vector<OUString>::const_iterator pIter = std::find( + maSlideNameList.begin(),maSlideNameList.end(),aPage); + + if ( pIter != maSlideNameList.end() ) + { + nPageIndex = pIter - maSlideNameList.begin(); + aPageUrl = OUString::number(256 + nPageIndex) + + "," + + OUString::number(nPageIndex + 1) + + ",Slide " + + OUString::number(nPageIndex + 1); + } + } + sal_uInt32 nHyperId(0); + if ( !aPageUrl.isEmpty() ) + nHyperId = ImplInsertBookmarkURL( aPageUrl, 1 | ( nPageIndex << 8 ) | ( 1U << 31 ), pFieldEntry->aRepresentation, "", "", aPageUrl ); + else + nHyperId = ImplInsertBookmarkURL( pFieldEntry->aFieldUrl, 2 | ( nHyperId << 8 ), aFile, aTarget, "", "" ); + + rOut.WriteUInt32( ( EPP_InteractiveInfo << 16 ) | 0xf ).WriteUInt32( 24 ) + .WriteUInt32( EPP_InteractiveInfoAtom << 16 ).WriteUInt32( 16 ) + .WriteUInt32( 0 ) // soundref + .WriteUInt32( nHyperId ) // hyperlink id + .WriteUChar( 4 ) // hyperlink action + .WriteUChar( 0 ) // ole verb + .WriteUChar( 0 ) // jump + .WriteUChar( 0 ) // flags + .WriteUChar( 8 ) // hyperlink type ? + .WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ) + .WriteUInt32( EPP_TxInteractiveInfoAtom << 16 ).WriteUInt32( 8 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ) + .WriteUInt32( pFieldEntry->nFieldEndPos ); + } + break; + case 5 : + { + rOut.WriteUInt32( EPP_GenericDateMCAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ); + } + break; + case 6 : + { + rOut.WriteUInt32( EPP_HeaderMCAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ); + } + break; + case 7 : + { + rOut.WriteUInt32( EPP_FooterMCAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ); + } + break; + default: + break; + } + } + } + } + + aTextObj.WriteTextSpecInfo( &rOut ); + + // write Star Office Default TabSizes (if necessary) + if ( aTextObj.ParagraphCount() ) + { + pPara = aTextObj.GetParagraph(0); + sal_uInt32 nParaFlags = 0x1f; + sal_Int16 nMask, nNumberingRule[ 10 ]; + const sal_uInt32 nTabs = pPara->maTabStop.getLength(); + const auto& rTabStops = pPara->maTabStop; + + for ( sal_uInt32 i = 0; i < aTextObj.ParagraphCount(); ++i ) + { + pPara = aTextObj.GetParagraph(i); + if ( pPara->bExtendedParameters ) + { + nMask = 1 << pPara->nDepth; + if ( nParaFlags & nMask ) + { + nParaFlags &=~ nMask; + if ( ( rParaSheet.maParaLevel[ pPara->nDepth ].mnTextOfs != pPara->nTextOfs ) || + ( rParaSheet.maParaLevel[ pPara->nDepth ].mnBulletOfs != pPara->nBulletOfs ) ) + { + nParaFlags |= nMask << 16; + nNumberingRule[ pPara->nDepth << 1 ] = pPara->nTextOfs; + nNumberingRule[ ( pPara->nDepth << 1 ) + 1 ] = static_cast<sal_Int16>(pPara->nBulletOfs); + } + } + } + } + nParaFlags >>= 16; + + sal_Int32 nDefaultTabSizeSrc = 2011; // I've no idea where this number came from, honestly + const uno::Reference< beans::XPropertySet > xPropSet( mXModel, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + if(ImplGetPropertyValue( xPropSet, "TabStop" )) + { + sal_Int32 nTabStop( 0 ); + if ( mAny >>= nTabStop ) + nDefaultTabSizeSrc = nTabStop; + } + } + const sal_uInt32 nDefaultTabSize = MapSize( awt::Size( nDefaultTabSizeSrc, 1 ) ).Width; + sal_uInt32 nDefaultTabs = std::abs( maRect.GetWidth() ) / nDefaultTabSize; + if ( nTabs ) + nDefaultTabs -= static_cast<sal_Int32>( convertMm100ToMasterUnit(rTabStops[ nTabs - 1 ].Position) / nDefaultTabSize ); + if ( static_cast<sal_Int32>(nDefaultTabs) < 0 ) + nDefaultTabs = 0; + + sal_uInt32 nTabCount = nTabs + nDefaultTabs; + sal_uInt32 i, nTextRulerAtomFlags = 0; + + if ( nTabCount ) + nTextRulerAtomFlags |= 4; + if ( nParaFlags ) + nTextRulerAtomFlags |= ( ( nParaFlags << 3 ) | ( nParaFlags << 8 ) ); + + if ( nTextRulerAtomFlags ) + { + SvStream* pRuleOut = &rOut; + if ( pTextRule ) + { + pTextRule->pOut.reset( new SvMemoryStream( 0x100, 0x100 ) ); + pRuleOut = pTextRule->pOut.get(); + } + + sal_uInt32 nRulePos = pRuleOut->Tell(); + pRuleOut->WriteUInt32( EPP_TextRulerAtom << 16 ).WriteUInt32( 0 ); + pRuleOut->WriteUInt32( nTextRulerAtomFlags ); + if ( nTextRulerAtomFlags & 4 ) + { + pRuleOut->WriteUInt16( nTabCount ); + for ( const css::style::TabStop& rTabStop : rTabStops ) + { + sal_uInt16 nPosition = static_cast<sal_uInt16>( convertMm100ToMasterUnit(rTabStop.Position) ); + sal_uInt16 nType; + switch ( rTabStop.Alignment ) + { + case css::style::TabAlign_DECIMAL : nType = 3; break; + case css::style::TabAlign_RIGHT : nType = 2; break; + case css::style::TabAlign_CENTER : nType = 1; break; + + case css::style::TabAlign_LEFT : + default: nType = 0; + } + pRuleOut->WriteUInt16( nPosition ) + .WriteUInt16( nType ); + } + + sal_uInt32 nWidth = 1; + if ( nTabs ) + nWidth += static_cast<sal_Int32>( convertMm100ToMasterUnit(rTabStops[ nTabs - 1 ].Position) / nDefaultTabSize ); + nWidth *= nDefaultTabSize; + for ( i = 0; i < nDefaultTabs; i++, nWidth += nDefaultTabSize ) + pRuleOut->WriteUInt32( nWidth ); + } + for ( i = 0; i < 5; i++ ) + { + if ( nTextRulerAtomFlags & ( 8 << i ) ) + pRuleOut->WriteInt16( nNumberingRule[ i << 1 ] ); + if ( nTextRulerAtomFlags & ( 256 << i ) ) + pRuleOut->WriteInt16( nNumberingRule[ ( i << 1 ) + 1 ] ); + } + sal_uInt32 nBufSize = pRuleOut->Tell() - nRulePos; + pRuleOut->SeekRel( - ( static_cast<sal_Int32>(nBufSize) - 4 ) ); + pRuleOut->WriteUInt32( nBufSize - 8 ); + pRuleOut->SeekRel( nBufSize - 8 ); + } + } + if ( !aTextObj.HasExtendedBullets() ) + return; + + if ( !aTextObj.ParagraphCount() ) + return; + + sal_uInt32 nNumberingType = 0, nPos2 = rExtBuStr.Tell(); + + rExtBuStr.WriteUInt32( EPP_PST_ExtendedParagraphAtom << 16 ).WriteUInt32( 0 ); + + for ( sal_uInt32 i = 0; i < aTextObj.ParagraphCount(); ++i ) + { + ParagraphObj* pBulletPara = aTextObj.GetParagraph(i); + sal_uInt32 nBulletFlags = 0; + sal_uInt16 nBulletId = pBulletPara->nBulletId; + + if ( pBulletPara->bExtendedBulletsUsed ) + { + nBulletFlags = 0x800000; + if ( pBulletPara->nNumberingType != SVX_NUM_BITMAP ) + nBulletFlags = 0x3000000; + } + rExtBuStr.WriteUInt32( nBulletFlags ); + + if ( nBulletFlags & 0x800000 ) + rExtBuStr.WriteUInt16( nBulletId ); + if ( nBulletFlags & 0x1000000 ) + { + switch( pBulletPara->nNumberingType ) + { + case SVX_NUM_NUMBER_NONE : + case SVX_NUM_CHAR_SPECIAL : + nNumberingType = 0; + break; + case SVX_NUM_CHARS_UPPER_LETTER : + case SVX_NUM_CHARS_UPPER_LETTER_N : + case SVX_NUM_CHARS_LOWER_LETTER : + case SVX_NUM_CHARS_LOWER_LETTER_N : + case SVX_NUM_ROMAN_UPPER : + case SVX_NUM_ROMAN_LOWER : + case SVX_NUM_ARABIC : + case SVX_NUM_NUMBER_UPPER_ZH: + case SVX_NUM_CIRCLE_NUMBER: + case SVX_NUM_NUMBER_UPPER_ZH_TW: + case SVX_NUM_NUMBER_LOWER_ZH: + case SVX_NUM_FULL_WIDTH_ARABIC: + nNumberingType = pBulletPara->nMappedNumType; + break; + + case SVX_NUM_BITMAP : + nNumberingType = 0; + break; + default: break; + } + rExtBuStr.WriteUInt32( nNumberingType ); + } + if ( nBulletFlags & 0x2000000 ) + rExtBuStr.WriteUInt16( pBulletPara->nStartWith ); + rExtBuStr.WriteUInt32( 0 ).WriteUInt32( 0 ); + } + sal_uInt32 nBulletSize = ( rExtBuStr.Tell() - nPos2 ) - 8; + rExtBuStr.SeekRel( - ( static_cast<sal_Int32>(nBulletSize) + 4 ) ); + rExtBuStr.WriteUInt32( nBulletSize ); + rExtBuStr.SeekRel( nBulletSize ); +} + +void PPTWriter::ImplWriteClickAction( SvStream& rSt, css::presentation::ClickAction eCa, bool bMediaClickAction ) +{ + sal_uInt32 nSoundRef = 0; // a reference to a sound in the sound collection, or NULL. + sal_uInt32 nHyperLinkID = 0;// a persistent unique identifier to an external hyperlink object (only valid when action == HyperlinkAction). + sal_uInt8 nAction = 0; // Action See Action Table + sal_uInt8 const nOleVerb = 0; // OleVerb Only valid when action == OLEAction. OLE verb to use, 0 = first verb, 1 = second verb, etc. + sal_uInt8 nJump = 0; // Jump See Jump Table + sal_uInt8 const nFlags = 0; // Bit 1: Animated. If 1, then button is animated + // Bit 2: Stop sound. If 1, then stop current sound when button is pressed. + // Bit 3: CustomShowReturn. If 1, and this is a jump to custom show, then return to this slide after custom show. + sal_uInt8 nHyperLinkType = 0;// HyperlinkType a value from the LinkTo enum, such as LT_URL (only valid when action == HyperlinkAction). + + OUString aFile; + + /* + Action Table: Action Value + NoAction 0 + MacroAction 1 + RunProgramAction 2 + JumpAction 3 + HyperlinkAction 4 + OLEAction 5 + MediaAction 6 + CustomShowAction 7 + + Jump Table: Jump Value + NoJump 0 + NextSlide, 1 + PreviousSlide, 2 + FirstSlide, 3 + LastSlide, 4 + LastSlideViewed 5 + EndShow 6 + */ + + if ( bMediaClickAction ) + nAction = 6; + else switch( eCa ) + { + case css::presentation::ClickAction_STOPPRESENTATION : + nJump += 2; + [[fallthrough]]; + case css::presentation::ClickAction_LASTPAGE : + nJump++; + [[fallthrough]]; + case css::presentation::ClickAction_FIRSTPAGE : + nJump++; + [[fallthrough]]; + case css::presentation::ClickAction_PREVPAGE : + nJump++; + [[fallthrough]]; + case css::presentation::ClickAction_NEXTPAGE : + { + nJump++; + nAction = 3; + } + break; + case css::presentation::ClickAction_SOUND : + { + if ( ImplGetPropertyValue( "Bookmark" ) ) + nSoundRef = maSoundCollection.GetId( *o3tl::doAccess<OUString>(mAny) ); + } + break; + case css::presentation::ClickAction_PROGRAM : + { + if ( ImplGetPropertyValue( "Bookmark" ) ) + { + INetURLObject aUrl( *o3tl::doAccess<OUString>(mAny) ); + if ( INetProtocol::File == aUrl.GetProtocol() ) + { + aFile = aUrl.PathToFileName(); + nAction = 2; + } + } + } + break; + + case css::presentation::ClickAction_BOOKMARK : + { + if ( ImplGetPropertyValue( "Bookmark" ) ) + { + OUString aBookmark( *o3tl::doAccess<OUString>(mAny) ); + sal_uInt32 nIndex = 0; + for ( const auto& rSlideName : maSlideNameList ) + { + if ( rSlideName == aBookmark ) + { + // Bookmark is a link to a document page + nAction = 4; + nHyperLinkType = 7; + + OUString aHyperString = OUString::number(256 + nIndex) + + "," + + OUString::number(nIndex + 1) + + ",Slide " + + OUString::number(nIndex + 1); + nHyperLinkID = ImplInsertBookmarkURL( aHyperString, 1 | ( nIndex << 8 ) | ( 1U << 31 ), aBookmark, "", "", aHyperString ); + } + nIndex++; + } + } + } + break; + + case css::presentation::ClickAction_DOCUMENT : + { + if ( ImplGetPropertyValue( "Bookmark" ) ) + { + OUString aBookmark( *o3tl::doAccess<OUString>(mAny) ); + if ( !aBookmark.isEmpty() ) + { + nAction = 4; + nHyperLinkType = 8; + + OUString aBookmarkFile( aBookmark ); + INetURLObject aUrl( aBookmark ); + if ( INetProtocol::File == aUrl.GetProtocol() ) + aBookmarkFile = aUrl.PathToFileName(); + nHyperLinkID = ImplInsertBookmarkURL( aBookmark, sal_uInt32(2 | ( 1U << 31 )), aBookmarkFile, aBookmark, "", "" ); + } + } + } + break; + + case css::presentation::ClickAction_INVISIBLE : + case css::presentation::ClickAction_VERB : + case css::presentation::ClickAction_VANISH : + case css::presentation::ClickAction_MACRO : + default : + break; + } + + sal_uInt32 nContainerSize = 24; + if ( nAction == 2 ) + nContainerSize += ( aFile.getLength() * 2 ) + 8; + rSt.WriteUInt32( ( EPP_InteractiveInfo << 16 ) | 0xf ).WriteUInt32( nContainerSize ) + .WriteUInt32( EPP_InteractiveInfoAtom << 16 ).WriteUInt32( 16 ) + .WriteUInt32( nSoundRef ) + .WriteUInt32( nHyperLinkID ) + .WriteUChar( nAction ) + .WriteUChar( nOleVerb ) + .WriteUChar( nJump ) + .WriteUChar( nFlags ) + .WriteUInt32( nHyperLinkType ); + + if ( nAction == 2 ) // run program Action + { + sal_Int32 nLen = aFile.getLength(); + rSt.WriteUInt32( ( EPP_CString << 16 ) | 0x20 ).WriteUInt32( nLen * 2 ); + for ( sal_Int32 i = 0; i < nLen; i++ ) + rSt.WriteUInt16( aFile[i] ); + } + + rSt.WriteUInt32( ( EPP_InteractiveInfo << 16 ) | 0x1f ).WriteUInt32( 24 ) // Mouse Over Action + .WriteUInt32( EPP_InteractiveInfo << 16 ).WriteUInt32( 16 ); + for ( int i = 0; i < 4; i++, rSt.WriteUInt32( 0 ) ) ; +} + +bool PPTWriter::ImplGetEffect( const css::uno::Reference< css::beans::XPropertySet > & rPropSet, + css::presentation::AnimationEffect& eEffect, + css::presentation::AnimationEffect& eTextEffect, + bool& bIsSound ) +{ + css::uno::Any aAny; + if ( GetPropertyValue( aAny, rPropSet, "Effect" ) ) + aAny >>= eEffect; + else + eEffect = css::presentation::AnimationEffect_NONE; + + if ( GetPropertyValue( aAny, rPropSet, "TextEffect" ) ) + aAny >>= eTextEffect; + else + eTextEffect = css::presentation::AnimationEffect_NONE; + if ( GetPropertyValue( aAny, rPropSet, "SoundOn" ) ) + aAny >>= bIsSound; + else + bIsSound = false; + + bool bHasEffect = ( ( eEffect != css::presentation::AnimationEffect_NONE ) + || ( eTextEffect != css::presentation::AnimationEffect_NONE ) + || bIsSound ); + return bHasEffect; +}; + +bool PPTWriter::ImplCreatePresentationPlaceholder( const bool bMasterPage, + const sal_uInt32 nStyleInstance, const sal_uInt8 nPlaceHolderId ) +{ + bool bRet = ImplGetText(); + if ( bRet && bMasterPage ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + sal_uInt32 nPresShapeID = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, nPresShapeID ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x50001 ); + mnTxId += 0x60; + aPropOpt.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropOpt.AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x110001 ); + aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x8000001 ); + aPropOpt.AddOpt( ESCHER_Prop_shadowColor, 0x8000002 ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + sal_uInt32 nLineFlags = 0x90001; + if ( aPropOpt.GetOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ) ) + nLineFlags |= 0x10001; // draw dashed line if no line + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ); + + SvMemoryStream aExtBu( 0x200, 0x200 ); + SvMemoryStream aClientTextBox( 0x200, 0x200 ); + ImplWriteTextStyleAtom( aClientTextBox, nStyleInstance, 0, nullptr, aExtBu, &aPropOpt ); + + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId ); + aPropOpt.CreateShapeProperties( mXShape ); + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 8, ESCHER_ClientAnchor ); + mpStrm->WriteInt16( maRect.Top() ).WriteInt16( maRect.Left() ).WriteInt16( maRect.Right() ).WriteInt16( maRect.Bottom() ); // top, left, right, bottom ???? + mpPptEscherEx->OpenContainer( ESCHER_ClientData ); + mpPptEscherEx->AddAtom( 8, EPP_OEPlaceholderAtom ); + mpStrm->WriteUInt32( 0 ) // PlacementID + .WriteUChar( nPlaceHolderId ) // PlaceHolderID + .WriteUChar( 0 ) // Size of PlaceHolder ( 0 = FULL, 1 = HALF, 2 = QUARTER ) + .WriteUInt16( 0 ); // padword + mpPptEscherEx->CloseContainer(); // ESCHER_ClientData + + if ( aClientTextBox.Tell() ) + { + mpStrm->WriteUInt32( ( ESCHER_ClientTextbox << 16 ) | 0xf ) + .WriteUInt32( aClientTextBox.Tell() ); + + mpStrm->WriteBytes(aClientTextBox.GetData(), aClientTextBox.Tell()); + } + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + else + bRet = false; + return bRet; +} + +void PPTWriter::ImplCreateShape( sal_uInt32 nType, ShapeFlag nFlags, EscherSolverContainer& rSolver ) +{ + sal_uInt32 nId = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( nType, nFlags, nId ); + rSolver.AddShape( mXShape, nId ); +} + +void PPTWriter::ImplCreateTextShape( EscherPropertyContainer& rPropOpt, EscherSolverContainer& rSolver, bool bFill ) +{ + mnTextStyle = EPP_TEXTSTYLE_TEXT; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, rSolver ); + if ( bFill ) + rPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + if ( ImplGetText() ) + { + mnTxId += 0x60; + rPropOpt.CreateTextProperties( mXPropSet, mnTxId ); + } +} + +void PPTWriter::ImplWritePage( const PHLayout& rLayout, EscherSolverContainer& aSolverContainer, PageType ePageType, bool bMasterPage, int nPageNumber ) +{ + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // sal_uInt32 nGroupLevel = 0; + + sal_uInt32 nGroups, nShapes, nShapeCount, nPer, nLastPer, nIndices, nOlePictureId; + css::awt::Point aTextRefPoint; + + nShapes = mXShapes->getCount(); + ResetGroupTable( nShapes ); + + nIndices = nLastPer = nShapeCount = 0; + + bool bIsTitlePossible = true; // powerpoint is not able to handle more than one title + + sal_uInt32 nOutlinerCount = 0; // the outline objects have to conform to the layout, + sal_uInt32 nPrevTextStyle = 0; // there are no more than two allowed + + nOlePictureId = 0; + + bool bAdditionalText = false; + + bool bSecOutl = false; + sal_uInt32 nPObjects = 0; + + std::unique_ptr<SvMemoryStream> pClientTextBox; + std::unique_ptr<SvMemoryStream> pClientData; + + while( GetNextGroupEntry() ) + { + nShapeCount++; + + nPer = ( 5 * nShapeCount ) / nShapes; + if ( nPer != nLastPer ) + { + nLastPer = nPer; + sal_uInt32 nValue = mnPagesWritten * 5 + nPer; + if ( nValue > mnStatMaxValue ) + nValue = mnStatMaxValue; + if ( mbStatusIndicator && ( nValue > mnLatestStatValue ) ) + { + mXStatusIndicator->setValue( nValue ); + mnLatestStatValue = nValue; + } + } + nGroups = GetGroupsClosed(); + for ( sal_uInt32 i = 0; i < nGroups; i++, mpPptEscherEx->LeaveGroup() ) ; + + if ( GetShapeByIndex( GetCurrentGroupIndex(), true ) ) + { + bool bIsSound; + bool bMediaClickAction = false; + css::presentation::AnimationEffect eAe; + css::presentation::AnimationEffect eTe; + + bool bEffect = ImplGetEffect( mXPropSet, eAe, eTe, bIsSound ); + css::presentation::ClickAction eCa = css::presentation::ClickAction_NONE; + if ( ImplGetPropertyValue( "OnClick" ) ) + mAny >>= eCa; + + bool bGroup = mType == "drawing.Group"; + bool bOpenBezier = mType == "drawing.OpenBezier"; + bool bClosedBezier = mType == "drawing.ClosedBezier"; + bool bPolyPolygon = mType == "drawing.PolyPolygon"; + bool bPolyLine = mType == "drawing.PolyLine"; + OUString aGraphicPropertyName("Graphic"); + + const css::awt::Size aSize100thmm( mXShape->getSize() ); + const css::awt::Point aPoint100thmm( mXShape->getPosition() ); + ::tools::Rectangle aRect100thmm( Point( aPoint100thmm.X, aPoint100thmm.Y ), Size( aSize100thmm.Width, aSize100thmm.Height ) ); + EscherPropertyContainer aPropOpt( mpPptEscherEx->GetGraphicProvider(), mpPicStrm.get(), aRect100thmm ); + + if ( bGroup ) + { + css::uno::Reference< css::container::XIndexAccess > + aXIndexAccess( mXShape, css::uno::UNO_QUERY ); + if ( EnterGroup( aXIndexAccess ) ) + { + std::unique_ptr<SvMemoryStream> pTmp; + if ( eCa != css::presentation::ClickAction_NONE ) + { + pTmp.reset(new SvMemoryStream(0x200, 0x200)); + ImplWriteClickAction( *pTmp, eCa, bMediaClickAction ); + } + sal_uInt32 nShapeId = mpPptEscherEx->EnterGroup(&maRect, pTmp.get()); + aSolverContainer.AddShape( mXShape, nShapeId ); + } + } + else + { + bool bIsFontwork = false; + bool bIsHatching = false; + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "IsFontwork", true ) ) + aAny >>= bIsFontwork; + if ( GetPropertyValue( aAny, mXPropSet, "FillStyle", true ) ) + { + css::drawing::FillStyle eFS; + aAny >>= eFS; + bIsHatching = eFS == css::drawing::FillStyle_HATCH; + if (mType == "drawing.Custom" && eFS == drawing::FillStyle_BITMAP) + { + ShapeFlag nMirrorFlags; + OUString sCustomShapeType; + MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( + mXShape, nMirrorFlags, sCustomShapeType); + if (eShapeType == mso_sptMax) + { + // We can't map this custom shape to a PPT preset and it has a bitmap + // fill. Make sure that at least the bitmap fill is not lost. + mType = "drawing.GraphicObject"; + aGraphicPropertyName = "Bitmap"; + } + } + } + if ( bIsHatching || bIsFontwork || ( mType == "drawing.Measure" ) || ( mType == "drawing.Caption" ) ) + { + if ( ImplGetPropertyValue( "BoundRect" ) ) + { + auto aRect = o3tl::doAccess<css::awt::Rectangle>(mAny); + maPosition = MapPoint( css::awt::Point( aRect->X, aRect->Y ) ); + maSize = MapSize( css::awt::Size( aRect->Width, aRect->Height ) ); + maRect = ::tools::Rectangle( Point( maPosition.X, maPosition.Y ), Size( maSize.Width, maSize.Height ) ); + } + mType = "drawing.dontknow"; + } + } + sal_uInt8 nPlaceHolderAtom = EPP_PLACEHOLDER_NONE; + + mnTextSize = 0; + mnTextStyle = EPP_TEXTSTYLE_NORMAL; + + if ( mType == "drawing.Custom" ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ShapeFlag nMirrorFlags; + OUString sCustomShapeType; + MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( mXShape, nMirrorFlags, sCustomShapeType ); + if ( sCustomShapeType == "col-502ad400" || sCustomShapeType == "col-60da8460" ) + { // sj: creating metafile for customshapes that can't be saved to ms format properly + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "MetaFile", false ) ) + { + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mXShape); + if ( pObj ) + { + ::tools::Rectangle aBound = pObj->GetCurrentBoundRect(); + maPosition = MapPoint( css::awt::Point( aBound.Left(), aBound.Top() ) ); + maSize = MapSize( css::awt::Size ( aBound.GetWidth(), aBound.GetHeight() ) ); + maRect = ::tools::Rectangle( Point( maPosition.X, maPosition.Y ), Size( maSize.Width, maSize.Height ) ); + mnAngle = 0; + } + } + } + else + { + ImplCreateShape( eShapeType, + nMirrorFlags | ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + aPropOpt.CreateCustomShapeProperties( eShapeType, mXShape ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape); + if ( ImplGetText() ) + { + if ( !aPropOpt.IsFontWork() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, true ); + } + } + } + } + else if ( mType == "drawing.Rectangle" ) + { + sal_Int32 nRadius = 0; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + if ( ImplGetPropertyValue( "CornerRadius" ) ) + { + mAny >>= nRadius; + nRadius = MapSize( css::awt::Size( nRadius, 0 ) ).Width; + } + if ( nRadius ) + { + ImplCreateShape( ESCHER_ShpInst_RoundRectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + sal_Int32 nLength = maRect.GetWidth(); + if ( nLength > maRect.GetHeight() ) + nLength = maRect.GetHeight(); + nLength >>= 1; + if ( nRadius >= nLength ) + nRadius = 0x2a30; // 0x2a30 is PPTs maximum radius + else + { + if (nLength != 0) + nRadius = ( 0x2a30 * nRadius ) / nLength; + else + nRadius = 0x2a30; // 0x2a30 is PPTs maximum radius + } + aPropOpt.AddOpt( ESCHER_Prop_adjustValue, nRadius ); + } + else + { + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + } + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + if ( ImplGetText() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, false, false ); + } + } + else if ( mType == "drawing.Ellipse" ) + { + css::drawing::CircleKind eCircleKind( css::drawing::CircleKind_FULL ); + PolyStyle ePolyKind = PolyStyle::Chord; + if ( ImplGetPropertyValue( "CircleKind" ) ) + { + mAny >>= eCircleKind; + switch ( eCircleKind ) + { + case css::drawing::CircleKind_SECTION : + { + ePolyKind = PolyStyle::Pie; + } + break; + case css::drawing::CircleKind_ARC : + { + ePolyKind = PolyStyle::Arc; + } + break; + + case css::drawing::CircleKind_CUT : + { + ePolyKind = PolyStyle::Chord; + } + break; + + default: + eCircleKind = css::drawing::CircleKind_FULL; + } + } + if ( eCircleKind == css::drawing::CircleKind_FULL ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_Ellipse, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + if ( ImplGetText() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, false, false ); + } + } + else + { + sal_Int32 nStartAngle, nEndAngle; + if ( !ImplGetPropertyValue( "CircleStartAngle" ) ) + continue; + nStartAngle = *o3tl::doAccess<sal_Int32>(mAny); + if( !ImplGetPropertyValue( "CircleEndAngle" ) ) + continue; + nEndAngle = *o3tl::doAccess<sal_Int32>(mAny); + css::awt::Point aPoint( mXShape->getPosition() ); + css::awt::Size aSize( mXShape->getSize() ); + css::awt::Point aStart, aEnd, aCenter; + ::tools::Rectangle aRect( Point( aPoint.X, aPoint.Y ), Size( aSize.Width, aSize.Height ) ); + aStart.X = static_cast<sal_Int32>( cos( basegfx::deg2rad<100>(nStartAngle) ) * 100.0 ); + aStart.Y = - static_cast<sal_Int32>( sin( basegfx::deg2rad<100>(nStartAngle) ) * 100.0 ); + aEnd.X = static_cast<sal_Int32>( cos( basegfx::deg2rad<100>(nEndAngle) ) * 100.0 ); + aEnd.Y = - static_cast<sal_Int32>( sin( basegfx::deg2rad<100>(nEndAngle) ) * 100.0 ); + aCenter.X = aPoint.X + ( aSize.Width / 2 ); + aCenter.Y = aPoint.Y + ( aSize.Height / 2 ); + aStart.X += aCenter.X; + aStart.Y += aCenter.Y; + aEnd.X += aCenter.X; + aEnd.Y += aCenter.Y; + tools::Polygon aPolygon( aRect, Point( aStart.X, aStart.Y ), Point( aEnd.X, aEnd.Y ), ePolyKind ); + bool bNeedText = true; + if ( mnAngle ) + { + aPolygon.Rotate( aRect.TopLeft(), Degree10(static_cast<sal_Int16>( mnAngle / 10 )) ); + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bNeedText = false; + bAdditionalText = true; + mnTextSize = 0; + } + mnAngle = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + switch ( ePolyKind ) + { + case PolyStyle::Pie : + case PolyStyle::Chord : + { + if ( aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect, &aPolygon ) ) + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + } + break; + + case PolyStyle::Arc : + { + if ( aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect, &aPolygon ) ) + aPropOpt.CreateLineProperties( mXPropSet, false ); + } + break; + } + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + if ( bNeedText && ImplGetText() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, false, false ); + } + } + } + else if ( mType == "drawing.Control" ) + { + css::uno::Reference< css::drawing::XControlShape > aXControlShape( mXShape, css::uno::UNO_QUERY ); + if ( !aXControlShape.is() ) + continue; + css::uno::Reference< css::awt::XControlModel > aXControlModel( aXControlShape->getControl() ); + if ( !aXControlModel.is() ) + continue; + + sal_Int64 nAspect = css::embed::Aspects::MSOLE_CONTENT; + try + { + // try to get the aspect when available + css::uno::Reference< css::beans::XPropertySet > xShapeProps( mXShape, css::uno::UNO_QUERY_THROW ); + xShapeProps->getPropertyValue("Aspect") >>= nAspect; + } + catch( css::uno::Exception& ) + {} + + mpExEmbed->WriteUInt32( 0xf | ( EPP_ExControl << 16 ) ) + .WriteUInt32( 0 ); // Size of this container + + sal_uInt32 nSize, nOldPos = mpExEmbed->Tell(); + + sal_uInt32 nPageId = nPageNumber; + if ( ePageType == MASTER ) + nPageId |= 0x80000000; + else + nPageId += 0x100; + mpExEmbed->WriteUInt32( EPP_ExControlAtom << 16 ) + .WriteUInt32( 4 ) + .WriteUInt32( nPageId ); + std::unique_ptr<PPTExOleObjEntry> pEntry( new PPTExOleObjEntry( OCX_CONTROL, mpExEmbed->Tell() ) ); + pEntry->xControlModel = aXControlModel; + pEntry->xShape = mXShape; + maExOleObj.push_back( std::move(pEntry) ); + + mnExEmbed++; + + mpExEmbed->WriteUInt32( 1 | ( EPP_ExOleObjAtom << 16 ) ) + .WriteUInt32( 24 ) + .WriteUInt32( nAspect ) + .WriteUInt32( 2 ) + .WriteUInt32( mnExEmbed ) + .WriteUInt32( 0 ) + .WriteUInt32( 4 ) // index to the persist table + .WriteUInt32( 0x0012de00 ); + + css::awt::Size aSize; + OUString aControlName; + tools::SvRef<SotStorage> xTemp( new SotStorage( new SvMemoryStream(), true ) ); + if ( oox::ole::MSConvertOCXControls::WriteOCXStream( mXModel, xTemp, aXControlModel, aSize, aControlName ) ) + { + OUString aUserName( xTemp->GetUserName() ); + OUString aOleIdentifier; + if ( !aUserName.isEmpty() ) + { + tools::SvRef<SotStorageStream> xCompObj = xTemp->OpenSotStream( + "\1CompObj", + StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYALL ); + sal_uInt32 const nStreamLen = xCompObj->remainingSize(); + sal_Int16 nVersion, nByteOrder; + sal_Int32 nWinVersion, nVal, nStringLen; + xCompObj->ReadInt16( nVersion ) + .ReadInt16( nByteOrder ) + .ReadInt32( nWinVersion ) + .ReadInt32( nVal ); + xCompObj->SeekRel( 16 ); // skipping clsid + xCompObj->ReadInt32( nStringLen ); + if ( ( xCompObj->Tell() + nStringLen ) < nStreamLen ) + { + xCompObj->SeekRel( nStringLen ); // now skipping the UserName; + xCompObj->ReadInt32( nStringLen ); + if ( ( xCompObj->Tell() + nStringLen ) < nStreamLen ) + { + xCompObj->SeekRel( nStringLen ); // now skipping the clipboard formatname + xCompObj->ReadInt32( nStringLen ); + if ( ( nStringLen > 1 ) && ( ( xCompObj->Tell() + nStringLen ) < nStreamLen ) ) + { // i think that the OleIdentifier will follow + OString aTemp = read_uInt8s_ToOString(*xCompObj, nStringLen - 1); + aOleIdentifier = OStringToOUString(aTemp, RTL_TEXTENCODING_MS_1252); + } + } + } + } + + PPTWriter::WriteCString( *mpExEmbed, aControlName, 1 ); + PPTWriter::WriteCString( *mpExEmbed, aOleIdentifier, 2 ); + PPTWriter::WriteCString( *mpExEmbed, aUserName, 3 ); + } + nSize = mpExEmbed->Tell() - nOldPos; + mpExEmbed->Seek( nOldPos - 4 ); + mpExEmbed->WriteUInt32( nSize ); + mpExEmbed->Seek( STREAM_SEEK_TO_END ); + nOlePictureId = mnExEmbed; + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ShapeFlag const nSpFlags = ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor | ShapeFlag::OLEShape; + ImplCreateShape( ESCHER_ShpInst_HostControl, nSpFlags, aSolverContainer ); + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "MetaFile", false ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + //export form control graphic + else if ( aPropOpt.CreateBlipPropertiesforOLEControl(mXPropSet,mXShape)) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + aPropOpt.AddOpt( ESCHER_Prop_pictureId, mnExEmbed ); + aPropOpt.AddOpt( ESCHER_Prop_pictureActive, 0x10000 ); + + if ( !aControlName.isEmpty() ) + { + aPropOpt.AddOpt(ESCHER_Prop_wzName, aControlName); + } + } + else if ( mType == "drawing.Connector" ) + { + sal_uInt16 nSpType; + ShapeFlag nSpFlags; + css::awt::Rectangle aNewRect; + if ( !aPropOpt.CreateConnectorProperties( mXShape, aSolverContainer, aNewRect, nSpType, nSpFlags ) ) + continue; + + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( nSpType, nSpFlags, aSolverContainer ); + + // #119459# for connector shape, the start point and end point is fixed, and should not be rotated. + mnAngle = 0; + } + else if ( mType == "drawing.Measure" ) + { + continue; + } + else if ( mType == "drawing.Line" ) + { + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_LINE, false, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + if ( ImplGetText() ) + { + aTextRefPoint = css::awt::Point( maRect.Left(), maRect.Top() ); + mnTextSize = 0; + bAdditionalText = true; + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( &maRect,0 ); + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ShapeFlag nFlags = ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty; + + if ( maRect.Top() > maRect.Bottom() ) + nFlags |= ShapeFlag::FlipV; + if ( maRect.Left() > maRect.Right() ) + nFlags |= ShapeFlag::FlipH; + + ImplCreateShape( ESCHER_ShpInst_Line, nFlags, aSolverContainer ); + aPropOpt.AddOpt( ESCHER_Prop_shapePath, ESCHER_ShapeComplex ); + aPropOpt.CreateLineProperties( mXPropSet, false ); + mnAngle = 0; + } + else if ( bPolyPolygon ) + { + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bAdditionalText = true; + mnTextSize = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + mnAngle = 0; + } + else if ( bPolyLine ) + { + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bAdditionalText = true; + mnTextSize = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + aPropOpt.CreateLineProperties( mXPropSet, false ); + mnAngle = 0; + } + else if ( bOpenBezier ) + { + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bAdditionalText = true; + mnTextSize = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, true, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + aPropOpt.CreateLineProperties( mXPropSet, false ); + mnAngle = 0; + } + else if ( bClosedBezier ) + { + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bAdditionalText = true; + mnTextSize = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, true, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + mnAngle = 0; + } + else if ( ( mType == "drawing.GraphicObject" ) || ( mType == "presentation.GraphicObject" ) ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + + // a GraphicObject can also be a ClickMe element + if ( mbEmptyPresObj && ( ePageType == NORMAL ) ) + { + nPlaceHolderAtom = rLayout.nUsedObjectPlaceHolder; + ImplCreateShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveAnchor | ShapeFlag::HaveMaster, aSolverContainer ); + mnTxId += 0x60; + aPropOpt.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, mnShapeMasterBody ); + } + else + { + mXText.set( mXShape, css::uno::UNO_QUERY ); + + if ( mXText.is() ) + mnTextSize = mXText->getString().getLength(); + + if ( mnTextSize ) // graphic object or area fill + { + /* SJ #i34951#: because M. documents are not allowing GraphicObjects containing text, we + have to create a simple Rectangle with fill bitmap instead (while not allowing BitmapMode_Repeat). + */ + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "Graphic", true, true, false ) ) + { + aPropOpt.AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapNone ); + aPropOpt.AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 ); + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x8000000 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + if ( ImplGetText() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, false, false ); + } + } + } + else + { + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + + if (aPropOpt.CreateGraphicProperties(mXPropSet, aGraphicPropertyName, false, + true)) + { + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + } + } + } + else if ( ( mType == "drawing.Text" ) || ( mType == "presentation.Notes" ) ) + { + if ( ( ePageType == NOTICE ) && mbPresObj ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Notes, EPP_PLACEHOLDER_MASTERNOTESBODYIMAGE ) ) + continue; + else + nPlaceHolderAtom = EPP_PLACEHOLDER_NOTESBODY; + } + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + else if ( mType == "presentation.TitleText" ) + { + if ( mbPresObj ) + { + if ( ( ePageType == NOTICE ) && mbEmptyPresObj ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + nPlaceHolderAtom = EPP_PLACEHOLDER_MASTERNOTESBODYIMAGE; + ImplCreateShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveAnchor, aSolverContainer ); + aPropOpt.CreateLineProperties( mXPropSet, false ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 ); + } + else if ( rLayout.bTitlePossible && bIsTitlePossible ) + { + bIsTitlePossible = false; + + ImplGetText(); + TextObjBinary aTextObj( mXText, EPP_TEXTTYPE_Title, maFontCollection, static_cast<PPTExBulletProvider&>(*this) ); + if ( ePageType == MASTER ) + { + if ( mnTextSize ) + { + OUString aUString( mXText->getString() ); + sal_uInt16 nChar; + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mnShapeMasterTitle = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + mnShapeMasterTitle ); + EscherPropertyContainer aPropertyOptions; + aPropertyOptions.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x50001 ); + mnTxId += 0x60; + aPropertyOptions.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropertyOptions.AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle ); + aPropertyOptions.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x110001 ); + aPropertyOptions.AddOpt( ESCHER_Prop_lineColor, 0x8000001 ); + aPropertyOptions.AddOpt( ESCHER_Prop_shadowColor, 0x8000002 ); + aPropertyOptions.CreateFillProperties( mXPropSet, true, mXShape ); + sal_uInt32 nLineFlags = 0x90001; + if ( aPropertyOptions.GetOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ) ) + nLineFlags |= 0x10001; // draw dashed line if no line + aPropertyOptions.AddOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ); + mnTxId += 0x60; + aPropertyOptions.CreateTextProperties( mXPropSet, mnTxId ); + ImplAdjustFirstLineLineSpacing( aTextObj, aPropOpt ); + aPropertyOptions.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 8, ESCHER_ClientAnchor ); + mpStrm->WriteInt16( maRect.Top() ).WriteInt16( maRect.Left() ).WriteInt16( maRect.Right() ).WriteInt16( maRect.Bottom() ); // top, left, right, bottom ???? + mpPptEscherEx->OpenContainer( ESCHER_ClientData ); + mpPptEscherEx->AddAtom( 8, EPP_OEPlaceholderAtom ); + mpStrm->WriteUInt32( 0 ) // PlacementID + .WriteUChar( EPP_PLACEHOLDER_MASTERTITLE ) // PlaceHolderID + .WriteUChar( 0 ) // Size of PlaceHolder ( 0 = FULL, 1 = HALF, 2 = QUARTER ) + .WriteUInt16( 0 ); // padword + mpPptEscherEx->CloseContainer(); // ESCHER_ClientData + mpPptEscherEx->OpenContainer( ESCHER_ClientTextbox ); + mpPptEscherEx->AddAtom( 4, EPP_TextHeaderAtom ); + mpStrm->WriteUInt32( EPP_TEXTTYPE_Title ); + mpPptEscherEx->AddAtom( mnTextSize << 1, EPP_TextCharsAtom ); + const sal_Unicode* pString = aUString.getStr(); + for ( sal_uInt32 i = 0; i < mnTextSize; i++ ) + { + nChar = pString[ i ]; // 0xa -> 0xb soft newline + if ( nChar == 0xa ) + nChar++; // 0xd -> 0xd hard newline + mpStrm->WriteUInt16( nChar ); + } + mpPptEscherEx->AddAtom( 6, EPP_BaseTextPropAtom ); + mpStrm->WriteUInt32( mnTextSize + 1 ).WriteUInt16( 0 ); + mpPptEscherEx->AddAtom( 10, EPP_TextSpecInfoAtom ); + mpStrm->WriteUInt32( mnTextSize + 1 ).WriteUInt32( 1 ).WriteUInt16( 0 ); + mpPptEscherEx->CloseContainer(); // ESCHER_ClientTextBox + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + continue; + } + else + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mnTextStyle = EPP_TEXTSTYLE_TITLE; + nPlaceHolderAtom = rLayout.nTypeOfTitle; + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveMaster, + aSolverContainer ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, mnShapeMasterTitle ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId ); + ImplAdjustFirstLineLineSpacing( aTextObj, aPropOpt ); + if ( mbEmptyPresObj ) + { + sal_uInt32 nNoLineDrawDash = 0; + aPropOpt.GetOpt( ESCHER_Prop_fNoLineDrawDash, nNoLineDrawDash ); + nNoLineDrawDash |= 0x10001; + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, nNoLineDrawDash ); + } + } + } + else + mbPresObj = false; + } + if ( !mbPresObj ) + { + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( ( mType == "presentation.Outliner" ) || ( mType == "presentation.Subtitle" ) ) + { + if ( mbPresObj ) + { + nOutlinerCount++; + if ( (rLayout.bOutlinerPossible && ( nOutlinerCount == 1 )) || + (( rLayout.bSecOutlinerPossible && ( nOutlinerCount == 2 ) ) && ( nPrevTextStyle == EPP_TEXTSTYLE_BODY )) + ) + { + ImplGetText(); + TextObjBinary aTextObj( mXText, EPP_TEXTTYPE_Body, maFontCollection, static_cast<PPTExBulletProvider&>(*this) ); + if ( ePageType == MASTER ) + { + nPrevTextStyle = EPP_TEXTSTYLE_TITLE; + if ( mnTextSize ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mnShapeMasterBody = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + mnShapeMasterBody ); + EscherPropertyContainer aPropOpt2; + aPropOpt2.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x50001 ); + mnTxId += 0x60; + aPropOpt2.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropOpt2.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x110001 ); + aPropOpt2.AddOpt( ESCHER_Prop_lineColor, 0x8000001 ); + aPropOpt2.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90001 ); + aPropOpt2.AddOpt( ESCHER_Prop_shadowColor, 0x8000002 ); + aPropOpt2.CreateFillProperties( mXPropSet, true, mXShape ); + sal_uInt32 nLineFlags = 0x90001; + if ( aPropOpt2.GetOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ) ) + nLineFlags |= 0x10001; // draw dashed line if no line + aPropOpt2.AddOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ); + mnTxId += 0x60; + aPropOpt2.CreateTextProperties( mXPropSet, mnTxId ); + ImplAdjustFirstLineLineSpacing( aTextObj, aPropOpt2 ); + aPropOpt2.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 8, ESCHER_ClientAnchor ); + mpStrm->WriteInt16( maRect.Top() ).WriteInt16( maRect.Left() ).WriteInt16( maRect.Right() ).WriteInt16( maRect.Bottom() ); // top, left, right, bottom ???? + mpPptEscherEx->OpenContainer( ESCHER_ClientData ); + mpPptEscherEx->AddAtom( 8, EPP_OEPlaceholderAtom ); + sal_uInt8 PlaceHolderID = ( mType == "presentation.Subtitle") ? EPP_PLACEHOLDER_MASTERSUBTITLE:EPP_PLACEHOLDER_MASTERBODY; + mpStrm->WriteUInt32( 1 ) // PlacementID + .WriteUChar( PlaceHolderID )/*(sal_uInt8)EPP_PLACEHOLDER_MASTERBODY */ // PlaceHolderID + .WriteUChar( 0 ) // Size of PlaceHolder ( 0 = FULL, 1 = HALF, 2 = QUARTER ) + .WriteUInt16( 0 ); // padword + mpPptEscherEx->CloseContainer(); // ESCHER_ClientData + mpPptEscherEx->OpenContainer( ESCHER_ClientTextbox ); // printf + mpPptEscherEx->AddAtom( 4, EPP_TextHeaderAtom ); + if ( mType == "presentation.Subtitle") + mpStrm->WriteUInt32( EPP_TEXTTYPE_CenterBody ); + else + mpStrm->WriteUInt32( EPP_TEXTTYPE_Body ); + mnTextSize = aTextObj.Count(); + aTextObj.Write( mpStrm.get() ); + mpPptEscherEx->BeginAtom(); + for ( sal_uInt32 i = 0; i < aTextObj.ParagraphCount() ; ++i ) + { + ParagraphObj* pPara = aTextObj.GetParagraph(i); + mpStrm->WriteUInt32( pPara->CharacterCount() ) + .WriteUInt16( pPara->nDepth ); + } + mpPptEscherEx->EndAtom( EPP_BaseTextPropAtom ); + mpPptEscherEx->AddAtom( 10, EPP_TextSpecInfoAtom ); + mpStrm->WriteUInt32( mnTextSize ).WriteUInt32( 1 ).WriteUInt16( 0 ); + + mpPptEscherEx->CloseContainer(); // ESCHER_ClientTextBox + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + continue; + } + else + { + mnTextStyle = EPP_TEXTSTYLE_BODY; + nPlaceHolderAtom = rLayout.nTypeOfOutliner; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveMaster, + aSolverContainer ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, mnShapeMasterBody ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId ); + ImplAdjustFirstLineLineSpacing( aTextObj, aPropOpt ); + if ( mbEmptyPresObj ) + { + sal_uInt32 nNoLineDrawDash = 0; + aPropOpt.GetOpt( ESCHER_Prop_fNoLineDrawDash, nNoLineDrawDash ); + nNoLineDrawDash |= 0x10001; + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, nNoLineDrawDash ); + } + } + } + else + mbPresObj = false; + } + if ( !mbPresObj ) + { + if (ePageType == MASTER ) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mXShape); + if (pObj && pObj->IsNotVisibleAsMaster()) + continue; + } + + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( ( mType == "drawing.Page" ) || ( mType == "presentation.Page" ) ) + { + if ( ( ePageType == NOTICE ) && mbPresObj ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Notes, EPP_PLACEHOLDER_MASTERNOTESSLIDEIMAGE ) ) + continue; + else + nPlaceHolderAtom = EPP_PLACEHOLDER_NOTESSLIDEIMAGE; + } + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + else if ( mType == "drawing.Frame" ) + { + continue; + } + else if ( ( mType == "drawing.OLE2" ) || ( mType == "presentation.OLE2" ) + || ( mType == "presentation.Chart" ) || ( mType == "presentation.Calc" ) + || ( mType == "presentation.OrgChart" ) ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + if ( mbEmptyPresObj && ( ePageType == NORMAL ) ) + { + nPlaceHolderAtom = rLayout.nUsedObjectPlaceHolder; + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveMaster, + aSolverContainer ); + mnTxId += 0x60; + aPropOpt.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, mnShapeMasterBody ); + } + else + { + mpExEmbed->WriteUInt32( 0xf | ( EPP_ExEmbed << 16 ) ) + .WriteUInt32( 0 ); // Size of this container + + sal_uInt32 nSize, nOldPos = mpExEmbed->Tell(); + + mpExEmbed->WriteUInt32( EPP_ExEmbedAtom << 16 ) + .WriteUInt32( 8 ) + .WriteUInt32( 0 ) // follow colorscheme : 0->do not follow + // 1->follow colorscheme + // 2->follow text and background scheme + .WriteUChar( 1 ) // (bool)set if embedded server can not be locked + .WriteUChar( 0 ) // (bool)do not need to send dimension + .WriteUChar( 0 ) // (bool)is object a world table + .WriteUChar( 0 ); // pad byte + + std::unique_ptr<PPTExOleObjEntry> pE( new PPTExOleObjEntry( NORMAL_OLE_OBJECT, mpExEmbed->Tell() ) ); + pE->xShape = mXShape; + maExOleObj.push_back( std::move(pE) ); + + mnExEmbed++; + + sal_Int64 nAspect = css::embed::Aspects::MSOLE_CONTENT; + try + { + // try to get the aspect when available + css::uno::Reference< css::beans::XPropertySet > xShapeProps( mXShape, css::uno::UNO_QUERY_THROW ); + xShapeProps->getPropertyValue("Aspect") >>= nAspect; + } + catch( css::uno::Exception& ) + {} + + mpExEmbed->WriteUInt32( 1 | ( EPP_ExOleObjAtom << 16 ) ) + .WriteUInt32( 24 ) + .WriteUInt32( nAspect ) // Aspect + .WriteUInt32( 0 ) + .WriteUInt32( mnExEmbed ) // index to the persist table + .WriteUInt32( 0 ) // subtype + .WriteUInt32( 0 ) + .WriteUInt32( 0x0012b600 ); + + nSize = mpExEmbed->Tell() - nOldPos; + mpExEmbed->Seek( nOldPos - 4 ); + mpExEmbed->WriteUInt32( nSize ); + mpExEmbed->Seek( STREAM_SEEK_TO_END ); + nOlePictureId = mnExEmbed; + + ShapeFlag nSpFlags = ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty; + if ( nOlePictureId ) + nSpFlags |= ShapeFlag::OLEShape; + ImplCreateShape( ESCHER_ShpInst_PictureFrame, nSpFlags, aSolverContainer ); + if ( aPropOpt.CreateOLEGraphicProperties( mXShape ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + if ( nOlePictureId ) + aPropOpt.AddOpt( ESCHER_Prop_pictureId, nOlePictureId ); + } + } + else if ( mType == "presentation.Header" ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Other, EPP_PLACEHOLDER_MASTERHEADER ) ) + continue; + else + { + mbPresObj = false; + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( mType == "presentation.Footer" ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Other, EPP_PLACEHOLDER_MASTERFOOTER ) ) + continue; + else + { + mbPresObj = false; + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( mType == "presentation.DateTime" ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Other, EPP_PLACEHOLDER_MASTERDATE ) ) + continue; + else + { + mbPresObj = false; + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( mType == "presentation.SlideNumber" ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Other, EPP_PLACEHOLDER_MASTERSLIDENUMBER ) ) + continue; + else + { + mbPresObj = false; + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( (mType.getLength() > 9) && (mType[8] == '3') && (mType[9] == 'D') ) // drawing.3D + { + // SceneObject, CubeObject, SphereObject, LatheObject, ExtrudeObject, PolygonObject + if ( !ImplGetPropertyValue( "Bitmap" ) ) + continue; + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "Bitmap", false ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + else if ( mType == "drawing.Media" ) + { + mnAngle = 0; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + if ( aPropOpt.CreateMediaGraphicProperties( mXShape ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, mXPropSet, "MediaURL", true ) ) + { + OUString aMediaURL; + if ( (aAny >>= aMediaURL ) && !aMediaURL.isEmpty() ) + { + // SJ: creating the Media RefObj + sal_uInt32 nRefId = ++mnExEmbed; + + mpExEmbed->WriteUInt16( 0xf ) + .WriteUInt16( EPP_ExMCIMovie ) // PPT_PST_ExAviMovie + .WriteUInt32( 0 ); + sal_uInt32 nSize, nStart = mpExEmbed->Tell(); + mpExEmbed->WriteUInt16( 0 ) + .WriteUInt16( EPP_ExObjRefAtom ) + .WriteUInt32( 4 ) + .WriteUInt32( nRefId ); + mpExEmbed->WriteUInt16( 0xf ) + .WriteUInt16( EPP_ExVideo ) + .WriteUInt32( 0 ); + + mpExEmbed->WriteUInt16( 0 ) + .WriteUInt16( EPP_ExMediaAtom ) + .WriteUInt32( 8 ) + .WriteUInt32( nRefId ) + .WriteUInt16( 0 ) + .WriteUInt16( 0x435 ); + + sal_uInt16 i, nStringLen = static_cast<sal_uInt16>(aMediaURL.getLength()); + mpExEmbed->WriteUInt32( EPP_CString << 16 ).WriteUInt32( nStringLen * 2 ); + for ( i = 0; i < nStringLen; i++ ) + { + sal_Unicode nChar = aMediaURL[ i ]; + mpExEmbed->WriteUInt16( nChar ); + } + nSize = mpExEmbed->Tell() - nStart; + mpExEmbed->SeekRel( - ( static_cast<sal_Int32>(nSize) + 4 ) ); + mpExEmbed->WriteUInt32( nSize ); // size of PPT_PST_ExMCIMovie + mpExEmbed->SeekRel( 0x10 ); + nSize -= 20; + mpExEmbed->WriteUInt32( nSize ); // PPT_PST_ExMediaAtom + mpExEmbed->SeekRel( nSize ); + + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + pClientData->WriteUInt16( 0 ) + .WriteUInt16( EPP_ExObjRefAtom ) + .WriteUInt32( 4 ) + .WriteUInt32( nRefId ); + // write EPP_InteractiveInfo container for no_action + pClientData->WriteUInt32( ( EPP_InteractiveInfo << 16 ) | 0xf ).WriteUInt32( 24 ); + pClientData->WriteUInt16( 0 ) + .WriteUInt16( EPP_InteractiveInfoAtom ) + .WriteUInt32( 16 ) + .WriteUInt32( 0 ) + .WriteUInt32( 0 ) + .WriteUChar( 6 ) + .WriteUChar( 0 ) + .WriteUChar( 0 ) + .WriteUChar( 0 ) + .WriteUInt32( 0 ); + } + } + } + else if ( (mType == "drawing.Table") || (mType == "presentation.Table") ) + { + if ( eCa != css::presentation::ClickAction_NONE ) + { + SvMemoryStream aTmp(0x200, 0x200); + ImplWriteClickAction( aTmp, eCa, bMediaClickAction ); + } + ImplCreateTable( mXShape, aSolverContainer, aPropOpt ); + continue; + } + else if ( mType == "drawing.dontknow" ) + { + mnAngle = 0; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "MetaFile", false ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + else + { + continue; + } + + bool bClientData = ( bEffect || ( eCa != css::presentation::ClickAction_NONE ) || + nPlaceHolderAtom || nOlePictureId ); + if ( bClientData ) + { + if ( nPlaceHolderAtom ) + { + sal_Int32 nPlacementID = -1; + if ( ( mnTextStyle == EPP_TEXTSTYLE_TITLE ) || ( mnTextStyle == EPP_TEXTSTYLE_BODY ) ) + nPlacementID = nIndices++; + else + { + switch ( nPlaceHolderAtom ) + { + default : + { + if ( nPlaceHolderAtom < 19 ) + break; + [[fallthrough]]; + } + case EPP_PLACEHOLDER_NOTESBODY : + case EPP_PLACEHOLDER_MASTERDATE : + case EPP_PLACEHOLDER_NOTESSLIDEIMAGE : + case EPP_PLACEHOLDER_MASTERNOTESBODYIMAGE : + nPlacementID = nIndices++; + } + } + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + + pClientData->WriteUInt32( EPP_OEPlaceholderAtom << 16 ).WriteUInt32( 8 ) + .WriteInt32( nPlacementID ) // PlacementID + .WriteUChar( nPlaceHolderAtom ) // PlaceHolderID + .WriteUChar( 0 ) // Size of PlaceHolder ( 0 = FULL, 1 = HALF, 2 = QUARTER ) + .WriteUInt16( 0 ); // padword + } + if ( nOlePictureId ) + { + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + + pClientData->WriteUInt32( EPP_ExObjRefAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( nOlePictureId ); + nOlePictureId = 0; + } + if ( bEffect && !pClientData ) + { + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + } + + if ( eCa != css::presentation::ClickAction_NONE ) + { + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + ImplWriteClickAction( *pClientData, eCa, bMediaClickAction ); + } + } + if ( ( mnTextStyle == EPP_TEXTSTYLE_TITLE ) || ( mnTextStyle == EPP_TEXTSTYLE_BODY ) ) + { + if ( !pClientTextBox ) + pClientTextBox.reset(new SvMemoryStream( 0x200, 0x200 )); + + if ( !mbEmptyPresObj ) + { + if ( ( ePageType == NORMAL ) && !bMasterPage ) + { + sal_uInt32 nTextType = EPP_TEXTTYPE_Body; + if ( mnTextStyle == EPP_TEXTSTYLE_BODY ) + { + if ( bSecOutl ) + nTextType = EPP_TEXTTYPE_HalfBody; + else if ( mType == "presentation.Subtitle" ) + nTextType = EPP_TEXTTYPE_CenterBody; + bSecOutl = true; + } + else + nTextType = EPP_TEXTTYPE_Title; + + TextRuleEntry aTextRule; + SvMemoryStream aExtBu( 0x200, 0x200 ); + ImplGetText(); + ImplWriteTextStyleAtom( *pClientTextBox, nTextType, nPObjects, &aTextRule, aExtBu, nullptr ); + ImplWriteExtParaHeader( aExtBu, nPObjects++, nTextType, nPageNumber + 0x100 ); + SvMemoryStream* pOut = aTextRule.pOut.get(); + if ( pOut ) + { + pClientTextBox->WriteBytes(pOut->GetData(), pOut->Tell()); + aTextRule.pOut.reset(); + } + if ( aExtBu.Tell() ) + { + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + ImplProgTagContainer( pClientData.get(), &aExtBu ); + } + } + } + } + else + { + if ( !aPropOpt.IsFontWork() ) + { + if ( mnTextSize || ( nPlaceHolderAtom == EPP_PLACEHOLDER_MASTERDATE ) || ( nPlaceHolderAtom == EPP_PLACEHOLDER_NOTESBODY ) ) + { + int nInstance2; + if ( ( nPlaceHolderAtom == EPP_PLACEHOLDER_MASTERDATE ) || ( nPlaceHolderAtom == EPP_PLACEHOLDER_NOTESBODY ) ) + nInstance2 = 2; + else + nInstance2 = EPP_TEXTTYPE_Other; // Text in a Shape + + if ( !pClientTextBox ) + pClientTextBox.reset(new SvMemoryStream( 0x200, 0x200 )); + + SvMemoryStream aExtBu( 0x200, 0x200 ); + ImplWriteTextStyleAtom( *pClientTextBox, nInstance2, 0, nullptr, aExtBu, &aPropOpt ); + if ( aExtBu.Tell() ) + { + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + ImplProgTagContainer( pClientData.get(), &aExtBu ); + } + } + else if ( nPlaceHolderAtom >= 19 ) + { + if ( !pClientTextBox ) + pClientTextBox.reset(new SvMemoryStream( 12 )); + + pClientTextBox->WriteUInt32( EPP_TextHeaderAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( 7 ); + } + } + } + + aPropOpt.CreateShadowProperties( mXPropSet ); + maRect.Justify(); + if ( mnAngle ) + ImplFlipBoundingBox( aPropOpt ); + aPropOpt.CreateShapeProperties( mXShape ); + aPropOpt.Commit( *mpStrm ); + if ( GetCurrentGroupLevel() > 0 ) + mpPptEscherEx->AddChildAnchor( maRect ); + else + mpPptEscherEx->AddClientAnchor( maRect ); + + if ( pClientData ) + { + mpStrm->WriteUInt32( ( ESCHER_ClientData << 16 ) | 0xf ) + .WriteUInt32( pClientData->Tell() ); + + mpStrm->WriteBytes(pClientData->GetData(), pClientData->Tell()); + pClientData.reset(); + } + if ( pClientTextBox ) + { + mpStrm->WriteUInt32( ( ESCHER_ClientTextbox << 16 ) | 0xf ) + .WriteUInt32( pClientTextBox->Tell() ); + + mpStrm->WriteBytes(pClientTextBox->GetData(), pClientTextBox->Tell()); + pClientTextBox.reset(); + } + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + nPrevTextStyle = mnTextStyle; + + if ( bAdditionalText ) + { + bAdditionalText = false; + + css::uno::Any aAny; + EscherPropertyContainer aPropOpt; + mnAngle = ( PropValue::GetPropertyValue( aAny, + mXPropSet, "RotateAngle", true ) ) + ? *o3tl::doAccess<sal_Int32>(aAny) + : 0; + + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 ); + if ( mType == "drawing.Line" ) + { + double fDist = hypot( maRect.GetWidth(), maRect.GetHeight() ); + maRect = ::tools::Rectangle( Point( aTextRefPoint.X, aTextRefPoint.Y ), + Point( static_cast<sal_Int32>( aTextRefPoint.X + fDist ), aTextRefPoint.Y - 1 ) ); + ImplCreateTextShape( aPropOpt, aSolverContainer, false ); + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x60006 ); // Size Shape To Fit Text + if ( mnAngle < 0 ) + mnAngle = ( 36000 + mnAngle ) % 36000; + if ( mnAngle ) + ImplFlipBoundingBox( aPropOpt ); + } + else + { + ImplCreateTextShape( aPropOpt, aSolverContainer, false ); + if ( mnAngle < 0 ) + mnAngle = ( 36000 + mnAngle ) % 36000; + else + mnAngle = ( 36000 - ( mnAngle % 36000 ) ); + + mnAngle *= 655; + mnAngle += 0x8000; + mnAngle &=~0xffff; // round nAngle to full grad + aPropOpt.AddOpt( ESCHER_Prop_Rotation, mnAngle ); + + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->SetGroupSnapRect( nGroupLevel, maRect ); + // mpPptEscherEx->SetGroupLogicRect( nGroupLevel, maRect ); + } + if ( !pClientTextBox ) + pClientTextBox.reset(new SvMemoryStream( 0x200, 0x200 )); + + SvMemoryStream aExtBu( 0x200, 0x200 ); + ImplWriteTextStyleAtom( *pClientTextBox, EPP_TEXTTYPE_Other, 0, nullptr, aExtBu, &aPropOpt ); + + aPropOpt.CreateShapeProperties( mXShape ); + aPropOpt.Commit( *mpStrm ); + if ( GetCurrentGroupLevel() > 0 ) + mpPptEscherEx->AddChildAnchor( maRect ); + else + mpPptEscherEx->AddClientAnchor( maRect ); + + mpStrm->WriteUInt32( ( ESCHER_ClientTextbox << 16 ) | 0xf ) + .WriteUInt32( pClientTextBox->Tell() ); + + mpStrm->WriteBytes(pClientTextBox->GetData(), pClientTextBox->Tell()); + pClientTextBox.reset(); + + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->LeaveGroup(); + } + } + ClearGroupTable(); // storing groups if any are still open, which should not be the case + nGroups = GetGroupsClosed(); + for ( sal_uInt32 i = 0; i < nGroups; i++, mpPptEscherEx->LeaveGroup() ) ; + mnPagesWritten++; +} + +struct CellBorder +{ + sal_Int32 mnPos; // specifies the distance to the top/left position of the table + table::BorderLine maCellBorder; + + CellBorder() : mnPos ( 0 ) {}; +}; + +bool PPTWriter::ImplCreateCellBorder( const CellBorder* pCellBorder, sal_Int32 nX1, sal_Int32 nY1, sal_Int32 nX2, sal_Int32 nY2) +{ + sal_Int32 nLineWidth = pCellBorder->maCellBorder.OuterLineWidth + pCellBorder->maCellBorder.InnerLineWidth; + if ( nLineWidth ) + { + nLineWidth *= 2; + mnAngle = 0; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + EscherPropertyContainer aPropOptSp; + + sal_uInt32 nId = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Line, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::Child, + nId ); + aPropOptSp.AddOpt( ESCHER_Prop_shapePath, ESCHER_ShapeComplex ); + aPropOptSp.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0xa0008 ); + aPropOptSp.AddOpt( ESCHER_Prop_fshadowObscured, 0x20000 ); + + sal_uInt32 nBorderColor = pCellBorder->maCellBorder.Color & 0xff00; // green + nBorderColor |= static_cast< sal_uInt8 >( pCellBorder->maCellBorder.Color ) << 16; // red + nBorderColor |= static_cast< sal_uInt8 >( pCellBorder->maCellBorder.Color >> 16 ); // blue + aPropOptSp.AddOpt( ESCHER_Prop_lineColor, nBorderColor ); + + aPropOptSp.AddOpt( ESCHER_Prop_lineWidth, nLineWidth * 360 ); + aPropOptSp.AddOpt( ESCHER_Prop_fc3DLightFace, 0x80000 ); + aPropOptSp.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 16, ESCHER_ChildAnchor ); + mpStrm ->WriteInt32( nX1 ) + .WriteInt32( nY1 ) + .WriteInt32( nX2 ) + .WriteInt32( nY2 ); + mpPptEscherEx->CloseContainer(); + return true; + } + return false; +} + +//get merged cell's width +static sal_Int32 GetCellRight( sal_Int32 nColumn, + ::tools::Rectangle const & rect, + std::vector< std::pair< sal_Int32, sal_Int32 > >& aColumns, + uno::Reference< table::XMergeableCell > const & xCell ) +{ + sal_Int32 nRight = aColumns[ nColumn ].first + aColumns[ nColumn ].second; + for ( sal_Int32 nColumnSpan = 1; nColumnSpan < xCell->getColumnSpan(); nColumnSpan++ ) + { + sal_uInt32 nC = nColumnSpan + nColumn; + if ( nC < aColumns.size() ) + nRight += aColumns[ nC ].second; + else + nRight = rect.Right(); + } + return nRight; +} +//get merged cell's height +static sal_Int32 GetCellBottom( sal_Int32 nRow, + ::tools::Rectangle const & rect, + std::vector< std::pair< sal_Int32, sal_Int32 > >& aRows, + uno::Reference< table::XMergeableCell > const & xCell ) +{ + sal_Int32 nBottom = aRows[nRow].first + aRows[nRow].second; + for ( sal_Int32 nRowSpan = 1; nRowSpan < xCell->getRowSpan(); nRowSpan++ ) + { + sal_uInt32 nR = nRowSpan + nRow; + if ( nR < aRows.size() ) + nBottom += aRows[ nR ].second; + else + nBottom = rect.Bottom(); + } + return nBottom; +} + +void PPTWriter::WriteCString( SvStream& rSt, const OUString& rString, sal_uInt32 nInstance ) +{ + sal_Int32 nLen = rString.getLength(); + if ( nLen ) + { + rSt.WriteUInt32( ( nInstance << 4 ) | ( EPP_CString << 16 ) ) + .WriteUInt32( nLen << 1 ); + for ( sal_Int32 i = 0; i < nLen; i++ ) + rSt.WriteUInt16( rString[i] ); + } +} + +namespace { + +class ContainerGuard +{ +private: + PptEscherEx* m_pPptEscherEx; +public: + ContainerGuard(PptEscherEx* pPptEscherEx, sal_uInt16 nRecord) + : m_pPptEscherEx(pPptEscherEx) + { + m_pPptEscherEx->OpenContainer(nRecord); + } + ~ContainerGuard() + { + m_pPptEscherEx->CloseContainer(); + } +}; + +} + +void PPTWriter::ImplCreateTable( uno::Reference< drawing::XShape > const & rXShape, EscherSolverContainer& aSolverContainer, + EscherPropertyContainer& aPropOpt ) +{ + try + { + uno::Reference< table::XTable > xTable; + if ( mXPropSet->getPropertyValue( "Model" ) >>= xTable ) + { + uno::Reference< table::XColumnRowRange > xColumnRowRange( xTable, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xColumns( xColumnRowRange->getColumns(), uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xRows( xColumnRowRange->getRows(), uno::UNO_QUERY_THROW ); + sal_uInt16 nRowCount = static_cast< sal_uInt16 >( xRows->getCount() ); + sal_uInt16 nColumnCount = static_cast< sal_uInt16 >( xColumns->getCount() ); + + std::vector< std::pair< sal_Int32, sal_Int32 > > aColumns; + std::vector< std::pair< sal_Int32, sal_Int32 > > aRows; + + awt::Point aPosition( MapPoint( rXShape->getPosition() ) ); + sal_Int32 nPosition = aPosition.X; + for ( sal_Int32 x = 0; x < nColumnCount; x++ ) + { + uno::Reference< beans::XPropertySet > xPropSet( xColumns->getByIndex( x ), uno::UNO_QUERY_THROW ); + awt::Size aS( 0, 0 ); + xPropSet->getPropertyValue( "Width" ) >>= aS.Width; + awt::Size aM( MapSize( aS ) ); + aColumns.emplace_back( nPosition, aM.Width ); + nPosition += aM.Width; + if ( x == nColumnCount - 1 && nPosition != maRect.Right() ) + maRect.SetRight( nPosition ); + } + + nPosition = aPosition.Y; + for ( sal_Int32 y = 0; y < nRowCount; y++ ) + { + uno::Reference< beans::XPropertySet > xPropSet( xRows->getByIndex( y ), uno::UNO_QUERY_THROW ); + awt::Size aS( 0, 0 ); + xPropSet->getPropertyValue( "Height" ) >>= aS.Height; + awt::Size aM( MapSize( aS ) ); + aRows.emplace_back( nPosition, aM.Height ); + nPosition += aM.Height; + if ( y == nRowCount - 1 && nPosition != maRect.Bottom()) + maRect.SetBottom( nPosition ); + } + std::optional<ContainerGuard> xSpgrContainer(std::in_place, mpPptEscherEx.get(), ESCHER_SpgrContainer); + std::optional<ContainerGuard> xSpContainer(std::in_place, mpPptEscherEx.get(), ESCHER_SpContainer); + mpPptEscherEx->AddAtom( 16, ESCHER_Spgr, 1 ); + mpStrm ->WriteInt32( maRect.Left() ) // Bounding box for the grouped shapes to which they are attached + .WriteInt32( maRect.Top() ) + .WriteInt32( maRect.Right() ) + .WriteInt32( maRect.Bottom() ); + + sal_uInt32 nShapeId = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Min, ShapeFlag::HaveAnchor | ShapeFlag::Group, nShapeId ); + // TODO: check flags, comment does not match code // Flags: Group | Patriarch + aSolverContainer.AddShape( rXShape, nShapeId ); + EscherPropertyContainer aPropOpt2; + + SvMemoryStream aMemStrm; + aMemStrm.WriteUInt16( nRowCount ) + .WriteUInt16( nRowCount ) + .WriteUInt16( 4 ); + + for( const auto& rRow : aRows ) + aMemStrm.WriteInt32( rRow.second ); + + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x1000100 ); + aPropOpt2.AddOpt( ESCHER_Prop_tableProperties, 1 ); + aPropOpt2.AddOpt(ESCHER_Prop_tableRowProperties, true, 0, aMemStrm); + aPropOpt.CreateShapeProperties( rXShape ); + aPropOpt.Commit( *mpStrm ); + aPropOpt2.Commit( *mpStrm, 3, ESCHER_UDefProp ); + if ( GetCurrentGroupLevel() > 0 ) + mpPptEscherEx->AddChildAnchor( maRect ); + else + mpPptEscherEx->AddClientAnchor( maRect ); + xSpContainer.reset(); //ESCHER_SpContainer + + uno::Reference< table::XCellRange > xCellRange( xTable, uno::UNO_QUERY_THROW ); + for( sal_Int32 nRow = 0; nRow < xRows->getCount(); nRow++ ) + { + for( sal_Int32 nColumn = 0; nColumn < xColumns->getCount(); nColumn++ ) + { + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nColumn, nRow ), uno::UNO_QUERY_THROW ); + if ( !xCell->isMerged() ) + { + sal_Int32 nLeft = aColumns[ nColumn ].first; + sal_Int32 nTop = aRows[ nRow ].first; + sal_Int32 nRight = GetCellRight( nColumn, maRect,aColumns,xCell ); + sal_Int32 nBottom = GetCellBottom( nRow, maRect,aRows,xCell ); + + mbFontIndependentLineSpacing = false; + mXPropSet.set( xCell, uno::UNO_QUERY_THROW ); + mXText.set( xCell, uno::UNO_QUERY_THROW ); + mnTextSize = mXText->getString().getLength(); + + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "FontIndependentLineSpacing", true ) ) + aAny >>= mbFontIndependentLineSpacing; + + EscherPropertyContainer aPropOptSp; + std::optional<ContainerGuard> xCellContainer(std::in_place, mpPptEscherEx.get(), ESCHER_SpContainer); + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::Child, + aSolverContainer ); + aPropOptSp.CreateFillProperties( mXPropSet, true ); + aPropOptSp.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); + mnTxId += 0x60; + aPropOptSp.CreateTextProperties( mXPropSet, mnTxId ); + aPropOptSp.AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapSquare ); + + SvMemoryStream aClientTextBox( 0x200, 0x200 ); + SvMemoryStream aExtBu( 0x200, 0x200 ); + + ImplWriteTextStyleAtom( aClientTextBox, EPP_TEXTTYPE_Other, 0, nullptr, aExtBu, &aPropOptSp ); + + // need write client data for extend bullet + if ( aExtBu.Tell() ) + { + SvMemoryStream aClientData( 0x200, 0x200 ); + ImplProgTagContainer( &aClientData, &aExtBu ); + mpStrm->WriteUInt32( ( ESCHER_ClientData << 16 ) | 0xf ) + .WriteUInt32( aClientData.Tell() ); + + mpStrm->WriteBytes(aClientData.GetData(), aClientData.Tell()); + } + + aPropOptSp.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 16, ESCHER_ChildAnchor ); + mpStrm ->WriteInt32( nLeft ) + .WriteInt32( nTop ) + .WriteInt32( nRight ) + .WriteInt32( nBottom ); + + mpStrm->WriteUInt32( ( ESCHER_ClientTextbox << 16 ) | 0xf ) + .WriteUInt32( aClientTextBox.Tell() ); + + mpStrm->WriteBytes(aClientTextBox.GetData(), aClientTextBox.Tell()); + xCellContainer.reset(); + } + } + } + + // creating horz lines + for( sal_Int32 nLine = 0; nLine < ( xRows->getCount() + 1 ); nLine++ ) + { + for( sal_Int32 nColumn = 0; nColumn < xColumns->getCount(); nColumn++ ) + { + CellBorder aCellBorder; + aCellBorder.mnPos = aColumns[ nColumn ].first; + bool bTop = false; + //write nLine*nColumn cell's top border + if ( nLine < xRows->getCount() ) + { // top border + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nColumn, nLine ), uno::UNO_QUERY_THROW ); + if ( !xCell->isMerged() ) + { + uno::Reference< beans::XPropertySet > xPropSet2( xCell, uno::UNO_QUERY_THROW ); + table::BorderLine aBorderLine; + if ( xPropSet2->getPropertyValue( "TopBorder" ) >>= aBorderLine ) + aCellBorder.maCellBorder = aBorderLine; + sal_Int32 nRight = GetCellRight( nColumn, maRect,aColumns,xCell ); + bTop = ImplCreateCellBorder( &aCellBorder, aCellBorder.mnPos, + aRows[ nLine ].first, nRight, aRows[ nLine ].first ); + } + } + + //if nLine*nColumn cell's top border is empty, check (nLine-1)*nColumn cell's bottom border + //and write the last row's bottom border + if (( nLine && !bTop ) || (nLine == xRows->getCount())) + { // bottom border + sal_Int32 nRow = nLine; + + while( nRow ) + { //find last no merged cell + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nColumn, nRow - 1 ), uno::UNO_QUERY_THROW ); + if ( !xCell->isMerged() ) + { + sal_Int32 nRight = GetCellRight( nColumn, maRect,aColumns,xCell ); + sal_Int32 nBottom = GetCellBottom( nRow - 1, maRect,aRows,xCell ); + if ( nBottom == ( aRows[ nLine-1 ].first + aRows[ nLine-1 ].second ) ) + { + uno::Reference< table::XMergeableCell > xCellOwn( xCellRange->getCellByPosition( nColumn, nRow - 1 ), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xPropSet2( xCellOwn, uno::UNO_QUERY_THROW ); + table::BorderLine aBorderLine; + if ( xPropSet2->getPropertyValue( "BottomBorder" ) >>= aBorderLine ) + aCellBorder.maCellBorder = aBorderLine; + ImplCreateCellBorder( &aCellBorder, aCellBorder.mnPos, + nBottom, nRight, nBottom); + } + nRow=0; + } + else + nRow--; + } + } + } + } + + // creating vertical lines + for( sal_Int32 nLine = 0; nLine < ( xColumns->getCount() + 1 ); nLine++ ) + { + for( sal_Int32 nRow = 0; nRow < xRows->getCount(); nRow++ ) + { + + CellBorder aCellBorder; + aCellBorder.mnPos = aRows[ nRow].first; + bool bLeft = false; + if ( nLine < xColumns->getCount() ) + { // left border + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nLine, nRow ), uno::UNO_QUERY_THROW ); + if (!xCell->isMerged() ) + { + uno::Reference< beans::XPropertySet > xCellSet( xCell, uno::UNO_QUERY_THROW ); + table::BorderLine aBorderLine; + if ( xCellSet->getPropertyValue( "LeftBorder" ) >>= aBorderLine ) + aCellBorder.maCellBorder = aBorderLine; + sal_Int32 nBottom = GetCellBottom( nRow, maRect, aRows,xCell ); + bLeft = ImplCreateCellBorder( &aCellBorder, aColumns[nLine].first, aCellBorder.mnPos, + aColumns[nLine].first, nBottom ); + } + } + if ( ( nLine && !bLeft )||(nLine == xColumns->getCount())) + { // right border + sal_Int32 nColumn = nLine; + while ( nColumn ) + { + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nColumn - 1, nRow ), uno::UNO_QUERY_THROW ); + if (!xCell->isMerged() ) + { + sal_Int32 nRight = GetCellRight( nColumn-1, maRect, aColumns,xCell ); + sal_Int32 nBottom = GetCellBottom( nRow, maRect, aRows, xCell ); + if ( nRight == (aColumns[nLine-1].first + aColumns[nLine-1].second) ) + { + uno::Reference< table::XMergeableCell > xCellOwn( xCellRange->getCellByPosition( nColumn - 1, nRow ), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xCellSet( xCellOwn, uno::UNO_QUERY_THROW ); + table::BorderLine aBorderLine; + if ( xCellSet->getPropertyValue( "RightBorder" ) >>= aBorderLine ) + aCellBorder.maCellBorder = aBorderLine; + ImplCreateCellBorder( &aCellBorder, nRight, aCellBorder.mnPos, + nRight, nBottom ); + } + nColumn = 0; + } + else + nColumn --; + } + } + } + } + + xSpgrContainer.reset(); //ESCHER_SpgrContainer + } + } + catch( uno::Exception& ) + { + } +} + +void TextObjBinary::Write( SvStream* pStrm ) +{ + sal_uInt32 nSize, nPos = pStrm->Tell(); + pStrm->WriteUInt32( EPP_TextCharsAtom << 16 ).WriteUInt32( 0 ); + for ( sal_uInt32 i = 0; i < ParagraphCount(); ++i ) + GetParagraph(i)->Write( pStrm ); + nSize = pStrm->Tell() - nPos; + pStrm->SeekRel( - ( static_cast<sal_Int32>(nSize) - 4 ) ); + pStrm->WriteUInt32( nSize - 8 ); + pStrm->SeekRel( nSize - 8 ); +} + +void TextObjBinary::WriteTextSpecInfo( SvStream* pStrm ) +{ + sal_uInt32 nCharactersLeft( Count() ); + if ( nCharactersLeft < 1 ) + return; + + EscherExAtom aAnimationInfoAtom( *pStrm, EPP_TextSpecInfoAtom, 0, 0 ); + for ( sal_uInt32 i = 0; nCharactersLeft && i < ParagraphCount(); ++i ) + { + ParagraphObj* pPtr = GetParagraph(i); + for ( std::vector<std::unique_ptr<PortionObj> >::const_iterator it = pPtr->begin(); nCharactersLeft && it != pPtr->end(); ++it ) + { + const PortionObj& rPortion = **it; + sal_Int32 nPortionSize = rPortion.mnTextSize >= nCharactersLeft ? nCharactersLeft : rPortion.mnTextSize; + sal_Int32 const nFlags = 7; + nCharactersLeft -= nPortionSize; + pStrm ->WriteUInt32( nPortionSize ) + .WriteInt32( nFlags ) + .WriteInt16( 1 ) // spellinfo -> needs rechecking + .WriteInt16( static_cast<sal_uInt16>(LanguageTag( rPortion.meCharLocale ).makeFallback().getLanguageType()) ) + .WriteInt16( 0 ); // alt language + } + } + if ( nCharactersLeft ) + pStrm->WriteUInt32( nCharactersLeft ).WriteInt32( 1 ).WriteInt16( 1 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/escherex.cxx b/sd/source/filter/eppt/escherex.cxx new file mode 100644 index 000000000..5032c2721 --- /dev/null +++ b/sd/source/filter/eppt/escherex.cxx @@ -0,0 +1,266 @@ +/* -*- 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 "escherex.hxx" + +PptEscherEx::PptEscherEx( SvStream& rOutStrm, const OUString& rBaseURI ) : + EscherEx( std::make_shared<EscherExGlobal>( ), &rOutStrm ) +{ + mxGlobal->SetBaseURI( rBaseURI ); + mnCurrentDg = 0; +} + +sal_uInt32 PptEscherEx::DrawingGroupContainerSize() +{ + return ImplDggContainerSize() + 8; +} + +void PptEscherEx::WriteDrawingGroupContainer( SvStream& rSt ) +{ + sal_uInt32 nSize = DrawingGroupContainerSize(); + rSt.WriteUInt32( 0xf | ( 1035 << 16 ) ) // EPP_PPDrawingGroup + .WriteUInt32( nSize - 8 ); + + ImplWriteDggContainer( rSt ); +} + +sal_uInt32 PptEscherEx::ImplDggContainerSize() +{ + sal_uInt32 nSize; + + nSize = mxGlobal->GetDggAtomSize(); + nSize += mxGlobal->GetBlibStoreContainerSize(); + nSize += ImplOptAtomSize(); + nSize += ImplSplitMenuColorsAtomSize(); + + return nSize + 8; +} + +void PptEscherEx::ImplWriteDggContainer( SvStream& rSt ) +{ + sal_uInt32 nSize = ImplDggContainerSize(); + if ( nSize ) + { + rSt.WriteUInt32( 0xf | ( ESCHER_DggContainer << 16 ) ) + .WriteUInt32( nSize - 8 ); + + mxGlobal->SetDggContainer(); + mxGlobal->WriteDggAtom( rSt ); + mxGlobal->WriteBlibStoreContainer( rSt ); + ImplWriteOptAtom( rSt ); + ImplWriteSplitMenuColorsAtom( rSt ); + } +} + +#define ESCHER_OPT_COUNT 6 + +sal_uInt32 PptEscherEx::ImplOptAtomSize() +{ + sal_uInt32 nSize = 0; + if ( ESCHER_OPT_COUNT != 0 ) + nSize = ( ESCHER_OPT_COUNT * 6 ) + 8; + return nSize; +} + +void PptEscherEx::ImplWriteOptAtom( SvStream& rSt ) +{ + sal_uInt32 nSize = ImplOptAtomSize(); + if ( nSize ) + { + rSt.WriteUInt32( ( ESCHER_OPT << 16 ) | ( ESCHER_OPT_COUNT << 4 ) | 0x3 ) + .WriteUInt32( nSize - 8 ) + .WriteUInt16( ESCHER_Prop_fillColor ) .WriteUInt32( 0xffb800 ) + .WriteUInt16( ESCHER_Prop_fillBackColor ) .WriteUInt32( 0 ) + .WriteUInt16( ESCHER_Prop_fNoFillHitTest ) .WriteUInt32( 0x00100010 ) + .WriteUInt16( ESCHER_Prop_lineColor ) .WriteUInt32( 0x8000001 ) + .WriteUInt16( ESCHER_Prop_fNoLineDrawDash ) .WriteUInt32( 0x00080008 ) + .WriteUInt16( ESCHER_Prop_shadowColor ) .WriteUInt32( 0x8000002 ); + } +} + +#define ESCHER_SPLIT_MENU_COLORS_COUNT 4 + +sal_uInt32 PptEscherEx::ImplSplitMenuColorsAtomSize() +{ + sal_uInt32 nSize = 0; + if ( ESCHER_SPLIT_MENU_COLORS_COUNT != 0 ) + nSize = ( ESCHER_SPLIT_MENU_COLORS_COUNT << 2 ) + 8; + return nSize; +} + +void PptEscherEx::ImplWriteSplitMenuColorsAtom( SvStream& rSt ) +{ + sal_uInt32 nSize = ImplSplitMenuColorsAtomSize(); + if ( nSize ) + { + rSt.WriteUInt32( ( ESCHER_SplitMenuColors << 16 ) | ( ESCHER_SPLIT_MENU_COLORS_COUNT << 4 ) ) + .WriteUInt32( nSize - 8 ) + .WriteUInt32( 0x08000004 ) + .WriteUInt32( 0x08000001 ) + .WriteUInt32( 0x08000002 ) + .WriteUInt32( 0x100000f7 ); + } + +} + +PptEscherEx::~PptEscherEx() +{ +} + +void PptEscherEx::OpenContainer( sal_uInt16 n_EscherContainer, int nRecInstance ) +{ + mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | 0xf ).WriteUInt16( n_EscherContainer ).WriteUInt32( 0 ); + mOffsets.push_back( mpOutStrm->Tell() - 4 ); + mRecTypes.push_back( n_EscherContainer ); + + switch( n_EscherContainer ) + { + case ESCHER_DgContainer : + { + if ( !mbEscherDg ) + { + mbEscherDg = true; + mnCurrentDg = mxGlobal->GenerateDrawingId(); + AddAtom( 8, ESCHER_Dg, 0, mnCurrentDg ); + PtReplaceOrInsert( ESCHER_Persist_Dg | mnCurrentDg, mpOutStrm->Tell() ); + mpOutStrm->WriteUInt32( 0 ) // The number of shapes in this drawing + .WriteUInt32( 0 ); // The last MSOSPID given to an SP in this DG + } + } + break; + + case ESCHER_SpgrContainer : + { + if ( mbEscherDg ) + { + mbEscherSpgr = true; + } + } + break; + + default: + break; + } +} + +void PptEscherEx::CloseContainer() +{ + /* SJ: #Issue 26747# + not creating group objects with a depth higher than 16, because then + PPT is having a big performance problem when starting a slide show + */ + if ( ( mRecTypes.back() == ESCHER_SpgrContainer ) && ( mnGroupLevel >= 12 ) ) + return; + + sal_uInt32 nSize, nPos = mpOutStrm->Tell(); + nSize = ( nPos - mOffsets.back() ) - 4; + mpOutStrm->Seek( mOffsets.back() ); + mpOutStrm->WriteUInt32( nSize ); + + switch( mRecTypes.back() ) + { + case ESCHER_DgContainer : + { + if ( mbEscherDg ) + { + mbEscherDg = false; + if ( DoSeek( ESCHER_Persist_Dg | mnCurrentDg ) ) + mpOutStrm->WriteUInt32( mxGlobal->GetDrawingShapeCount( mnCurrentDg ) ).WriteUInt32( mxGlobal->GetLastShapeId( mnCurrentDg ) ); + } + } + break; + + case ESCHER_SpgrContainer : + { + if ( mbEscherSpgr ) + { + mbEscherSpgr = false; + } + } + break; + + default: + break; + } + mOffsets.pop_back(); + mRecTypes.pop_back(); + mpOutStrm->Seek( nPos ); +} + +sal_uInt32 PptEscherEx::EnterGroup( ::tools::Rectangle const * pBoundRect, SvMemoryStream* pClientData ) +{ + sal_uInt32 nShapeId = 0; + /* SJ: #Issue 26747# + not creating group objects with a depth higher than 16, because then + PPT is having a big performance problem when starting a slide show + */ + if ( mnGroupLevel < 12 ) + { + ::tools::Rectangle aRect; + if ( pBoundRect ) + aRect = *pBoundRect; + + OpenContainer( ESCHER_SpgrContainer ); + OpenContainer( ESCHER_SpContainer ); + AddAtom( 16, ESCHER_Spgr, 1 ); + PtReplaceOrInsert( ESCHER_Persist_Grouping_Snap | mnGroupLevel, mpOutStrm->Tell() ); + mpOutStrm ->WriteInt32( aRect.Left() ) // bounding box for the grouped shapes to which they are attached + .WriteInt32( aRect.Top() ) + .WriteInt32( aRect.Right() ) + .WriteInt32( aRect.Bottom() ); + + nShapeId = GenerateShapeId(); + if ( !mnGroupLevel ) + AddShape( ESCHER_ShpInst_Min, ShapeFlag::Group | ShapeFlag::Patriarch, nShapeId ); + else + { + AddShape( ESCHER_ShpInst_Min, ShapeFlag::HaveAnchor | ShapeFlag::Group, nShapeId ); + if ( mnGroupLevel == 1 ) + { + AddAtom( 8, ESCHER_ClientAnchor ); + PtReplaceOrInsert( ESCHER_Persist_Grouping_Logic | mnGroupLevel, mpOutStrm->Tell() ); + mpOutStrm->WriteInt16( aRect.Top() ).WriteInt16( aRect.Left() ).WriteInt16( aRect.Right() ).WriteInt16( aRect.Bottom() ); + } + else + { + AddAtom( 16, ESCHER_ChildAnchor ); + PtReplaceOrInsert( ESCHER_Persist_Grouping_Snap | mnGroupLevel, mpOutStrm->Tell() ); + mpOutStrm ->WriteInt32( aRect.Left() ) + .WriteInt32( aRect.Top() ) + .WriteInt32( aRect.Right() ) + .WriteInt32( aRect.Bottom() ); + } + } + if ( pClientData ) + { + sal_uInt32 nSize = pClientData->TellEnd(); + if ( nSize ) + { + mpOutStrm->WriteUInt32( ( ESCHER_ClientData << 16 ) | 0xf ) + .WriteUInt32( nSize ); + mpOutStrm->WriteBytes(pClientData->GetData(), nSize); + } + } + CloseContainer(); // ESCHER_SpContainer + } + mnGroupLevel++; + return nShapeId; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/escherex.hxx b/sd/source/filter/eppt/escherex.hxx new file mode 100644 index 000000000..8f64419f4 --- /dev/null +++ b/sd/source/filter/eppt/escherex.hxx @@ -0,0 +1,64 @@ +/* -*- 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 <filter/msfilter/escherex.hxx> + +/// Values for the sal_uLong in PPT_PST_TextHeaderAtom. +enum PPT_TextHeader +{ + PPTTH_TITLE, + PPTTH_BODY, + PPTTH_NOTES, + PPTTH_NOTUSED, + PPTTH_OTHER, ///< Text in a Shape + PPTTH_CENTERBODY, ///< Subtitle in Title-Slide + PPTTH_CENTERTITLE, ///< Title in Title-Slide + PPTTH_HALFBODY, ///< Body in two-column slide + PPTTH_QUARTERBODY ///< Body in four-body slide +}; + +class PptEscherEx : public EscherEx +{ + sal_uInt32 ImplDggContainerSize(); + void ImplWriteDggContainer( SvStream& rSt ); + + static sal_uInt32 ImplOptAtomSize(); + static void ImplWriteOptAtom( SvStream& rSt ); + + static sal_uInt32 ImplSplitMenuColorsAtomSize(); + static void ImplWriteSplitMenuColorsAtom( SvStream& rSt ); + + public: + + PptEscherEx( SvStream& rOut, const OUString& ); + virtual ~PptEscherEx() override; + + void OpenContainer( sal_uInt16 n_EscherContainer, int nRecInstance = 0 ) override; + void CloseContainer() override; + + sal_uInt32 EnterGroup( ::tools::Rectangle const * pBoundRect, SvMemoryStream* pClientData ); + + sal_uInt32 DrawingGroupContainerSize(); + void WriteDrawingGroupContainer( SvStream& rSt ); + + using EscherEx::EnterGroup; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/grouptable.hxx b/sd/source/filter/eppt/grouptable.hxx new file mode 100644 index 000000000..885f95741 --- /dev/null +++ b/sd/source/filter/eppt/grouptable.hxx @@ -0,0 +1,69 @@ +/* -*- 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 <com/sun/star/container/XIndexAccess.hpp> +#include <memory> +#include <vector> + +struct GroupEntry +{ + sal_uInt32 mnCurrentPos; + sal_uInt32 mnCount; + css::uno::Reference< css::container::XIndexAccess > mXIndexAccess; + + explicit GroupEntry( css::uno::Reference< css::container::XIndexAccess > const & rIndex ) + : mnCurrentPos(0), + mnCount(rIndex->getCount()), + mXIndexAccess(rIndex) + { + }; + + explicit GroupEntry( sal_uInt32 nCount ) + : mnCurrentPos(0), + mnCount(nCount) + { + }; +}; + +class GroupTable +{ + protected: + + sal_uInt32 mnIndex; + sal_uInt32 mnGroupsClosed; + std::vector<GroupEntry> mvGroupEntry; + + public: + + sal_uInt32 GetCurrentGroupIndex() const { return mnIndex; }; + sal_Int32 GetCurrentGroupLevel() const { return mvGroupEntry.size() - 1; }; + const css::uno::Reference< css::container::XIndexAccess > & + GetCurrentGroupAccess() const { return mvGroupEntry.back().mXIndexAccess; }; + sal_uInt32 GetGroupsClosed(); + void ResetGroupTable( sal_uInt32 nCount ); + void ClearGroupTable(); + bool EnterGroup( css::uno::Reference< css::container::XIndexAccess > const & rIndex ); + bool GetNextGroupEntry(); + GroupTable(); + ~GroupTable(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptexanimations.cxx b/sd/source/filter/eppt/pptexanimations.cxx new file mode 100644 index 000000000..99fe0b443 --- /dev/null +++ b/sd/source/filter/eppt/pptexanimations.cxx @@ -0,0 +1,2150 @@ +/* -*- 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 <com/sun/star/animations/XAnimationNodeSupplier.hpp> +#include <com/sun/star/animations/AnimationFill.hpp> +#include <com/sun/star/animations/AnimationRestart.hpp> +#include <com/sun/star/animations/Timing.hpp> +#include <com/sun/star/animations/Event.hpp> +#include <com/sun/star/animations/AnimationEndSync.hpp> +#include <com/sun/star/animations/EventTrigger.hpp> +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/presentation/EffectPresetClass.hpp> +#include <com/sun/star/animations/AnimationNodeType.hpp> +#include <com/sun/star/animations/AnimationTransformType.hpp> +#include <com/sun/star/animations/AnimationCalcMode.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/animations/AnimationAdditiveMode.hpp> +#include <com/sun/star/animations/XAnimateSet.hpp> +#include <com/sun/star/animations/XAudio.hpp> +#include <com/sun/star/animations/XTransitionFilter.hpp> +#include <com/sun/star/animations/XAnimateColor.hpp> +#include <com/sun/star/animations/XAnimateMotion.hpp> +#include <com/sun/star/animations/XAnimateTransform.hpp> +#include <com/sun/star/animations/ValuePair.hpp> +#include <com/sun/star/animations/AnimationColorSpace.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <com/sun/star/text/XSimpleText.hpp> +#include <com/sun/star/animations/XIterateContainer.hpp> +#include <com/sun/star/presentation/TextAnimationType.hpp> + +#include <oox/ppt/pptfilterhelpers.hxx> +#include "pptexanimations.hxx" +#include "pptexsoundcollection.hxx" +#include "../ppt/pptanimations.hxx" +#include <filter/msfilter/escherex.hxx> +#include <osl/diagnose.h> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <o3tl/string_view.hxx> + +#include <algorithm> + +using ::com::sun::star::uno::Any; +using ::com::sun::star::util::XCloneable; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; + +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +namespace ppt +{ + +static void ImplTranslateAttribute( OUString& rString, const TranslateMode eTranslateMode ) +{ + if ( eTranslateMode == TRANSLATE_NONE ) + return; + + if ( ( eTranslateMode & TRANSLATE_VALUE ) || ( eTranslateMode & TRANSLATE_ATTRIBUTE ) ) + { + const oox::ppt::ImplAttributeNameConversion* p = oox::ppt::getAttributeConversionList(); + while( p->mpAPIName ) + { + if( rString.equalsAscii( p->mpAPIName ) ) + break; + p++; + } + if( p->mpMSName ) + { + if ( eTranslateMode & TRANSLATE_VALUE ) + { + rString = "#"; + rString += OUString::createFromAscii( p->mpMSName ); + } + else + rString = OUString::createFromAscii( p->mpMSName ); + } + } + else if ( eTranslateMode & TRANSLATE_MEASURE ) + { + const char* pDest[] = { "#ppt_x", "#ppt_y", "#ppt_w", "#ppt_h", nullptr }; + const char* pSource[] = { "x", "y", "width", "height", nullptr }; + sal_Int32 nIndex = 0; + + const char** ps = pSource; + const char** pd = pDest; + + while( *ps ) + { + const OUString aSearch( OUString::createFromAscii( *ps ) ); + while( (nIndex = rString.indexOf( aSearch, nIndex )) != -1 ) + { + sal_Int32 nLength = aSearch.getLength(); + if( nIndex && ( rString[nIndex-1] == '#' ) ) + { + nIndex--; + nLength++; + } + + const OUString aNew( OUString::createFromAscii( *pd ) ); + rString = rString.replaceAt( nIndex, nLength, aNew ); + nIndex += aNew.getLength(); + } + ps++; + pd++; + } + } +} + +sal_uInt32 AnimationExporter::TranslatePresetSubType( const sal_uInt32 nPresetClass, const sal_uInt32 nPresetId, std::u16string_view rPresetSubType ) +{ + sal_uInt32 nPresetSubType = 0; + bool bTranslated = false; + + if ( ( nPresetClass == sal_uInt32(EffectPresetClass::ENTRANCE) ) || ( nPresetClass == sal_uInt32(EffectPresetClass::EXIT) ) ) + { + if ( nPresetId != 21 ) + { + switch( nPresetId ) + { + case 5 : + { + if ( rPresetSubType == u"downward" ) + { + nPresetSubType = 5; + bTranslated = true; + } + else if ( rPresetSubType == u"across" ) + { + nPresetSubType = 10; + bTranslated = true; + } + } + break; + case 17 : + { + if ( rPresetSubType == u"across" ) + { + nPresetSubType = 10; + bTranslated = true; + } + } + break; + case 18 : + { + if ( rPresetSubType == u"right-to-top" ) + { + nPresetSubType = 3; + bTranslated = true; + } + else if ( rPresetSubType == u"right-to-bottom" ) + { + nPresetSubType = 6; + bTranslated = true; + } + else if ( rPresetSubType == u"left-to-top" ) + { + nPresetSubType = 9; + bTranslated = true; + } + else if ( rPresetSubType == u"left-to-bottom" ) + { + nPresetSubType = 12; + bTranslated = true; + } + } + break; + } + } + if ( !bTranslated ) + { + const oox::ppt::convert_subtype* p = oox::ppt::convert_subtype::getList(); + while( p->mpStrSubType ) + { + if ( o3tl::equalsAscii( rPresetSubType, p->mpStrSubType ) ) + { + nPresetSubType = p->mnID; + bTranslated = true; + break; + } + p++; + } + } + } + if ( !bTranslated ) + nPresetSubType = o3tl::toUInt32(rPresetSubType); + return nPresetSubType; +} + +const char* AnimationExporter::FindTransitionName( const sal_Int16 nType, const sal_Int16 nSubType, const bool bDirection ) +{ + const char* pRet = nullptr; + int nFit = 0; + + const oox::ppt::transition* p = oox::ppt::transition::getList(); + while( p->mpName ) + { + int nF = 0; + if ( nType == p->mnType ) + nF += 4; + if ( nSubType == p->mnSubType ) + nF += 2; + if ( bDirection == p->mbDirection ) + nF += 1; + if ( nF > nFit ) + { + pRet = p->mpName; + nFit = nF; + } + if ( nFit == 7 ) // maximum + break; + p++; + } + return pRet; +} + +SvStream& WriteAnimationNode(SvStream& rOut, AnimationNode const & rNode ) +{ + rOut.WriteInt32( rNode.mnU1 ); + rOut.WriteInt32( rNode.mnRestart ); + rOut.WriteInt32( rNode.mnGroupType ); + rOut.WriteInt32( rNode.mnFill ); + rOut.WriteInt32( rNode.mnU3 ); + rOut.WriteInt32( rNode.mnU4 ); + rOut.WriteInt32( rNode.mnDuration ); + rOut.WriteInt32( rNode.mnNodeType ); + + return rOut; +} + +AnimationExporter::AnimationExporter( const EscherSolverContainer& rSolverContainer, ppt::ExSoundCollection& rExSoundCollection ) : + mrSolverContainer ( rSolverContainer ), + mrExSoundCollection ( rExSoundCollection ), + mnCurrentGroup(0) +{ +} + +sal_Int16 AnimationExporter::GetFillMode( const Reference< XAnimationNode >& xNode, const sal_Int16 nFillDefault ) +{ + sal_Int16 nFill = xNode->getFill(); + //#i119699 <Animation> The animation effect "Emphasis->FlashBulb" play incorrectly in Aoo saves a .ppt to another .ppt and plays the saved one. + //#i119740 <Animation> The animation effect "Entrance->Flash Once" fails to play in Aoo while Aoo saves a .ppt to another .ppt and plays the saved one. + if ((xNode->getType() == AnimationNodeType::ANIMATE) + ||(xNode->getType() == AnimationNodeType::SET) + ||(xNode->getType() == AnimationNodeType::TRANSITIONFILTER)) + { + if ( nFill == AnimationFill::DEFAULT ) + return nFill; + } + + if ( nFill == AnimationFill::DEFAULT ) + { + nFill = nFillDefault; + } + if( nFill == AnimationFill::AUTO ) + { + nFill = AnimationFill::REMOVE; + bool bIsIndefiniteTiming = true; + Any aAny = xNode->getDuration(); + if( aAny.hasValue() ) + { + Timing eTiming; + if( aAny >>= eTiming ) + bIsIndefiniteTiming = eTiming == Timing_INDEFINITE; + } + if ( bIsIndefiniteTiming ) + { + aAny = xNode->getEnd(); + if( aAny.hasValue() ) + { + Timing eTiming; + if( aAny >>= eTiming ) + bIsIndefiniteTiming = eTiming == Timing_INDEFINITE; + } + if ( bIsIndefiniteTiming ) + { + if ( !xNode->getRepeatCount().hasValue() ) + { + aAny = xNode->getRepeatDuration(); + if( aAny.hasValue() ) + { + Timing eTiming; + if( aAny >>= eTiming ) + bIsIndefiniteTiming = eTiming == Timing_INDEFINITE; + } + if ( bIsIndefiniteTiming ) + nFill = AnimationFill::FREEZE; + } + } + } + } + return nFill; +} + +void AnimationExporter::doexport( const Reference< XDrawPage >& xPage, SvStream& rStrm ) +{ + Reference< XAnimationNodeSupplier > xNodeSupplier( xPage, UNO_QUERY ); + if( xNodeSupplier.is() ) + { + const Reference< XAnimationNode > xRootNode( xNodeSupplier->getAnimationNode() ); + if( xRootNode.is() ) + { + processAfterEffectNodes( xRootNode ); + exportNode( rStrm, xRootNode, DFF_msofbtAnimGroup, 1, 0, false, AnimationFill::AUTO ); + } + } +} + +void AnimationExporter::processAfterEffectNodes( const Reference< XAnimationNode >& xRootNode ) +{ + try + { + Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + Reference< XEnumerationAccess > xEnumerationAccess2( xNode, UNO_QUERY ); + if ( xEnumerationAccess2.is() ) + { + Reference< XEnumeration > xEnumeration2( xEnumerationAccess2->createEnumeration(), css::uno::UNO_SET_THROW ); + while( xEnumeration2->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration2->nextElement(), UNO_QUERY_THROW ); + + Reference< XEnumerationAccess > xEnumerationAccess3( xChildNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration3( xEnumerationAccess3->createEnumeration(), css::uno::UNO_SET_THROW ); + while( xEnumeration3->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode2( xEnumeration3->nextElement(), UNO_QUERY_THROW ); + + Reference< XEnumerationAccess > xEnumerationAccess4( xChildNode2, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration4( xEnumerationAccess4->createEnumeration(), css::uno::UNO_SET_THROW ); + while( xEnumeration4->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode3( xEnumeration4->nextElement(), UNO_QUERY_THROW ); + + switch( xChildNode3->getType() ) + { + // found an after effect + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATECOLOR: + { + Reference< XAnimationNode > xMaster; + + const Sequence< NamedValue > aUserData( xChildNode3->getUserData() ); + const NamedValue* p = std::find_if(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "master-element"; }); + + if (p != aUserData.end()) + p->Value >>= xMaster; + + AfterEffectNodePtr pAfterEffectNode = std::make_shared<AfterEffectNode>( xChildNode3, xMaster ); + maAfterEffectNodes.push_back( pAfterEffectNode ); + } + break; + } + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "(@CL)AnimationExporter::processAfterEffectNodes()" ); + } +} + +bool AnimationExporter::isAfterEffectNode( const Reference< XAnimationNode >& xNode ) const +{ + return std::any_of(maAfterEffectNodes.begin(), maAfterEffectNodes.end(), + [&xNode](const AfterEffectNodePtr& rxNode) { return rxNode->mxNode == xNode; }); +} + +bool AnimationExporter::hasAfterEffectNode( const Reference< XAnimationNode >& xNode, Reference< XAnimationNode >& xAfterEffectNode ) const +{ + auto aIter = std::find_if(maAfterEffectNodes.begin(), maAfterEffectNodes.end(), + [&xNode](const AfterEffectNodePtr& rxNode) { return rxNode->mxMaster == xNode; }); + if (aIter != maAfterEffectNodes.end()) + { + xAfterEffectNode = (*aIter)->mxNode; + return true; + } + + return false; +} + +// check if this group only contain empty groups. this may happen when +// after effect nodes are not exported at their original position +bool AnimationExporter::isEmptyNode( const Reference< XAnimationNode >& xNode ) const +{ + if( xNode.is() ) switch( xNode->getType() ) + { + case AnimationNodeType::PAR : + case AnimationNodeType::SEQ : + case AnimationNodeType::ITERATE : + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xChildNode.is() && !isEmptyNode( xChildNode ) ) + return false; + } + } + } + } + break; + + case AnimationNodeType::SET : + case AnimationNodeType::ANIMATECOLOR : + return isAfterEffectNode( xNode ); + default: + return false; + } + + return true; +} + +void AnimationExporter::exportNode( SvStream& rStrm, Reference< XAnimationNode > const & xNode_in, const sal_uInt16 nContainerRecType, + const sal_uInt16 nInstance, const sal_Int32 nGroupLevel, const bool bTakeBackInteractiveSequenceTiming, const sal_Int16 nFDef ) +{ + auto xNode = xNode_in; + + if( (nGroupLevel == 4) && isEmptyNode( xNode ) ) + return; + + if ( ( nContainerRecType == DFF_msofbtAnimGroup ) && ( nGroupLevel == 2 ) && isEmptyNode( xNode ) ) + return; + + if( nContainerRecType == DFF_msofbtAnimGroup ) + mnCurrentGroup++; + + bool bTakeBackInteractiveSequenceTimingForChild = false; + sal_Int16 nFillDefault = GetFillMode( xNode, nFDef ); + + Reference< XAnimationNode > xAudioNode; + static sal_uInt32 nAudioGroup; + + { + bool bSkipChildren = false; + EscherExContainer aContainer( rStrm, nContainerRecType, nInstance ); + switch( xNode->getType() ) + { + case AnimationNodeType::CUSTOM : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::PAR : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + sal_Int32 nFlags = nGroupLevel == 2 ? 0x10 : 0; + if ( bTakeBackInteractiveSequenceTiming ) + nFlags |= 0x40; + exportAnimEvent( rStrm, xNode, nFlags ); + exportAnimValue( rStrm, xNode, nGroupLevel == 4 ); + } + break; + + case AnimationNodeType::SEQ : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + sal_Int16 nNodeType = exportAnimPropertySet( rStrm, xNode ); + sal_Int32 nFlags = 12; + if ( ( nGroupLevel == 1 ) && ( nNodeType == css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE ) ) + { + nFlags |= 0x20; + bTakeBackInteractiveSequenceTimingForChild = true; + } + exportAnimAction( rStrm, xNode ); + exportAnimEvent( rStrm, xNode, nFlags ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::ITERATE : + { + { + EscherExAtom aAnimNodeExAtom( rStrm, DFF_msofbtAnimNode ); + AnimationNode aAnim; + aAnim.mnGroupType = mso_Anim_GroupType_PAR; + aAnim.mnNodeType = 1; + // attribute Restart + switch( xNode->getRestart() ) + { + default: + case AnimationRestart::DEFAULT : aAnim.mnRestart = 0; break; + case AnimationRestart::ALWAYS : aAnim.mnRestart = 1; break; + case AnimationRestart::WHEN_NOT_ACTIVE : aAnim.mnRestart = 2; break; + case AnimationRestart::NEVER : aAnim.mnRestart = 3; break; + } + // attribute Fill + switch( xNode->getFill() ) + { + default: + case AnimationFill::DEFAULT : aAnim.mnFill = 0; break; + case AnimationFill::REMOVE : aAnim.mnFill = 1; break; + case AnimationFill::FREEZE : aAnim.mnFill = 2; break; + case AnimationFill::HOLD : aAnim.mnFill = 3; break; + case AnimationFill::TRANSITION : aAnim.mnFill = 4; break; + } + WriteAnimationNode( rStrm, aAnim ); + } + exportIterate( rStrm, xNode ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::ANIMATE : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + exportAnimate( rStrm, xNode ); + } + break; + + case AnimationNodeType::SET : + { + bool bIsAfterEffectNode( isAfterEffectNode( xNode ) ); + if( (nGroupLevel != 4) || !bIsAfterEffectNode ) + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimateSet( rStrm, xNode, bIsAfterEffectNode ? AFTEREFFECT_SET : AFTEREFFECT_NONE ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + else + { + bSkipChildren = true; + } + } + break; + + case AnimationNodeType::ANIMATEMOTION : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimateMotion( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::ANIMATECOLOR : + { + bool bIsAfterEffectNode( isAfterEffectNode( xNode ) ); + if( (nGroupLevel != 4) || !bIsAfterEffectNode ) + { + if( bIsAfterEffectNode ) + xNode = createAfterEffectNodeClone( xNode ); + + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimateColor( rStrm, xNode, bIsAfterEffectNode ? AFTEREFFECT_COLOR : AFTEREFFECT_NONE ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + else + { + bSkipChildren = true; + } + } + break; + + case AnimationNodeType::ANIMATETRANSFORM : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimateTransform( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::TRANSITIONFILTER : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + exportTransitionFilter( rStrm, xNode ); + } + break; + + case AnimationNodeType::AUDIO : // #i58428# + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + + Reference< XAudio > xAudio( xNode, UNO_QUERY ); + if( xAudio.is() ) + { + Any aAny( xAudio->getSource() ); + OUString aURL; + + if ( ( aAny >>= aURL) && !aURL.isEmpty() ) + { + sal_Int32 nU1 = 2; + sal_Int32 nTrigger = 3; + sal_Int32 nU3 = nAudioGroup; + sal_Int32 nBegin = 0; + { + EscherExContainer aAnimEvent( rStrm, DFF_msofbtAnimEvent, 1 ); + { + EscherExAtom aAnimTrigger( rStrm, DFF_msofbtAnimTrigger ); + rStrm.WriteInt32( nU1 ).WriteInt32( nTrigger ).WriteInt32( nU3 ).WriteInt32( nBegin ); + } + } + nU1 = 1; + nTrigger = 0xb; + nU3 = 0; + { + EscherExContainer aAnimEvent( rStrm, DFF_msofbtAnimEvent, 2 ); + { + EscherExAtom aAnimTrigger( rStrm, DFF_msofbtAnimTrigger ); + rStrm.WriteInt32( nU1 ).WriteInt32( nTrigger ).WriteInt32( nU3 ).WriteInt32( nBegin ); + } + } + EscherExContainer aAnimateTargetElement( rStrm, DFF_msofbtAnimateTargetElement ); + { + sal_uInt32 const nRefMode = 3; + sal_uInt32 const nRefType = 2; + sal_uInt32 nRefId = mrExSoundCollection.GetId( aURL ); + sal_Int32 const begin = -1; + sal_Int32 const end = -1; + + EscherExAtom aAnimReference( rStrm, DFF_msofbtAnimReference ); + rStrm.WriteUInt32( nRefMode ).WriteUInt32( nRefType ).WriteUInt32( nRefId ).WriteInt32( begin ).WriteInt32( end ); + } + } + } + exportAnimValue( rStrm, xNode, false ); + } + break; + } + if( !bSkipChildren ) + { + // export after effect node if one exists for this node + Reference< XAnimationNode > xAfterEffectNode; + if( hasAfterEffectNode( xNode, xAfterEffectNode ) ) + { + exportNode( rStrm, xAfterEffectNode, DFF_msofbtAnimSubGoup, 1, nGroupLevel + 1, bTakeBackInteractiveSequenceTimingForChild, nFillDefault ); + } + + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xChildNode.is() ) + { + if ( xChildNode->getType() == AnimationNodeType::AUDIO ) + { + xAudioNode = xChildNode; + nAudioGroup = mnCurrentGroup; + } + else + exportNode( rStrm, xChildNode, DFF_msofbtAnimGroup, 1, nGroupLevel + 1, bTakeBackInteractiveSequenceTimingForChild, nFillDefault ); + } + } + } + } + } + } + if ( xAudioNode.is() ) + exportNode( rStrm, xAudioNode, DFF_msofbtAnimGroup, 1, nGroupLevel, bTakeBackInteractiveSequenceTimingForChild, nFillDefault ); + + if( xNode->getType() == AnimationNodeType::ITERATE ) + aTarget = Any(); +} + +Reference< XAnimationNode > AnimationExporter::createAfterEffectNodeClone( const Reference< XAnimationNode >& xNode ) +{ + try + { + Reference< css::util::XCloneable > xClonable( xNode, UNO_QUERY_THROW ); + Reference< XAnimationNode > xCloneNode( xClonable->createClone(), UNO_QUERY_THROW ); + + Any aEmpty; + xCloneNode->setBegin( aEmpty ); + + return xCloneNode; + } + catch( Exception& ) + { + OSL_FAIL("(@CL)sd::ppt::AnimationExporter::createAfterEffectNodeClone(), could not create clone!" ); + } + return xNode; +} + +bool AnimationExporter::GetNodeType( const Reference< XAnimationNode >& xNode, sal_Int16& nType ) +{ + // trying to get the nodetype + const Sequence< NamedValue > aUserData = xNode->getUserData(); + for( const NamedValue& rProp : aUserData ) + { + if ( rProp.Name == "node-type" ) + { + if ( rProp.Value >>= nType ) + return true; + } + } + + return false; +} + +void AnimationExporter::exportAnimNode( SvStream& rStrm, const Reference< XAnimationNode >& xNode, + const sal_Int16 nFillDefault ) +{ + EscherExAtom aAnimNodeExAtom( rStrm, DFF_msofbtAnimNode ); + AnimationNode aAnim; + + // attribute Restart + switch( xNode->getRestart() ) + { + default: + case AnimationRestart::DEFAULT : aAnim.mnRestart = 0; break; + case AnimationRestart::ALWAYS : aAnim.mnRestart = 1; break; + case AnimationRestart::WHEN_NOT_ACTIVE : aAnim.mnRestart = 2; break; + case AnimationRestart::NEVER : aAnim.mnRestart = 3; break; + } + + switch( nFillDefault ) + { + default: + case AnimationFill::DEFAULT : aAnim.mnFill = 0; break; + case AnimationFill::REMOVE : aAnim.mnFill = 1; break; + case AnimationFill::FREEZE : + case AnimationFill::HOLD : aAnim.mnFill = 3; break; + case AnimationFill::TRANSITION : aAnim.mnFill = 4; break; + } + // attribute Duration + double fDuration = 0.0; + css::animations::Timing eTiming; + if ( xNode->getDuration() >>= eTiming ) + { + if ( eTiming == Timing_INDEFINITE ) + aAnim.mnDuration = -1; + } + else if ( xNode->getDuration() >>= fDuration ) + { + aAnim.mnDuration = static_cast<sal_Int32>( fDuration * 1000.0 ); + } + else + aAnim.mnDuration = -1; + + // NodeType, NodeGroup + aAnim.mnNodeType = 1; + aAnim.mnGroupType = mso_Anim_GroupType_SEQ; + switch( xNode->getType() ) + { + case AnimationNodeType::PAR : + aAnim.mnGroupType = mso_Anim_GroupType_PAR; + [[fallthrough]]; + case AnimationNodeType::SEQ : + { + sal_Int16 nType = 0; + if( GetNodeType( xNode, nType ) ) + switch( nType ) + { + case css::presentation::EffectNodeType::TIMING_ROOT : aAnim.mnNodeType = 0x12; break; + case css::presentation::EffectNodeType::MAIN_SEQUENCE : aAnim.mnNodeType = 0x18; break; + } + } + break; + + case AnimationNodeType::ANIMATE : + case AnimationNodeType::SET : + + case AnimationNodeType::CUSTOM : + case AnimationNodeType::ITERATE : + case AnimationNodeType::ANIMATEMOTION : + case AnimationNodeType::ANIMATECOLOR : + case AnimationNodeType::ANIMATETRANSFORM : + { + aAnim.mnGroupType = mso_Anim_GroupType_NODE; + aAnim.mnNodeType = mso_Anim_Behaviour_ANIMATION; + } + break; + + case AnimationNodeType::AUDIO : + { + aAnim.mnGroupType = mso_Anim_GroupType_MEDIA; + aAnim.mnNodeType = mso_Anim_Behaviour_ANIMATION; + } + break; + + case AnimationNodeType::TRANSITIONFILTER : + { + aAnim.mnGroupType = mso_Anim_GroupType_NODE; + aAnim.mnNodeType = mso_Anim_Behaviour_FILTER; + } + break; + } + + WriteAnimationNode( rStrm, aAnim ); +} + +void AnimationExporter::GetUserData( const Sequence< NamedValue >& rUserData, const Any ** pAny, std::size_t nLen ) +{ + // storing user data into pAny, to allow direct access later + memset( pAny, 0, nLen ); + if ( !rUserData.hasElements() ) + return; + + for( const NamedValue& rProp : rUserData ) + { + if ( rProp.Name == "node-type" ) + { + pAny[ DFF_ANIM_NODE_TYPE ] = &(rProp.Value); + } + else if ( rProp.Name == "preset-class" ) + { + pAny[ DFF_ANIM_PRESET_CLASS ] = &(rProp.Value); + } + else if ( rProp.Name == "preset-id" ) + { + pAny[ DFF_ANIM_PRESET_ID ] = &(rProp.Value); + } + else if ( rProp.Name == "preset-sub-type" ) + { + pAny[ DFF_ANIM_PRESET_SUB_TYPE ] = &(rProp.Value); + } + else if ( rProp.Name == "master-element" ) + { + pAny[ DFF_ANIM_AFTEREFFECT ] = &(rProp.Value); + } + } +} + +sal_uInt32 AnimationExporter::GetPresetID( const OUString& rPreset, sal_uInt32 nAPIPresetClass, bool& bPresetId ) +{ + sal_uInt32 nPresetId = 0; + bPresetId = false; + + if ( rPreset.match("ppt_", 0) ) + { + sal_Int32 nLast = rPreset.lastIndexOf( '_' ); + if ( ( nLast != -1 ) && ( ( nLast + 1 ) < rPreset.getLength() ) ) + { + std::u16string_view aNumber( rPreset.subView( nLast + 1 ) ); + nPresetId = o3tl::toUInt32(aNumber); + bPresetId = true; + } + } + else + { + const oox::ppt::preset_mapping* p = oox::ppt::preset_mapping::getList(); + while( p->mpStrPresetId && ((p->mnPresetClass != static_cast<sal_Int32>(nAPIPresetClass)) || !rPreset.equalsAscii( p->mpStrPresetId )) ) + p++; + + if( p->mpStrPresetId ) + { + nPresetId = p->mnPresetId; + bPresetId = true; + } + } + + return nPresetId; +} + +sal_Int16 AnimationExporter::exportAnimPropertySet( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + sal_Int16 nNodeType = css::presentation::EffectNodeType::DEFAULT; + + EscherExContainer aAnimPropertySet( rStrm, DFF_msofbtAnimPropertySet ); + + Reference< XAnimationNode > xMaster; + + Any aMasterRel, aOverride, aRunTimeContext; + + // storing user data into pAny, to allow direct access later + const Sequence< NamedValue > aUserData = xNode->getUserData(); + const css::uno::Any* pAny[ DFF_ANIM_PROPERTY_ID_COUNT ]; + GetUserData( aUserData, pAny, sizeof( pAny ) ); + + if( pAny[ DFF_ANIM_AFTEREFFECT ] ) + ( *pAny[ DFF_ANIM_AFTEREFFECT ] ) >>= xMaster; + + // calculate master-rel + if( xMaster.is() ) + { + sal_Int32 nMasterRel = 2; + if( xNode.is() && xMaster.is() && (xNode->getParent() == xMaster->getParent() ) ) + nMasterRel = 0; + + aMasterRel <<= nMasterRel; + + pAny[ DFF_ANIM_MASTERREL ] = &aMasterRel; + + aOverride <<= sal_Int32(1); + pAny[ DFF_ANIM_OVERRIDE ] = &aOverride; + + aRunTimeContext <<= sal_Int32(1); + pAny[ DFF_ANIM_RUNTIMECONTEXT ] = &aRunTimeContext; + } + + // the order is important + if ( pAny[ DFF_ANIM_NODE_TYPE ] ) + { + if ( *pAny[ DFF_ANIM_NODE_TYPE ] >>= nNodeType ) + { + sal_uInt32 nPPTNodeType = DFF_ANIM_NODE_TYPE_ON_CLICK; + switch( nNodeType ) + { + case css::presentation::EffectNodeType::ON_CLICK : nPPTNodeType = DFF_ANIM_NODE_TYPE_ON_CLICK; break; + case css::presentation::EffectNodeType::WITH_PREVIOUS : nPPTNodeType = DFF_ANIM_NODE_TYPE_WITH_PREVIOUS; break; + case css::presentation::EffectNodeType::AFTER_PREVIOUS : nPPTNodeType = DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS; break; + case css::presentation::EffectNodeType::MAIN_SEQUENCE : nPPTNodeType = DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE; break; + case css::presentation::EffectNodeType::TIMING_ROOT : nPPTNodeType = DFF_ANIM_NODE_TYPE_TIMING_ROOT; break; + case css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE: nPPTNodeType = DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ; break; + } + exportAnimPropertyuInt32( rStrm, DFF_ANIM_NODE_TYPE, nPPTNodeType ); + } + } + sal_uInt32 nPresetId = 0; + sal_uInt32 nPresetSubType = 0; + sal_uInt32 nAPIPresetClass = EffectPresetClass::CUSTOM; + sal_uInt32 nPresetClass = DFF_ANIM_PRESS_CLASS_USER_DEFINED; + bool bPresetClass, bPresetId, bPresetSubType; + bPresetId = bPresetClass = bPresetSubType = false; + + if ( pAny[ DFF_ANIM_PRESET_CLASS ] ) + { + if ( *pAny[ DFF_ANIM_PRESET_CLASS ] >>= nAPIPresetClass ) + { + sal_uInt8 nPPTPresetClass; + switch( nAPIPresetClass ) + { + case EffectPresetClass::ENTRANCE : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_ENTRANCE; break; + case EffectPresetClass::EXIT : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_EXIT; break; + case EffectPresetClass::EMPHASIS : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_EMPHASIS; break; + case EffectPresetClass::MOTIONPATH : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_MOTIONPATH; break; + case EffectPresetClass::OLEACTION : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_OLE_ACTION; break; + case EffectPresetClass::MEDIACALL : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_MEDIACALL; break; + default : + nPPTPresetClass = DFF_ANIM_PRESS_CLASS_USER_DEFINED; + } + nPresetClass = nPPTPresetClass; + bPresetClass = true; + } + } + if ( pAny[ DFF_ANIM_PRESET_ID ] ) + { + OUString sPreset; + if ( *pAny[ DFF_ANIM_PRESET_ID ] >>= sPreset ) + nPresetId = GetPresetID( sPreset, nAPIPresetClass, bPresetId ); + } + + if ( pAny[ DFF_ANIM_PRESET_SUB_TYPE ] ) + { + OUString sPresetSubType; + if ( *pAny[ DFF_ANIM_PRESET_SUB_TYPE ] >>= sPresetSubType ) + { + nPresetSubType = TranslatePresetSubType( nPresetClass, nPresetId, sPresetSubType ); + bPresetSubType = true; + } + } + if ( bPresetId ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_PRESET_ID, nPresetId ); + if ( bPresetSubType ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_PRESET_SUB_TYPE, nPresetSubType ); + if ( bPresetClass ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_PRESET_CLASS, nPresetClass ); + + if ( pAny[ DFF_ANIM_ID ] ) + { + // TODO DFF_ANIM_ID + } + + if ( pAny[ DFF_ANIM_AFTEREFFECT ] ) + { + bool bAfterEffect = false; + if ( *pAny[ DFF_ANIM_AFTEREFFECT ] >>= bAfterEffect ) + exportAnimPropertyByte( rStrm, DFF_ANIM_AFTEREFFECT, int(bAfterEffect) ); + } + + if ( pAny[ DFF_ANIM_RUNTIMECONTEXT ] ) + { + sal_Int32 nRunTimeContext = 0; + if ( *pAny[ DFF_ANIM_RUNTIMECONTEXT ] >>= nRunTimeContext ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_RUNTIMECONTEXT, nRunTimeContext ); + } + if ( pAny[ DFF_ANIM_PATH_EDIT_MODE ] ) + { + // TODO DFF_ANIM_ID + } + + if( !xMaster.is() ) + { + Reference< XAnimateColor > xColor( xNode, UNO_QUERY ); + if( xColor.is() ) + { + + bool bDirection = !xColor->getDirection(); + exportAnimPropertyuInt32( rStrm, DFF_ANIM_DIRECTION, bDirection ? 1 : 0 ); + } + } + + if ( pAny[ DFF_ANIM_OVERRIDE ] ) + { + sal_Int32 nOverride = 0; + if ( *pAny[ DFF_ANIM_OVERRIDE ] >>= nOverride ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_OVERRIDE, nOverride ); + } + + if ( pAny[ DFF_ANIM_MASTERREL ] ) + { + sal_Int32 nMasterRel = 0; + if ( *pAny[ DFF_ANIM_MASTERREL ] >>= nMasterRel ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_MASTERREL, nMasterRel ); + } + +/* todo + Reference< XAudio > xAudio( xNode, UNO_QUERY ); + if( xAudio.is() ) + { + sal_Int16 nEndAfterSlide = 0; + nEndAfterSlide = xAudio->getEndAfterSlide(); + exportAnimPropertyuInt32( rStrm, DFF_ANIM_ENDAFTERSLIDE, nEndAfterSlide, TRANSLATE_NONE ); + } +*/ + Reference< XAnimate > xAnim( xNode, UNO_QUERY ); + if( xAnim.is() ) + { + // TODO: DFF_ANIM_TIMEFILTER + } + if ( pAny[ DFF_ANIM_EVENT_FILTER ] ) + { + // TODO DFF_ANIM_EVENT_FILTER + } + if ( pAny[ DFF_ANIM_VOLUME ] ) + { + // TODO DFF_ANIM_VOLUME + } + return nNodeType; +} + +bool AnimationExporter::exportAnimProperty( SvStream& rStrm, const sal_uInt16 nPropertyId, const css::uno::Any& rAny, const TranslateMode eTranslateMode ) +{ + bool bRet = false; + if ( rAny.hasValue() ) + { + switch( rAny.getValueType().getTypeClass() ) + { + case css::uno::TypeClass_UNSIGNED_SHORT : + case css::uno::TypeClass_SHORT : + case css::uno::TypeClass_UNSIGNED_LONG : + case css::uno::TypeClass_LONG : + { + sal_Int32 nVal = 0; + if ( rAny >>= nVal ) + { + exportAnimPropertyuInt32( rStrm, nPropertyId, nVal ); + bRet = true; + } + } + break; + + case css::uno::TypeClass_DOUBLE : + { + double fVal = 0.0; + if ( rAny >>= fVal ) + { + exportAnimPropertyFloat( rStrm, nPropertyId, fVal ); + bRet = true; + } + } + break; + case css::uno::TypeClass_FLOAT : + { + float fVal = 0.0; + if ( rAny >>= fVal ) + { + if ( eTranslateMode & TRANSLATE_NUMBER_TO_STRING ) + { + OUString aNumber( OUString::number( fVal ) ); + exportAnimPropertyString( rStrm, nPropertyId, aNumber, eTranslateMode ); + } + else + { + exportAnimPropertyFloat( rStrm, nPropertyId, fVal ); + bRet = true; + } + } + } + break; + case css::uno::TypeClass_STRING : + { + OUString aStr; + if ( rAny >>= aStr ) + { + exportAnimPropertyString( rStrm, nPropertyId, aStr, eTranslateMode ); + bRet = true; + } + } + break; + default: + break; + } + } + return bRet; +} +void AnimationExporter::exportAnimPropertyString( SvStream& rStrm, const sal_uInt16 nPropertyId, const OUString& rVal, const TranslateMode eTranslateMode ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAttributeValue, nPropertyId ); + rStrm.WriteUChar( DFF_ANIM_PROP_TYPE_UNISTRING ); + OUString aStr( rVal ); + if ( eTranslateMode != TRANSLATE_NONE ) + ImplTranslateAttribute( aStr, eTranslateMode ); + writeZString( rStrm, aStr ); +} + +void AnimationExporter::exportAnimPropertyFloat( SvStream& rStrm, const sal_uInt16 nPropertyId, const double& rVal ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAttributeValue, nPropertyId ); + float fFloat = static_cast<float>(rVal); + rStrm.WriteUChar( DFF_ANIM_PROP_TYPE_FLOAT ) + .WriteFloat( fFloat ); +} + +void AnimationExporter::exportAnimPropertyuInt32( SvStream& rStrm, const sal_uInt16 nPropertyId, const sal_uInt32 nVal ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAttributeValue, nPropertyId ); + rStrm.WriteUChar( DFF_ANIM_PROP_TYPE_INT32 ) + .WriteUInt32( nVal ); +} + +void AnimationExporter::exportAnimPropertyByte( SvStream& rStrm, const sal_uInt16 nPropertyId, const sal_uInt8 nVal ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAttributeValue, nPropertyId ); + rStrm.WriteUChar( DFF_ANIM_PROP_TYPE_BYTE ) + .WriteUChar( nVal ); +} + +void AnimationExporter::writeZString( SvStream& rStrm, const OUString& rVal ) +{ + sal_Int32 i; + for ( i = 0; i < rVal.getLength(); i++ ) + rStrm.WriteUInt16( rVal[ i ] ); + rStrm.WriteUInt16( 0 ); +} + +void AnimationExporter::exportAnimAction( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAction ); + + sal_Int32 const nConcurrent = 1; + sal_Int32 const nNextAction = 1; + sal_Int32 nEndSync = 0; + sal_Int32 const nU4 = 0; + sal_Int32 const nU5 = 3; + + sal_Int16 nAnimationEndSync = 0; + if ( xNode->getEndSync() >>= nAnimationEndSync ) + { + if ( nAnimationEndSync == AnimationEndSync::ALL ) + nEndSync = 1; + } + rStrm.WriteInt32( nConcurrent ) + .WriteInt32( nNextAction ) + .WriteInt32( nEndSync ) + .WriteInt32( nU4 ) + .WriteInt32( nU5 ); + +} + +// nFlags Bit 6 = fixInteractiveSequenceTiming (for child) +// nFlags Bit 5 = fixInteractiveSequenceTiming (for root) +// nFlags Bit 4 = first node of main sequence -> begin event next has to be replaced to indefinite +void AnimationExporter::exportAnimEvent( SvStream& rStrm, const Reference< XAnimationNode >& xNode, const sal_Int32 nFlags ) +{ + sal_uInt16 i; + for ( i = 0; i < 4; i++ ) + { + sal_Int32 nU1 = 0; + sal_Int32 nTrigger = 0; + sal_Int32 nU3 = 0; + sal_Int32 nBegin = 0; + + bool bCreateEvent = false; + Any aSource; + + switch( i ) + { + case 0 : + case 1 : + { + Any aAny; + Event aEvent; + css::animations::Timing eTiming; + if ( i == 0 ) + { + if ( nFlags & 0x20 ) + { + // taking the first child + Reference< XEnumerationAccess > xEA( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xE( xEA->createEnumeration(), css::uno::UNO_SET_THROW ); + if ( xE->hasMoreElements() ) + { + Reference< XAnimationNode > xClickNode( xE->nextElement(), UNO_QUERY ); + aAny = xClickNode->getBegin(); + } + } + else if ( nFlags & 0x40 ) + { + // begin has to be replaced with void, so don't do anything + } + else + { + aAny = xNode->getBegin(); + if ( nFlags & 0x10 ) // replace ON_NEXT with INDEFINITE + { + if ( ( aAny >>= aEvent ) && ( aEvent.Trigger == EventTrigger::ON_NEXT ) ) + { + eTiming = Timing_INDEFINITE; + aAny <<= eTiming; + } + } + } + } + else + aAny = xNode->getEnd(); + + double fTiming = 0.0; + if ( aAny >>= aEvent ) + { + bCreateEvent = true; + switch( aEvent.Trigger ) + { + case EventTrigger::NONE : nTrigger = 0; break; + case EventTrigger::ON_BEGIN : nTrigger = 1; break; + case EventTrigger::ON_END : nTrigger = 2; break; + case EventTrigger::BEGIN_EVENT : nTrigger = 3; break; + case EventTrigger::END_EVENT : nTrigger = 4; nU1 = 2; nU3 = mnCurrentGroup; break; + case EventTrigger::ON_CLICK : nTrigger = 5; break; + case EventTrigger::ON_DBL_CLICK : nTrigger = 6; break; + case EventTrigger::ON_MOUSE_ENTER : nTrigger = 7; break; + case EventTrigger::ON_MOUSE_LEAVE : nTrigger = 8; break; + case EventTrigger::ON_NEXT : nTrigger = 9; break; + case EventTrigger::ON_PREV : nTrigger = 10; break; + case EventTrigger::ON_STOP_AUDIO : nTrigger = 11; break; + } + if ( aEvent.Offset.hasValue() ) + { + if ( aEvent.Offset >>= eTiming ) + { + if ( eTiming == Timing_INDEFINITE ) + nBegin = -1; + } + else if ( aEvent.Offset >>= fTiming ) + nBegin = static_cast<sal_Int32>( fTiming * 1000.0 ); + } + aSource = aEvent.Source; + } + else if ( aAny >>= eTiming ) + { + bCreateEvent = true; + if ( eTiming == Timing_INDEFINITE ) + nBegin = -1; + } + else if ( aAny >>= fTiming ) + { + bCreateEvent = true; + nBegin = static_cast<sal_Int32>( fTiming * 1000.0 ); + } + } + break; + + case 2 : + { + if ( nFlags & ( 1 << i ) ) + { + bCreateEvent = true; + nU1 = 1; + nTrigger = 9; + } + } + break; + case 3 : + { + if ( nFlags & ( 1 << i ) ) + { + bCreateEvent = true; + nU1 = 1; + nTrigger = 10; + } + } + break; + } + if ( bCreateEvent ) + { + EscherExContainer aAnimEvent( rStrm, DFF_msofbtAnimEvent, i + 1 ); + { + EscherExAtom aAnimTrigger( rStrm, DFF_msofbtAnimTrigger ); + rStrm.WriteInt32( nU1 ) + .WriteInt32( nTrigger ) + .WriteInt32( nU3 ) + .WriteInt32( nBegin ); + } + exportAnimateTargetElement( rStrm, aSource, ( nFlags & ( 1 << i ) ) != 0 ); + } + } +} + +Any AnimationExporter::convertAnimateValue( const Any& rSourceValue, std::u16string_view rAttributeName ) +{ + OUString aDest; + if ( rAttributeName == u"X" + || rAttributeName == u"Y" + || rAttributeName == u"Width" + || rAttributeName == u"Height" + ) + { + OUString aStr; + if ( rSourceValue >>= aStr ) + { + ImplTranslateAttribute( aStr, TRANSLATE_MEASURE ); + aDest += aStr; + } + } + else if ( rAttributeName == u"Rotate" // "r" or "style.rotation" ? + || rAttributeName == u"Opacity" + || rAttributeName == u"CharHeight" + || rAttributeName == u"SkewX" + ) + { + double fNumber = 0.0; + if ( rSourceValue >>= fNumber ) + aDest += OUString::number( fNumber ); + } + else if ( rAttributeName == u"Color" + || rAttributeName == u"FillColor" // "Fillcolor" or "FillColor" ? + || rAttributeName == u"LineColor" + || rAttributeName == u"CharColor" + ) + { + sal_Int32 nColor = 0; + Sequence< double > aHSL( 3 ); + OUString aP( "," ); + if ( rSourceValue >>= aHSL ) + { + aDest += "hsl(" + + OUString::number( static_cast<sal_Int32>( aHSL[ 0 ] / ( 360.0 / 255 ) ) ) + + aP + + OUString::number( static_cast<sal_Int32>( aHSL[ 1 ] * 255.0 ) ) + + aP + + OUString::number( static_cast<sal_Int32>( aHSL[ 2 ] * 255.0 ) ) + + ")"; + } + else if ( rSourceValue >>= nColor ) + { + aDest += "rgb(" + + OUString::number( static_cast<sal_Int8>(nColor) ) + + aP + + OUString::number( static_cast<sal_Int8>( nColor >> 8 ) ) + + aP + + OUString::number( static_cast<sal_Int8>( nColor >> 16 ) ) + + ")"; + } + } + else if ( rAttributeName == u"FillStyle" ) + { + css::drawing::FillStyle eFillStyle; + if ( rSourceValue >>= eFillStyle ) + { + if ( eFillStyle == css::drawing::FillStyle_NONE ) + aDest += "none"; // ? + else + aDest += "solid"; + } + } + else if (rAttributeName == u"FillOn") + { + bool bFillOn; + if ( rSourceValue >>= bFillOn ) + { + if ( bFillOn ) + aDest += "true"; + else + aDest += "false"; + } + } + else if ( rAttributeName == u"LineStyle" ) + { + css::drawing::LineStyle eLineStyle; + if ( rSourceValue >>= eLineStyle ) + { + if ( eLineStyle == css::drawing::LineStyle_NONE ) + aDest += "false"; + else + aDest += "true"; + } + } + else if ( rAttributeName == u"CharWeight" ) + { + float fFontWeight = 0.0; + if ( rSourceValue >>= fFontWeight ) + { + if ( fFontWeight == css::awt::FontWeight::BOLD ) + aDest += "bold"; + else + aDest += "normal"; + } + } + else if ( rAttributeName == u"CharUnderline" ) + { + sal_Int16 nFontUnderline = 0; + if ( rSourceValue >>= nFontUnderline ) + { + if ( nFontUnderline == css::awt::FontUnderline::NONE ) + aDest += "false"; + else + aDest += "true"; + } + } + else if ( rAttributeName == u"CharPosture" ) + { + css::awt::FontSlant eFontSlant; + if ( rSourceValue >>= eFontSlant ) + { + if ( eFontSlant == css::awt::FontSlant_ITALIC ) + aDest += "italic"; + else + aDest += "normal"; // ? + } + } + else if ( rAttributeName == u"Visibility" ) + { + bool bVisible = true; + if ( rSourceValue >>= bVisible ) + { + if ( bVisible ) + aDest += "visible"; + else + aDest += "hidden"; + } + } + Any aRet; + if ( !aDest.isEmpty() ) + aRet <<= aDest; + else + aRet = rSourceValue; + return aRet; +} + +void AnimationExporter::exportAnimateSet( SvStream& rStrm, const Reference< XAnimationNode >& xNode, int nAfterEffectType ) +{ + Reference< XAnimateSet > xSet( xNode, UNO_QUERY ); + if( !xSet.is() ) + return; + + EscherExContainer aAnimateSet( rStrm, DFF_msofbtAnimateSet, 0 ); + { + EscherExAtom aAnimateSetData( rStrm, DFF_msofbtAnimateSetData ); + sal_uInt32 const nId1 = 1; // ?? + sal_uInt32 const nId2 = 1; // ?? + rStrm.WriteUInt32( nId1 ).WriteUInt32( nId2 ); + } + Any aConvertedValue( convertAnimateValue( xSet->getTo(), xSet->getAttributeName() ) ); + if ( aConvertedValue.hasValue() ) + exportAnimProperty( rStrm, 1, aConvertedValue, TRANSLATE_NONE ); + exportAnimateTarget( rStrm, xNode, 0, nAfterEffectType ); +} + +sal_uInt32 AnimationExporter::GetValueTypeForAttributeName( const OUString& rAttributeName ) +{ + sal_uInt32 nValueType = 0; + + struct Entry + { + const char* pName; + sal_uInt8 nType; + }; + static const Entry lcl_attributeMap[] = + { + { "charcolor", 2 }, + { "charfontname", 0 }, + { "charheight", 1 }, + { "charposture", 0 }, + // TODO(Q1): This should prolly be changed in PPT import + // { "charrotation", ATTRIBUTE_CHAR_ROTATION }, + { "charrotation", 1 }, + { "charunderline", 0 }, + { "charweight", 0 }, + { "color", 2 }, + { "dimcolor", 2 }, + { "fillcolor", 2 }, + { "fillstyle", 0 }, + { "height", 1 }, + { "linecolor", 2 }, + { "linestyle", 0 }, + { "opacity", 0 }, + { "rotate", 1 }, + { "skewx", 1 }, + { "skewy", 1 }, + { "visibility", 1 }, + { "width", 1 }, + { "x", 1 }, + { "y", 1 }, + { nullptr, 0 } + }; + const Entry* pPtr = &lcl_attributeMap[ 0 ]; + while( pPtr->pName ) + { + if ( rAttributeName.equalsIgnoreAsciiCaseAscii( pPtr->pName ) ) + { + nValueType = pPtr->nType; + break; + } + pPtr++; + } + DBG_ASSERT( pPtr->pName, "GetValueTypeForAttributeName, unknown property value!" ); + return nValueType; +} + +void AnimationExporter::exportAnimate( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimate > xAnimate( xNode, UNO_QUERY ); + if ( !xAnimate.is() ) + return; + + Any aBy ( xAnimate->getBy() ); + Any aFrom( xAnimate->getFrom() ); + Any aTo ( xAnimate->getTo() ); + + EscherExContainer aContainer( rStrm, DFF_msofbtAnimate, 0 ); + { + EscherExAtom aAnimateData( rStrm, DFF_msofbtAnimateData ); + sal_uInt32 nBits = 0x38; + sal_Int16 nTmp = xAnimate->getCalcMode(); + sal_uInt32 nCalcMode = /* (nTmp == AnimationCalcMode::FORMULA) ? 2 : */ (nTmp == AnimationCalcMode::LINEAR) ? 1 : 0; + sal_uInt32 nValueType = GetValueTypeForAttributeName( xAnimate->getAttributeName() ); + + if ( aBy.hasValue() ) + nBits |= 1; + if ( aFrom.hasValue() ) + nBits |= 2; + if ( aTo.hasValue() ) + nBits |= 4; + + rStrm.WriteUInt32( nCalcMode ) + .WriteUInt32( nBits ) + .WriteUInt32( nValueType ); + } + if ( aBy.hasValue() ) + exportAnimProperty( rStrm, 1, aBy, TRANSLATE_NUMBER_TO_STRING | TRANSLATE_MEASURE ); + if ( aFrom.hasValue() ) + exportAnimProperty( rStrm, 2, aFrom, TRANSLATE_NUMBER_TO_STRING | TRANSLATE_MEASURE ); + if ( aTo.hasValue() ) + exportAnimProperty( rStrm, 3, aTo, TRANSLATE_NUMBER_TO_STRING | TRANSLATE_MEASURE ); + + exportAnimateKeyPoints( rStrm, xAnimate ); + exportAnimateTarget( rStrm, xNode ); +} + +void AnimationExporter::exportAnimateTarget( SvStream& rStrm, const Reference< XAnimationNode >& xNode, const sal_uInt32 nForceAttributeNames, int nAfterEffectType ) +{ + EscherExContainer aAnimateTarget( rStrm, DFF_msofbtAnimateTarget, 0 ); + Reference< XAnimate > xAnimate( xNode, UNO_QUERY ); + if ( !xAnimate.is() ) + return; + + { + EscherExAtom aAnimateTargetSettings( rStrm, DFF_msofbtAnimateTargetSettings, 0 ); + // nBits %0001: additive, %0010: accumulate, %0100: attributeName, %1000: transformtype + // nAdditive 0 = base, 1 = sum, 2 = replace, 3 = multiply, 4 = none + // nAccumulate 0 = none, 1 = always + // nTransformType 0: "property" else "image" + sal_uInt32 nBits = 0; + sal_uInt32 nAdditive = 0; + sal_uInt32 nAccumulate = 0; + sal_uInt32 const nTransformType = 0; + if ( xAnimate.is() ) + { + if ( !xAnimate->getAttributeName().isEmpty() ) + nBits |= 4; // what is attributeName ?, maybe this is set if a DFF_msofbtAnimateAttributeNames is written + sal_Int16 nAdditiveMode = xAnimate->getAdditive(); + if ( nAdditiveMode != AnimationAdditiveMode::BASE ) + { + nBits |= 1; + switch( nAdditiveMode ) + { + case AnimationAdditiveMode::SUM : nAdditive = 1; break; + case AnimationAdditiveMode::REPLACE : nAdditive = 2; break; + case AnimationAdditiveMode::MULTIPLY : nAdditive = 3; break; + case AnimationAdditiveMode::NONE : nAdditive = 4; break; + } + } + if ( xAnimate->getAccumulate() ) + { + nBits |= 2; + nAccumulate = 1; + } + } + rStrm.WriteUInt32( nBits ) + .WriteUInt32( nAdditive ) + .WriteUInt32( nAccumulate ) + .WriteUInt32( nTransformType ); + } + if ( !xAnimate->getAttributeName().isEmpty() || nForceAttributeNames ) + { + EscherExContainer aAnimateAttributeNames( rStrm, DFF_msofbtAnimateAttributeNames, 1 ); + OUString aAttributeName( xAnimate->getAttributeName() ); + if ( nForceAttributeNames ) + { + if( nForceAttributeNames == 1 ) + { + aAttributeName = "r"; + } + } + sal_Int32 nIndex = 0; + do + { + OUString aToken( aAttributeName.getToken( 0, ';', nIndex ) ); + exportAnimPropertyString( rStrm, 0, aToken, TRANSLATE_ATTRIBUTE ); + } + while ( nIndex >= 0 ); + } + + if( nAfterEffectType != AFTEREFFECT_NONE ) + { + EscherExContainer aAnimPropertySet( rStrm, DFF_msofbtAnimPropertySet ); + exportAnimPropertyuInt32( rStrm, 6, 1 ); + if( nAfterEffectType == AFTEREFFECT_COLOR ) + { + exportAnimPropertyuInt32( rStrm, 4, 0 ); + exportAnimPropertyuInt32( rStrm, 5, 0 ); + } + } + exportAnimateTargetElement( rStrm, aTarget.hasValue() ? aTarget : xAnimate->getTarget(), false ); +} + +Reference< XShape > AnimationExporter::getTargetElementShape( const Any& rAny, sal_Int32& rBegin, sal_Int32& rEnd, bool& rParagraphTarget ) +{ + Reference< XShape > xShape; + rAny >>= xShape; + + rParagraphTarget = false; + + if( xShape.is() ) + return xShape; + + ParagraphTarget aParaTarget; + if( rAny >>= aParaTarget ) + xShape = aParaTarget.Shape; + if ( !xShape.is() ) + return xShape; + + // now calculating the character range for the paragraph + sal_Int16 nParagraph = aParaTarget.Paragraph; + Reference< XSimpleText > xText( xShape, UNO_QUERY ); + if ( !xText.is() ) + return xShape; + + rParagraphTarget = true; + Reference< XEnumerationAccess > xTextParagraphEnumerationAccess( xText, UNO_QUERY ); + if ( !xTextParagraphEnumerationAccess.is() ) + return xShape; + + Reference< XEnumeration > xTextParagraphEnumeration( xTextParagraphEnumerationAccess->createEnumeration() ); + if ( !xTextParagraphEnumeration.is() ) + return xShape; + + sal_Int16 nCurrentParagraph; + rBegin = rEnd = nCurrentParagraph = 0; + while ( xTextParagraphEnumeration->hasMoreElements() ) + { + Reference< XTextRange > xTextRange( xTextParagraphEnumeration->nextElement(), UNO_QUERY ); + if ( xTextRange.is() ) + { + OUString aParaText( xTextRange->getString() ); + sal_Int32 nLength = aParaText.getLength() + 1; + rEnd += nLength; + if ( nCurrentParagraph == nParagraph ) + break; + nCurrentParagraph++; + rBegin += nLength; + } + } + + return xShape; +} + +void AnimationExporter::exportAnimateTargetElement( SvStream& rStrm, const Any& rAny, const bool bCreate2b01Atom ) +{ + sal_uInt32 nRefMode = 0; // nRefMode == 2 -> Paragraph + sal_Int32 begin = -1; + sal_Int32 end = -1; + bool bParagraphTarget; + + Reference< XShape > xShape = getTargetElementShape(rAny, begin, end, bParagraphTarget); + + if( bParagraphTarget ) + nRefMode = 2; + + if ( !(xShape.is() || bCreate2b01Atom) ) + return; + + EscherExContainer aAnimateTargetElement( rStrm, DFF_msofbtAnimateTargetElement ); + if ( xShape.is() ) + { + EscherExAtom aAnimReference( rStrm, DFF_msofbtAnimReference ); + + sal_uInt32 const nRefType = 1; // TODO: nRefType == 2 -> Sound; + sal_uInt32 nRefId = mrSolverContainer.GetShapeId( xShape ); + + rStrm.WriteUInt32( nRefMode ) + .WriteUInt32( nRefType ) + .WriteUInt32( nRefId ) + .WriteInt32( begin ) + .WriteInt32( end ); + } + if ( bCreate2b01Atom ) + { + EscherExAtom a2b01Atom( rStrm, 0x2b01 ); + rStrm.WriteUInt32( 1 ); // ? + } +} + +void AnimationExporter::exportAnimateKeyPoints( SvStream& rStrm, const Reference< XAnimate >& xAnimate ) +{ + Sequence< double > aKeyTimes( xAnimate->getKeyTimes() ); + Sequence< Any > aValues( xAnimate->getValues() ); + OUString aFormula( xAnimate->getFormula() ); + if ( !aKeyTimes.hasElements() ) + return; + + EscherExContainer aAnimKeyPoints( rStrm, DFF_msofbtAnimKeyPoints ); + sal_Int32 i; + for ( i = 0; i < aKeyTimes.getLength(); i++ ) + { + { + EscherExAtom aAnimKeyTime( rStrm, DFF_msofbtAnimKeyTime ); + sal_Int32 nKeyTime = static_cast<sal_Int32>( aKeyTimes[ i ] * 1000.0 ); + rStrm.WriteInt32( nKeyTime ); + } + Any aAny[ 2 ]; + if ( aValues[ i ].hasValue() ) + { + ValuePair aPair; + if ( aValues[ i ] >>= aPair ) + { + aAny[ 0 ] = convertAnimateValue( aPair.First, xAnimate->getAttributeName() ); + aAny[ 1 ] = convertAnimateValue( aPair.Second, xAnimate->getAttributeName() ); + } + else + { + aAny[ 0 ] = convertAnimateValue( aValues[ i ], xAnimate->getAttributeName() ); + } + if ( !i && !aFormula.isEmpty() ) + { + ImplTranslateAttribute( aFormula, TRANSLATE_MEASURE ); + aAny[ 1 ] <<= aFormula; + } + exportAnimProperty( rStrm, 0, aAny[ 0 ], TRANSLATE_NONE ); + exportAnimProperty( rStrm, 1, aAny[ 1 ], TRANSLATE_NONE ); + } + } +} + +void AnimationExporter::exportAnimValue( SvStream& rStrm, const Reference< XAnimationNode >& xNode, const bool bExportAlways ) +{ + Any aAny; + // repeat count (0) + double fRepeat = 0.0; + float fRepeatCount = 0.0; + css::animations::Timing eTiming; + aAny = xNode->getRepeatCount(); + if ( aAny >>= eTiming ) + { + if ( eTiming == Timing_INDEFINITE ) + fRepeatCount = (float(3.40282346638528860e+38)); + } + else if ( aAny >>= fRepeat ) + fRepeatCount = static_cast<float>(fRepeat); + if ( fRepeatCount != 0.0 ) + { + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimValue ); + sal_uInt32 const nType = 0; + rStrm.WriteUInt32( nType ) + .WriteFloat( fRepeatCount ); + } + // accelerate (3) + float fAccelerate = static_cast<float>(xNode->getAcceleration()); + if ( bExportAlways || ( fAccelerate != 0.0 ) ) + { + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimValue ); + sal_uInt32 const nType = 3; + rStrm.WriteUInt32( nType ) + .WriteFloat( fAccelerate ); + } + + // decelerate (4) + float fDecelerate = static_cast<float>(xNode->getDecelerate()); + if ( bExportAlways || ( fDecelerate != 0.0 ) ) + { + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimValue ); + sal_uInt32 const nType = 4; + rStrm.WriteUInt32( nType ) + .WriteFloat( fDecelerate ); + } + + // autoreverse (5) + bool bAutoReverse = xNode->getAutoReverse(); + if ( bExportAlways || bAutoReverse ) + { + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimValue ); + sal_uInt32 const nType = 5; + sal_uInt32 nVal = bAutoReverse ? 1 : 0; + rStrm.WriteUInt32( nType ) + .WriteUInt32( nVal ); + } +} + +void AnimationExporter::exportTransitionFilter( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XTransitionFilter > xFilter( xNode, UNO_QUERY ); + if ( !xFilter.is() ) + return; + + EscherExContainer aAnimateFilter( rStrm, DFF_msofbtAnimateFilter ); + { + EscherExAtom aAnimateFilterData( rStrm, DFF_msofbtAnimateFilterData ); + sal_uInt32 const nBits = 3; // bit 0 -> use AnimAttributeValue + // bit 1 -> use nTransition + + sal_uInt32 nTransition = xFilter->getMode() ? 0 : 1; + rStrm.WriteUInt32( nBits ) + .WriteUInt32( nTransition ); + } + const char* pFilter = FindTransitionName( xFilter->getTransition(), xFilter->getSubtype(), xFilter->getDirection() ); + if ( pFilter ) + { + const OUString aStr( OUString::createFromAscii( pFilter ) ); + exportAnimPropertyString( rStrm, 1, aStr, TRANSLATE_NONE ); + } + exportAnimateTarget( rStrm, xNode ); +} + +void AnimationExporter::exportAnimateMotion( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateMotion > xMotion( xNode, UNO_QUERY ); + if ( !xMotion.is() ) + return; + + EscherExContainer aAnimateMotion( rStrm, DFF_msofbtAnimateMotion ); + { + { //SJ: Ignored from import filter + EscherExAtom aAnimateMotionData( rStrm, DFF_msofbtAnimateMotionData ); + sal_uInt32 const nBits = 0x98; + sal_uInt32 const nOrigin = 0x2; + float const fByX = 100.0; // nBits&1 + float const fByY = 100.0; // nBits&1 + float const fFromX = 0.0; // nBits&2 + float const fFromY = 0.0; // nBits&2 + float const fToX = 100.0; // nBits&4 + float const fToY = 100.0; // nBits&4 + rStrm.WriteUInt32( nBits ).WriteFloat( fByX ).WriteFloat( fByY ).WriteFloat( fFromX ).WriteFloat( fFromY ).WriteFloat( fToX ).WriteFloat( fToY ).WriteUInt32( nOrigin ); + } + + OUString aStr; + if ( xMotion->getPath() >>= aStr ) + { + if ( !aStr.isEmpty() ) + exportAnimPropertyString( rStrm, 1, aStr, TRANSLATE_NONE ); + } + exportAnimateTarget( rStrm, xNode ); + } +} + +void AnimationExporter::exportAnimateTransform( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateTransform > xTransform( xNode, UNO_QUERY ); + if ( !xTransform.is() ) + return; + + if ( xTransform->getTransformType() == AnimationTransformType::SCALE ) + { + EscherExContainer aAnimateScale( rStrm, DFF_msofbtAnimateScale ); + { + EscherExAtom aAnimateScaleData( rStrm, DFF_msofbtAnimateScaleData ); + sal_uInt32 nBits = 0; + sal_uInt32 const nZoomContents = 1; + float fByX = 100.0; + float fByY = 100.0; + float fFromX = 0.0; + float fFromY = 0.0; + float fToX = 100.0; + float fToY = 100.0; + + double fX = 0.0, fY = 0.0; + ValuePair aPair; + if ( xTransform->getBy() >>= aPair ) + { + if ( ( aPair.First >>= fX ) && ( aPair.Second >>= fY ) ) + { + nBits |= 1; + fByX = static_cast<float>( fX * 100 ); + fByY = static_cast<float>( fY * 100 ); + } + } + if ( xTransform->getFrom() >>= aPair ) + { + if ( ( aPair.First >>= fX ) && ( aPair.Second >>= fY ) ) + { + nBits |= 2; + fFromX = static_cast<float>( fX * 100 ); + fFromY = static_cast<float>( fY * 100 ); + } + } + if( xTransform->getTo() >>= aPair ) + { + if ( ( aPair.First >>= fX ) && ( aPair.Second >>= fY ) ) + { + nBits |= 4; + fToX = static_cast<float>( fX * 100 ); + fToY = static_cast<float>( fY * 100 ); + } + } + + // TODO: ZoomContents: + //if( nBits & 8 ) + //( fprintf( mpFile, " zoomContents=\"%s\"", nZoomContents ? "true" : "false" ); + + rStrm.WriteUInt32( nBits ).WriteFloat( fByX ).WriteFloat( fByY ).WriteFloat( fFromX ).WriteFloat( fFromY ).WriteFloat( fToX ).WriteFloat( fToY ).WriteUInt32( nZoomContents ); + } + exportAnimateTarget( rStrm, xNode ); + } + else if ( xTransform->getTransformType() == AnimationTransformType::ROTATE ) + { + EscherExContainer aAnimateRotation( rStrm, DFF_msofbtAnimateRotation ); + { + EscherExAtom aAnimateRotationData( rStrm, DFF_msofbtAnimateRotationData ); + sal_uInt32 nBits = 0; + sal_uInt32 const nU1 = 0; + float fBy = 360.0; + float fFrom = 0.0; + float fTo = 360.0; + + double fVal = 0.0; + if ( xTransform->getBy() >>= fVal ) + { + nBits |= 1; + fBy = static_cast<float>(fVal); + } + if ( xTransform->getFrom() >>= fVal ) + { + nBits |= 2; + fFrom = static_cast<float>(fVal); + } + if ( xTransform->getTo() >>= fVal ) + { + nBits |= 4; + fTo = static_cast<float>(fVal); + } + rStrm.WriteUInt32( nBits ).WriteFloat( fBy ).WriteFloat( fFrom ).WriteFloat( fTo ).WriteUInt32( nU1 ); + } + exportAnimateTarget( rStrm, xNode, 1 ); + } +} + +bool AnimationExporter::getColorAny( const Any& rAny, const sal_Int16 nColorSpace, sal_Int32& rMode, sal_Int32& rA, sal_Int32& rB, sal_Int32& rC ) +{ + bool bIsColor = true; + + rMode = 0; + if ( nColorSpace == AnimationColorSpace::HSL ) + rMode = 1; + + sal_Int32 nColor = 0; + Sequence< double > aHSL( 3 ); + if ( rAny >>= nColor ) // RGB color + { + rA = static_cast<sal_uInt8>( nColor >> 16 ); + rB = static_cast<sal_uInt8>( nColor >> 8 ); + rC = static_cast<sal_uInt8>(nColor); + } + else if ( rAny >>= aHSL ) // HSL + { + rA = static_cast<sal_Int32>( aHSL[ 0 ] * 255.0 / 360.0 ); + rB = static_cast<sal_Int32>( aHSL[ 1 ] * 255.0 ); + rC = static_cast<sal_Int32>( aHSL[ 2 ] * 255.0 ); + } + else + bIsColor = false; + return bIsColor; +} + +void AnimationExporter::exportAnimateColor( SvStream& rStrm, const Reference< XAnimationNode >& xNode, int nAfterEffectType ) +{ + Reference< XAnimateColor > xColor( xNode, UNO_QUERY ); + if ( !xColor.is() ) + return; + + EscherExContainer aAnimateColor( rStrm, DFF_msofbtAnimateColor ); + { + EscherExAtom aAnimateColorData( rStrm, DFF_msofbtAnimateColorData ); + sal_uInt32 nBits = 8; + + sal_Int32 nByMode, nByA, nByB, nByC; + nByMode = nByA = nByB = nByC = 0; + + sal_Int32 nFromMode, nFromA, nFromB, nFromC; + nFromMode = nFromA = nFromB = nFromC = 0; + + sal_Int32 nToMode, nToA, nToB, nToC; + nToMode = nToA = nToB = nToC = 0; + + sal_Int16 nColorSpace = xColor->getColorInterpolation(); + + Any aAny( xColor->getBy() ); + if ( aAny.hasValue() ) + { + if ( getColorAny( aAny, nColorSpace, nByMode, nByA, nByB, nByC ) ) + nBits |= 0x11; + } + aAny = xColor->getFrom(); + if ( aAny.hasValue() ) + { + if ( getColorAny( aAny, nColorSpace, nFromMode, nFromA, nFromB, nFromC ) ) + nBits |= 0x12; + } + aAny = xColor->getTo(); + if ( aAny.hasValue() ) + { + if ( getColorAny( aAny, nColorSpace, nToMode, nToA, nToB, nToC ) ) + nBits |= 0x14; + } + rStrm .WriteUInt32( nBits ) + .WriteInt32( nByMode ).WriteInt32( nByA ).WriteInt32( nByB ).WriteInt32( nByC ) + .WriteInt32( nFromMode ).WriteInt32( nFromA ).WriteInt32( nFromB ).WriteInt32( nFromC ) + .WriteInt32( nToMode ).WriteInt32( nToA ).WriteInt32( nToB ).WriteInt32( nToC ); + } + exportAnimateTarget( rStrm, xNode, 0, nAfterEffectType ); +} + +void AnimationExporter::exportIterate( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XIterateContainer > xIterate( xNode, UNO_QUERY ); + if ( !xIterate.is() ) + return; + + EscherExAtom aAnimIteration( rStrm, DFF_msofbtAnimIteration ); + + float fInterval = 10.0; + sal_Int32 nTextUnitEffect = 0; + sal_Int32 const nU1 = 1; + sal_Int32 const nU2 = 1; + sal_Int32 const nU3 = 0xe; + + sal_Int16 nIterateType = xIterate->getIterateType(); + switch( nIterateType ) + { + case TextAnimationType::BY_WORD : nTextUnitEffect = 1; break; + case TextAnimationType::BY_LETTER : nTextUnitEffect = 2; break; + } + + fInterval = static_cast<float>(xIterate->getIterateInterval()); + + // convert interval from absolute to percentage + double fDuration = 0.0; + + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xChildNode.is() ) + { + double fChildBegin = 0.0; + double fChildDuration = 0.0; + xChildNode->getBegin() >>= fChildBegin; + xChildNode->getDuration() >>= fChildDuration; + + fChildDuration += fChildBegin; + if( fChildDuration > fDuration ) + fDuration = fChildDuration; + } + } + } + } + + if( fDuration ) + fInterval = static_cast<float>(100.0 * fInterval / fDuration); + + rStrm.WriteFloat( fInterval ).WriteInt32( nTextUnitEffect ).WriteInt32( nU1 ).WriteInt32( nU2 ).WriteInt32( nU3 ); + aTarget = xIterate->getTarget(); +} + +} // namespace ppt; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptexanimations.hxx b/sd/source/filter/eppt/pptexanimations.hxx new file mode 100644 index 000000000..daa54d85b --- /dev/null +++ b/sd/source/filter/eppt/pptexanimations.hxx @@ -0,0 +1,134 @@ +/* -*- 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 + +#ifdef DBG_ANIM_LOG +#include <stdio.h> +#endif + +#include <rtl/ustring.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.h> + +#include <memory> +#include <vector> + +namespace com::sun::star::animations { class XAnimate; } +namespace com::sun::star::animations { class XAnimationNode; } +namespace com::sun::star::beans { struct NamedValue; } +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XShape; } +namespace ppt { class ExSoundCollection; } + +class SvStream; +class EscherSolverContainer; + +namespace ppt +{ + + struct AfterEffectNode + { + css::uno::Reference< css::animations::XAnimationNode > mxNode; + css::uno::Reference< css::animations::XAnimationNode > mxMaster; + + AfterEffectNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode, + const css::uno::Reference< css::animations::XAnimationNode >& xMaster ) + : mxNode( xNode ), mxMaster( xMaster ) {} + }; + + typedef std::shared_ptr< AfterEffectNode > AfterEffectNodePtr; + +typedef sal_uInt32 TranslateMode; +#define TRANSLATE_NONE 0 +#define TRANSLATE_VALUE 1 +#define TRANSLATE_ATTRIBUTE 2 +#define TRANSLATE_MEASURE 4 +#define TRANSLATE_NUMBER_TO_STRING 8 + +const int AFTEREFFECT_NONE = 0; +const int AFTEREFFECT_COLOR = 1; +const int AFTEREFFECT_SET = 2; + +class AnimationExporter +{ + css::uno::Any aTarget; + const EscherSolverContainer& mrSolverContainer; + ppt::ExSoundCollection& mrExSoundCollection; + std::vector< AfterEffectNodePtr > maAfterEffectNodes; + sal_Int32 mnCurrentGroup; + + static void writeZString( SvStream& rStrm, const OUString& rVal ); + static bool getColorAny( const css::uno::Any& rAny, const sal_Int16 nColorSpace, sal_Int32& rMode, sal_Int32& rA, sal_Int32& rB, sal_Int32& rC ); + static bool exportAnimProperty( SvStream& rStrm, const sal_uInt16 nPropertyId, const css::uno::Any& rAny, const TranslateMode eTranslateMode ); + static void exportAnimPropertyString( SvStream& rStrm, const sal_uInt16 nPropertyId, const OUString& rVal, const TranslateMode eTranslateMode ); + static void exportAnimPropertyFloat( SvStream& rStrm, const sal_uInt16 nPropertyId, const double& rVal ); + static void exportAnimPropertyuInt32( SvStream& rStrm, const sal_uInt16 nPropertyId, const sal_uInt32 nVal ); + static void exportAnimPropertyByte( SvStream& rStrm, const sal_uInt16 nPropertyId, const sal_uInt8 nVal ); + + /** if available exportAnimPropertySet + @return the css::presentation::EffectNodeType*/ + static sal_Int16 exportAnimPropertySet( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + static void exportAnimNode( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, + const sal_Int16 nFillDefault ); + void exportAnimate( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimateTarget( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, const sal_uInt32 nForceAttributeName = 0, int nAfterEffectType = AFTEREFFECT_NONE ); + void exportAnimateSet( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, int nAfterEffectType ); + static void exportAnimAction( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimEvent( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, const sal_Int32 nFlags = 0 ); + void exportNode( SvStream& rStrm, css::uno::Reference< css::animations::XAnimationNode > const & xNode, + const sal_uInt16 nContainerRecType, const sal_uInt16 nInstance, const sal_Int32 nGroupLevel, const bool bTakeBackInteractiveSequenceTiming, + const sal_Int16 nFillDefault ); + void exportAnimateTargetElement( SvStream& rStrm, const css::uno::Any& rAny, const bool bCreate2b01Atom ); + static void exportAnimateKeyPoints( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimate >& xAnimate ); + static void exportAnimValue( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, const bool bExportAlways ); + void exportTransitionFilter( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimateMotion( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimateTransform( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimateColor( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, int nAfterEffectType ); + void exportIterate( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + void processAfterEffectNodes( const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + bool isAfterEffectNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const; + bool hasAfterEffectNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode, css::uno::Reference< css::animations::XAnimationNode >& xAfterEffectNode ) const; + bool isEmptyNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const; + + static css::uno::Reference< css::animations::XAnimationNode > createAfterEffectNodeClone( const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + +public: + AnimationExporter( const EscherSolverContainer& rSolverContainer, ppt::ExSoundCollection& rExSoundCollection ); + + void doexport( const css::uno::Reference< css::drawing::XDrawPage >& xPage, SvStream& rStrm ); + + // helper methods also used in ooxml export + static css::uno::Any convertAnimateValue( const css::uno::Any& rSource, std::u16string_view rAttributeName ); + static bool GetNodeType( const css::uno::Reference< css::animations::XAnimationNode >& xNode, sal_Int16& nType ); + static sal_Int16 GetFillMode( const css::uno::Reference< css::animations::XAnimationNode >& xNode, const sal_Int16 nFillDefault ); + static void GetUserData( const css::uno::Sequence< css::beans::NamedValue >& rUserData, const css::uno::Any ** pAny, std::size_t nLen ); + static sal_uInt32 TranslatePresetSubType( const sal_uInt32 nPresetClass, const sal_uInt32 nPresetId, std::u16string_view rPresetSubType ); + static sal_uInt32 GetPresetID( const OUString& rPreset, sal_uInt32 nAPIPresetClass, bool& bPresetId ); + static sal_uInt32 GetValueTypeForAttributeName( const OUString& rAttributeName ); + + static const char* FindTransitionName( const sal_Int16 nType, const sal_Int16 nSubType, const bool bDirection ); + static css::uno::Reference< css::drawing::XShape > getTargetElementShape( const css::uno::Any& rAny, sal_Int32& rBegin, sal_Int32& rEnd, bool& rParagraphTarget ); +}; +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptexsoundcollection.cxx b/sd/source/filter/eppt/pptexsoundcollection.cxx new file mode 100644 index 000000000..c4770e644 --- /dev/null +++ b/sd/source/filter/eppt/pptexsoundcollection.cxx @@ -0,0 +1,213 @@ +/* -*- 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 <memory> +#include "pptexsoundcollection.hxx" +#include "epptdef.hxx" +#include <tools/stream.hxx> +#include <tools/urlobj.hxx> +#include <ucbhelper/content.hxx> +#include <comphelper/processfactory.hxx> +#include <unotools/ucbstreamhelper.hxx> + +namespace ppt +{ + +ExSoundEntry::ExSoundEntry(const OUString& rString) + : nFileSize(0) + , aSoundURL(rString) +{ + try + { + ::ucbhelper::Content aCnt( aSoundURL, + css::uno::Reference< css::ucb::XCommandEnvironment >(), + comphelper::getProcessComponentContext() ); + sal_Int64 nVal = 0; + aCnt.getPropertyValue("Size") >>= nVal; + nFileSize = static_cast<sal_uInt32>(nVal); + } + catch( css::uno::Exception& ) + { + + } +}; + +OUString ExSoundEntry::ImplGetName() const +{ + INetURLObject aTmp( aSoundURL ); + return aTmp.GetLastName(); +} + +OUString ExSoundEntry::ImplGetExtension() const +{ + INetURLObject aTmp( aSoundURL ); + OUString aExtension(aTmp.GetFileExtension()); + if ( !aExtension.isEmpty() ) + { + aExtension = "." + aExtension; + } + return aExtension; +} + +bool ExSoundEntry::IsSameURL(std::u16string_view rURL) const +{ + return ( rURL == aSoundURL ); +} + +sal_uInt32 ExSoundEntry::GetSize( sal_uInt32 nId ) const +{ + OUString aName( ImplGetName() ); + OUString aExtension( ImplGetExtension() ); + + sal_uInt32 nSize = 8; // SoundContainer Header + if ( !aName.isEmpty() ) // String Atom ( instance 0 - name of sound ) + nSize += aName.getLength() * 2 + 8; + if ( !aExtension.isEmpty() ) // String Atom ( instance 1 - extension of sound ) + nSize += aExtension.getLength() * 2 + 8; + + OUString aId( OUString::number(nId) ); // String Atom ( instance 2 - reference id ) + nSize += 2 * aId.getLength() + 8; + + nSize += nFileSize + 8; // SoundData Atom + + return nSize; +} + +void ExSoundEntry::Write( SvStream& rSt, sal_uInt32 nId ) const +{ + try + { + ::ucbhelper::Content aLoadContentIfExists( aSoundURL, + css::uno::Reference< css::ucb::XCommandEnvironment >(), + comphelper::getProcessComponentContext() ); + + // create SoundContainer + rSt.WriteUInt32( ( EPP_Sound << 16 ) | 0xf ).WriteUInt32( GetSize( nId ) - 8 ); + + OUString aSoundName( ImplGetName() ); + sal_Int32 i, nSoundNameLen = aSoundName.getLength(); + if ( nSoundNameLen ) + { + // name of sound ( instance 0 ) + rSt.WriteUInt32( EPP_CString << 16 ).WriteUInt32( nSoundNameLen * 2 ); + for ( i = 0; i < nSoundNameLen; ++i ) + rSt.WriteUInt16( aSoundName[i] ); + } + OUString aExtension( ImplGetExtension() ); + sal_Int32 nExtensionLen = aExtension.getLength(); + if ( nExtensionLen ) + { + // extension of sound ( instance 1 ) + rSt.WriteUInt32( ( EPP_CString << 16 ) | 16 ).WriteUInt32( nExtensionLen * 2 ); + for ( i = 0; i < nExtensionLen; ++i ) + rSt.WriteUInt16( aExtension[i] ); + } + // id of sound ( instance 2 ) + OUString aId( OUString::number(nId ) ); + sal_Int32 nIdLen = aId.getLength(); + rSt.WriteUInt32( ( EPP_CString << 16 ) | 32 ).WriteUInt32( nIdLen * 2 ); + for ( i = 0; i < nIdLen; ++i ) + rSt.WriteUInt16( aId[i] ); + + rSt.WriteUInt32( EPP_SoundData << 16 ).WriteUInt32( nFileSize ); + sal_uInt32 nBytesLeft = nFileSize; + std::unique_ptr<SvStream> pSourceFile = ::utl::UcbStreamHelper::CreateStream( aSoundURL, StreamMode::READ ); + if ( pSourceFile ) + { + std::unique_ptr<sal_uInt8[]> pBuf( new sal_uInt8[ 0x10000 ] ); // 64 kB Buffer + while ( nBytesLeft ) + { + sal_uInt32 nToDo = std::min<sal_uInt32>( nBytesLeft, 0x10000 ); + pSourceFile->ReadBytes(pBuf.get(), nToDo); + rSt.WriteBytes(pBuf.get(), nToDo); + nBytesLeft -= nToDo; + } + } + } + catch( css::uno::Exception& ) + { + + } +} + +sal_uInt32 ExSoundCollection::GetId(const OUString& rString) +{ + sal_uInt32 nSoundId = 0; + if (!rString.isEmpty()) + { + const sal_uInt32 nSoundCount = maEntries.size(); + + auto iter = std::find_if(maEntries.begin(), maEntries.end(), + [&rString](const ExSoundEntry& rEntry) { return rEntry.IsSameURL(rString); }); + nSoundId = static_cast<sal_uInt32>(std::distance(maEntries.begin(), iter)); + + if ( nSoundId++ == nSoundCount ) + { + ExSoundEntry aEntry( rString ); + if ( aEntry.GetFileSize() ) + maEntries.push_back(aEntry); + else + { + nSoundId = 0; // only insert sounds that are accessible + } + } + } + return nSoundId; +} + +sal_uInt32 ExSoundCollection::GetSize() const +{ + sal_uInt32 nSize = 0; + if (!maEntries.empty()) + { + nSize += 8 + 12; // size of SoundCollectionContainerHeader + SoundCollAtom + sal_uInt32 i = 1; + for ( const auto& rEntry : maEntries ) + { + nSize += rEntry.GetSize(i); + ++i; + } + } + return nSize; +} + +void ExSoundCollection::Write( SvStream& rSt ) const +{ + if (maEntries.empty()) + return; + + sal_uInt32 i = 1; + sal_uInt32 nSoundCount = maEntries.size(); + + // create SoundCollection Container + rSt.WriteUInt16( 0xf ).WriteUInt16( EPP_SoundCollection ).WriteUInt32( GetSize() - 8 ); + + // create SoundCollAtom ( reference to the next free SoundId ); + rSt.WriteUInt32( EPP_SoundCollAtom << 16 ).WriteUInt32( 4 ).WriteUInt32( nSoundCount ); + + for ( const auto& rEntry : maEntries ) + { + rEntry.Write(rSt,i); + ++i; + } +} + +} // namespace ppt; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptexsoundcollection.hxx b/sd/source/filter/eppt/pptexsoundcollection.hxx new file mode 100644 index 000000000..d81bb8118 --- /dev/null +++ b/sd/source/filter/eppt/pptexsoundcollection.hxx @@ -0,0 +1,71 @@ +/* -*- 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 <vector> + +#ifdef DBG_ANIM_LOG +#include <stdio.h> +#endif +#include <rtl/ustring.hxx> + +class SvStream; + +namespace ppt +{ + +class ExSoundEntry +{ + sal_uInt32 nFileSize; + OUString aSoundURL; + + OUString ImplGetName() const; + OUString ImplGetExtension() const; + + public: + + bool IsSameURL(std::u16string_view rURL) const; + sal_uInt32 GetFileSize( ) const { return nFileSize; }; + + ExSoundEntry(const OUString& rSoundURL); + + /// @return size of a complete SoundContainer. + sal_uInt32 GetSize( sal_uInt32 nId ) const; + void Write( SvStream& rSt, sal_uInt32 nId ) const; +}; + +class ExSoundCollection +{ + public: + + sal_uInt32 GetId(const OUString&); + + /// @return size of a complete SoundCollectionContainer. + sal_uInt32 GetSize() const; + void Write( SvStream& rSt ) const; + +private: + + std::vector<ExSoundEntry> maEntries; +}; + +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-animations.cxx b/sd/source/filter/eppt/pptx-animations.cxx new file mode 100644 index 000000000..1c901573c --- /dev/null +++ b/sd/source/filter/eppt/pptx-animations.cxx @@ -0,0 +1,1539 @@ +/* -*- 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 <o3tl/any.hxx> +#include <oox/token/tokens.hxx> +#include "epptooxml.hxx" +#include <sax/fshelper.hxx> +#include <sal/log.hxx> +#include <rtl/math.hxx> +#include <comphelper/sequenceashashmap.hxx> + +#include <com/sun/star/animations/AnimationAdditiveMode.hpp> +#include <com/sun/star/animations/AnimationCalcMode.hpp> +#include <com/sun/star/animations/AnimationFill.hpp> +#include <com/sun/star/animations/AnimationNodeType.hpp> +#include <com/sun/star/animations/AnimationRestart.hpp> +#include <com/sun/star/animations/AnimationTransformType.hpp> +#include <com/sun/star/animations/AnimationValueType.hpp> +#include <com/sun/star/animations/AnimationColorSpace.hpp> +#include <com/sun/star/animations/Event.hpp> +#include <com/sun/star/animations/EventTrigger.hpp> +#include <com/sun/star/animations/Timing.hpp> +#include <com/sun/star/animations/ValuePair.hpp> +#include <com/sun/star/animations/XAnimateMotion.hpp> +#include <com/sun/star/animations/XAnimateTransform.hpp> +#include <com/sun/star/animations/XAnimationNode.hpp> +#include <com/sun/star/animations/XAnimationNodeSupplier.hpp> +#include <com/sun/star/animations/XAnimateColor.hpp> +#include <com/sun/star/animations/XCommand.hpp> +#include <com/sun/star/animations/XAudio.hpp> +#include <com/sun/star/animations/XTransitionFilter.hpp> +#include <com/sun/star/animations/XIterateContainer.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/presentation/EffectCommands.hpp> +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/presentation/EffectPresetClass.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <com/sun/star/presentation/TextAnimationType.hpp> +#include <com/sun/star/text/XSimpleText.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <oox/export/utils.hxx> +#include <oox/ppt/pptfilterhelpers.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> + +#include "pptexanimations.hxx" +#include "pptx-animations.hxx" +#include "../ppt/pptanimations.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::uno; +using namespace ::ppt; +using namespace oox::drawingml; +using namespace oox::core; +using namespace oox; + +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::drawing::XDrawPage; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::text::XSimpleText; +using ::sax_fastparser::FSHelperPtr; + +namespace +{ +void WriteAnimationProperty(const FSHelperPtr& pFS, const Any& rAny, sal_Int32 nToken = 0) +{ + if (!rAny.hasValue()) + return; + + ValuePair aPair; + + if (rAny >>= aPair) + { + double x, y; + if ((aPair.First >>= x) && (aPair.Second >>= y)) + { + if (nToken == XML_by) + { + // MS needs ending values but we have offset values. + x += 1.0; + y += 1.0; + } + pFS->singleElementNS(XML_p, nToken, XML_x, OString::number(x * 100000), XML_y, + OString::number(y * 100000)); + } + return; + } + + sal_Int32 nRgb = {}; // spurious -Werror=maybe-uninitialized + double fDouble = {}; // spurious -Werror=maybe-uninitialized + + TypeClass aClass = rAny.getValueType().getTypeClass(); + bool bWriteToken + = nToken + && (aClass == TypeClass_LONG || aClass == TypeClass_DOUBLE || aClass == TypeClass_STRING); + + if (bWriteToken) + pFS->startElementNS(XML_p, nToken); + + switch (rAny.getValueType().getTypeClass()) + { + case TypeClass_LONG: + if (!(rAny >>= nRgb)) + { + assert(false); + } + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nRgb)); + break; + case TypeClass_DOUBLE: + if (!(rAny >>= fDouble)) + { + assert(false); + } + pFS->singleElementNS(XML_p, XML_fltVal, XML_val, OString::number(fDouble)); + break; + case TypeClass_STRING: + pFS->singleElementNS(XML_p, XML_strVal, XML_val, *o3tl::doAccess<OUString>(rAny)); + break; + default: + break; + } + + if (bWriteToken) + pFS->endElementNS(XML_p, nToken); +} + +void WriteAnimateColorColor(const FSHelperPtr& pFS, const Any& rAny, sal_Int32 nToken) +{ + if (!rAny.hasValue()) + return; + + sal_Int32 nColor = 0; + if (rAny >>= nColor) + { + pFS->startElementNS(XML_p, nToken); + + if (nToken == XML_by) + { + // CT_TLByRgbColorTransform + SAL_WARN("sd.eppt", "Export p:rgb in p:by of animClr isn't implemented yet."); + } + else + { + // CT_Color + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor)); + } + + pFS->endElementNS(XML_p, nToken); + } + + Sequence<double> aHSL(3); + if (!(rAny >>= aHSL)) + return; + + pFS->startElementNS(XML_p, nToken); + + if (nToken == XML_by) + { + // CT_TLByHslColorTransform + pFS->singleElementNS(XML_p, XML_hsl, XML_h, OString::number(aHSL[0] * 60000), // ST_Angel + XML_s, OString::number(aHSL[1] * 100000), XML_l, + OString::number(aHSL[2] * 100000)); + } + else + { + // CT_Color + SAL_WARN("sd.eppt", "Export p:hsl in p:from or p:to of animClr isn't implemented yet."); + } + + pFS->endElementNS(XML_p, nToken); +} + +void WriteAnimateTo(const FSHelperPtr& pFS, const Any& rValue, const OUString& rAttributeName) +{ + if (!rValue.hasValue()) + return; + + SAL_INFO("sd.eppt", "to attribute name: " << rAttributeName.toUtf8()); + + WriteAnimationProperty(pFS, AnimationExporter::convertAnimateValue(rValue, rAttributeName), + XML_to); +} + +void WriteAnimateValues(const FSHelperPtr& pFS, const Reference<XAnimate>& rXAnimate) +{ + const Sequence<double> aKeyTimes = rXAnimate->getKeyTimes(); + if (!aKeyTimes.hasElements()) + return; + const Sequence<Any> aValues = rXAnimate->getValues(); + const OUString& sFormula = rXAnimate->getFormula(); + const OUString& rAttributeName = rXAnimate->getAttributeName(); + + SAL_INFO("sd.eppt", "animate values, formula: " << sFormula.toUtf8()); + + assert(aValues.getLength() == aKeyTimes.getLength()); + + pFS->startElementNS(XML_p, XML_tavLst); + + for (int i = 0; i < aKeyTimes.getLength(); i++) + { + SAL_INFO("sd.eppt", "animate value " << i << ": " << aKeyTimes[i]); + if (aValues[i].hasValue()) + { + pFS->startElementNS(XML_p, XML_tav, XML_fmla, + sax_fastparser::UseIf(sFormula, !sFormula.isEmpty()), XML_tm, + OString::number(static_cast<sal_Int32>(aKeyTimes[i] * 100000.0))); + pFS->startElementNS(XML_p, XML_val); + ValuePair aPair; + if (aValues[i] >>= aPair) + { + WriteAnimationProperty( + pFS, AnimationExporter::convertAnimateValue(aPair.First, rAttributeName)); + WriteAnimationProperty( + pFS, AnimationExporter::convertAnimateValue(aPair.Second, rAttributeName)); + } + else + WriteAnimationProperty( + pFS, AnimationExporter::convertAnimateValue(aValues[i], rAttributeName)); + + pFS->endElementNS(XML_p, XML_val); + pFS->endElementNS(XML_p, XML_tav); + } + } + + pFS->endElementNS(XML_p, XML_tavLst); +} + +// Write condition list ( either prevCondlst or nextCondlst ) of Seq. +void WriteAnimationCondListForSeq(const FSHelperPtr& pFS, sal_Int32 nToken) +{ + const char* pEvent = (nToken == XML_prevCondLst) ? "onPrev" : "onNext"; + + pFS->startElementNS(XML_p, nToken); + pFS->startElementNS(XML_p, XML_cond, XML_evt, pEvent); + pFS->startElementNS(XML_p, XML_tgtEl); + pFS->singleElementNS(XML_p, XML_sldTgt); + pFS->endElementNS(XML_p, XML_tgtEl); + pFS->endElementNS(XML_p, XML_cond); + pFS->endElementNS(XML_p, nToken); +} + +const char* convertEventTrigger(sal_Int16 nTrigger) +{ + const char* pEvent = nullptr; + switch (nTrigger) + { + case EventTrigger::ON_NEXT: + pEvent = "onNext"; + break; + case EventTrigger::ON_PREV: + pEvent = "onPrev"; + break; + case EventTrigger::BEGIN_EVENT: + pEvent = "begin"; + break; + case EventTrigger::END_EVENT: + pEvent = "end"; + break; + case EventTrigger::ON_BEGIN: + pEvent = "onBegin"; + break; + case EventTrigger::ON_END: + pEvent = "onEnd"; + break; + case EventTrigger::ON_CLICK: + pEvent = "onClick"; + break; + case EventTrigger::ON_DBL_CLICK: + pEvent = "onDblClick"; + break; + case EventTrigger::ON_STOP_AUDIO: + pEvent = "onStopAudio"; + break; + case EventTrigger::ON_MOUSE_ENTER: + pEvent = "onMouseOver"; // not exact? + break; + case EventTrigger::ON_MOUSE_LEAVE: + pEvent = "onMouseOut"; + break; + } + return pEvent; +} + +void WriteAnimationAttributeName(const FSHelperPtr& pFS, const OUString& rAttributeName) +{ + if (rAttributeName.isEmpty()) + return; + + pFS->startElementNS(XML_p, XML_attrNameLst); + + SAL_INFO("sd.eppt", "write attribute name: " << rAttributeName.toUtf8()); + + if (rAttributeName == "X;Y") + { + pFS->startElementNS(XML_p, XML_attrName); + pFS->writeEscaped("ppt_x"); + pFS->endElementNS(XML_p, XML_attrName); + + pFS->startElementNS(XML_p, XML_attrName); + pFS->writeEscaped("ppt_y"); + pFS->endElementNS(XML_p, XML_attrName); + } + else + { + const oox::ppt::ImplAttributeNameConversion* attrConv + = oox::ppt::getAttributeConversionList(); + const char* pAttribute = nullptr; + + while (attrConv->mpAPIName != nullptr) + { + if (rAttributeName.equalsAscii(attrConv->mpAPIName)) + { + pAttribute = attrConv->mpMSName; + break; + } + attrConv++; + } + + if (pAttribute) + { + pFS->startElementNS(XML_p, XML_attrName); + pFS->writeEscaped(pAttribute); + pFS->endElementNS(XML_p, XML_attrName); + } + else + { + SAL_WARN("sd.eppt", "unhandled animation attribute name: " << rAttributeName); + } + } + + pFS->endElementNS(XML_p, XML_attrNameLst); +} + +bool isValidTarget(const Any& rTarget) +{ + Reference<XShape> xShape; + + if ((rTarget >>= xShape) && xShape.is()) + return true; + + ParagraphTarget aParagraphTarget; + + return (rTarget >>= aParagraphTarget) && aParagraphTarget.Shape.is(); +} + +/// extract ooxml node type from a XAnimationNode. +sal_Int32 extractNodeType(const Reference<XAnimationNode>& rXNode) +{ + sal_Int16 nType = rXNode->getType(); + sal_Int32 xmlNodeType = -1; + switch (nType) + { + case AnimationNodeType::ITERATE: + case AnimationNodeType::PAR: + xmlNodeType = XML_par; + break; + case AnimationNodeType::SEQ: + xmlNodeType = XML_seq; + break; + case AnimationNodeType::ANIMATE: + xmlNodeType = XML_anim; + break; + case AnimationNodeType::ANIMATEMOTION: + xmlNodeType = XML_animMotion; + break; + case AnimationNodeType::ANIMATETRANSFORM: + { + Reference<XAnimateTransform> xTransform(rXNode, UNO_QUERY); + if (xTransform.is()) + { + if (xTransform->getTransformType() == AnimationTransformType::SCALE) + xmlNodeType = XML_animScale; + else if (xTransform->getTransformType() == AnimationTransformType::ROTATE) + xmlNodeType = XML_animRot; + } + break; + } + case AnimationNodeType::ANIMATECOLOR: + xmlNodeType = XML_animClr; + break; + case AnimationNodeType::SET: + xmlNodeType = XML_set; + break; + case AnimationNodeType::TRANSITIONFILTER: + xmlNodeType = XML_animEffect; + break; + case AnimationNodeType::COMMAND: + xmlNodeType = XML_cmd; + break; + case AnimationNodeType::AUDIO: + xmlNodeType = XML_audio; + break; + default: + SAL_WARN("sd.eppt", "unhandled animation node: " << nType); + break; + } + return xmlNodeType; +} + +/// Convert AnimationRestart to ST_TLTimeNodeRestartType value. +const char* convertAnimationRestart(sal_Int16 nRestart) +{ + const char* pRestart = nullptr; + switch (nRestart) + { + case AnimationRestart::ALWAYS: + pRestart = "always"; + break; + case AnimationRestart::WHEN_NOT_ACTIVE: + pRestart = "whenNotActive"; + break; + case AnimationRestart::NEVER: + pRestart = "never"; + break; + } + return pRestart; +} + +/// Convert EffectNodeType to ST_TLTimeNodeType +const char* convertEffectNodeType(sal_Int16 nType) +{ + const char* pNodeType = nullptr; + switch (nType) + { + case EffectNodeType::TIMING_ROOT: + pNodeType = "tmRoot"; + break; + case EffectNodeType::MAIN_SEQUENCE: + pNodeType = "mainSeq"; + break; + case EffectNodeType::ON_CLICK: + pNodeType = "clickEffect"; + break; + case EffectNodeType::AFTER_PREVIOUS: + pNodeType = "afterEffect"; + break; + case EffectNodeType::WITH_PREVIOUS: + pNodeType = "withEffect"; + break; + case EffectNodeType::INTERACTIVE_SEQUENCE: + pNodeType = "interactiveSeq"; + break; + } + return pNodeType; +} + +/// Convert EffectPresetClass to ST_TLTimeNodePresetClassType +const char* convertEffectPresetClass(sal_Int16 nPresetClass) +{ + const char* pPresetClass = nullptr; + switch (nPresetClass) + { + case EffectPresetClass::ENTRANCE: + pPresetClass = "entr"; + break; + case EffectPresetClass::EXIT: + pPresetClass = "exit"; + break; + case EffectPresetClass::EMPHASIS: + pPresetClass = "emph"; + break; + case EffectPresetClass::MOTIONPATH: + pPresetClass = "path"; + break; + case EffectPresetClass::OLEACTION: + pPresetClass = "verb"; // ? + break; + case EffectPresetClass::MEDIACALL: + pPresetClass = "mediacall"; + break; + } + return pPresetClass; +} + +/// convert AnimationFill to ST_TLTimeNodeFillType. +const char* convertAnimationFill(sal_Int16 nFill) +{ + const char* pFill = nullptr; + switch (nFill) + { + case AnimationFill::FREEZE: + pFill = "hold"; + break; + case AnimationFill::HOLD: + pFill = "hold"; + break; + case AnimationFill::REMOVE: + pFill = "remove"; + break; + case AnimationFill::TRANSITION: + pFill = "transition"; + break; + } + return pFill; +} + +/// Convert TextAnimationType to ST_IterateType. +const char* convertTextAnimationType(sal_Int16 nType) +{ + const char* sType = nullptr; + switch (nType) + { + case TextAnimationType::BY_PARAGRAPH: + sType = "el"; + break; + case TextAnimationType::BY_LETTER: + sType = "lt"; + break; + case TextAnimationType::BY_WORD: + default: + sType = "wd"; + break; + } + return sType; +} + +class NodeContext; + +typedef std::unique_ptr<NodeContext> NodeContextPtr; + +class NodeContext +{ + const Reference<XAnimationNode> mxNode; + const bool mbMainSeqChild; + + std::vector<NodeContextPtr> maChildNodes; + // if the node has valid target or contains at least one valid target. + bool mbValid; + + // Attributes initialized from mxNode->getUserData(). + sal_Int16 mnEffectNodeType; + sal_Int16 mnEffectPresetClass; + OUString msEffectPresetId; + OUString msEffectPresetSubType; + + /// constructor helper for initializing user data. + void initUserData(); + + /// constructor helper to initialize maChildNodes. + /// return true if at least one childnode is valid. + bool initChildNodes(); + + /// constructor helper to initialize mbValid + void initValid(bool bHasValidChild, bool bIsIterateChild); + +public: + NodeContext(const Reference<XAnimationNode>& xNode, bool bMainSeqChild, bool bIsIterateChild); + const Reference<XAnimationNode>& getNode() const { return mxNode; } + bool isMainSeqChild() const { return mbMainSeqChild; } + sal_Int16 getEffectNodeType() const { return mnEffectNodeType; } + sal_Int16 getEffectPresetClass() const { return mnEffectPresetClass; } + const OUString& getEffectPresetId() const { return msEffectPresetId; } + const OUString& getEffectPresetSubType() const { return msEffectPresetSubType; } + bool isValid() const { return mbValid; } + const std::vector<NodeContextPtr>& getChildNodes() const { return maChildNodes; }; + Any getCondition(bool bBegin) const; +}; + +struct Cond +{ + OString msDelay; + const char* mpEvent; + Reference<XShape> mxShape; + Reference<XAnimationNode> mxNode; + + Cond(const Any& rAny, bool bIsMainSeqChild); + + bool isValid() const { return msDelay.getLength() || mpEvent; } + const char* getDelay() const { return msDelay.getLength() ? msDelay.getStr() : nullptr; } +}; + +Cond::Cond(const Any& rAny, bool bIsMainSeqChild) + : mpEvent(nullptr) +{ + bool bHasFDelay = false; + double fDelay = 0; + Timing eTiming; + Event aEvent; + + if (rAny >>= eTiming) + { + if (eTiming == Timing_INDEFINITE) + msDelay = "indefinite"; + } + else if (rAny >>= aEvent) + { + if (aEvent.Trigger == EventTrigger::ON_NEXT && bIsMainSeqChild) + msDelay = "indefinite"; + else + { + mpEvent = convertEventTrigger(aEvent.Trigger); + if (!(aEvent.Source >>= mxShape)) + aEvent.Source >>= mxNode; + + if (aEvent.Offset >>= fDelay) + bHasFDelay = true; + } + } + else if (rAny >>= fDelay) + bHasFDelay = true; + + if (bHasFDelay) + { + sal_Int32 nDelay = static_cast<sal_uInt32>(fDelay * 1000.0); + msDelay = OString::number(nDelay); + } +} + +class PPTXAnimationExport +{ + void WriteAnimationNode(const NodeContextPtr& pContext); + void WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType); + void WriteAnimationNodeAnimateInside(bool bSimple, bool bWriteTo = true); + void WriteAnimationNodeSeq(); + void WriteAnimationNodeEffect(); + void WriteAnimationNodeCommand(); + void WriteAnimationNodeAudio(); + void WriteAnimationNodeCommonPropsStart(); + void WriteAnimationTarget(const Any& rTarget); + void WriteAnimationCondList(const Any& rAny, sal_Int32 nToken); + void WriteAnimationCond(const Cond& rCond); + bool isMainSeqChild() const; + const Reference<XAnimationNode>& getCurrentNode() const; + + PowerPointExport& mrPowerPointExport; + const FSHelperPtr& mpFS; + const NodeContext* mpContext; + + std::map<Reference<XAnimationNode>, sal_Int32> maAnimationNodeIdMap; + sal_Int32 GetNextAnimationNodeId(const Reference<XAnimationNode>& rNode); + sal_Int32 GetAnimationNodeId(const Reference<XAnimationNode>& rNode); + +public: + PPTXAnimationExport(PowerPointExport& rExport, const FSHelperPtr& pFS); + void WriteAnimations(const Reference<XDrawPage>& rXDrawPage); +}; + +/// Returns if rURL has an extension which is an audio format. +bool IsAudioURL(const OUString& rURL) +{ + return rURL.endsWithIgnoreAsciiCase(".wav") || rURL.endsWithIgnoreAsciiCase(".m4a"); +} + +/// Returns if rURL has an extension which is a video format. +bool IsVideoURL(const OUString& rURL) { return rURL.endsWithIgnoreAsciiCase(".mp4"); } +} + +namespace oox::core +{ +void WriteAnimations(const FSHelperPtr& pFS, const Reference<XDrawPage>& rXDrawPage, + PowerPointExport& rExport) +{ + PPTXAnimationExport aAnimationExport(rExport, pFS); + aAnimationExport.WriteAnimations(rXDrawPage); +} +} + +PPTXAnimationExport::PPTXAnimationExport(PowerPointExport& rExport, const FSHelperPtr& pFS) + : mrPowerPointExport(rExport) + , mpFS(pFS) + , mpContext(nullptr) +{ +} + +bool PPTXAnimationExport::isMainSeqChild() const +{ + assert(mpContext); + return mpContext->isMainSeqChild(); +} + +const Reference<XAnimationNode>& PPTXAnimationExport::getCurrentNode() const +{ + assert(mpContext); + return mpContext->getNode(); +} + +void PPTXAnimationExport::WriteAnimationTarget(const Any& rTarget) +{ + sal_Int32 nParagraph = -1; + bool bParagraphTarget = false; + + Reference<XShape> rXShape; + rTarget >>= rXShape; + + if (!rXShape.is()) + { + ParagraphTarget aParagraphTarget; + if (rTarget >>= aParagraphTarget) + rXShape = aParagraphTarget.Shape; + if (rXShape.is()) + { + nParagraph = static_cast<sal_Int32>(aParagraphTarget.Paragraph); + Reference<XSimpleText> xText(rXShape, UNO_QUERY); + if (xText.is()) + { + bParagraphTarget = true; + } + } + } + + if (!rXShape.is()) + return; + + sal_Int32 nShapeID = mrPowerPointExport.GetShapeID(rXShape); + + mpFS->startElementNS(XML_p, XML_tgtEl); + mpFS->startElementNS(XML_p, XML_spTgt, XML_spid, OString::number(nShapeID)); + if (bParagraphTarget) + { + mpFS->startElementNS(XML_p, XML_txEl); + mpFS->singleElementNS(XML_p, XML_pRg, XML_st, OString::number(nParagraph), XML_end, + OString::number(nParagraph)); + mpFS->endElementNS(XML_p, XML_txEl); + } + mpFS->endElementNS(XML_p, XML_spTgt); + mpFS->endElementNS(XML_p, XML_tgtEl); +} + +void PPTXAnimationExport::WriteAnimationCondList(const Any& rAny, sal_Int32 nToken) +{ + if (!rAny.hasValue()) + return; + + std::vector<Cond> aList; + + bool bIsMainSeqChild = isMainSeqChild(); + + Sequence<Any> aCondSeq; + if (rAny >>= aCondSeq) + { + for (const auto& rCond : std::as_const(aCondSeq)) + { + Cond aCond(rCond, bIsMainSeqChild); + if (aCond.isValid()) + aList.push_back(aCond); + } + } + else + { + Cond aCond(rAny, bIsMainSeqChild); + if (aCond.isValid()) + aList.push_back(aCond); + } + + if (aList.size() > 0) + { + mpFS->startElementNS(XML_p, nToken); + + for (const Cond& rCond : aList) + WriteAnimationCond(rCond); + + mpFS->endElementNS(XML_p, nToken); + } +} + +void PPTXAnimationExport::WriteAnimationCond(const Cond& rCond) +{ + if (rCond.mpEvent) + { + sal_Int32 nId = -1; + if (rCond.mxShape.is()) + { + mpFS->startElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt, + rCond.mpEvent); + WriteAnimationTarget(Any(rCond.mxShape)); + mpFS->endElementNS(XML_p, XML_cond); + } + else if (rCond.mxNode.is() && (nId = GetAnimationNodeId(rCond.mxNode)) != -1) + { + mpFS->startElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt, + rCond.mpEvent); + mpFS->singleElementNS(XML_p, XML_tn, XML_val, OString::number(nId)); + mpFS->endElementNS(XML_p, XML_cond); + } + else + { + mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt, + rCond.mpEvent); + } + } + else + mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay()); +} + +void PPTXAnimationExport::WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType) +{ + const Reference<XAnimationNode>& rXNode = getCurrentNode(); + Reference<XAnimate> rXAnimate(rXNode, UNO_QUERY); + if (!rXAnimate.is()) + return; + + const char* pCalcMode = nullptr; + const char* pValueType = nullptr; + bool bSimple = (nXmlNodeType != XML_anim); + bool bTo = true; + + if (!bSimple) + { + switch (rXAnimate->getCalcMode()) + { + case AnimationCalcMode::DISCRETE: + pCalcMode = "discrete"; + break; + case AnimationCalcMode::LINEAR: + pCalcMode = "lin"; + break; + } + + switch (AnimationExporter::GetValueTypeForAttributeName(rXAnimate->getAttributeName())) + { + case AnimationValueType::STRING: + pValueType = "str"; + break; + case AnimationValueType::NUMBER: + pValueType = "num"; + break; + case AnimationValueType::COLOR: + pValueType = "clr"; + break; + } + } + + if (nXmlNodeType == XML_animMotion) + { + OUString aPath; + Reference<XAnimateMotion> xMotion(rXNode, UNO_QUERY); + if (xMotion.is()) + { + xMotion->getPath() >>= aPath; + ::basegfx::B2DPolyPolygon aPolyPoly; + if (::basegfx::utils::importFromSvgD(aPolyPoly, aPath, true, nullptr)) + aPath = ::basegfx::utils::exportToSvgD(aPolyPoly, false, false, true, true); + } + + mpFS->startElementNS(XML_p, nXmlNodeType, XML_origin, "layout", XML_path, aPath); + } + else if (nXmlNodeType == XML_animRot) + { + // when const char* is nullptr, the attribute is completely omitted in the output + const char* pBy = nullptr; + const char* pFrom = nullptr; + const char* pTo = nullptr; + OString aBy, aFrom, aTo; + + Reference<XAnimateTransform> xTransform(rXNode, UNO_QUERY); + if (xTransform.is()) + { + double value; + if (xTransform->getBy() >>= value) + { + aBy = OString::number(static_cast<int>(value * PER_DEGREE)); + pBy = aBy.getStr(); + } + + if (xTransform->getFrom() >>= value) + { + aFrom = OString::number(static_cast<int>(value * PER_DEGREE)); + pFrom = aFrom.getStr(); + } + + if (xTransform->getTo() >>= value) + { + aTo = OString::number(static_cast<int>(value * PER_DEGREE)); + pTo = aTo.getStr(); + } + } + + mpFS->startElementNS(XML_p, nXmlNodeType, XML_by, pBy, XML_from, pFrom, XML_to, pTo); + } + else if (nXmlNodeType == XML_animClr) + { + Reference<XAnimateColor> xColor(rXNode, UNO_QUERY); + const char* pColorSpace = "rgb"; + const char* pDirection = nullptr; + if (xColor.is() && xColor->getColorInterpolation() == AnimationColorSpace::HSL) + { + // Note: from, to, by can still be specified in any supported format. + pColorSpace = "hsl"; + pDirection = xColor->getDirection() ? "cw" : "ccw"; + } + mpFS->startElementNS(XML_p, nXmlNodeType, XML_clrSpc, pColorSpace, XML_dir, pDirection, + XML_calcmode, pCalcMode, XML_valueType, pValueType); + } + else + { + OUString sFrom, sTo, sBy; + if (rXAnimate.is() && nXmlNodeType == XML_anim) + { + OUString sAttributeName = rXAnimate->getAttributeName(); + Any aFrom + = AnimationExporter::convertAnimateValue(rXAnimate->getFrom(), sAttributeName); + aFrom >>= sFrom; + Any aTo = AnimationExporter::convertAnimateValue(rXAnimate->getTo(), sAttributeName); + aTo >>= sTo; + Any aBy = AnimationExporter::convertAnimateValue(rXAnimate->getBy(), sAttributeName); + aBy >>= sBy; + } + + mpFS->startElementNS(XML_p, nXmlNodeType, XML_calcmode, pCalcMode, XML_valueType, + pValueType, XML_from, sax_fastparser::UseIf(sFrom, !sFrom.isEmpty()), + XML_to, sax_fastparser::UseIf(sTo, !sTo.isEmpty()), XML_by, + sax_fastparser::UseIf(sBy, !sBy.isEmpty())); + bTo = sTo.isEmpty() && sFrom.isEmpty() && sBy.isEmpty(); + } + + WriteAnimationNodeAnimateInside(bSimple, bTo); + mpFS->endElementNS(XML_p, nXmlNodeType); +} + +void PPTXAnimationExport::WriteAnimationNodeAnimateInside(bool bSimple, bool bWriteTo) +{ + const Reference<XAnimationNode>& rXNode = getCurrentNode(); + Reference<XAnimate> rXAnimate(rXNode, UNO_QUERY); + if (!rXAnimate.is()) + return; + + const char* pAdditive = nullptr; + + if (!bSimple) + { + switch (rXAnimate->getAdditive()) + { + case AnimationAdditiveMode::BASE: + pAdditive = "base"; + break; + case AnimationAdditiveMode::SUM: + pAdditive = "sum"; + break; + case AnimationAdditiveMode::REPLACE: + pAdditive = "repl"; + break; + case AnimationAdditiveMode::MULTIPLY: + pAdditive = "mult"; + break; + case AnimationAdditiveMode::NONE: + pAdditive = "none"; + break; + } + } + + mpFS->startElementNS(XML_p, XML_cBhvr, XML_additive, pAdditive); + WriteAnimationNodeCommonPropsStart(); + + Reference<XIterateContainer> xIterate(rXNode->getParent(), UNO_QUERY); + WriteAnimationTarget(xIterate.is() ? xIterate->getTarget() : rXAnimate->getTarget()); + + Reference<XAnimateTransform> xTransform(rXNode, UNO_QUERY); + + // The attribute name of AnimateTransform is "Transform", we have to fix it. + OUString sNewAttr; + if (xTransform.is() && xTransform->getTransformType() == AnimationTransformType::ROTATE) + sNewAttr = "Rotate"; + + WriteAnimationAttributeName(mpFS, xTransform.is() ? sNewAttr : rXAnimate->getAttributeName()); + + mpFS->endElementNS(XML_p, XML_cBhvr); + WriteAnimateValues(mpFS, rXAnimate); + + Reference<XAnimateColor> xColor(rXNode, UNO_QUERY); + + if (xColor.is()) + { + WriteAnimateColorColor(mpFS, xColor->getBy(), XML_by); + WriteAnimateColorColor(mpFS, xColor->getFrom(), XML_from); + WriteAnimateColorColor(mpFS, xColor->getTo(), XML_to); + } + else if (xTransform.is() && xTransform->getTransformType() == AnimationTransformType::SCALE) + { + WriteAnimationProperty(mpFS, rXAnimate->getBy(), XML_by); + WriteAnimationProperty(mpFS, rXAnimate->getFrom(), XML_from); + WriteAnimationProperty(mpFS, rXAnimate->getTo(), XML_to); + } + else if (bWriteTo) + WriteAnimateTo(mpFS, rXAnimate->getTo(), rXAnimate->getAttributeName()); +} + +void PPTXAnimationExport::WriteAnimationNodeCommonPropsStart() +{ + const Reference<XAnimationNode>& rXNode = getCurrentNode(); + std::optional<OString> sDuration; + std::optional<OString> sRepeatCount; + const char* pRestart = nullptr; + const char* pNodeType = nullptr; + const char* pPresetClass = nullptr; + const char* pFill = nullptr; + double fDuration = 0; + double fRepeatCount = 0; + Any aAny; + assert(mpContext); + + aAny = rXNode->getDuration(); + if (aAny.hasValue()) + { + Timing eTiming; + + if (aAny >>= eTiming) + { + if (eTiming == Timing_INDEFINITE) + sDuration = "indefinite"; + } + else + aAny >>= fDuration; + } + + pRestart = convertAnimationRestart(rXNode->getRestart()); + + sal_Int16 nType = mpContext->getEffectNodeType(); + if (nType != -1) + { + pNodeType = convertEffectNodeType(nType); + if (nType == EffectNodeType::TIMING_ROOT) + { + if (!sDuration) + sDuration = "indefinite"; + if (!pRestart) + pRestart = "never"; + } + else if (nType == EffectNodeType::MAIN_SEQUENCE) + { + sDuration = "indefinite"; + } + } + + if (fDuration != 0) + sDuration = OString::number(static_cast<sal_Int32>(fDuration * 1000.0)); + + sal_uInt32 nPresetClass = mpContext->getEffectPresetClass(); + if (nPresetClass != DFF_ANIM_PRESS_CLASS_USER_DEFINED) + pPresetClass = convertEffectPresetClass(nPresetClass); + + sal_uInt32 nPresetId = 0; + bool bPresetId = false; + const OUString& rPresetId = mpContext->getEffectPresetId(); + if (rPresetId.getLength() > 0) + { + nPresetId = AnimationExporter::GetPresetID(rPresetId, nPresetClass, bPresetId); + bPresetId = true; + } + + sal_uInt32 nPresetSubType = 0; + bool bPresetSubType = false; + const OUString& sPresetSubType = mpContext->getEffectPresetSubType(); + if (sPresetSubType.getLength() > 0) + { + nPresetSubType + = AnimationExporter::TranslatePresetSubType(nPresetClass, nPresetId, sPresetSubType); + bPresetSubType = true; + } + + if (nType != EffectNodeType::TIMING_ROOT && nType != EffectNodeType::MAIN_SEQUENCE) + { + // it doesn't seem to work right on root and mainseq nodes + sal_Int16 nFill = AnimationExporter::GetFillMode(rXNode, AnimationFill::AUTO); + pFill = convertAnimationFill(nFill); + } + + bool bAutoReverse = rXNode->getAutoReverse(); + + aAny = rXNode->getRepeatCount(); + if (aAny.hasValue()) + { + Timing eTiming; + + if (aAny >>= eTiming) + { + if (eTiming == Timing_INDEFINITE) + sRepeatCount = "indefinite"; + } + else + aAny >>= fRepeatCount; + } + + if (fRepeatCount != 0) + sRepeatCount = OString::number(static_cast<sal_Int32>(fRepeatCount * 1000.0)); + + mpFS->startElementNS( + XML_p, XML_cTn, XML_id, OString::number(GetNextAnimationNodeId(rXNode)), XML_dur, sDuration, + XML_autoRev, sax_fastparser::UseIf("1", bAutoReverse), XML_restart, pRestart, XML_nodeType, + pNodeType, XML_fill, pFill, XML_presetClass, pPresetClass, XML_presetID, + sax_fastparser::UseIf(OString::number(nPresetId), bPresetId), XML_presetSubtype, + sax_fastparser::UseIf(OString::number(nPresetSubType), bPresetSubType), XML_repeatCount, + sRepeatCount); + + WriteAnimationCondList(mpContext->getCondition(true), XML_stCondLst); + WriteAnimationCondList(mpContext->getCondition(false), XML_endCondLst); + + if (rXNode->getType() == AnimationNodeType::ITERATE) + { + Reference<XIterateContainer> xIterate(rXNode, UNO_QUERY); + if (xIterate.is()) + { + const char* sType = convertTextAnimationType(xIterate->getIterateType()); + + mpFS->startElementNS(XML_p, XML_iterate, XML_type, sType); + mpFS->singleElementNS(XML_p, XML_tmAbs, XML_val, + OString::number(xIterate->getIterateInterval() * 1000)); + mpFS->endElementNS(XML_p, XML_iterate); + } + } + + const std::vector<NodeContextPtr>& aChildNodes = mpContext->getChildNodes(); + if (!aChildNodes.empty()) + { + mpFS->startElementNS(XML_p, XML_childTnLst); + for (const NodeContextPtr& pChildContext : aChildNodes) + { + if (pChildContext->isValid()) + WriteAnimationNode(pChildContext); + } + mpFS->endElementNS(XML_p, XML_childTnLst); + } + mpFS->endElementNS(XML_p, XML_cTn); +} + +void PPTXAnimationExport::WriteAnimationNodeSeq() +{ + SAL_INFO("sd.eppt", "write animation node SEQ"); + + mpFS->startElementNS(XML_p, XML_seq); + + WriteAnimationNodeCommonPropsStart(); + + WriteAnimationCondListForSeq(mpFS, XML_prevCondLst); + WriteAnimationCondListForSeq(mpFS, XML_nextCondLst); + + mpFS->endElementNS(XML_p, XML_seq); +} + +void PPTXAnimationExport::WriteAnimationNodeEffect() +{ + SAL_INFO("sd.eppt", "write animation node FILTER"); + Reference<XTransitionFilter> xFilter(getCurrentNode(), UNO_QUERY); + if (xFilter.is()) + { + const char* pFilter = ::ppt::AnimationExporter::FindTransitionName( + xFilter->getTransition(), xFilter->getSubtype(), xFilter->getDirection()); + const char* pMode = xFilter->getMode() ? "in" : "out"; + mpFS->startElementNS(XML_p, XML_animEffect, XML_filter, pFilter, XML_transition, pMode); + + WriteAnimationNodeAnimateInside(false); + + mpFS->endElementNS(XML_p, XML_animEffect); + } +} + +void PPTXAnimationExport::WriteAnimationNodeCommand() +{ + SAL_INFO("sd.eppt", "write animation node COMMAND"); + Reference<XCommand> xCommand(getCurrentNode(), UNO_QUERY); + if (!xCommand.is()) + return; + + const char* pType = "call"; + OString aCommand; + switch (xCommand->getCommand()) + { + case EffectCommands::VERB: + pType = "verb"; + aCommand = "1"; /* FIXME hardcoded viewing */ + break; + case EffectCommands::PLAY: + { + aCommand = "play"; + uno::Sequence<beans::NamedValue> aParamSeq; + xCommand->getParameter() >>= aParamSeq; + comphelper::SequenceAsHashMap aMap(aParamSeq); + auto it = aMap.find("MediaTime"); + if (it != aMap.end()) + { + double fMediaTime = 0; + it->second >>= fMediaTime; + // PowerPoint represents 0 as 0.0, so just use a single decimal. + OString aMediaTime + = rtl::math::doubleToString(fMediaTime, rtl_math_StringFormat_F, 1, '.'); + aCommand += "From(" + aMediaTime + ")"; + } + break; + } + case EffectCommands::TOGGLEPAUSE: + aCommand = "togglePause"; + break; + case EffectCommands::STOP: + aCommand = "stop"; + break; + default: + SAL_WARN("sd.eppt", "unknown command: " << xCommand->getCommand()); + break; + } + + mpFS->startElementNS(XML_p, XML_cmd, XML_type, pType, XML_cmd, aCommand.getStr()); + + WriteAnimationNodeAnimateInside(false); + mpFS->startElementNS(XML_p, XML_cBhvr); + WriteAnimationNodeCommonPropsStart(); + WriteAnimationTarget(xCommand->getTarget()); + mpFS->endElementNS(XML_p, XML_cBhvr); + + mpFS->endElementNS(XML_p, XML_cmd); +} + +void PPTXAnimationExport::WriteAnimationNodeAudio() +{ + SAL_INFO("sd.eppt", "write animation node audio"); + Reference<XAudio> xAudio(getCurrentNode(), UNO_QUERY); + + OUString sUrl; + uno::Reference<drawing::XShape> xShape; + OUString sRelId; + OUString sName; + + if (!xAudio.is()) + { + return; + } + + bool bValid = false; + if ((xAudio->getSource() >>= sUrl) && !sUrl.isEmpty() && IsAudioURL(sUrl)) + { + bValid = true; + } + + bool bVideo = false; + if (!bValid) + { + if (xAudio->getSource() >>= xShape) + { + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + bool bHasMediaURL = xShapeProps->getPropertySetInfo()->hasPropertyByName("MediaURL"); + if (bHasMediaURL && (xShapeProps->getPropertyValue("MediaURL") >>= sUrl)) + { + bVideo = IsVideoURL(sUrl); + bValid = IsAudioURL(sUrl) || bVideo; + } + } + } + + if (!bValid) + return; + + if (!xShape.is()) + { + mrPowerPointExport.embedEffectAudio(mpFS, sUrl, sRelId, sName); + } + + if (bVideo) + { + mpFS->startElementNS(XML_p, XML_video); + mpFS->startElementNS(XML_p, XML_cMediaNode); + } + else + { + bool bNarration = xAudio->getNarration(); + mpFS->startElementNS(XML_p, XML_audio, XML_isNarration, bNarration ? "1" : "0"); + bool bHideDuringShow = xAudio->getHideDuringShow(); + mpFS->startElementNS(XML_p, XML_cMediaNode, XML_showWhenStopped, + bHideDuringShow ? "0" : "1"); + } + + animations::Timing eTiming{}; + bool bLooping + = (xAudio->getRepeatCount() >>= eTiming) && eTiming == animations::Timing_INDEFINITE; + if (bVideo && bLooping) + { + mpFS->startElementNS(XML_p, XML_cTn, XML_repeatCount, "indefinite"); + } + else + { + mpFS->startElementNS(XML_p, XML_cTn); + } + WriteAnimationCondList(mpContext->getCondition(true), XML_stCondLst); + WriteAnimationCondList(mpContext->getCondition(false), XML_endCondLst); + mpFS->endElementNS(XML_p, XML_cTn); + + mpFS->startElementNS(XML_p, XML_tgtEl); + if (xShape.is()) + { + sal_Int32 nShapeID = mrPowerPointExport.GetShapeID(xShape); + mpFS->singleElementNS(XML_p, XML_spTgt, XML_spid, OString::number(nShapeID)); + } + else + { + mpFS->singleElementNS(XML_p, XML_sndTgt, FSNS(XML_r, XML_embed), + sax_fastparser::UseIf(sRelId, !sRelId.isEmpty()), XML_name, + sax_fastparser::UseIf(sName, !sUrl.isEmpty())); + } + mpFS->endElementNS(XML_p, XML_tgtEl); + + mpFS->endElementNS(XML_p, XML_cMediaNode); + if (bVideo) + { + mpFS->endElementNS(XML_p, XML_video); + } + else + { + mpFS->endElementNS(XML_p, XML_audio); + } +} + +void PPTXAnimationExport::WriteAnimationNode(const NodeContextPtr& pContext) +{ + const NodeContext* pSavedContext = mpContext; + mpContext = pContext.get(); + + const Reference<XAnimationNode>& rXNode = getCurrentNode(); + + SAL_INFO("sd.eppt", "export node type: " << rXNode->getType()); + sal_Int32 xmlNodeType = extractNodeType(rXNode); + + switch (xmlNodeType) + { + case XML_par: + mpFS->startElementNS(XML_p, xmlNodeType); + WriteAnimationNodeCommonPropsStart(); + mpFS->endElementNS(XML_p, xmlNodeType); + break; + case XML_seq: + WriteAnimationNodeSeq(); + break; + case XML_animScale: + case XML_animRot: + case XML_anim: + case XML_animMotion: + case XML_animClr: + case XML_set: + WriteAnimationNodeAnimate(xmlNodeType); + break; + case XML_animEffect: + WriteAnimationNodeEffect(); + break; + case XML_cmd: + WriteAnimationNodeCommand(); + break; + case XML_audio: + WriteAnimationNodeAudio(); + break; + default: + SAL_WARN("sd.eppt", "export ooxml node type: " << xmlNodeType); + break; + } + + mpContext = pSavedContext; +} + +void PPTXAnimationExport::WriteAnimations(const Reference<XDrawPage>& rXDrawPage) +{ + Reference<XAnimationNodeSupplier> xNodeSupplier(rXDrawPage, UNO_QUERY); + if (!xNodeSupplier.is()) + return; + + const Reference<XAnimationNode> xNode(xNodeSupplier->getAnimationNode()); + if (!xNode.is()) + return; + + Reference<XEnumerationAccess> xEnumerationAccess(xNode, UNO_QUERY); + if (!xEnumerationAccess.is()) + return; + + Reference<XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration(); + if (!(xEnumeration.is() && xEnumeration->hasMoreElements())) + return; + + auto pNodeContext = std::make_unique<NodeContext>(xNode, false, false); + if (pNodeContext->isValid()) + { + mpFS->startElementNS(XML_p, XML_timing); + mpFS->startElementNS(XML_p, XML_tnLst); + + WriteAnimationNode(pNodeContext); + + mpFS->endElementNS(XML_p, XML_tnLst); + mpFS->endElementNS(XML_p, XML_timing); + } +} + +sal_Int32 PPTXAnimationExport::GetNextAnimationNodeId(const Reference<XAnimationNode>& xNode) +{ + sal_Int32 nId = mrPowerPointExport.GetNextAnimationNodeID(); + maAnimationNodeIdMap[xNode] = nId; + return nId; +} + +sal_Int32 PPTXAnimationExport::GetAnimationNodeId(const Reference<XAnimationNode>& xNode) +{ + sal_Int32 nId = -1; + const auto& aIter = maAnimationNodeIdMap.find(xNode); + if (aIter != maAnimationNodeIdMap.end()) + { + nId = aIter->second; + } + return nId; +} + +NodeContext::NodeContext(const Reference<XAnimationNode>& xNode, bool bMainSeqChild, + bool bIsIterateChild) + : mxNode(xNode) + , mbMainSeqChild(bMainSeqChild) + , mbValid(true) + , mnEffectNodeType(-1) + , mnEffectPresetClass(DFF_ANIM_PRESS_CLASS_USER_DEFINED) +{ + assert(xNode.is()); + + initUserData(); + + initValid(initChildNodes(), bIsIterateChild); +} + +void NodeContext::initUserData() +{ + assert(mxNode.is()); + + Sequence<NamedValue> aUserData = mxNode->getUserData(); + const Any* aIndexedData[DFF_ANIM_PROPERTY_ID_COUNT]; + AnimationExporter::GetUserData(aUserData, aIndexedData, sizeof(aIndexedData)); + + const Any* pAny = aIndexedData[DFF_ANIM_NODE_TYPE]; + if (pAny) + *pAny >>= mnEffectNodeType; + + pAny = aIndexedData[DFF_ANIM_PRESET_CLASS]; + if (pAny) + *pAny >>= mnEffectPresetClass; + + pAny = aIndexedData[DFF_ANIM_PRESET_ID]; + if (pAny) + *pAny >>= msEffectPresetId; + + pAny = aIndexedData[DFF_ANIM_PRESET_SUB_TYPE]; + if (pAny) + *pAny >>= msEffectPresetSubType; +} + +void NodeContext::initValid(bool bHasValidChild, bool bIsIterateChild) +{ + sal_Int16 nType = mxNode->getType(); + + if (nType == AnimationNodeType::ITERATE) + { + Reference<XIterateContainer> xIterate(mxNode, UNO_QUERY); + mbValid = xIterate.is() && (bIsIterateChild || isValidTarget(xIterate->getTarget())) + && !maChildNodes.empty(); + } + else if (nType == AnimationNodeType::COMMAND) + { + Reference<XCommand> xCommand(mxNode, UNO_QUERY); + mbValid = xCommand.is() && (bIsIterateChild || isValidTarget(xCommand->getTarget())); + } + else if (nType == AnimationNodeType::PAR || nType == AnimationNodeType::SEQ) + { + mbValid = bHasValidChild; + } + else if (nType == AnimationNodeType::AUDIO) + { + Reference<XAudio> xAudio(mxNode, UNO_QUERY); + OUString sURL; + uno::Reference<drawing::XShape> xShape; + mbValid = false; + if (xAudio.is()) + { + if (xAudio->getSource() >>= sURL) + { + mbValid = IsAudioURL(sURL); + } + else if (xAudio->getSource() >>= xShape) + { + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + bool bHasMediaURL + = xShapeProps->getPropertySetInfo()->hasPropertyByName("MediaURL"); + if (bHasMediaURL && (xShapeProps->getPropertyValue("MediaURL") >>= sURL)) + { + mbValid = IsAudioURL(sURL) || IsVideoURL(sURL); + } + } + } + } + else + { + Reference<XAnimate> xAnimate(mxNode, UNO_QUERY); + mbValid = xAnimate.is() && (bIsIterateChild || isValidTarget(xAnimate->getTarget())); + } +} + +bool NodeContext::initChildNodes() +{ + bool bValid = false; + Reference<XEnumerationAccess> xEnumerationAccess(mxNode, UNO_QUERY); + if (xEnumerationAccess.is()) + { + Reference<XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration(); + bool bIsMainSeq = mnEffectNodeType == EffectNodeType::MAIN_SEQUENCE; + bool bIsIterateChild = mxNode->getType() == AnimationNodeType::ITERATE; + if (xEnumeration.is()) + { + while (xEnumeration->hasMoreElements()) + { + Reference<XAnimationNode> xChildNode(xEnumeration->nextElement(), UNO_QUERY); + if (xChildNode.is()) + { + auto pChildContext + = std::make_unique<NodeContext>(xChildNode, bIsMainSeq, bIsIterateChild); + if (pChildContext->isValid()) + bValid = true; + maChildNodes.push_back(std::move(pChildContext)); + } + } + } + } + return bValid; +} + +Any NodeContext::getCondition(bool bBegin) const +{ + const bool bParent + = (mnEffectNodeType != EffectNodeType::INTERACTIVE_SEQUENCE || maChildNodes.empty()); + const Reference<XAnimationNode>& rNode = bParent ? mxNode : maChildNodes[0]->getNode(); + + return bBegin ? rNode->getBegin() : rNode->getEnd(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-animations.hxx b/sd/source/filter/eppt/pptx-animations.hxx new file mode 100644 index 000000000..24654abb8 --- /dev/null +++ b/sd/source/filter/eppt/pptx-animations.hxx @@ -0,0 +1,25 @@ +/* -*- 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/. + */ +#pragma once + +#include <sax/fshelper.hxx> + +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/uno/Reference.hxx> + +#include "epptooxml.hxx" + +namespace oox::core +{ +void WriteAnimations(const ::sax_fastparser::FSHelperPtr& pFS, + const css::uno::Reference<css::drawing::XDrawPage>& rXDrawPage, + PowerPointExport& rExport); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-epptbase.cxx b/sd/source/filter/eppt/pptx-epptbase.cxx new file mode 100644 index 000000000..690738996 --- /dev/null +++ b/sd/source/filter/eppt/pptx-epptbase.cxx @@ -0,0 +1,1000 @@ +/* -*- 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 "epptbase.hxx" +#include "epptdef.hxx" +#include "../ppt/pptanimations.hxx" + +#include <o3tl/any.hxx> +#include <vcl/outdev.hxx> +#include <rtl/ustring.hxx> +#include <rtl/strbuf.hxx> +#include <sal/log.hxx> +#include <tools/UnitConversion.hxx> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/XMasterPageTarget.hpp> +#include <com/sun/star/drawing/XMasterPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPages.hpp> +#include <com/sun/star/animations/TransitionType.hpp> +#include <com/sun/star/animations/TransitionSubType.hpp> +#include <com/sun/star/awt/FontFamily.hpp> +#include <com/sun/star/awt/FontPitch.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/presentation/XPresentationPage.hpp> +#include <com/sun/star/text/XSimpleText.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> + + +using namespace com::sun::star; + +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::awt::FontFamily; +using namespace ::com::sun::star::awt::FontPitch; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::container::XNamed; +using ::com::sun::star::drawing::XMasterPageTarget; +using ::com::sun::star::drawing::XDrawPage; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::style::XStyleFamiliesSupplier; +using ::com::sun::star::style::XStyle; +using ::com::sun::star::task::XStatusIndicator; +using ::com::sun::star::text::XSimpleText; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; + +PHLayout const pPHLayout[] = +{ + { EppLayout::TITLESLIDE, { 0x0d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x10, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x14, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, true, true, true }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x0e, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x14, 0x0d, 0x0e, true, true, false }, + { EppLayout::BLANKSLIDE, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, false, false, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x0e, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x16, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x14, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x14, 0x0d, 0x0e, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x15, 0x0d, 0x0e, true, false, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x16, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x16, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x0e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, false, false }, + { EppLayout::RIGHTCOLUMN2ROWS, { 0x0d, 0x0e, 0x13, 0x13, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x13, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOROWSANDTITLE, { 0x0d, 0x13, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::LEFTCOLUMN2ROWS, { 0x0d, 0x13, 0x13, 0x0e, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TOPROW2COLUMN, { 0x0d, 0x13, 0x13, 0x0e, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOROWSANDTITLE, { 0x0d, 0x0e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::FOUROBJECTS, { 0x0d, 0x13, 0x13, 0x13, 0x13, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, false, false }, + { EppLayout::ONLYTITLE, { 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, true, false, false }, + { EppLayout::BLANKSLIDE, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, false, false, false }, + { EppLayout::TITLERIGHT2BODIESLEFT, { 0x11, 0x12, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x14, 0x11, 0x12, true, true, false }, + { EppLayout::TITLERIGHTBODYLEFT, { 0x11, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x11, 0x12, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x12, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x16, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x16, 0x0d, 0x12, true, true, false } +}; + +PPTWriterBase::PPTWriterBase() + : mbStatusIndicator(false) + , mbPresObj(false) + , mbEmptyPresObj(false) + , mbIsBackgroundDark(false) + , mnAngle(0) + , mnPages(0) + , mnMasterPages(0) + , maFraction(1, 576) + , maMapModeSrc(MapUnit::Map100thMM) + , maMapModeDest(MapUnit::MapInch, Point(), maFraction, maFraction) + , meLatestPageType(NORMAL) + , mpStyleSheet(nullptr) +{ + SAL_INFO("sd.eppt", "PPTWriterBase::PPTWriterBase()"); +} + +PPTWriterBase::PPTWriterBase( const Reference< XModel > & rXModel, + const Reference< XStatusIndicator > & rXStatInd ) + : mXModel(rXModel) + , mXStatusIndicator(rXStatInd) + , mbStatusIndicator(false) + , mbPresObj(false) + , mbEmptyPresObj(false) + , mbIsBackgroundDark(false) + , mnAngle(0) + , mnPages(0) + , mnMasterPages(0) + , maFraction(1, 576) + , maMapModeSrc(MapUnit::Map100thMM) + , maMapModeDest(MapUnit::MapInch, Point(), maFraction, maFraction) + , meLatestPageType (NORMAL) + , mpStyleSheet(nullptr) +{ +} + +PPTWriterBase::~PPTWriterBase() +{ + // Possibly unnecessary sanity check for mXStatusIndicator.is(). + // In 3.3 we had a bug report of a crash where it was null, + // https://bugzilla.novell.com/show_bug.cgi?id=694119 (non-public, + // bug report, sorry). + if ( mbStatusIndicator && mXStatusIndicator.is() ) + mXStatusIndicator->end(); +} + +void PPTWriterBase::exportPPT( const std::vector< css::beans::PropertyValue >& rMediaData ) +{ + if ( !InitSOIface() ) + return; + + FontCollectionEntry aDefaultFontDesc( "Times New Roman", + ROMAN, + awt::FontPitch::VARIABLE, + RTL_TEXTENCODING_MS_1252 ); + maFontCollection.GetId( aDefaultFontDesc ); // default is always times new roman + + if ( !GetPageByIndex( 0, NOTICE ) ) + return; + + sal_Int32 nWidth = 21000; + if ( ImplGetPropertyValue( mXPagePropSet, "Width" ) ) + mAny >>= nWidth; + sal_Int32 nHeight = 29700; + if ( ImplGetPropertyValue( mXPagePropSet, "Height" ) ) + mAny >>= nHeight; + + maNotesPageSize = MapSize( awt::Size( nWidth, nHeight ) ); + + if ( !GetPageByIndex( 0, MASTER ) ) + return; + + nWidth = 28000; + if ( ImplGetPropertyValue( mXPagePropSet, "Width" ) ) + mAny >>= nWidth; + nHeight = 21000; + if ( ImplGetPropertyValue( mXPagePropSet, "Height" ) ) + mAny >>= nHeight; + maDestPageSize = MapSize( awt::Size( nWidth, nHeight ) ); + maPageSize = awt::Size(nWidth, nHeight); + + SAL_INFO("sd.eppt", "call exportDocumentPre()"); + exportPPTPre(rMediaData); + + if ( !GetStyleSheets() ) + return; + + if ( !ImplCreateDocument() ) + return; + + sal_uInt32 i; + + for ( i = 0; i < mnMasterPages; i++ ) + { + if ( !CreateSlideMaster( i ) ) + return; + } + if ( !CreateMainNotes() ) + return; + + for ( i = 0; i < mnPages; i++ ) + { + SAL_INFO("sd.eppt", "call ImplCreateSlide( " << i << " )"); + if ( !CreateSlide( i ) ) + return; + } + + for ( i = 0; i < mnPages; i++ ) + { + if ( !CreateNotes( i ) ) + return; + } + + SAL_INFO("sd.eppt", "call exportDocumentPost()"); + exportPPTPost(); +} + +bool PPTWriterBase::InitSOIface() +{ + while( true ) + { + mXDrawPagesSupplier.set( mXModel, UNO_QUERY ); + if ( !mXDrawPagesSupplier.is() ) + break; + + mXMasterPagesSupplier.set( mXModel, UNO_QUERY ); + if ( !mXMasterPagesSupplier.is() ) + break; + mXDrawPages = mXMasterPagesSupplier->getMasterPages(); + if ( !mXDrawPages.is() ) + break; + mnMasterPages = mXDrawPages->getCount(); + mXDrawPages = mXDrawPagesSupplier->getDrawPages(); + if( !mXDrawPages.is() ) + break; + mnPages = mXDrawPages->getCount(); + if ( !GetPageByIndex( 0, NORMAL ) ) + break; + + return true; + } + return false; +} + +bool PPTWriterBase::GetPageByIndex( sal_uInt32 nIndex, PageType ePageType ) +{ + while( true ) + { + if ( ePageType != meLatestPageType ) + { + switch( ePageType ) + { + case NORMAL : + case NOTICE : + { + mXDrawPages = mXDrawPagesSupplier->getDrawPages(); + if( !mXDrawPages.is() ) + return false; + } + break; + + case MASTER : + { + mXDrawPages = mXMasterPagesSupplier->getMasterPages(); + if( !mXDrawPages.is() ) + return false; + } + break; + default: + break; + } + meLatestPageType = ePageType; + } + Any aAny( mXDrawPages->getByIndex( nIndex ) ); + aAny >>= mXDrawPage; + if ( !mXDrawPage.is() ) + break; + if ( ePageType == NOTICE ) + { + Reference< XPresentationPage > aXPresentationPage( mXDrawPage, UNO_QUERY ); + if ( !aXPresentationPage.is() ) + break; + mXDrawPage = aXPresentationPage->getNotesPage(); + if ( !mXDrawPage.is() ) + break; + } + mXPagePropSet.set( mXDrawPage, UNO_QUERY ); + if ( !mXPagePropSet.is() ) + break; + + if (GetPropertyValue( aAny, mXPagePropSet, "IsBackgroundDark" ) ) + aAny >>= mbIsBackgroundDark; + + mXShapes = mXDrawPage; + if ( !mXShapes.is() ) + break; + + /* try to get the "real" background PropertySet. If the normal page is not supporting this property, it is + taken the property from the master */ + bool bHasBackground = GetPropertyValue( aAny, mXPagePropSet, "Background", true ); + if ( bHasBackground ) + bHasBackground = ( aAny >>= mXBackgroundPropSet ); + if ( !bHasBackground ) + { + Reference< XMasterPageTarget > aXMasterPageTarget( mXDrawPage, UNO_QUERY ); + if ( aXMasterPageTarget.is() ) + { + Reference< XDrawPage > aXMasterDrawPage = aXMasterPageTarget->getMasterPage(); + if ( aXMasterDrawPage.is() ) + { + Reference< XPropertySet > aXMasterPagePropSet; + aXMasterPagePropSet.set( aXMasterDrawPage, UNO_QUERY ); + if ( aXMasterPagePropSet.is() ) + { + bool bBackground = GetPropertyValue( aAny, aXMasterPagePropSet, "Background" ); + if ( bBackground ) + { + aAny >>= mXBackgroundPropSet; + } + } + } + } + } + return true; + } + return false; +} + +bool PPTWriterBase::CreateSlide( sal_uInt32 nPageNum ) +{ + Any aAny; + + if ( !GetPageByIndex( nPageNum, NORMAL ) ) + return false; + + sal_uInt32 nMasterNum = GetMasterIndex( NORMAL ); + SetCurrentStyleSheet( nMasterNum ); + + Reference< XPropertySet > aXBackgroundPropSet; + bool bHasBackground = GetPropertyValue( aAny, mXPagePropSet, "Background" ); + if ( bHasBackground ) + bHasBackground = ( aAny >>= aXBackgroundPropSet ); + + sal_uInt16 nMode = 7; // Bit 1: Follow master objects, Bit 2: Follow master scheme, Bit 3: Follow master background + if ( bHasBackground ) + nMode &=~4; + +/* sj: Don't know what's IsBackgroundVisible for, have to ask cl + if ( GetPropertyValue( aAny, mXPagePropSet, OUString( "IsBackgroundVisible" ) ) ) + { + bool bBackgroundVisible; + if ( aAny >>= bBackgroundVisible ) + { + if ( bBackgroundVisible ) + nMode &= ~4; + } + } +*/ + if ( GetPropertyValue( aAny, mXPagePropSet, "IsBackgroundObjectsVisible" ) ) + { + bool bBackgroundObjectsVisible = false; + if ( aAny >>= bBackgroundObjectsVisible ) + { + if ( !bBackgroundObjectsVisible ) + nMode &= ~1; + } + } + + ImplWriteSlide( nPageNum, nMasterNum, nMode, bHasBackground, aXBackgroundPropSet ); + + return true; +}; + +bool PPTWriterBase::CreateNotes( sal_uInt32 nPageNum ) +{ + if ( !GetPageByIndex( nPageNum, NOTICE ) ) + return false; + SetCurrentStyleSheet( GetMasterIndex( NORMAL ) ); + + ImplWriteNotes( nPageNum ); + + return true; +}; + +bool PPTWriterBase::CreateSlideMaster( sal_uInt32 nPageNum ) +{ + if ( !GetPageByIndex( nPageNum, MASTER ) ) + return false; + SetCurrentStyleSheet( nPageNum ); + + css::uno::Reference< css::beans::XPropertySet > aXBackgroundPropSet; + if (ImplGetPropertyValue(mXPagePropSet, "Background")) // load background shape + mAny >>= aXBackgroundPropSet; + + ImplWriteSlideMaster( nPageNum, aXBackgroundPropSet ); + + return true; +}; + +sal_Int32 PPTWriterBase::GetLayoutOffset( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ) +{ + css::uno::Any aAny; + sal_Int32 nLayout = 20; + if ( GetPropertyValue( aAny, rXPropSet, "Layout", true ) ) + aAny >>= nLayout; + + SAL_INFO("sd.eppt", "GetLayoutOffset " << nLayout); + + return nLayout; +} + +sal_Int32 PPTWriterBase::GetLayoutOffsetFixed( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ) +{ + sal_Int32 nLayout = GetLayoutOffset( rXPropSet ); + + if ( ( nLayout >= 21 ) && ( nLayout <= 26 ) ) // NOTES _> HANDOUT6 + nLayout = 20; + if ( ( nLayout >= 27 ) && ( nLayout <= 30 ) ) // VERTICAL LAYOUT + nLayout -= 6; + else if ( nLayout > 30 ) + nLayout = 20; + + return nLayout; +} + +PHLayout const & PPTWriterBase::GetLayout( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ) +{ + return pPHLayout[ GetLayoutOffsetFixed( rXPropSet ) ]; +} + +PHLayout const & PPTWriterBase::GetLayout( sal_Int32 nOffset ) +{ + if( nOffset >= 0 && nOffset < EPP_LAYOUT_SIZE ) + return pPHLayout[ nOffset ]; + + SAL_INFO("sd.eppt", "asked " << nOffset << " for layout outside of 0, " << EPP_LAYOUT_SIZE << " array scope"); + + return pPHLayout[ 0 ]; +} + +sal_uInt32 PPTWriterBase::GetMasterIndex( PageType ePageType ) +{ + sal_uInt32 nRetValue = 0; + css::uno::Reference< css::drawing::XMasterPageTarget >aXMasterPageTarget( mXDrawPage, css::uno::UNO_QUERY ); + + if ( aXMasterPageTarget.is() ) + { + css::uno::Reference< css::drawing::XDrawPage >aXDrawPage = aXMasterPageTarget->getMasterPage(); + if ( aXDrawPage.is() ) + { + css::uno::Reference< css::beans::XPropertySet > aXPropertySet( aXDrawPage, css::uno::UNO_QUERY ); + if ( aXPropertySet.is() ) + { + if ( ImplGetPropertyValue( aXPropertySet, "Number" ) ) + nRetValue |= *o3tl::doAccess<sal_Int16>(mAny); + if ( nRetValue & 0xffff ) // avoid overflow + nRetValue--; + } + } + } + if ( ePageType == NOTICE ) + nRetValue += mnMasterPages; + return nRetValue; +} + +void PPTWriterBase::SetCurrentStyleSheet( sal_uInt32 nPageNum ) +{ + if ( nPageNum >= maStyleSheetList.size() ) + nPageNum = 0; + mpStyleSheet = maStyleSheetList[ nPageNum ].get(); +} + +bool PPTWriterBase::GetStyleSheets() +{ + int nInstance, nLevel; + bool bRetValue = false; + sal_uInt32 nPageNum; + + for ( nPageNum = 0; nPageNum < mnMasterPages; nPageNum++ ) + { + Reference< XNamed > + aXNamed; + + Reference< XNameAccess > + aXNameAccess; + + Reference< XStyleFamiliesSupplier > + aXStyleFamiliesSupplier( mXModel, UNO_QUERY ); + + Reference< XPropertySet > + aXPropSet( mXModel, UNO_QUERY ); + + sal_uInt16 nDefaultTab = ( aXPropSet.is() && ImplGetPropertyValue( aXPropSet, "TabStop" ) ) + ? static_cast<sal_uInt16>( convertMm100ToMasterUnit(*o3tl::doAccess<sal_Int32>(mAny)) ) + : 1250; + + maStyleSheetList.emplace_back( new PPTExStyleSheet( nDefaultTab, dynamic_cast<PPTExBulletProvider*>(this) ) ); + SetCurrentStyleSheet( nPageNum ); + if ( GetPageByIndex( nPageNum, MASTER ) ) + aXNamed.set( mXDrawPage, UNO_QUERY ); + + if ( aXStyleFamiliesSupplier.is() ) + aXNameAccess = aXStyleFamiliesSupplier->getStyleFamilies(); + + bRetValue = aXNamed.is() && aXNameAccess.is() && aXStyleFamiliesSupplier.is(); + if ( bRetValue ) + { + for ( nInstance = EPP_TEXTTYPE_Title; nInstance <= EPP_TEXTTYPE_CenterTitle; nInstance++ ) + { + OUString aStyle; + OUString aFamily; + switch ( nInstance ) + { + case EPP_TEXTTYPE_CenterTitle : + case EPP_TEXTTYPE_Title : + { + aStyle = "title"; + aFamily = aXNamed->getName(); + } + break; + case EPP_TEXTTYPE_Body : + { + aStyle = "outline1"; // SD_LT_SEPARATOR + aFamily = aXNamed->getName(); + } + break; + case EPP_TEXTTYPE_Other : + { + aStyle = "standard"; + aFamily = "graphics"; + } + break; + case EPP_TEXTTYPE_CenterBody : + { + aStyle = "subtitle"; + aFamily = aXNamed->getName(); + } + break; + } + if ( !aStyle.isEmpty() && !aFamily.isEmpty() ) + { + try + { + Reference< XNameAccess >xNameAccess; + if ( aXNameAccess->hasByName( aFamily ) ) + { + Any aAny( aXNameAccess->getByName( aFamily ) ); + xNameAccess.set(aAny, css::uno::UNO_QUERY); + if( xNameAccess.is() ) + { + Reference< XNameAccess > aXFamily; + if ( aAny >>= aXFamily ) + { + if ( aXFamily->hasByName( aStyle ) ) + { + aAny = aXFamily->getByName( aStyle ); + Reference< XStyle > xStyle( + aAny, css::uno::UNO_QUERY); + if( xStyle.is() ) + { + Reference< XStyle > aXStyle; + aAny >>= aXStyle; + Reference< XPropertySet > + xPropSet( aXStyle, UNO_QUERY ); + if( xPropSet.is() ) + mpStyleSheet->SetStyleSheet( xPropSet, maFontCollection, nInstance, 0 ); + for ( nLevel = 1; nLevel < 5; nLevel++ ) + { + if ( nInstance == EPP_TEXTTYPE_Body ) + { + sal_Unicode cTemp = aStyle[aStyle.getLength() - 1]; + aStyle = aStyle.subView(0, aStyle.getLength() - 1) + OUStringChar(++cTemp); + if ( aXFamily->hasByName( aStyle ) ) + { + aXFamily->getByName( aStyle ) >>= xStyle; + if( xStyle.is() ) + { + Reference< XPropertySet > + xPropertySet( xStyle, UNO_QUERY ); + if ( xPropertySet.is() ) + mpStyleSheet->SetStyleSheet( xPropertySet, maFontCollection, nInstance, nLevel ); + } + } + } + else + mpStyleSheet->SetStyleSheet( xPropSet, maFontCollection, nInstance, nLevel ); + } + } + } + } + } + } + } + catch( Exception& ) + { + + } + } + } + for ( ; nInstance <= EPP_TEXTTYPE_QuarterBody; nInstance++ ) + { + + } + } + } + return bRetValue; +} + +bool PPTWriterBase::CreateMainNotes() +{ + if ( !GetPageByIndex( 0, NOTICE ) ) + return false; + SetCurrentStyleSheet( 0 ); + + css::uno::Reference< css::drawing::XMasterPageTarget > aXMasterPageTarget( mXDrawPage, css::uno::UNO_QUERY ); + + if ( !aXMasterPageTarget.is() ) + return false; + + mXDrawPage = aXMasterPageTarget->getMasterPage(); + if ( !mXDrawPage.is() ) + return false; + + mXPropSet.set( mXDrawPage, css::uno::UNO_QUERY ); + if ( !mXPropSet.is() ) + return false; + + mXShapes = mXDrawPage; + if ( !mXShapes.is() ) + return false; + + return ImplCreateMainNotes(); +} + +awt::Size PPTWriterBase::MapSize( const awt::Size& rSize ) +{ + Size aRetSize( OutputDevice::LogicToLogic( Size( rSize.Width, rSize.Height ), maMapModeSrc, maMapModeDest ) ); + + if ( !aRetSize.Width() ) + aRetSize.AdjustWidth( 1 ); + if ( !aRetSize.Height() ) + aRetSize.AdjustHeight( 1 ); + return awt::Size( aRetSize.Width(), aRetSize.Height() ); +} + +awt::Point PPTWriterBase::MapPoint( const awt::Point& rPoint ) +{ + Point aRet( OutputDevice::LogicToLogic( Point( rPoint.X, rPoint.Y ), maMapModeSrc, maMapModeDest ) ); + return awt::Point( aRet.X(), aRet.Y() ); +} + +::tools::Rectangle PPTWriterBase::MapRectangle( const awt::Rectangle& rRect ) +{ + css::awt::Point aPoint( rRect.X, rRect.Y ); + css::awt::Size aSize( rRect.Width, rRect.Height ); + css::awt::Point aP( MapPoint( aPoint ) ); + css::awt::Size aS( MapSize( aSize ) ); + return ::tools::Rectangle( Point( aP.X, aP.Y ), Size( aS.Width, aS.Height ) ); +} + +bool PPTWriterBase::GetShapeByIndex( sal_uInt32 nIndex, bool bGroup ) +{ + while(true) + { + if ( !bGroup || ( GetCurrentGroupLevel() == 0 ) ) + { + Any aAny( mXShapes->getByIndex( nIndex ) ); + aAny >>= mXShape; + } + else + { + Any aAny( GetCurrentGroupAccess()->getByIndex( GetCurrentGroupIndex() ) ); + aAny >>= mXShape; + } + if ( !mXShape.is() ) + break; + + Any aAny( mXShape->queryInterface( cppu::UnoType<XPropertySet>::get())); + aAny >>= mXPropSet; + + if ( !mXPropSet.is() ) + break; + maPosition = MapPoint( mXShape->getPosition() ); + maSize = MapSize( mXShape->getSize() ); + maRect = ::tools::Rectangle( Point( maPosition.X, maPosition.Y ), Size( maSize.Width, maSize.Height ) ); + + OStringBuffer aTypeBuffer(OUStringToOString( + mXShape->getShapeType(), RTL_TEXTENCODING_UTF8)); + // remove "com.sun.star." + aTypeBuffer.remove(0, RTL_CONSTASCII_LENGTH("com.sun.star.")); + + sal_Int32 nPos = aTypeBuffer.toString().indexOf("Shape"); + aTypeBuffer.remove(nPos, RTL_CONSTASCII_LENGTH("Shape")); + mType = aTypeBuffer.makeStringAndClear(); + + mbPresObj = mbEmptyPresObj = false; + if ( ImplGetPropertyValue( "IsPresentationObject" ) ) + mAny >>= mbPresObj; + + if ( mbPresObj && ImplGetPropertyValue( "IsEmptyPresentationObject" ) ) + mAny >>= mbEmptyPresObj; + + mnAngle = ( PropValue::GetPropertyValue( aAny, + mXPropSet, "RotateAngle", true ) ) + ? *o3tl::doAccess<sal_Int32>(aAny) + : 0; + + return true; + } + return false; +} + +sal_Int8 PPTWriterBase::GetTransition( sal_Int16 nTransitionType, sal_Int16 nTransitionSubtype, FadeEffect eEffect, + sal_Int32 nTransitionFadeColor, sal_uInt8& nDirection ) +{ + sal_Int8 nPPTTransitionType = 0; + nDirection = 0; + + switch( nTransitionType ) + { + case TransitionType::FADE : + { + if ( nTransitionSubtype == TransitionSubType::CROSSFADE ) + nPPTTransitionType = PPT_TRANSITION_TYPE_SMOOTHFADE; + else if ( nTransitionSubtype == TransitionSubType::FADEOVERCOLOR ) + { + if( nTransitionFadeColor == static_cast<sal_Int32>(COL_WHITE) ) + nPPTTransitionType = PPT_TRANSITION_TYPE_FLASH; + else + nPPTTransitionType = PPT_TRANSITION_TYPE_FADE; + } + } + break; + case TransitionType::PUSHWIPE : + { + if (nTransitionSubtype == TransitionSubType::COMBVERTICAL || + nTransitionSubtype == TransitionSubType::COMBHORIZONTAL) + { + nPPTTransitionType = PPT_TRANSITION_TYPE_COMB; + } + else + { + nPPTTransitionType = PPT_TRANSITION_TYPE_PUSH; + } + switch (nTransitionSubtype) + { + case TransitionSubType::FROMRIGHT: nDirection = 0; break; + case TransitionSubType::FROMBOTTOM: nDirection = 1; break; + case TransitionSubType::FROMLEFT: nDirection = 2; break; + case TransitionSubType::FROMTOP: nDirection = 3; break; + case TransitionSubType::COMBHORIZONTAL: nDirection = 0; break; + case TransitionSubType::COMBVERTICAL: nDirection = 1; break; + } + } + break; + case TransitionType::PINWHEELWIPE : + { + nPPTTransitionType = PPT_TRANSITION_TYPE_WHEEL; + switch( nTransitionSubtype ) + { + case TransitionSubType::ONEBLADE: nDirection = 1; break; + case TransitionSubType::TWOBLADEVERTICAL : nDirection = 2; break; + case TransitionSubType::THREEBLADE : nDirection = 3; break; + case TransitionSubType::FOURBLADE: nDirection = 4; break; + case TransitionSubType::EIGHTBLADE: nDirection = 8; break; + } + } + break; + case TransitionType::FANWIPE : + { + nPPTTransitionType = PPT_TRANSITION_TYPE_WEDGE; + } + break; + case TransitionType::ELLIPSEWIPE : + { + switch( nTransitionSubtype ) { + case TransitionSubType::VERTICAL: + case TransitionSubType::HORIZONTAL: + // no ellipse or oval in PPT or OOXML, fallback to circle + default: + nPPTTransitionType = PPT_TRANSITION_TYPE_CIRCLE; + } + } + break; + case TransitionType::FOURBOXWIPE : + { + nPPTTransitionType = PPT_TRANSITION_TYPE_PLUS; + } + break; + case TransitionType::IRISWIPE : + { + switch( nTransitionSubtype ) { + case TransitionSubType::RECTANGLE: + nPPTTransitionType = PPT_TRANSITION_TYPE_ZOOM; + nDirection = (eEffect == FadeEffect_FADE_FROM_CENTER) ? 0 : 1; + break; + default: + nPPTTransitionType = PPT_TRANSITION_TYPE_DIAMOND; + break; + } + } + break; + case TransitionType::ZOOM: + { + switch(nTransitionSubtype) + { + case TransitionSubType::ROTATEIN: + nPPTTransitionType = PPT_TRANSITION_TYPE_NEWSFLASH; + break; + default: + break; + } + } + break; + } + + return nPPTTransitionType; +} + +sal_Int8 PPTWriterBase::GetTransition( FadeEffect eEffect, sal_uInt8& nDirection ) +{ + sal_Int8 nPPTTransitionType = 0; + + switch ( eEffect ) + { + default : + case FadeEffect_RANDOM : + nPPTTransitionType = PPT_TRANSITION_TYPE_RANDOM; + break; + + case FadeEffect_HORIZONTAL_STRIPES : + nDirection++; + [[fallthrough]]; + case FadeEffect_VERTICAL_STRIPES : + nPPTTransitionType = PPT_TRANSITION_TYPE_BLINDS; + break; + + case FadeEffect_VERTICAL_CHECKERBOARD : + nDirection++; + [[fallthrough]]; + case FadeEffect_HORIZONTAL_CHECKERBOARD : + nPPTTransitionType = PPT_TRANSITION_TYPE_CHECKER; + break; + + case FadeEffect_MOVE_FROM_UPPERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_UPPERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_LOWERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_LOWERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_TOP : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_LEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_BOTTOM : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_RIGHT : + nPPTTransitionType = PPT_TRANSITION_TYPE_COVER; + break; + + case FadeEffect_DISSOLVE : + nPPTTransitionType = PPT_TRANSITION_TYPE_DISSOLVE; + break; + + case FadeEffect_VERTICAL_LINES : + nDirection++; + [[fallthrough]]; + case FadeEffect_HORIZONTAL_LINES : + nPPTTransitionType = PPT_TRANSITION_TYPE_RANDOM_BARS; + break; + + case FadeEffect_CLOSE_HORIZONTAL : + nDirection++; + [[fallthrough]]; + case FadeEffect_OPEN_HORIZONTAL : + nDirection++; + [[fallthrough]]; + case FadeEffect_CLOSE_VERTICAL : + nDirection++; + [[fallthrough]]; + case FadeEffect_OPEN_VERTICAL : + nPPTTransitionType = PPT_TRANSITION_TYPE_SPLIT; + break; + + case FadeEffect_FADE_FROM_UPPERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_UPPERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_LOWERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_LOWERRIGHT : + nDirection += 4; + nPPTTransitionType = PPT_TRANSITION_TYPE_STRIPS; + break; + + case FadeEffect_UNCOVER_TO_LOWERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_LOWERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_UPPERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_UPPERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_BOTTOM : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_RIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_TOP : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_LEFT : + nPPTTransitionType = PPT_TRANSITION_TYPE_PULL; + break; + + case FadeEffect_FADE_FROM_TOP : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_LEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_BOTTOM : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_RIGHT : + nPPTTransitionType = PPT_TRANSITION_TYPE_WIPE; + break; + + case FadeEffect_ROLL_FROM_TOP : + nDirection++; + [[fallthrough]]; + case FadeEffect_ROLL_FROM_LEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_ROLL_FROM_BOTTOM : + nDirection++; + [[fallthrough]]; + case FadeEffect_ROLL_FROM_RIGHT : + nPPTTransitionType = PPT_TRANSITION_TYPE_WIPE; + break; + + case FadeEffect_FADE_TO_CENTER : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_CENTER : + nPPTTransitionType = PPT_TRANSITION_TYPE_ZOOM; + break; + + case FadeEffect_NONE : + nDirection = 2; + break; + } + + return nPPTTransitionType; +} + +bool PPTWriterBase::ContainsOtherShapeThanPlaceholders() +{ + sal_uInt32 nShapes = mXShapes->getCount(); + bool bOtherThanPlaceHolders = false; + + if ( nShapes ) + for ( sal_uInt32 nIndex = 0; ( nIndex < nShapes ) && !bOtherThanPlaceHolders; nIndex++ ) + { + if ( GetShapeByIndex( nIndex, false ) && mType != "drawing.Page" ) + { + if( mType == "presentation.Page" || mType == "presentation.Notes" ) + { + Reference< XSimpleText > rXText( mXShape, UNO_QUERY ); + + if( rXText.is() && !rXText->getString().isEmpty() ) + bOtherThanPlaceHolders = true; + } + else + bOtherThanPlaceHolders = true; + } + SAL_INFO("sd.eppt", "mType == " << mType); + } + + return bOtherThanPlaceHolders; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx new file mode 100644 index 000000000..6a338a6a6 --- /dev/null +++ b/sd/source/filter/eppt/pptx-epptooxml.cxx @@ -0,0 +1,2594 @@ +/* -*- 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 <stdio.h> +#include <oox/drawingml/clrscheme.hxx> +#include <oox/token/namespaces.hxx> +#include <oox/token/tokens.hxx> +#include <oox/token/relationship.hxx> +#include <oox/ole/vbaproject.hxx> +#include "epptooxml.hxx" +#include <oox/export/shapes.hxx> + +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/xmltools.hxx> +#include <sax/fshelper.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <tools/UnitConversion.hxx> +#include <tools/datetime.hxx> +#include <com/sun/star/animations/TransitionType.hpp> +#include <com/sun/star/animations/TransitionSubType.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/XDrawPages.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/geometry/RealPoint2D.hpp> +#include <com/sun/star/office/XAnnotationEnumeration.hpp> +#include <com/sun/star/office/XAnnotationAccess.hpp> +#include <com/sun/star/presentation/AnimationSpeed.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/presentation/XCustomPresentationSupplier.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/presentation/XPresentationSupplier.hpp> +#include <tools/diagnose_ex.h> + +#include <oox/export/utils.hxx> + +#include "pptx-animations.hxx" +#include "../ppt/pptanimations.hxx" + +#include <i18nlangtag/languagetag.hxx> +#include <svx/svdpage.hxx> +#include <svx/unoapi.hxx> +#include <svx/svdogrp.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <utility> +#if OSL_DEBUG_LEVEL > 1 +#include <com/sun/star/drawing/RectanglePoint.hpp> +#endif + +// presentation namespaces +#define PNMSS FSNS(XML_xmlns, XML_a), OUStringToOString(this->getNamespaceURL(OOX_NS(dml)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p), OUStringToOString(this->getNamespaceURL(OOX_NS(ppt)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_r), OUStringToOString(this->getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p14), OUStringToOString(this->getNamespaceURL(OOX_NS(p14)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p15), OUStringToOString(this->getNamespaceURL(OOX_NS(p15)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_mc), OUStringToOString(this->getNamespaceURL(OOX_NS(mce)), RTL_TEXTENCODING_UTF8).getStr() + +// presentationPr namespace +#define PPRNMSS FSNS(XML_xmlns, XML_a), OUStringToOString(this->getNamespaceURL(OOX_NS(dml)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_r), OUStringToOString(this->getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p), OUStringToOString(this->getNamespaceURL(OOX_NS(ppt)), RTL_TEXTENCODING_UTF8).getStr() + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::geometry; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::office; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::ppt; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::beans::XPropertySetInfo; +using ::sax_fastparser::FSHelperPtr; +using namespace oox::drawingml; +using namespace oox::core; + +#if OSL_DEBUG_LEVEL > 1 +void dump_pset(Reference< XPropertySet > const& rXPropSet); +#endif + +namespace oox::core +{ + +class PowerPointShapeExport : public ShapeExport +{ + PowerPointExport& mrExport; + PageType mePageType; + bool mbMaster; +public: + PowerPointShapeExport(FSHelperPtr pFS, ShapeHashMap* pShapeMap, PowerPointExport* pFB); + void SetMaster(bool bMaster); + void SetPageType(PageType ePageType); + ShapeExport& WriteNonVisualProperties(const Reference< XShape >& xShape) override; + ShapeExport& WriteTextShape(const Reference< XShape >& xShape) override; + ShapeExport& WriteUnknownShape(const Reference< XShape >& xShape) override; + ShapeExport& WritePlaceholderShape(const Reference< XShape >& xShape, PlaceholderType ePlaceholder); + /** Writes a placeholder shape that references the placeholder on the master slide */ + ShapeExport& WritePlaceholderReferenceShape(PlaceholderType ePlaceholder, sal_Int32 nReferencedPlaceholderIdx, PageType ePageType, const Reference<XPropertySet>& rXPagePropSet); + ShapeExport& WritePageShape(const Reference< XShape >& xShape, PageType ePageType, bool bPresObj); + /** Writes textbody of a placeholder that references the placeholder on the master slide */ + ShapeExport& WritePlaceholderReferenceTextBody(PlaceholderType ePlaceholder, PageType ePageType, const Reference<XPropertySet> xPagePropSet); + + // helper parts + bool WritePlaceholder(const Reference< XShape >& xShape, PlaceholderType ePlaceholder, bool bMaster); +}; + + +namespace +{ +void WriteSndAc(const FSHelperPtr& pFS, const OUString& sSoundRelId, const OUString& sSoundName) +{ + pFS->startElementNS(XML_p, XML_sndAc); + pFS->startElementNS(XML_p, XML_stSnd); + pFS->singleElementNS(XML_p, XML_snd, FSNS(XML_r, XML_embed), + sax_fastparser::UseIf(sSoundRelId, !sSoundRelId.isEmpty()), XML_name, + sax_fastparser::UseIf(sSoundName, !sSoundName.isEmpty())); + pFS->endElement(FSNS(XML_p, XML_stSnd)); + pFS->endElement(FSNS(XML_p, XML_sndAc)); +} + +const char* getPlaceholderTypeName(PlaceholderType ePlaceholder) +{ + switch (ePlaceholder) + { + case SlideImage: + return "sldImg"; + case Notes: + return "body"; + case Header: + return "hdr"; + case Footer: + return "ftr"; + case SlideNumber: + return "sldNum"; + case DateAndTime: + return "dt"; + case Outliner: + return "body"; + case Title: + return "title"; + case Subtitle: + return "subTitle"; + default: + SAL_INFO("sd.eppt", "warning: unhandled placeholder type: " << ePlaceholder); + return ""; + } +} +} +} + +namespace { + +enum PPTXLayout +{ + LAYOUT_BLANK, + LAYOUT_TITLE_SLIDE, + LAYOUT_TITLE_CONTENT, + LAYOUT_TITLE_2CONTENT, + LAYOUT_TITLE, + LAYOUT_CENTERED_TEXT, + LAYOUT_TITLE_2CONTENT_CONTENT, + LAYOUT_TITLE_CONTENT_2CONTENT, + LAYOUT_TITLE_2CONTENT_OVER_CONTENT, + LAYOUT_TITLE_CONTENT_OVER_CONTENT, + LAYOUT_TITLE_4CONTENT, + LAYOUT_TITLE_6CONTENT, + LAYOUT_SIZE +}; + +struct PPTXLayoutInfo +{ + int nType; + const char* sName; + const char* sType; +}; + +} + +const PPTXLayoutInfo aLayoutInfo[LAYOUT_SIZE] = +{ + { 20, "Blank Slide", "blank" }, + { 0, "Title Slide", "tx" }, + { 1, "Title, Content", "obj" }, + { 3, "Title, 2 Content", "twoObj" }, + { 19, "Title Only", "titleOnly" }, + { 32, "Centered Text", "objOnly" }, // not exactly, but close + { 15, "Title, 2 Content and Content", "twoObjAndObj" }, + { 12, "Title Content and 2 Content", "objAndTwoObj" }, + { 16, "Title, 2 Content over Content", "twoObjOverTx" }, // not exactly, but close + { 14, "Title, Content over Content", "objOverTx" }, // not exactly, but close + { 18, "Title, 4 Content", "fourObj" }, + { 34, "Title, 6 Content", "blank" } // not defined => blank +}; + +int PowerPointExport::GetPPTXLayoutId(int nOffset) +{ + int nId = LAYOUT_BLANK; + + SAL_INFO("sd.eppt", "GetPPTXLayoutId " << nOffset); + + switch (nOffset) + { + case 0: + nId = LAYOUT_TITLE_SLIDE; + break; + case 1: + nId = LAYOUT_TITLE_CONTENT; + break; + case 3: + nId = LAYOUT_TITLE_2CONTENT; + break; + case 19: + nId = LAYOUT_TITLE; + break; + case 15: + nId = LAYOUT_TITLE_2CONTENT_CONTENT; + break; + case 12: + nId = LAYOUT_TITLE_CONTENT_2CONTENT; + break; + case 16: + nId = LAYOUT_TITLE_2CONTENT_OVER_CONTENT; + break; + case 14: + nId = LAYOUT_TITLE_CONTENT_OVER_CONTENT; + break; + case 18: + nId = LAYOUT_TITLE_4CONTENT; + break; + case 32: + nId = LAYOUT_CENTERED_TEXT; + break; + case 34: + nId = LAYOUT_TITLE_6CONTENT; + break; + case 20: + default: + nId = LAYOUT_BLANK; + break; + } + + return nId; +} + +PowerPointShapeExport::PowerPointShapeExport(FSHelperPtr pFS, ShapeHashMap* pShapeMap, + PowerPointExport* pFB) + : ShapeExport(XML_p, std::move(pFS), pShapeMap, pFB) + , mrExport(*pFB) + , mePageType(UNDEFINED) + , mbMaster(false) +{ +} + +void PowerPointShapeExport::SetMaster(bool bMaster) +{ + mbMaster = bMaster; +} + +void PowerPointShapeExport::SetPageType(PageType ePageType) +{ + mePageType = ePageType; +} + +ShapeExport& PowerPointShapeExport::WriteNonVisualProperties(const Reference< XShape >&) +{ + GetFS()->singleElementNS(XML_p, XML_nvPr); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WriteTextShape(const Reference< XShape >& xShape) +{ + OUString sShapeType = xShape->getShapeType(); + + SAL_INFO("sd.eppt", "shape(text) : " << sShapeType.toUtf8()); + + if (sShapeType == "com.sun.star.drawing.TextShape" || sShapeType == "com.sun.star.drawing.GraphicObjectShape") + { + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.DateTimeShape") + { + if (!WritePlaceholder(xShape, DateAndTime, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.FooterShape") + { + if (!WritePlaceholder(xShape, Footer, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.HeaderShape") + { + if (!WritePlaceholder(xShape, Header, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.NotesShape") + { + if (mePageType == NOTICE && mrExport.GetPresObj()) + WritePlaceholderShape(xShape, Notes); + else + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.OutlinerShape") + { + if (!WritePlaceholder(xShape, Outliner, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.SlideNumberShape") + { + if (!WritePlaceholder(xShape, SlideNumber, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.TitleTextShape") + { + if (!WritePlaceholder(xShape, Title, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else + SAL_WARN("sd.eppt", "PowerPointShapeExport::WriteTextShape: shape of type '" << sShapeType << "' is ignored"); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WriteUnknownShape(const Reference< XShape >& xShape) +{ + OUString sShapeType = xShape->getShapeType(); + + SAL_INFO("sd.eppt", "shape(unknown): " << sShapeType.toUtf8()); + + if (sShapeType == "com.sun.star.presentation.PageShape") + { + WritePageShape(xShape, mePageType, mrExport.GetPresObj()); + } + else if (sShapeType == "com.sun.star.presentation.SubtitleShape") + { + if(mePageType != MASTER) + { + if (!WritePlaceholder(xShape, Subtitle, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + } + else + SAL_WARN("sd.eppt", "unknown shape not handled: " << sShapeType.toUtf8()); + + return *this; +} + +PowerPointExport::PowerPointExport(const Reference< XComponentContext >& rContext, const uno::Sequence<uno::Any>& rArguments) + : XmlFilterBase(rContext) + , mnLayoutFileIdMax(1) + , mnSlideIdMax(1 << 8) + , mnSlideMasterIdMax(1U << 31) + , mnAnimationNodeIdMax(1) + , mnDiagramId(1) + , mbCreateNotes(false) + , mnPlaceholderIndexMax(1) +{ + comphelper::SequenceAsHashMap aArgumentsMap(rArguments); + mbPptm = aArgumentsMap.getUnpackedValueOrDefault("IsPPTM", false); + mbExportTemplate = aArgumentsMap.getUnpackedValueOrDefault("IsTemplate", false); +} + +PowerPointExport::~PowerPointExport() +{ +} + +void PowerPointExport::writeDocumentProperties() +{ + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(mXModel, uno::UNO_QUERY); + uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties(); + + if (xDocProps.is()) + { + bool bSecurityOptOpenReadOnly = false; + uno::Reference< lang::XMultiServiceFactory > xFactory(mXModel, uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xSettings(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY); + try + { + xSettings->getPropertyValue("LoadReadonly") >>= bSecurityOptOpenReadOnly; + } + catch( Exception& ) + { + } + exportDocumentProperties(xDocProps, bSecurityOptOpenReadOnly); + } + + exportCustomFragments(); +} + +bool PowerPointExport::importDocument() noexcept +{ + return false; +} + +bool PowerPointExport::exportDocument() +{ + DrawingML::PushExportGraphics(); + maShapeMap.clear(); + + mXModel = getModel(); + + //write document properties + writeDocumentProperties(); + + addRelation(oox::getRelationship(Relationship::OFFICEDOCUMENT), u"ppt/presentation.xml"); + + OUString aMediaType; + if (mbPptm) + { + if (mbExportTemplate) + { + aMediaType = "application/vnd.ms-powerpoint.template.macroEnabled.main+xml"; + } + else + { + aMediaType = "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml"; + } + } + else + { + if (mbExportTemplate) + { + aMediaType = "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml"; + } + else + { + aMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"; + } + } + + mPresentationFS = openFragmentStreamWithSerializer("ppt/presentation.xml", aMediaType); + + addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + u"theme/theme1.xml"); + + mPresentationFS->startElementNS(XML_p, XML_presentation, PNMSS); + + mXStatusIndicator = getStatusIndicator(); + + std::vector< PropertyValue > aProperties; + PropertyValue aProperty; + aProperty.Name = "BaseURI"; + aProperty.Value <<= getFileUrl(); + aProperties.push_back(aProperty); + + exportPPT(aProperties); + + mPresentationFS->singleElementNS(XML_p, XML_sldSz, + XML_cx, OString::number(PPTtoEMU(maDestPageSize.Width)), + XML_cy, OString::number(PPTtoEMU(maDestPageSize.Height))); + // for some reason if added before slides list it will not load the slides (alas with error reports) in mso + mPresentationFS->singleElementNS(XML_p, XML_notesSz, + XML_cx, OString::number(PPTtoEMU(maNotesPageSize.Width)), + XML_cy, OString::number(PPTtoEMU(maNotesPageSize.Height))); + + WriteCustomSlideShow(); + + WritePresentationProps(); + + WriteAuthors(); + + WriteVBA(); + + WriteModifyVerifier(); + + mPresentationFS->endElementNS(XML_p, XML_presentation); + mPresentationFS.reset(); + // Free all FSHelperPtr, to flush data before committing storage + mpSlidesFSArray.clear(); + + commitStorage(); + + DrawingML::PopExportGraphics(); + maShapeMap.clear(); + maAuthors.clear(); + maRelId.clear(); + + return true; +} + +::oox::ole::VbaProject* PowerPointExport::implCreateVbaProject() const +{ + return new ::oox::ole::VbaProject(getComponentContext(), getModel(), u"Impress"); +} + +void PowerPointExport::WriteCustomSlideShow() +{ + Reference<XCustomPresentationSupplier> aXCPSup(mXModel, css::uno::UNO_QUERY); + if (!aXCPSup.is() || !aXCPSup->getCustomPresentations()->hasElements()) + return; + + mPresentationFS->startElementNS(XML_p, XML_custShowLst); + + Reference<XDrawPagesSupplier> xDPS(getModel(), uno::UNO_QUERY_THROW); + Reference<XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW); + Reference<XNameContainer> aXNameCont(aXCPSup->getCustomPresentations()); + const Sequence<OUString> aNameSeq(aXNameCont->getElementNames()); + + OUString sRelId; + sal_uInt32 nCustomShowIndex = 0; + sal_Int32 nSlideCount = xDrawPages->getCount(); + + for (OUString const& customShowName : aNameSeq) + { + mPresentationFS->startElementNS(XML_p, XML_custShow, XML_name, customShowName, XML_id, + OUString::number(nCustomShowIndex++)); + + mAny = aXNameCont->getByName(customShowName); + Reference<XIndexContainer> aXIContainer; + if (mAny >>= aXIContainer) + { + mPresentationFS->startElementNS(XML_p, XML_sldLst); + + sal_Int32 nCustomShowSlideCount = aXIContainer->getCount(); + for (sal_Int32 i = 0; i < nCustomShowSlideCount; ++i) + { + Reference<XDrawPage> aXCustomShowDrawPage; + aXIContainer->getByIndex(i) >>= aXCustomShowDrawPage; + Reference<XNamed> aXName(aXCustomShowDrawPage, UNO_QUERY_THROW); + OUString sCustomShowSlideName = aXName->getName(); + + for (sal_Int32 j = 0; j < nSlideCount; ++j) + { + Reference<XDrawPage> xDrawPage; + xDrawPages->getByIndex(j) >>= xDrawPage; + Reference<XNamed> xNamed(xDrawPage, UNO_QUERY_THROW); + OUString sSlideName = xNamed->getName(); + + if (sCustomShowSlideName == sSlideName) + { + sRelId = maRelId[j]; + break; + } + } + mPresentationFS->singleElementNS(XML_p, XML_sld, FSNS(XML_r, XML_id), sRelId); + } + mPresentationFS->endElementNS(XML_p, XML_sldLst); + } + mPresentationFS->endElementNS(XML_p, XML_custShow); + } + mPresentationFS->endElementNS(XML_p, XML_custShowLst); +} + +void PowerPointExport::ImplWriteBackground(const FSHelperPtr& pFS, const Reference< XPropertySet >& rXPropSet) +{ + FillStyle aFillStyle(FillStyle_NONE); + if (ImplGetPropertyValue(rXPropSet, "FillStyle")) + mAny >>= aFillStyle; + + if (aFillStyle == FillStyle_NONE || + aFillStyle == FillStyle_HATCH) + return; + + pFS->startElementNS(XML_p, XML_bg); + pFS->startElementNS(XML_p, XML_bgPr); + + PowerPointShapeExport aDML(pFS, &maShapeMap, this); + aDML.SetBackgroundDark(mbIsBackgroundDark); + aDML.WriteFill(rXPropSet); + + pFS->endElementNS(XML_p, XML_bgPr); + pFS->endElementNS(XML_p, XML_bg); +} + +#define MAIN_GROUP \ + "<p:nvGrpSpPr>\ + <p:cNvPr id=\"1\" name=\"\"/>\ + <p:cNvGrpSpPr/>\ + <p:nvPr/>\ + </p:nvGrpSpPr>\ + <p:grpSpPr>\ + <a:xfrm>\ + <a:off x=\"0\" y=\"0\"/>\ + <a:ext cx=\"0\" cy=\"0\"/>\ + <a:chOff x=\"0\" y=\"0\"/>\ + <a:chExt cx=\"0\" cy=\"0\"/>\ + </a:xfrm>\ + </p:grpSpPr>" + +const char* PowerPointExport::GetSideDirection(sal_uInt8 nDirection) +{ + const char* pDirection = nullptr; + + switch (nDirection) + { + case 0: + pDirection = "l"; + break; + case 1: + pDirection = "u"; + break; + case 2: + pDirection = "r"; + break; + case 3: + pDirection = "d"; + break; + } + + return pDirection; +} + +const char* PowerPointExport::GetCornerDirection(sal_uInt8 nDirection) +{ + const char* pDirection = nullptr; + + switch (nDirection) + { + case 4: + pDirection = "lu"; + break; + case 5: + pDirection = "ru"; + break; + case 6: + pDirection = "ld"; + break; + case 7: + pDirection = "rd"; + break; + } + + return pDirection; +} + +const char* PowerPointExport::Get8Direction(sal_uInt8 nDirection) +{ + const char* pDirection = GetSideDirection(nDirection); + + if (!pDirection) + pDirection = GetCornerDirection(nDirection); + + return pDirection; +} + +void PowerPointExport::WriteTransition(const FSHelperPtr& pFS) +{ + FadeEffect eFadeEffect = FadeEffect_NONE; + if (ImplGetPropertyValue(mXPagePropSet, "Effect")) + mAny >>= eFadeEffect; + + sal_Int16 nTransitionType = 0, nTransitionSubtype = 0; + sal_Int8 nPPTTransitionType = 0; + sal_uInt8 nDirection = 0; + + OUString sSoundUrl; + OUString sSoundRelId; + OUString sSoundName; + + if (ImplGetPropertyValue(mXPagePropSet, "TransitionType") && (mAny >>= nTransitionType) && + ImplGetPropertyValue(mXPagePropSet, "TransitionSubtype") && (mAny >>= nTransitionSubtype)) + { + // FADEOVERCOLOR with black -> fade, with white -> flash + sal_Int32 nTransitionFadeColor = 0; + if( ImplGetPropertyValue(mXPagePropSet, "TransitionFadeColor")) + mAny >>= nTransitionFadeColor; + nPPTTransitionType = GetTransition(nTransitionType, nTransitionSubtype, eFadeEffect, nTransitionFadeColor, nDirection); + } + + if (!nPPTTransitionType && eFadeEffect != FadeEffect_NONE) + nPPTTransitionType = GetTransition(eFadeEffect, nDirection); + + if (ImplGetPropertyValue(mXPagePropSet, "Sound") && (mAny >>= sSoundUrl)) + embedEffectAudio(pFS, sSoundUrl, sSoundRelId, sSoundName); + + bool bOOXmlSpecificTransition = false; + + sal_Int32 nTransition = 0; + const char* pDirection = nullptr; + const char* pOrientation = nullptr; + const char* pThruBlk = nullptr; + const char* pSpokes = nullptr; + + char pSpokesTmp[2] = "0"; + + // p14 + sal_Int32 nTransition14 = 0; + const char* pDirection14 = nullptr; + const char* pInverted = nullptr; + const char* pPattern = nullptr; // diamond or hexagon + + //p15 + const char* pPresetTransition = nullptr; + + if (!nPPTTransitionType) + { + switch (nTransitionType) + { + case animations::TransitionType::BARWIPE: + { + if (nTransitionSubtype == animations::TransitionSubType::FADEOVERCOLOR) + { + nTransition = XML_cut; + pThruBlk = "true"; + bOOXmlSpecificTransition = true; + } + break; + } + case animations::TransitionType::MISCSHAPEWIPE: + { + switch (nTransitionSubtype) + { + case animations::TransitionSubType::TOPTOBOTTOM: // Turn around + nTransition = XML_fade; + nTransition14 = XML_flip; + pDirection14 = "l"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::BOTTOMRIGHT: // Rochade + nTransition = XML_fade; + nTransition14 = XML_switch; + pDirection14 = "r"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::VERTICAL: // Vortex + nTransition = XML_fade; + nTransition14 = XML_vortex; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::HORIZONTAL: // Ripple + nTransition = XML_fade; + nTransition14 = XML_ripple; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::LEFTTORIGHT: // Fall + nTransition = XML_fade; + pPresetTransition = "fallOver"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::CORNERSIN: // Inside turning cube + pInverted = "true"; + [[fallthrough]]; + case animations::TransitionSubType::CORNERSOUT: // Outside turning cube + nTransition = XML_fade; + nTransition14 = XML_prism; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::DIAMOND: // Glitter + nTransition = XML_fade; + nTransition14 = XML_glitter; + pDirection14 = "l"; + pPattern = "hexagon"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::HEART: // Honeycomb + nTransition = XML_fade; + nTransition14 = XML_honeycomb; + bOOXmlSpecificTransition = true; + break; + } + break; + } + } + } + + AnimationSpeed animationSpeed = AnimationSpeed_MEDIUM; + const char* speed = nullptr; + sal_Int32 advanceTiming = -1; + sal_Int32 changeType = 0; + + sal_Int32 nTransitionDuration = -1; + bool isTransitionDurationSet = false; + + // try to use TransitionDuration instead of old Speed property + if (ImplGetPropertyValue(mXPagePropSet, "TransitionDuration")) + { + double fTransitionDuration = -1.0; + mAny >>= fTransitionDuration; + if (fTransitionDuration >= 0) + { + nTransitionDuration = fTransitionDuration * 1000.0; + + // override values because in MS formats meaning of fast/medium/slow is different + if (nTransitionDuration <= 500) + { + // fast is default + speed = nullptr; + } + else if (nTransitionDuration >= 1000) + { + speed = "slow"; + } + else + { + speed = "med"; + } + + bool isStandardValue = nTransitionDuration == 500 + || nTransitionDuration == 750 + || nTransitionDuration == 1000; + + if(!isStandardValue) + isTransitionDurationSet = true; + } + } + else if (ImplGetPropertyValue(mXPagePropSet, "Speed")) + { + mAny >>= animationSpeed; + + switch (animationSpeed) + { + default: + case AnimationSpeed_MEDIUM: + speed = "med"; + break; + case AnimationSpeed_SLOW: + speed = "slow"; + break; + case AnimationSpeed_FAST: + break; + } + } + + // check if we resolved what transition to export or time is set + if (!nPPTTransitionType && !bOOXmlSpecificTransition && !isTransitionDurationSet) + return; + + if (ImplGetPropertyValue(mXPagePropSet, "Change")) + mAny >>= changeType; + + // 1 means automatic, 2 half automatic - not sure what it means - at least I don't see it in UI + if (changeType == 1 && ImplGetPropertyValue(mXPagePropSet, "Duration")) + mAny >>= advanceTiming; + + if (!bOOXmlSpecificTransition) + { + switch (nPPTTransitionType) + { + case PPT_TRANSITION_TYPE_BLINDS: + nTransition = XML_blinds; + pDirection = (nDirection == 0) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_CHECKER: + nTransition = XML_checker; + pDirection = (nDirection == 1) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_CIRCLE: + nTransition = XML_circle; + break; + case PPT_TRANSITION_TYPE_COMB: + nTransition = XML_comb; + pDirection = (nDirection == 1) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_COVER: + nTransition = XML_cover; + pDirection = Get8Direction(nDirection); + break; + case PPT_TRANSITION_TYPE_DIAMOND: + nTransition = XML_diamond; + break; + case PPT_TRANSITION_TYPE_DISSOLVE: + nTransition = XML_dissolve; + break; + case PPT_TRANSITION_TYPE_FADE: + nTransition = XML_fade; + pThruBlk = "true"; + break; + case PPT_TRANSITION_TYPE_SMOOTHFADE: + nTransition = XML_fade; + break; + case PPT_TRANSITION_TYPE_NEWSFLASH: + nTransition = XML_newsflash; + break; + case PPT_TRANSITION_TYPE_PLUS: + nTransition = XML_plus; + break; + case PPT_TRANSITION_TYPE_PULL: + nTransition = XML_pull; + pDirection = Get8Direction(nDirection); + break; + case PPT_TRANSITION_TYPE_PUSH: + nTransition = XML_push; + pDirection = GetSideDirection(nDirection); + break; + case PPT_TRANSITION_TYPE_RANDOM: + nTransition = XML_random; + break; + case PPT_TRANSITION_TYPE_RANDOM_BARS: + nTransition = XML_randomBar; + pDirection = (nDirection == 1) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_SPLIT: + nTransition = XML_split; + pDirection = (nDirection & 1) ? "in" : "out"; + pOrientation = (nDirection < 2) ? "horz" : "vert"; + break; + case PPT_TRANSITION_TYPE_STRIPS: + nTransition = XML_strips; + pDirection = GetCornerDirection(nDirection); + break; + case PPT_TRANSITION_TYPE_WEDGE: + nTransition = XML_wedge; + break; + case PPT_TRANSITION_TYPE_WHEEL: + nTransition = XML_wheel; + if (nDirection != 4 && nDirection <= 9) + { + pSpokesTmp[0] = '0' + nDirection; + pSpokes = pSpokesTmp; + } + break; + case PPT_TRANSITION_TYPE_WIPE: + nTransition = XML_wipe; + pDirection = GetSideDirection(nDirection); + break; + case PPT_TRANSITION_TYPE_ZOOM: + nTransition = XML_zoom; + pDirection = (nDirection == 1) ? "in" : "out"; + break; + case PPT_TRANSITION_TYPE_FLASH: + nTransition14 = XML_flash; + nTransition = XML_fade; + bOOXmlSpecificTransition = true; + break; + // coverity[dead_error_line] - following conditions exist to avoid compiler warning + case PPT_TRANSITION_TYPE_NONE: + default: + nTransition = 0; + break; + } + } + + bool isAdvanceTimingSet = advanceTiming != -1; + if (nTransition14 || pPresetTransition || isTransitionDurationSet) + { + const char* pRequiresNS = (nTransition14 || isTransitionDurationSet) ? "p14" : "p15"; + + pFS->startElement(FSNS(XML_mc, XML_AlternateContent)); + pFS->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, pRequiresNS); + + if(isTransitionDurationSet && isAdvanceTimingSet) + { + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + XML_advTm, OString::number(advanceTiming * 1000), + FSNS(XML_p14, XML_dur), OString::number(nTransitionDuration)); + } + else if(isTransitionDurationSet) + { + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + FSNS(XML_p14, XML_dur), OString::number(nTransitionDuration)); + } + else if(isAdvanceTimingSet) + { + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + XML_advTm, OString::number(advanceTiming * 1000)); + } + else + { + pFS->startElementNS(XML_p, XML_transition, XML_spd, speed); + } + + if (nTransition14) + { + pFS->singleElementNS(XML_p14, nTransition14, + XML_isInverted, pInverted, + XML_dir, pDirection14, + XML_pattern, pPattern); + } + else if (pPresetTransition) + { + pFS->singleElementNS(XML_p15, XML_prstTrans, + XML_prst, pPresetTransition); + } + else if (isTransitionDurationSet && nTransition) + { + pFS->singleElementNS(XML_p, nTransition, + XML_dir, pDirection, + XML_orient, pOrientation, + XML_spokes, pSpokes, + XML_thruBlk, pThruBlk); + } + + if (!sSoundRelId.isEmpty()) + WriteSndAc(pFS, sSoundRelId, sSoundName); + + pFS->endElement(FSNS(XML_p, XML_transition)); + + pFS->endElement(FSNS(XML_mc, XML_Choice)); + pFS->startElement(FSNS(XML_mc, XML_Fallback)); + } + + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + XML_advTm, sax_fastparser::UseIf(OString::number(advanceTiming * 1000), isAdvanceTimingSet)); + + if (nTransition) + { + pFS->singleElementNS(XML_p, nTransition, + XML_dir, pDirection, + XML_orient, pOrientation, + XML_spokes, pSpokes, + XML_thruBlk, pThruBlk); + } + + if (!sSoundRelId.isEmpty()) + WriteSndAc(pFS, sSoundRelId, sSoundName); + + pFS->endElementNS(XML_p, XML_transition); + + if (nTransition14 || pPresetTransition || isTransitionDurationSet) + { + pFS->endElement(FSNS(XML_mc, XML_Fallback)); + pFS->endElement(FSNS(XML_mc, XML_AlternateContent)); + } +} + +static OUString lcl_GetInitials(const OUString& sName) +{ + OUStringBuffer sRet; + + if (!sName.isEmpty()) + { + sRet.append(sName[0]); + sal_Int32 nStart = 0, nOffset; + + while ((nOffset = sName.indexOf(' ', nStart)) != -1) + { + if (nOffset + 1 < sName.getLength()) + sRet.append(sName[ nOffset + 1 ]); + nStart = nOffset + 1; + } + } + + return sRet.makeStringAndClear(); +} + +void PowerPointExport::WriteAuthors() +{ + if (maAuthors.empty()) + return; + + FSHelperPtr pFS = openFragmentStreamWithSerializer("ppt/commentAuthors.xml", + "application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml"); + addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::COMMENTAUTHORS), + u"commentAuthors.xml"); + + pFS->startElementNS(XML_p, XML_cmAuthorLst, + FSNS(XML_xmlns, XML_p), getNamespaceURL(OOX_NS(ppt))); + + for (const AuthorsMap::value_type& i : maAuthors) + { + pFS->singleElementNS(XML_p, XML_cmAuthor, + XML_id, OString::number(i.second.nId), + XML_name, i.first, + XML_initials, lcl_GetInitials(i.first), + XML_lastIdx, OString::number(i.second.nLastIndex), + XML_clrIdx, OString::number(i.second.nId)); + } + + pFS->endElementNS(XML_p, XML_cmAuthorLst); +} + +sal_Int32 PowerPointExport::GetAuthorIdAndLastIndex(const OUString& sAuthor, sal_Int32& nLastIndex) +{ + if (maAuthors.count(sAuthor) <= 0) + { + struct AuthorComments aAuthorComments; + + aAuthorComments.nId = maAuthors.size(); + aAuthorComments.nLastIndex = 0; + + maAuthors[ sAuthor ] = aAuthorComments; + } + + nLastIndex = ++maAuthors[ sAuthor ].nLastIndex; + + return maAuthors[ sAuthor ].nId; +} + +void PowerPointExport::WritePresentationProps() +{ + Reference<XPresentationSupplier> xPresentationSupplier(mXModel, uno::UNO_QUERY); + if (!xPresentationSupplier.is()) + return; + + Reference<beans::XPropertySet> xPresentationProps(xPresentationSupplier->getPresentation(), + uno::UNO_QUERY); + bool bEndlessVal = xPresentationProps->getPropertyValue("IsEndless").get<bool>(); + bool bChangeManually = xPresentationProps->getPropertyValue("IsAutomatic").get<bool>(); + OUString sFirstPage = xPresentationProps->getPropertyValue("FirstPage").get<OUString>(); + OUString sCustomShow = xPresentationProps->getPropertyValue("CustomShow").get<OUString>(); + + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/presProps.xml", + "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"); + + addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::PRESPROPS), u"presProps.xml"); + + pFS->startElementNS(XML_p, XML_presentationPr, PPRNMSS); + + pFS->startElementNS(XML_p, XML_showPr, XML_loop, sax_fastparser::UseIf("1", bEndlessVal), + XML_useTimings, sax_fastparser::UseIf("0", bChangeManually), + XML_showNarration, "1"); + + Reference<drawing::XDrawPagesSupplier> xDPS(mXModel, uno::UNO_QUERY_THROW); + Reference<drawing::XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW); + if (!sFirstPage.isEmpty()) + { + sal_Int32 nStartSlide = 1; + sal_Int32 nEndSlide = xDrawPages->getCount(); + for (sal_Int32 i = 0; i < nEndSlide; i++) + { + Reference<drawing::XDrawPage> xDrawPage; + xDrawPages->getByIndex(i) >>= xDrawPage; + Reference<container::XNamed> xNamed(xDrawPage, uno::UNO_QUERY_THROW); + if (xNamed->getName() == sFirstPage) + { + nStartSlide = i + 1; + break; + } + } + + pFS->singleElementNS(XML_p, XML_sldRg, XML_st, OUString::number(nStartSlide), XML_end, + OUString::number(nEndSlide)); + } + + if (!sCustomShow.isEmpty()) + { + css::uno::Reference<css::presentation::XCustomPresentationSupplier> + XCustPresentationSupplier(mXModel, css::uno::UNO_QUERY_THROW); + css::uno::Reference<css::container::XNameContainer> mxCustShows; + mxCustShows = XCustPresentationSupplier->getCustomPresentations(); + const css::uno::Sequence<OUString> aNameSeq(mxCustShows->getElementNames()); + + sal_Int32 nCustShowIndex = 0; + for (sal_Int32 i = 0; i < aNameSeq.getLength(); i++) + { + if (aNameSeq[i] == sCustomShow) + { + nCustShowIndex = i; + break; + } + } + + pFS->singleElementNS(XML_p, XML_custShow, XML_id, OUString::number(nCustShowIndex)); + } + + pFS->endElementNS(XML_p, XML_showPr); + + pFS->endElementNS(XML_p, XML_presentationPr); +} + +bool PowerPointExport::WriteComments(sal_uInt32 nPageNum) +{ + Reference< XAnnotationAccess > xAnnotationAccess(mXDrawPage, uno::UNO_QUERY); + if (xAnnotationAccess.is()) + { + Reference< XAnnotationEnumeration > xAnnotationEnumeration(xAnnotationAccess->createAnnotationEnumeration()); + + if (xAnnotationEnumeration->hasMoreElements()) + { + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/comments/comment" + OUString::number(nPageNum + 1) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.comments+xml"); + + pFS->startElementNS(XML_p, XML_cmLst, + FSNS(XML_xmlns, XML_p), this->getNamespaceURL(OOX_NS(ppt))); + + do + { + Reference< XAnnotation > xAnnotation(xAnnotationEnumeration->nextElement()); + util::DateTime aDateTime(xAnnotation->getDateTime()); + RealPoint2D aRealPoint2D(xAnnotation->getPosition()); + Reference< XText > xText(xAnnotation->getTextRange()); + sal_Int32 nLastIndex; + sal_Int32 nId = GetAuthorIdAndLastIndex(xAnnotation->getAuthor(), nLastIndex); + char cDateTime[sizeof("-32768-65535-65535T65535:65535:65535.4294967295")]; + // reserve enough space for hypothetical max length + + snprintf(cDateTime, sizeof cDateTime, "%02" SAL_PRIdINT32 "-%02" SAL_PRIuUINT32 "-%02" SAL_PRIuUINT32 "T%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ".%09" SAL_PRIuUINT32, sal_Int32(aDateTime.Year), sal_uInt32(aDateTime.Month), sal_uInt32(aDateTime.Day), sal_uInt32(aDateTime.Hours), sal_uInt32(aDateTime.Minutes), sal_uInt32(aDateTime.Seconds), aDateTime.NanoSeconds); + + pFS->startElementNS(XML_p, XML_cm, + XML_authorId, OString::number(nId), + XML_dt, cDateTime, + XML_idx, OString::number(nLastIndex)); + + pFS->singleElementNS(XML_p, XML_pos, + XML_x, OString::number(std::round(convertMm100ToMasterUnit(aRealPoint2D.X * 100))), + XML_y, OString::number(std::round(convertMm100ToMasterUnit(aRealPoint2D.Y * 100)))); + + pFS->startElementNS(XML_p, XML_text); + pFS->write(xText->getString()); + pFS->endElementNS(XML_p, XML_text); + + pFS->endElementNS(XML_p, XML_cm); + + } + while (xAnnotationEnumeration->hasMoreElements()); + + pFS->endElementNS(XML_p, XML_cmLst); + + return true; + } + } + + return false; +} + +void PowerPointExport::WriteVBA() +{ + if (!mbPptm) + return; + + uno::Reference<document::XStorageBasedDocument> xStorageBasedDocument(getModel(), uno::UNO_QUERY); + if (!xStorageBasedDocument.is()) + return; + + uno::Reference<embed::XStorage> xDocumentStorage = xStorageBasedDocument->getDocumentStorage(); + OUString aMacrosName("_MS_VBA_Macros"); + if (!xDocumentStorage.is() || !xDocumentStorage->hasByName(aMacrosName)) + return; + + const sal_Int32 nOpenMode = embed::ElementModes::READ; + uno::Reference<io::XInputStream> xMacrosStream(xDocumentStorage->openStreamElement(aMacrosName, nOpenMode), uno::UNO_QUERY); + if (!xMacrosStream.is()) + return; + + uno::Reference<io::XOutputStream> xOutputStream = openFragmentStream("ppt/vbaProject.bin", "application/vnd.ms-office.vbaProject"); + comphelper::OStorageHelper::CopyInputToOutput(xMacrosStream, xOutputStream); + + // Write the relationship. + addRelation(mPresentationFS->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), u"vbaProject.bin"); +} + +void PowerPointExport::WriteModifyVerifier() +{ + Sequence<PropertyValue> aInfo; + + try + { + Reference<lang::XMultiServiceFactory> xFactory(mXModel, UNO_QUERY); + Reference<XPropertySet> xDocSettings( + xFactory->createInstance("com.sun.star.document.Settings"), UNO_QUERY); + xDocSettings->getPropertyValue("ModifyPasswordInfo") >>= aInfo; + } + catch (const Exception&) + { + } + + if (aInfo.hasElements()) + { + OUString sAlgorithm, sSalt, sHash; + sal_Int32 nCount = 0; + for (auto& prop : aInfo) + { + if (prop.Name == "algorithm-name") + prop.Value >>= sAlgorithm; + else if (prop.Name == "salt") + prop.Value >>= sSalt; + else if (prop.Name == "iteration-count") + prop.Value >>= nCount; + else if (prop.Name == "hash") + prop.Value >>= sHash; + } + if (!sAlgorithm.isEmpty() && !sSalt.isEmpty() && !sHash.isEmpty()) + { + sal_Int32 nAlgorithmSid = 0; + if (sAlgorithm == "MD2") + nAlgorithmSid = 1; + else if (sAlgorithm == "MD4") + nAlgorithmSid = 2; + else if (sAlgorithm == "MD5") + nAlgorithmSid = 3; + else if (sAlgorithm == "SHA-1") + nAlgorithmSid = 4; + else if (sAlgorithm == "MAC") + nAlgorithmSid = 5; + else if (sAlgorithm == "RIPEMD") + nAlgorithmSid = 6; + else if (sAlgorithm == "RIPEMD-160") + nAlgorithmSid = 7; + else if (sAlgorithm == "HMAC") + nAlgorithmSid = 9; + else if (sAlgorithm == "SHA-256") + nAlgorithmSid = 12; + else if (sAlgorithm == "SHA-384") + nAlgorithmSid = 13; + else if (sAlgorithm == "SHA-512") + nAlgorithmSid = 14; + + if (nAlgorithmSid != 0) + mPresentationFS->singleElementNS(XML_p, XML_modifyVerifier, + XML_cryptProviderType, "rsaAES", + XML_cryptAlgorithmClass, "hash", + XML_cryptAlgorithmType, "typeAny", + XML_cryptAlgorithmSid, OString::number(nAlgorithmSid).getStr(), + XML_spinCount, OString::number(nCount).getStr(), + XML_saltData, sSalt.toUtf8().getStr(), + XML_hashData, sHash.toUtf8().getStr()); + } + } +} + +void PowerPointExport::ImplWriteSlide(sal_uInt32 nPageNum, sal_uInt32 nMasterNum, sal_uInt16 /* nMode */, + bool bHasBackground, Reference< XPropertySet > const& aXBackgroundPropSet) +{ + SAL_INFO("sd.eppt", "write slide: " << nPageNum << "\n----------------"); + + // slides list + if (nPageNum == 0) + mPresentationFS->startElementNS(XML_p, XML_sldIdLst); + + // add explicit relation of presentation to this slide + OUString sRelId = addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDE), + OUStringConcatenation("slides/slide" + OUString::number(nPageNum + 1) +".xml")); + + mPresentationFS->singleElementNS(XML_p, XML_sldId, + XML_id, OString::number(GetNewSlideId()), + FSNS(XML_r, XML_id), sRelId); + + maRelId.push_back(sRelId); + + if (nPageNum == mnPages - 1) + mPresentationFS->endElementNS(XML_p, XML_sldIdLst); + + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/slides/slide" + OUString::number(nPageNum + 1) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.slide+xml"); + + if (mpSlidesFSArray.size() < mnPages) + mpSlidesFSArray.resize(mnPages); + mpSlidesFSArray[ nPageNum ] = pFS; + + const char* pShow = nullptr; + const char* pShowMasterShape = nullptr; + + if (ImplGetPropertyValue(mXPagePropSet, "Visible")) + { + bool bShow(false); + if ((mAny >>= bShow) && !bShow) + pShow = "0"; + } + + if (ImplGetPropertyValue(mXPagePropSet, "IsBackgroundObjectsVisible")) + { + bool bShowMasterShape(false); + if ((mAny >>= bShowMasterShape) && !bShowMasterShape) + pShowMasterShape = "0"; + } + + pFS->startElementNS(XML_p, XML_sld, PNMSS, XML_show, pShow, XML_showMasterSp, pShowMasterShape); + + pFS->startElementNS(XML_p, XML_cSld); + + // background + if (bHasBackground) + { + ImplWriteBackground(pFS, aXBackgroundPropSet); + } + + WriteShapeTree(pFS, NORMAL, false); + + pFS->endElementNS(XML_p, XML_cSld); + + WriteTransition(pFS); + WriteAnimations(pFS, mXDrawPage, *this); + + pFS->endElementNS(XML_p, XML_sld); + + // add implicit relation to slide layout + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDELAYOUT), + OUStringConcatenation("../slideLayouts/slideLayout" + + OUString::number(GetLayoutFileId(GetPPTXLayoutId(GetLayoutOffset(mXPagePropSet)), nMasterNum)) + + ".xml")); + + if (WriteComments(nPageNum)) + // add implicit relation to slide comments + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::COMMENTS), + OUStringConcatenation("../comments/comment" + OUString::number(nPageNum + 1) + ".xml")); + + SAL_INFO("sd.eppt", "----------------"); +} + +void PowerPointExport::ImplWriteNotes(sal_uInt32 nPageNum) +{ + if (!mbCreateNotes || !ContainsOtherShapeThanPlaceholders()) + return; + + SAL_INFO("sd.eppt", "write Notes " << nPageNum << "\n----------------"); + + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/notesSlides/notesSlide" + + OUString::number(nPageNum + 1) + + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"); + + pFS->startElementNS(XML_p, XML_notes, PNMSS); + + pFS->startElementNS(XML_p, XML_cSld); + + WriteShapeTree(pFS, NOTICE, false); + + pFS->endElementNS(XML_p, XML_cSld); + + pFS->endElementNS(XML_p, XML_notes); + + // add implicit relation to slide + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDE), + OUStringConcatenation("../slides/slide" + OUString::number(nPageNum + 1) + ".xml")); + + // add slide implicit relation to notes + if (nPageNum < mpSlidesFSArray.size()) + addRelation(mpSlidesFSArray[ nPageNum ]->getOutputStream(), + oox::getRelationship(Relationship::NOTESSLIDE), + OUStringConcatenation("../notesSlides/notesSlide" + OUString::number(nPageNum + 1) + ".xml")); + + // add implicit relation to notes master + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::NOTESMASTER), + u"../notesMasters/notesMaster1.xml"); + + SAL_INFO("sd.eppt", "-----------------"); +} + +void PowerPointExport::AddLayoutIdAndRelation(const FSHelperPtr& pFS, sal_Int32 nLayoutFileId) +{ + // add implicit relation of slide master to slide layout + OUString sRelId = addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDELAYOUT), + OUStringConcatenation("../slideLayouts/slideLayout" + OUString::number(nLayoutFileId) + ".xml")); + + pFS->singleElementNS(XML_p, XML_sldLayoutId, + XML_id, OString::number(GetNewSlideMasterId()), + FSNS(XML_r, XML_id), sRelId); +} + +void PowerPointExport::ImplWriteSlideMaster(sal_uInt32 nPageNum, Reference< XPropertySet > const& aXBackgroundPropSet) +{ + SAL_INFO("sd.eppt", "write master slide: " << nPageNum << "\n--------------"); + + // slides list + if (nPageNum == 0) + mPresentationFS->startElementNS(XML_p, XML_sldMasterIdLst); + + OUString sRelId = addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDEMASTER), + OUStringConcatenation("slideMasters/slideMaster" + OUString::number(nPageNum + 1) + ".xml")); + + mPresentationFS->singleElementNS(XML_p, XML_sldMasterId, + XML_id, OString::number(GetNewSlideMasterId()), + FSNS(XML_r, XML_id), sRelId); + + if (nPageNum == mnMasterPages - 1) + mPresentationFS->endElementNS(XML_p, XML_sldMasterIdLst); + + FSHelperPtr pFS = + openFragmentStreamWithSerializer("ppt/slideMasters/slideMaster" + + OUString::number(nPageNum + 1) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"); + + SdrPage* pMasterPage = SdPage::getImplementation(mXDrawPage); + svx::Theme* pTheme = nullptr; + if (pMasterPage) + { + pTheme = pMasterPage->getSdrPageProperties().GetTheme(); + } + + // write theme per master + WriteTheme(nPageNum, pTheme); + + // add implicit relation to the presentation theme + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + OUStringConcatenation("../theme/theme" + OUString::number(nPageNum + 1) + ".xml")); + + pFS->startElementNS(XML_p, XML_sldMaster, PNMSS); + + pFS->startElementNS(XML_p, XML_cSld); + + if (aXBackgroundPropSet) + ImplWriteBackground(pFS, aXBackgroundPropSet); + WriteShapeTree(pFS, MASTER, true); + + pFS->endElementNS(XML_p, XML_cSld); + + // color map - now it uses colors from hardcoded theme, once we eventually generate theme, this might need update + pFS->singleElementNS(XML_p, XML_clrMap, + XML_bg1, "lt1", + XML_bg2, "lt2", + XML_tx1, "dk1", + XML_tx2, "dk2", + XML_accent1, "accent1", + XML_accent2, "accent2", + XML_accent3, "accent3", + XML_accent4, "accent4", + XML_accent5, "accent5", + XML_accent6, "accent6", + XML_hlink, "hlink", + XML_folHlink, "folHlink"); + + // use master's id type as they have same range, mso does that as well + pFS->startElementNS(XML_p, XML_sldLayoutIdLst); + + for (int i = 0; i < LAYOUT_SIZE; i++) + { + sal_Int32 nLayoutFileId = GetLayoutFileId(i, nPageNum); + if (nLayoutFileId > 0) + { + AddLayoutIdAndRelation(pFS, nLayoutFileId); + } + else + { + ImplWritePPTXLayout(i, nPageNum); + AddLayoutIdAndRelation(pFS, GetLayoutFileId(i, nPageNum)); + } + } + + pFS->endElementNS(XML_p, XML_sldLayoutIdLst); + + pFS->endElementNS(XML_p, XML_sldMaster); + + SAL_INFO("sd.eppt", "----------------"); +} + +sal_Int32 PowerPointExport::GetLayoutFileId(sal_Int32 nOffset, sal_uInt32 nMasterNum) +{ + SAL_INFO("sd.eppt", "GetLayoutFileId offset: " << nOffset << " master: " << nMasterNum); + if (mLayoutInfo[ nOffset ].mnFileIdArray.size() <= nMasterNum) + return 0; + + return mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ]; +} + +void PowerPointExport::ImplWritePPTXLayout(sal_Int32 nOffset, sal_uInt32 nMasterNum) +{ + SAL_INFO("sd.eppt", "write layout: " << nOffset); + + Reference< drawing::XDrawPagesSupplier > xDPS(getModel(), uno::UNO_QUERY); + Reference< drawing::XDrawPages > xDrawPages = xDPS->getDrawPages(); + Reference< drawing::XDrawPage > xSlide = xDrawPages->insertNewByIndex(xDrawPages->getCount()); + +#ifdef DEBUG + if (xSlide.is()) + printf("new page created\n"); +#endif + + Reference< beans::XPropertySet > xPropSet(xSlide, uno::UNO_QUERY); + xPropSet->setPropertyValue("Layout", Any(short(aLayoutInfo[ nOffset ].nType))); +#if OSL_DEBUG_LEVEL > 1 + dump_pset(xPropSet); +#endif + mXPagePropSet.set(xSlide, UNO_QUERY); + mXShapes = xSlide; + + if (mLayoutInfo[ nOffset ].mnFileIdArray.size() < mnMasterPages) + { + mLayoutInfo[ nOffset ].mnFileIdArray.resize(mnMasterPages); + } + + if (mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ] != 0) + return; + + FSHelperPtr pFS + = openFragmentStreamWithSerializer("ppt/slideLayouts/slideLayout" + + OUString::number(mnLayoutFileIdMax) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"); + + // add implicit relation of slide layout to slide master + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDEMASTER), + OUStringConcatenation("../slideMasters/slideMaster" + OUString::number(nMasterNum + 1) + ".xml")); + + pFS->startElementNS(XML_p, XML_sldLayout, + PNMSS, + XML_type, aLayoutInfo[ nOffset ].sType, + XML_preserve, "1"); + + pFS->startElementNS(XML_p, XML_cSld, + XML_name, aLayoutInfo[ nOffset ].sName); + //pFS->write( MINIMAL_SPTREE ); // TODO: write actual shape tree + WriteShapeTree(pFS, LAYOUT, true); + + pFS->endElementNS(XML_p, XML_cSld); + + pFS->endElementNS(XML_p, XML_sldLayout); + + mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ] = mnLayoutFileIdMax; + + mnLayoutFileIdMax ++; + + xDrawPages->remove(xSlide); +} + +void PowerPointExport::WriteShapeTree(const FSHelperPtr& pFS, PageType ePageType, bool bMaster) +{ + PowerPointShapeExport aDML(pFS, &maShapeMap, this); + aDML.SetMaster(bMaster); + aDML.SetPageType(ePageType); + aDML.SetBackgroundDark(mbIsBackgroundDark); + + pFS->startElementNS(XML_p, XML_spTree); + pFS->write(MAIN_GROUP); + + ResetGroupTable(mXShapes->getCount()); + + while (GetNextGroupEntry()) + { + + sal_uInt32 nGroups = GetGroupsClosed(); + for (sal_uInt32 i = 0; i < nGroups; i++) + { + SAL_INFO("sd.eppt", "leave group"); + } + + if (GetShapeByIndex(GetCurrentGroupIndex(), true)) + { + SAL_INFO("sd.eppt", "mType: " << mType); + const SdrObjGroup* pDiagramCandidate(dynamic_cast<const SdrObjGroup*>(SdrObject::getSdrObjectFromXShape(mXShape))); + const bool bIsDiagram(nullptr != pDiagramCandidate && pDiagramCandidate->isDiagram()); + + if (bIsDiagram) + WriteDiagram(pFS, aDML, mXShape, mnDiagramId++); + else + aDML.WriteShape(mXShape); + } + } + + if ( ePageType == NORMAL || ePageType == LAYOUT ) + WritePlaceholderReferenceShapes(aDML, ePageType); + pFS->endElementNS(XML_p, XML_spTree); +} + +ShapeExport& PowerPointShapeExport::WritePageShape(const Reference< XShape >& xShape, PageType ePageType, bool bPresObj) +{ + if ((ePageType == NOTICE && bPresObj) || ePageType == LAYOUT || ePageType == MASTER) + return WritePlaceholderShape(xShape, SlideImage); + + return WriteTextShape(xShape); +} + +bool PowerPointShapeExport::WritePlaceholder(const Reference< XShape >& xShape, PlaceholderType ePlaceholder, bool bMaster) +{ + SAL_INFO("sd.eppt", "WritePlaceholder " << bMaster << " " << ShapeExport::NonEmptyText(xShape)); + if (!xShape) + return false; + try + { + Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY); + if (xShapeProps->getPropertyValue("IsPresentationObject").get<bool>()) + { + WritePlaceholderShape(xShape, ePlaceholder); + + return true; + } + } + catch (Exception&) + { + return false; + } + return false; +} + +ShapeExport& PowerPointShapeExport::WritePlaceholderShape(const Reference< XShape >& xShape, PlaceholderType ePlaceholder) +{ + Reference<XPropertySet> xProps(xShape, UNO_QUERY); + bool bUseBackground(false); + if (xProps.is() && xProps->getPropertySetInfo()->hasPropertyByName("FillUseSlideBackground")) + xProps->getPropertyValue("FillUseSlideBackground") >>= bUseBackground; + + if (bUseBackground) + mpFS->startElementNS(XML_p, XML_sp, XML_useBgFill, "1"); + else + mpFS->startElementNS(XML_p, XML_sp); + + // non visual shape properties + mpFS->startElementNS(XML_p, XML_nvSpPr); + const OString aPlaceholderID("PlaceHolder " + OString::number(mnShapeIdMax++)); + WriteNonVisualDrawingProperties(xShape, aPlaceholderID.getStr()); + mpFS->startElementNS(XML_p, XML_cNvSpPr); + mpFS->singleElementNS(XML_a, XML_spLocks, XML_noGrp, "1"); + mpFS->endElementNS(XML_p, XML_cNvSpPr); + mpFS->startElementNS(XML_p, XML_nvPr); + + bool bUsePlaceholderIndex + = ePlaceholder == Footer || ePlaceholder == DateAndTime || ePlaceholder == SlideNumber; + const char* pType = getPlaceholderTypeName(ePlaceholder); + + SAL_INFO("sd.eppt", "write placeholder " << pType); + if (bUsePlaceholderIndex) + { + mpFS->singleElementNS( + XML_p, XML_ph, XML_type, pType, XML_idx, + OString::number( + static_cast<PowerPointExport*>(GetFB())->CreateNewPlaceholderIndex(xShape))); + } + else + { + if ((mePageType == PageType::LAYOUT || mePageType == PageType::NORMAL) + && ePlaceholder == Outliner) + mpFS->singleElementNS(XML_p, XML_ph); + else + mpFS->singleElementNS(XML_p, XML_ph, XML_type, pType); + } + mpFS->endElementNS(XML_p, XML_nvPr); + mpFS->endElementNS(XML_p, XML_nvSpPr); + + // visual shape properties + mpFS->startElementNS(XML_p, XML_spPr); + WriteShapeTransformation(xShape, XML_a); + WritePresetShape("rect"); + if (xProps.is()) + { + WriteBlipFill(xProps, "Graphic"); + // Do not forget to export the visible properties. + WriteFill( xProps ); + WriteOutline( xProps ); + WriteShapeEffects( xProps ); + + bool bHas3DEffectinShape = false; + uno::Sequence<beans::PropertyValue> grabBag; + if (xProps->getPropertySetInfo()->hasPropertyByName("InteropGrabBag")) + xProps->getPropertyValue("InteropGrabBag") >>= grabBag; + + for (auto const& it : std::as_const(grabBag)) + if (it.Name == "3DEffectProperties") + bHas3DEffectinShape = true; + + if( bHas3DEffectinShape) + Write3DEffects( xProps, /*bIsText=*/false ); + } + mpFS->endElementNS(XML_p, XML_spPr); + + WriteTextBox(xShape, XML_p, /*bWritePropertiesAsLstStyles=*/bUsePlaceholderIndex); + + mpFS->endElementNS(XML_p, XML_sp); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WritePlaceholderReferenceShape( + PlaceholderType ePlaceholder, sal_Int32 nReferencedPlaceholderIdx, PageType ePageType, + const Reference<XPropertySet>& rXPagePropSet) +{ + mpFS->startElementNS(XML_p, XML_sp); + + // non visual shape properties + mpFS->startElementNS(XML_p, XML_nvSpPr); + const OString aPlaceholderID("PlaceHolder " + OString::number(mnShapeIdMax++)); + GetFS()->singleElementNS(XML_p, XML_cNvPr, XML_id, OString::number(mnShapeIdMax), XML_name, + aPlaceholderID.getStr()); + + mpFS->startElementNS(XML_p, XML_cNvSpPr); + mpFS->singleElementNS(XML_a, XML_spLocks, XML_noGrp, "1"); + mpFS->endElementNS(XML_p, XML_cNvSpPr); + mpFS->startElementNS(XML_p, XML_nvPr); + + const char* pType = getPlaceholderTypeName(ePlaceholder); + mpFS->singleElementNS(XML_p, XML_ph, XML_type, pType, XML_idx, + OString::number(nReferencedPlaceholderIdx)); + mpFS->endElementNS(XML_p, XML_nvPr); + mpFS->endElementNS(XML_p, XML_nvSpPr); + + // visual shape properties + mpFS->startElementNS(XML_p, XML_spPr); + mpFS->endElementNS(XML_p, XML_spPr); + + WritePlaceholderReferenceTextBody(ePlaceholder, ePageType, rXPagePropSet); + + mpFS->endElementNS(XML_p, XML_sp); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WritePlaceholderReferenceTextBody( + PlaceholderType ePlaceholder, PageType ePageType, const Reference<XPropertySet> xPagePropSet) +{ + mpFS->startElementNS(XML_p, XML_txBody); + mpFS->singleElementNS(XML_a, XML_bodyPr); + mpFS->startElementNS(XML_a, XML_p); + + switch (ePlaceholder) + { + case Header: + break; + case Footer: + { + OUString aFooterText; + if (ePageType == LAYOUT) + { + aFooterText = "Footer"; + } + else + { + xPagePropSet->getPropertyValue("FooterText") >>= aFooterText; + } + mpFS->startElementNS(XML_a, XML_r); + mpFS->startElementNS(XML_a, XML_t); + mpFS->writeEscaped(aFooterText); + mpFS->endElementNS(XML_a, XML_t); + mpFS->endElementNS(XML_a, XML_r); + break; + } + case SlideNumber: + { + OUString aSlideNum; + sal_Int32 nSlideNum = 0; + if (ePageType == LAYOUT) + { + aSlideNum = "<#>"; + } + else + { + xPagePropSet->getPropertyValue("Number") >>= nSlideNum; + aSlideNum = OUString::number(nSlideNum); + } + OString aUUID(comphelper::xml::generateGUIDString()); + mpFS->startElementNS(XML_a, XML_fld, XML_id, aUUID.getStr(), XML_type, "slidenum"); + mpFS->startElementNS(XML_a, XML_t); + mpFS->writeEscaped(aSlideNum); + mpFS->endElementNS(XML_a, XML_t); + mpFS->endElementNS(XML_a, XML_fld); + break; + } + case DateAndTime: + { + OUString aDateTimeType = "datetime1"; + bool bIsDateTimeFixed = false; + xPagePropSet->getPropertyValue("IsDateTimeFixed") >>= bIsDateTimeFixed; + + OUString aDateTimeText = "Date"; + const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag(); + + if(ePageType != LAYOUT && !bIsDateTimeFixed) + { + sal_Int32 nDateTimeFormat = 0; + xPagePropSet->getPropertyValue("DateTimeFormat") >>= nDateTimeFormat; + + // 4 LSBs represent the date + SvxDateFormat eDate = static_cast<SvxDateFormat>(nDateTimeFormat & 0x0f); + // the 4 bits after the date bits represent the time + SvxTimeFormat eTime = static_cast<SvxTimeFormat>(nDateTimeFormat >> 4); + aDateTimeType = GetDatetimeTypeFromDateTime(eDate, eTime); + + if (aDateTimeType == "datetime") + aDateTimeType = "datetime1"; + + ::DateTime aDateTime( ::DateTime::SYSTEM ); + + aDateTimeText = SvxDateTimeField::GetFormatted( + aDateTime, aDateTime, eDate, + eTime, *(SD_MOD()->GetNumberFormatter()), + rLanguageTag.getLanguageType()); + } + + if(!bIsDateTimeFixed) + { + OString aUUID(comphelper::xml::generateGUIDString()); + mpFS->startElementNS(XML_a, XML_fld, XML_id, aUUID.getStr(), XML_type, aDateTimeType); + } + else + { + xPagePropSet->getPropertyValue("DateTimeText") >>= aDateTimeText; + mpFS->startElementNS(XML_a, XML_r); + } + + mpFS->startElementNS(XML_a, XML_rPr, XML_lang, rLanguageTag.getBcp47MS()); + mpFS->endElementNS(XML_a, XML_rPr); + + mpFS->startElementNS(XML_a, XML_t); + mpFS->writeEscaped(aDateTimeText); + mpFS->endElementNS(XML_a, XML_t); + + mpFS->endElementNS(XML_a, bIsDateTimeFixed ? XML_r : XML_fld); + break; + } + default: + SAL_INFO("sd.eppt", "warning: no defined textbody for referenced placeholder type: " + << ePlaceholder); + } + mpFS->endElementNS(XML_a, XML_p); + mpFS->endElementNS(XML_p, XML_txBody); + + return *this; +} + +#define SYS_COLOR_SCHEMES " <a:dk1>\ + <a:sysClr val=\"windowText\" lastClr=\"000000\"/>\ + </a:dk1>\ + <a:lt1>\ + <a:sysClr val=\"window\" lastClr=\"FFFFFF\"/>\ + </a:lt1>" + +#define MINIMAL_THEME " <a:fontScheme name=\"Office\">\ + <a:majorFont>\ + <a:latin typeface=\"Arial\"/>\ + <a:ea typeface=\"DejaVu Sans\"/>\ + <a:cs typeface=\"DejaVu Sans\"/>\ + </a:majorFont>\ + <a:minorFont>\ + <a:latin typeface=\"Arial\"/>\ + <a:ea typeface=\"DejaVu Sans\"/>\ + <a:cs typeface=\"DejaVu Sans\"/>\ + </a:minorFont>\ + </a:fontScheme>\ + <a:fmtScheme name=\"Office\">\ + <a:fillStyleLst>\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\"/>\ + </a:solidFill>\ + <a:gradFill rotWithShape=\"1\">\ + <a:gsLst>\ + <a:gs pos=\"0\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"50000\"/>\ + <a:satMod val=\"300000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"35000\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"37000\"/>\ + <a:satMod val=\"300000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"100000\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"15000\"/>\ + <a:satMod val=\"350000\"/>\ + </a:schemeClr>\ + </a:gs>\ + </a:gsLst>\ + <a:lin ang=\"16200000\" scaled=\"1\"/>\ + </a:gradFill>\ + <a:gradFill rotWithShape=\"1\">\ + <a:gsLst>\ + <a:gs pos=\"0\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"51000\"/>\ + <a:satMod val=\"130000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"80000\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"93000\"/>\ + <a:satMod val=\"130000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"100000\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"94000\"/>\ + <a:satMod val=\"135000\"/>\ + </a:schemeClr>\ + </a:gs>\ + </a:gsLst>\ + <a:lin ang=\"16200000\" scaled=\"0\"/>\ + </a:gradFill>\ + </a:fillStyleLst>\ + <a:lnStyleLst>\ + <a:ln w=\"6350\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"95000\"/>\ + <a:satMod val=\"105000\"/>\ + </a:schemeClr>\ + </a:solidFill>\ + <a:prstDash val=\"solid\"/>\ + <a:miter/>\ + </a:ln>\ + <a:ln w=\"12700\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\"/>\ + </a:solidFill>\ + <a:prstDash val=\"solid\"/>\ + <a:miter/>\ + </a:ln>\ + <a:ln w=\"19050\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\"/>\ + </a:solidFill>\ + <a:prstDash val=\"solid\"/>\ + <a:miter/>\ + </a:ln>\ + </a:lnStyleLst>\ + <a:effectStyleLst>\ + <a:effectStyle>\ + <a:effectLst/>\ + </a:effectStyle>\ + <a:effectStyle>\ + <a:effectLst/>\ + </a:effectStyle>\ + <a:effectStyle>\ + <a:effectLst>\ + <a:outerShdw blurRad=\"40000\" dist=\"23000\" dir=\"5400000\" rotWithShape=\"0\">\ + <a:srgbClr val=\"000000\">\ + <a:alpha val=\"35000\"/>\ + </a:srgbClr>\ + </a:outerShdw>\ + </a:effectLst>\ + <a:scene3d>\ + <a:camera prst=\"orthographicFront\">\ + <a:rot lat=\"0\" lon=\"0\" rev=\"0\"/>\ + </a:camera>\ + <a:lightRig rig=\"threePt\" dir=\"t\">\ + <a:rot lat=\"0\" lon=\"0\" rev=\"1200000\"/>\ + </a:lightRig>\ + </a:scene3d>\ + <a:sp3d>\ + <a:bevelT w=\"63500\" h=\"25400\"/>\ + </a:sp3d>\ + </a:effectStyle>\ + </a:effectStyleLst>\ + <a:bgFillStyleLst>\ + <a:solidFill>\ + <a:schemeClr val=\"phClr\"/>\ + </a:solidFill>\ + <a:gradFill rotWithShape=\"1\">\ + <a:gsLst>\ + <a:gs pos=\"0\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"40000\"/>\ + <a:satMod val=\"350000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"40000\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"45000\"/>\ + <a:shade val=\"99000\"/>\ + <a:satMod val=\"350000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"100000\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"20000\"/>\ + <a:satMod val=\"255000\"/>\ + </a:schemeClr>\ + </a:gs>\ + </a:gsLst>\ + <a:path path=\"circle\">\ + <a:fillToRect l=\"50000\" t=\"-80000\" r=\"50000\" b=\"180000\"/>\ + </a:path>\ + </a:gradFill>\ + <a:gradFill rotWithShape=\"1\">\ + <a:gsLst>\ + <a:gs pos=\"0\">\ + <a:schemeClr val=\"phClr\">\ + <a:tint val=\"80000\"/>\ + <a:satMod val=\"300000\"/>\ + </a:schemeClr>\ + </a:gs>\ + <a:gs pos=\"100000\">\ + <a:schemeClr val=\"phClr\">\ + <a:shade val=\"30000\"/>\ + <a:satMod val=\"200000\"/>\ + </a:schemeClr>\ + </a:gs>\ + </a:gsLst>\ + <a:path path=\"circle\">\ + <a:fillToRect l=\"50000\" t=\"50000\" r=\"50000\" b=\"50000\"/>\ + </a:path>\ + </a:gradFill>\ + </a:bgFillStyleLst>\ + </a:fmtScheme>" + +void PowerPointExport::WriteDefaultColorSchemes(const FSHelperPtr& pFS) +{ + for (int nId = PredefinedClrSchemeId::dk2; nId != PredefinedClrSchemeId::Count; nId++) + { + OUString sName = PredefinedClrNames[static_cast<PredefinedClrSchemeId>(nId)]; + sal_Int32 nColor = 0; + + switch (nId) + { + case PredefinedClrSchemeId::dk2: + nColor = 0x1F497D; + break; + case PredefinedClrSchemeId::lt2: + nColor = 0xEEECE1; + break; + case PredefinedClrSchemeId::accent1: + nColor = 0x4F81BD; + break; + case PredefinedClrSchemeId::accent2: + nColor = 0xC0504D; + break; + case PredefinedClrSchemeId::accent3: + nColor = 0x9BBB59; + break; + case PredefinedClrSchemeId::accent4: + nColor = 0x8064A2; + break; + case PredefinedClrSchemeId::accent5: + nColor = 0x4BACC6; + break; + case PredefinedClrSchemeId::accent6: + nColor = 0xF79646; + break; + case PredefinedClrSchemeId::hlink: + nColor = 0x0000FF; + break; + case PredefinedClrSchemeId::folHlink: + nColor = 0x800080; + break; + } + + OUString sOpenColorScheme = "<a:" + sName + ">"; + pFS->write(sOpenColorScheme); + + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor)); + + OUString sCloseColorScheme = "</a:" + sName + ">"; + pFS->write(sCloseColorScheme); + } +} + +bool PowerPointExport::WriteColorSets(const FSHelperPtr& pFS, svx::Theme* pTheme) +{ + static std::map<PredefinedClrSchemeId, sal_Int32> aPredefinedClrTokens = + { + { dk1, XML_dk1 }, + { lt1, XML_lt1 }, + { dk2, XML_dk2 }, + { lt2, XML_lt2 }, + { accent1, XML_accent1 }, + { accent2, XML_accent2 }, + { accent3, XML_accent3 }, + { accent4, XML_accent4 }, + { accent5, XML_accent5 }, + { accent6, XML_accent6 }, + { hlink, XML_hlink }, + { folHlink, XML_folHlink } + }; + + if (!pTheme) + { + return false; + } + + svx::ColorSet* pColorSet = pTheme->GetColorSet(); + if (!pColorSet) + { + return false; + } + + for (int nId = PredefinedClrSchemeId::dk1; nId < PredefinedClrSchemeId::Count; nId++) + { + sal_Int32 nToken = aPredefinedClrTokens[static_cast<PredefinedClrSchemeId>(nId)]; + pFS->startElementNS(XML_a, nToken); + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(static_cast<sal_Int32>(pColorSet->getColor(nId)))); + pFS->endElementNS(XML_a, nToken); + } + + return true; +} + +bool PowerPointExport::WriteColorSchemes(const FSHelperPtr& pFS, const OUString& rThemePath) +{ + try + { + uno::Reference<beans::XPropertySet> xDocProps(getModel(), uno::UNO_QUERY); + if (xDocProps.is()) + { + uno::Reference<beans::XPropertySetInfo> xPropsInfo = xDocProps->getPropertySetInfo(); + + static const OUStringLiteral aGrabBagPropName = u"InteropGrabBag"; + if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(aGrabBagPropName)) + { + comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(aGrabBagPropName)); + uno::Sequence<beans::PropertyValue> aCurrentTheme; + + aGrabBag.getValue(rThemePath) >>= aCurrentTheme; + + if (!aCurrentTheme.hasElements()) + return false; + + // Order is important + for (int nId = PredefinedClrSchemeId::dk2; nId != PredefinedClrSchemeId::Count; nId++) + { + OUString sName = PredefinedClrNames[static_cast<PredefinedClrSchemeId>(nId)]; + sal_Int32 nColor = 0; + + for (auto aIt = std::cbegin(aCurrentTheme); aIt != std::cend(aCurrentTheme); aIt++) + { + if (aIt->Name == sName) + { + aIt->Value >>= nColor; + break; + } + } + + OUString sOpenColorScheme ="<a:" + sName + ">"; + pFS->write(sOpenColorScheme); + + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor)); + + OUString sCloseColorScheme = "</a:" + sName + ">"; + pFS->write(sCloseColorScheme); + } + + // TODO: write complete color schemes & only if successful, protection against partial export + return true; + } + } + } + catch (const uno::Exception&) + { + SAL_WARN("writerfilter", "Failed to save documents grab bag"); + } + + return false; +} + +void PowerPointExport::WriteTheme(sal_Int32 nThemeNum, svx::Theme* pTheme) +{ + OUString sThemePath = "ppt/theme/theme" + OUString::number(nThemeNum + 1) + ".xml"; + + FSHelperPtr pFS = openFragmentStreamWithSerializer(sThemePath, + "application/vnd.openxmlformats-officedocument.theme+xml"); + + OUString aThemeName("Office Theme"); + if (pTheme) + { + aThemeName = pTheme->GetName(); + } + pFS->startElementNS(XML_a, XML_theme, + FSNS(XML_xmlns, XML_a), this->getNamespaceURL(OOX_NS(dml)), + XML_name, aThemeName); + + pFS->startElementNS(XML_a, XML_themeElements); + OUString aColorSchemeName("Office"); + if (pTheme) + { + svx::ColorSet* pColorSet = pTheme->GetColorSet(); + if (pColorSet) + { + aColorSchemeName = pColorSet->getName(); + } + } + pFS->startElementNS(XML_a, XML_clrScheme, XML_name, aColorSchemeName); + + if (!WriteColorSets(pFS, pTheme)) + { + pFS->write(SYS_COLOR_SCHEMES); + if (!WriteColorSchemes(pFS, sThemePath)) + { + // if style is not defined, try to use first one + if (!WriteColorSchemes(pFS, "ppt/theme/theme1.xml")) + { + // color schemes are required - use default values + WriteDefaultColorSchemes(pFS); + } + } + } + + pFS->endElementNS(XML_a, XML_clrScheme); + + // export remaining part + pFS->write(MINIMAL_THEME); + + pFS->endElementNS(XML_a, XML_themeElements); + pFS->endElementNS(XML_a, XML_theme); +} + +bool PowerPointExport::ImplCreateDocument() +{ + mbCreateNotes = false; + + for (sal_uInt32 i = 0; i < mnPages; i++) + { + if (!GetPageByIndex(i, NOTICE)) + return false; + + if (ContainsOtherShapeThanPlaceholders()) + { + mbCreateNotes = true; + break; + } + } + + return true; +} + +void PowerPointExport::WriteNotesMaster() +{ + SAL_INFO("sd.eppt", "write Notes master\n---------------"); + + mPresentationFS->startElementNS(XML_p, XML_notesMasterIdLst); + + OUString sRelId = addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::NOTESMASTER), + u"notesMasters/notesMaster1.xml"); + + mPresentationFS->singleElementNS(XML_p, XML_notesMasterId, + FSNS(XML_r, XML_id), sRelId); + + mPresentationFS->endElementNS(XML_p, XML_notesMasterIdLst); + + FSHelperPtr pFS = + openFragmentStreamWithSerializer("ppt/notesMasters/notesMaster1.xml", + "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml"); + // write theme per master + WriteTheme(mnMasterPages, nullptr); + + // add implicit relation to the presentation theme + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + OUStringConcatenation("../theme/theme" + OUString::number(mnMasterPages + 1) + ".xml")); + + pFS->startElementNS(XML_p, XML_notesMaster, PNMSS); + + pFS->startElementNS(XML_p, XML_cSld); + + Reference< XPropertySet > aXBackgroundPropSet; + if (ImplGetPropertyValue(mXPagePropSet, "Background") && + (mAny >>= aXBackgroundPropSet)) + ImplWriteBackground(pFS, aXBackgroundPropSet); + + WriteShapeTree(pFS, NOTICE, true); + + pFS->endElementNS(XML_p, XML_cSld); + + // color map - now it uses colors from hardcoded theme, once we eventually generate theme, this might need update + pFS->singleElementNS(XML_p, XML_clrMap, + XML_bg1, "lt1", + XML_bg2, "lt2", + XML_tx1, "dk1", + XML_tx2, "dk2", + XML_accent1, "accent1", + XML_accent2, "accent2", + XML_accent3, "accent3", + XML_accent4, "accent4", + XML_accent5, "accent5", + XML_accent6, "accent6", + XML_hlink, "hlink", + XML_folHlink, "folHlink"); + + pFS->endElementNS(XML_p, XML_notesMaster); + + SAL_INFO("sd.eppt", "----------------"); +} + +void PowerPointExport::embedEffectAudio(const FSHelperPtr& pFS, const OUString& sUrl, OUString& sRelId, OUString& sName) +{ + comphelper::LifecycleProxy aProxy; + + if (!sUrl.endsWithIgnoreAsciiCase(".wav")) + return; + + uno::Reference<io::XInputStream> xAudioStream; + try + { + if (sUrl.startsWith("vnd.sun.star.Package:")) + { + uno::Reference<document::XStorageBasedDocument> xStorageBasedDocument(getModel(), uno::UNO_QUERY); + if (!xStorageBasedDocument.is()) + return; + + uno::Reference<embed::XStorage> xDocumentStorage = xStorageBasedDocument->getDocumentStorage(); + if (!xDocumentStorage.is()) + return; + + uno::Reference<io::XStream> xStream = comphelper::OStorageHelper::GetStreamAtPackageURL(xDocumentStorage, sUrl, + css::embed::ElementModes::READ, aProxy); + + if (xStream.is()) + xAudioStream = xStream->getInputStream(); + } + else + xAudioStream = comphelper::OStorageHelper::GetInputStreamFromURL(sUrl, getComponentContext()); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd", "PowerPointExport::embedEffectAudio"); + } + + if (!xAudioStream.is()) + return; + + int nLastSlash = sUrl.lastIndexOf('/'); + sName = sUrl.copy(nLastSlash >= 0 ? nLastSlash + 1 : 0); + + OUString sPath = "../media/" + sName; + sRelId = addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::AUDIO), sPath); + + uno::Reference<io::XOutputStream> xOutputStream = openFragmentStream(sPath.replaceAt(0, 2, u"/ppt"), + "audio/x-wav"); + + comphelper::OStorageHelper::CopyInputToOutput(xAudioStream, xOutputStream); +} + +sal_Int32 PowerPointExport::GetShapeID(const Reference<XShape>& rXShape) +{ + return ShapeExport::GetShapeID(rXShape, &maShapeMap); +} + +sal_Int32 PowerPointExport::GetNextAnimationNodeID() +{ + return mnAnimationNodeIdMax++; +} + +bool PowerPointExport::ImplCreateMainNotes() +{ + if (mbCreateNotes) + WriteNotesMaster(); + + return true; +} + +OUString PowerPointExport::getImplementationName() +{ + return "com.sun.star.comp.Impress.oox.PowerPointExport"; +} + +void PowerPointExport::WriteDiagram(const FSHelperPtr& pFS, PowerPointShapeExport& rDML, const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId) +{ + SAL_INFO("sd.eppt", "writing Diagram " + OUString::number(nDiagramId)); + pFS->startElementNS(XML_p, XML_graphicFrame); + rDML.WriteDiagram(rXShape, nDiagramId); + pFS->endElementNS(XML_p, XML_graphicFrame); +} + +void PowerPointExport::WritePlaceholderReferenceShapes(PowerPointShapeExport& rDML, PageType ePageType) +{ + bool bCheckProps = ePageType == NORMAL; + Reference<XShape> xShape; + Any aAny; + OUString aText; + if (ePageType == LAYOUT + || (bCheckProps && PropValue::GetPropertyValue(aAny, mXPagePropSet, "IsFooterVisible", true) + && aAny == true && GetPropertyValue(aAny, mXPagePropSet, "FooterText", true) + && (aAny >>= aText) && !aText.isEmpty())) + { + if ((xShape = GetReferencedPlaceholderXShape(Footer, ePageType))) + rDML.WritePlaceholderReferenceShape(Footer, + maPlaceholderShapeToIndexMap.find(xShape)->second, + ePageType, mXPagePropSet); + } + + if (ePageType == LAYOUT + || (bCheckProps + && PropValue::GetPropertyValue(aAny, mXPagePropSet, "IsPageNumberVisible", true) + && aAny == true)) + { + if ((xShape = GetReferencedPlaceholderXShape(SlideNumber, ePageType))) + rDML.WritePlaceholderReferenceShape(SlideNumber, + maPlaceholderShapeToIndexMap.find(xShape)->second, + ePageType, mXPagePropSet); + } + + if (ePageType == LAYOUT + || (bCheckProps + && PropValue::GetPropertyValue(aAny, mXPagePropSet, "IsDateTimeVisible", true) + && aAny == true + && ((GetPropertyValue(aAny, mXPagePropSet, "DateTimeText", true) && (aAny >>= aText) + && !aText.isEmpty()) + || mXPagePropSet->getPropertyValue("IsDateTimeFixed") == false))) + { + if ((xShape = GetReferencedPlaceholderXShape(DateAndTime, ePageType))) + rDML.WritePlaceholderReferenceShape(DateAndTime, + maPlaceholderShapeToIndexMap.find(xShape)->second, + ePageType, mXPagePropSet); + } +} + +sal_Int32 PowerPointExport::CreateNewPlaceholderIndex(const css::uno::Reference<XShape> &rXShape) +{ + maPlaceholderShapeToIndexMap.insert({rXShape, mnPlaceholderIndexMax}); + return mnPlaceholderIndexMax++; +} + +Reference<XShape> PowerPointExport::GetReferencedPlaceholderXShape(const PlaceholderType eType, + PageType ePageType) const +{ + PresObjKind ePresObjKind = PresObjKind::NONE; + switch (eType) + { + case oox::core::None: + break; + case oox::core::SlideImage: + break; + case oox::core::Notes: + break; + case oox::core::Header: + ePresObjKind = PresObjKind::Header; + break; + case oox::core::Footer: + ePresObjKind = PresObjKind::Footer; + break; + case oox::core::SlideNumber: + ePresObjKind = PresObjKind::SlideNumber; + break; + case oox::core::DateAndTime: + ePresObjKind = PresObjKind::DateTime; + break; + case oox::core::Outliner: + break; + case oox::core::Title: + ePresObjKind = PresObjKind::Title; + break; + case oox::core::Subtitle: + break; + } + if (ePresObjKind != PresObjKind::NONE) + { + SdPage* pMasterPage; + if (ePageType == LAYOUT) + { + // since Layout pages do not have drawpages themselves - mXDrawPage is still the master they reference to.. + pMasterPage = SdPage::getImplementation(mXDrawPage); + } + else + { + pMasterPage + = &static_cast<SdPage&>(SdPage::getImplementation(mXDrawPage)->TRG_GetMasterPage()); + } + if (SdrObject* pMasterFooter = pMasterPage->GetPresObj(ePresObjKind)) + return GetXShapeForSdrObject(pMasterFooter); + } + return nullptr; +} + +// UNO component +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +css_comp_Impress_oox_PowerPointExport(uno::XComponentContext* rxCtxt, + uno::Sequence<css::uno::Any> const& rArguments) +{ + return cppu::acquire(new PowerPointExport(rxCtxt, rArguments)); +} + +#if OSL_DEBUG_LEVEL > 1 +void dump_pset(Reference< XPropertySet > const& rXPropSet) +{ + Reference< XPropertySetInfo > info = rXPropSet->getPropertySetInfo(); + Sequence< beans::Property > props = info->getProperties(); + + for (int i=0; i < props.getLength(); i++) + { + OString name = OUStringToOString(props [i].Name, RTL_TEXTENCODING_UTF8); + + Any value = rXPropSet->getPropertyValue(props [i].Name); + + OUString strValue; + sal_Int32 intValue; + bool boolValue; + RectanglePoint pointValue; + + if (value >>= strValue) + SAL_INFO("sd.eppt", name << " = \"" << strValue << "\""); + else if (value >>= intValue) + SAL_INFO("sd.eppt", name << " = " << intValue << "(hex : " << std::hex << intValue << ")"); + else if (value >>= boolValue) + SAL_INFO("sd.eppt", name << " = " << boolValue << " (bool)"); + else if (value >>= pointValue) + SAL_INFO("sd.eppt", name << " = " << static_cast<int>(pointValue) << " (RectanglePoint)"); + else + SAL_INFO("sd.eppt", "??? <unhandled type>"); + } +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-grouptable.cxx b/sd/source/filter/eppt/pptx-grouptable.cxx new file mode 100644 index 000000000..bf91f2fb6 --- /dev/null +++ b/sd/source/filter/eppt/pptx-grouptable.cxx @@ -0,0 +1,85 @@ +/* -*- 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 "grouptable.hxx" + +using ::com::sun::star::container::XIndexAccess; + +GroupTable::GroupTable() + : mnIndex(0) + , mnGroupsClosed(0) +{ + mvGroupEntry.reserve(32); +} + +GroupTable::~GroupTable() +{ +} + +bool GroupTable::EnterGroup( css::uno::Reference< css::container::XIndexAccess > const & rXIndexAccessRef ) +{ + bool bRet = false; + if ( rXIndexAccessRef.is() ) + { + GroupEntry aNewGroup( rXIndexAccessRef ); + if ( aNewGroup.mnCount ) + { + mvGroupEntry.push_back( std::move(aNewGroup) ); + bRet = true; + } + } + return bRet; +} + +sal_uInt32 GroupTable::GetGroupsClosed() +{ + sal_uInt32 nRet = mnGroupsClosed; + mnGroupsClosed = 0; + return nRet; +} + +void GroupTable::ClearGroupTable() +{ + mvGroupEntry.clear(); +} + +void GroupTable::ResetGroupTable( sal_uInt32 nCount ) +{ + ClearGroupTable(); + mvGroupEntry.push_back( GroupEntry( nCount ) ); +} + +bool GroupTable::GetNextGroupEntry() +{ + while ( !mvGroupEntry.empty() ) + { + mnIndex = mvGroupEntry.back().mnCurrentPos++; + + if ( mvGroupEntry.back().mnCount > mnIndex ) + return true; + + mvGroupEntry.pop_back(); + + if ( !mvGroupEntry.empty() ) + mnGroupsClosed++; + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-stylesheet.cxx b/sd/source/filter/eppt/pptx-stylesheet.cxx new file mode 100644 index 000000000..459020278 --- /dev/null +++ b/sd/source/filter/eppt/pptx-stylesheet.cxx @@ -0,0 +1,489 @@ +/* -*- 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 "epptbase.hxx" +#include "epptdef.hxx" +#include "text.hxx" +#include <tools/color.hxx> +#include <tools/UnitConversion.hxx> +#include <editeng/svxenum.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> + +using namespace ::com::sun::star; + +PPTExCharSheet::PPTExCharSheet( int nInstance ) +{ + sal_uInt16 nFontHeight = 24; + + for ( int nDepth = 0; nDepth < 5; nDepth++ ) + { + PPTExCharLevel& rLev = maCharLevel[ nDepth ]; + switch ( nInstance ) + { + case EPP_TEXTTYPE_Title : + case EPP_TEXTTYPE_CenterTitle : + nFontHeight = 44; + break; + case EPP_TEXTTYPE_Body : + case EPP_TEXTTYPE_CenterBody : + case EPP_TEXTTYPE_HalfBody : + case EPP_TEXTTYPE_QuarterBody : + { + switch ( nDepth ) + { + case 0 : nFontHeight = 32; break; + case 1 : nFontHeight = 28; break; + case 2 : nFontHeight = 24; break; + default :nFontHeight = 20; break; + } + } + break; + case EPP_TEXTTYPE_Notes : + nFontHeight = 12; + break; + case EPP_TEXTTYPE_notUsed : + case EPP_TEXTTYPE_Other : + nFontHeight = 24; + break; + } + rLev.mnFlags = 0; + rLev.mnFont = 0; + rLev.mnAsianOrComplexFont = 0xffff; + rLev.mnFontHeight = nFontHeight; + rLev.mnFontColor = 0; + rLev.mnEscapement = 0; + } +} + +void PPTExCharSheet::SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + FontCollection& rFontCollection, int nLevel ) +{ + PortionObj aPortionObj( rXPropSet, rFontCollection ); + + PPTExCharLevel& rLev = maCharLevel[ nLevel ]; + + if ( aPortionObj.meCharColor == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnFontColor = Color(ColorTransparency, aPortionObj.mnCharColor); + if ( aPortionObj.meCharEscapement == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnEscapement = aPortionObj.mnCharEscapement; + if ( aPortionObj.meCharHeight == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnFontHeight = aPortionObj.mnCharHeight; + if ( aPortionObj.meFontName == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnFont = aPortionObj.mnFont; + if ( aPortionObj.meAsianOrComplexFont == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnAsianOrComplexFont = aPortionObj.mnAsianOrComplexFont; + rLev.mnFlags = aPortionObj.mnCharAttr; +} + +void PPTExCharSheet::Write( SvStream& rSt, sal_uInt16 nLev, bool bSimpleText, + const css::uno::Reference< css::beans::XPropertySet > & rPagePropSet ) +{ + const PPTExCharLevel& rLev = maCharLevel[ nLev ]; + + sal_uInt32 nCharFlags = 0xefffff; + if ( bSimpleText ) + nCharFlags = 0x7ffff; + + rSt.WriteUInt32( nCharFlags ) + .WriteUInt16( rLev.mnFlags ) + .WriteUInt16( rLev.mnFont ); + + Color nFontColor = rLev.mnFontColor; + if ( nFontColor == COL_AUTO ) + { + bool bIsDark = false; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, rPagePropSet, "IsBackgroundDark", true ) ) + aAny >>= bIsDark; + nFontColor = Color(ColorTransparency, bIsDark ? 0xffffff : 0x000000); + } + nFontColor.SetAlpha(1); + if ( bSimpleText ) + { + rSt.WriteUInt16( rLev.mnFontHeight ) + .WriteUInt32( sal_uInt32(nFontColor) ); + } + else + { + rSt.WriteUInt16( rLev.mnAsianOrComplexFont ) + .WriteUInt16( 0xffff ) // unknown + .WriteUInt16( 0xffff ) // unknown + .WriteUInt16( rLev.mnFontHeight ) + .WriteUInt32( sal_uInt32(nFontColor) ) + .WriteUInt16( rLev.mnEscapement ); + } +} + +PPTExParaSheet::PPTExParaSheet( int nInstance, sal_uInt16 nDefaultTab, PPTExBulletProvider* pProv ) : + pBuProv ( pProv ), + mnInstance ( nInstance ) +{ + bool bHasBullet = false; + + sal_uInt16 nUpperDist = 0; + sal_uInt16 nBulletChar = 0x2022; + sal_uInt16 nBulletOfs = 0; + sal_uInt16 nTextOfs = 0; + + for ( int nDepth = 0; nDepth < 5; nDepth++ ) + { + PPTExParaLevel& rLev = maParaLevel[ nDepth ]; + switch ( nInstance ) + { + case EPP_TEXTTYPE_Title : + case EPP_TEXTTYPE_CenterTitle : + break; + case EPP_TEXTTYPE_Body : + case EPP_TEXTTYPE_CenterBody : + case EPP_TEXTTYPE_HalfBody : + case EPP_TEXTTYPE_QuarterBody : + { + bHasBullet = true; + nUpperDist = 0x14; + } + break; + case EPP_TEXTTYPE_Notes : + nUpperDist = 0x1e; + break; + + } + switch ( nDepth ) + { + case 0 : + { + nBulletChar = 0x2022; + nBulletOfs = 0; + nTextOfs = bHasBullet ? 0xd8 : 0; + } + break; + case 1 : + { + nBulletChar = 0x2013; + nBulletOfs = 0x120; + nTextOfs = 0x1d4; + } + break; + case 2 : + { + nBulletChar = 0x2022; + nBulletOfs = 0x240; + nTextOfs = 0x2d0; + } + break; + case 3 : + { + nBulletChar = 0x2013; + nBulletOfs = 0x360; + nTextOfs = 0x3f0; + } + break; + case 4 : + { + nBulletChar = 0xbb; + nBulletOfs = 0x480; + nTextOfs = 0x510; + } + break; + } + rLev.mbIsBullet = bHasBullet; + rLev.mnBulletChar = nBulletChar; + rLev.mnBulletFont = 0; + rLev.mnBulletHeight = 100; + rLev.mnBulletColor = 0; + rLev.mnAdjust = 0; + rLev.mnLineFeed = 100; + rLev.mnLowerDist = 0; + rLev.mnUpperDist = nUpperDist; + rLev.mnTextOfs = nTextOfs; + rLev.mnBulletOfs = nBulletOfs; + rLev.mnDefaultTab = nDefaultTab; + rLev.mnAsianSettings = 2; + rLev.mnBiDi = 0; + + rLev.mbExtendedBulletsUsed = false; + rLev.mnBulletId = 0xffff; + rLev.mnBulletStart = 0; + rLev.mnMappedNumType = 0; + rLev.mnNumberingType = 0; + } +} + +void PPTExParaSheet::SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + FontCollection& rFontCollection, int nLevel, const PPTExCharLevel& rCharLevel ) +{ + ParagraphObj aParagraphObj( rXPropSet, pBuProv ); + aParagraphObj.CalculateGraphicBulletSize( rCharLevel.mnFontHeight ); + PPTExParaLevel& rLev = maParaLevel[ nLevel ]; + + if ( aParagraphObj.meTextAdjust == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnAdjust = aParagraphObj.mnTextAdjust; + if ( aParagraphObj.meLineSpacing == css::beans::PropertyState_DIRECT_VALUE ) + { + sal_Int16 nLineSpacing = aParagraphObj.mnLineSpacing; + if ( nLineSpacing > 0 ) // if nLinespacing is < 0 the linespacing is an absolute spacing + { + bool bFixedLineSpacing = false; + uno::Any aAny = rXPropSet->getPropertyValue("FontIndependentLineSpacing"); + if( !(aAny >>= bFixedLineSpacing) || !bFixedLineSpacing ) + { + const FontCollectionEntry* pDesc = rFontCollection.GetById( rCharLevel.mnFont ); + if ( pDesc ) + nLineSpacing = static_cast<sal_Int16>( static_cast<double>(nLineSpacing) * pDesc->Scaling + 0.5 ); + } + } + else + { + if ( rCharLevel.mnFontHeight > static_cast<sal_uInt16>( static_cast<double>(-nLineSpacing) * 0.001 * 72.0 / 2.54 ) ) // 1/100mm to point + { + const FontCollectionEntry* pDesc = rFontCollection.GetById( rCharLevel.mnFont ); + if ( pDesc ) + nLineSpacing = static_cast<sal_Int16>( 100.0 * pDesc->Scaling + 0.5 ); + else + nLineSpacing = 100; + } + else + nLineSpacing = static_cast<sal_Int16>(convertMm100ToMasterUnit(nLineSpacing)); + } + rLev.mnLineFeed = nLineSpacing; + } + if ( aParagraphObj.meLineSpacingBottom == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnLowerDist = aParagraphObj.mnLineSpacingBottom; + if ( aParagraphObj.meLineSpacingTop == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnUpperDist = aParagraphObj.mnLineSpacingTop; + if ( aParagraphObj.meForbiddenRules == css::beans::PropertyState_DIRECT_VALUE ) + { + rLev.mnAsianSettings &=~1; + if ( aParagraphObj.mbForbiddenRules ) + rLev.mnAsianSettings |= 1; + } + if ( aParagraphObj.meParagraphPunctation == css::beans::PropertyState_DIRECT_VALUE ) + { + rLev.mnAsianSettings &=~4; + if ( aParagraphObj.mbParagraphPunctation ) + rLev.mnAsianSettings |= 4; + } + + if ( aParagraphObj.meBiDi == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnBiDi = aParagraphObj.mnBiDi; + + rLev.mbIsBullet = aParagraphObj.mbIsBullet; //( ( aParagraphObj.nBulletFlags & 1 ) != 0 ); + + if ( nLevel ) + return; + + if (!(aParagraphObj.bExtendedParameters && + aParagraphObj.meBullet == css::beans::PropertyState_DIRECT_VALUE)) + return; + + for ( sal_Int16 i = 0; i < 5; i++ ) + { + PPTExParaLevel& rLevel = maParaLevel[ i ]; + if ( i ) + aParagraphObj.ImplGetNumberingLevel( pBuProv, i, false, false ); + rLevel.mnTextOfs = aParagraphObj.nTextOfs; + rLevel.mnBulletOfs = static_cast<sal_uInt16>(aParagraphObj.nBulletOfs); + rLevel.mnBulletChar = aParagraphObj.cBulletId; + FontCollectionEntry aFontDescEntry( aParagraphObj.aFontDesc.Name, aParagraphObj.aFontDesc.Family, + aParagraphObj.aFontDesc.Pitch, aParagraphObj.aFontDesc.CharSet ); + rLevel.mnBulletFont = static_cast<sal_uInt16>(rFontCollection.GetId( aFontDescEntry )); + rLevel.mnBulletHeight = aParagraphObj.nBulletRealSize; + rLevel.mnBulletColor = aParagraphObj.nBulletColor; + + rLevel.mbExtendedBulletsUsed = aParagraphObj.bExtendedBulletsUsed; + rLevel.mnBulletId = aParagraphObj.nBulletId; + rLevel.mnNumberingType = aParagraphObj.nNumberingType; + rLevel.mnBulletStart = aParagraphObj.nStartWith; + rLevel.mnMappedNumType = aParagraphObj.nMappedNumType; + } +} + +void PPTExParaSheet::Write( SvStream& rSt, sal_uInt16 nLev, bool bSimpleText, + const css::uno::Reference< css::beans::XPropertySet > & rPagePropSet ) +{ + const PPTExParaLevel& rLev = maParaLevel[ nLev ]; + + if ( maParaLevel[ 0 ].mbExtendedBulletsUsed || maParaLevel[ 1 ].mbExtendedBulletsUsed || + maParaLevel[ 2 ].mbExtendedBulletsUsed || maParaLevel[ 3 ].mbExtendedBulletsUsed || + maParaLevel[ 4 ].mbExtendedBulletsUsed ) + { + SvStream& rOut = pBuProv->aBuExMasterStream; + if ( !nLev ) + { + rOut.WriteUInt32( ( EPP_PST_ExtendedParagraphMasterAtom << 16 ) | ( mnInstance << 4 ) ) + .WriteUInt32( 5 * 16 + 2 ) + .WriteUInt16( 5 ); // depth + } + sal_uInt16 nBulletId = rLev.mnBulletId; + if ( rLev.mnNumberingType != SVX_NUM_BITMAP ) + nBulletId = 0xffff; + rOut.WriteUInt32( 0x03800000 ) + .WriteUInt16( nBulletId ) + .WriteUInt32( rLev.mnMappedNumType ) + .WriteUInt16( rLev.mnBulletStart ) + .WriteUInt32( 0 ); + } + + sal_uInt32 nParaFlags = 0x3ffdff; + sal_uInt16 nBulletFlags = ( rLev.mbIsBullet ) ? 0xf : 0xe; + + if ( nLev ) + nParaFlags &= 0x207fff; + if ( bSimpleText ) + nParaFlags &= 0x7fff; + sal_uInt32 nBulletColor = rLev.mnBulletColor; + if ( nBulletColor == sal_uInt32(COL_AUTO) ) + { + bool bIsDark = false; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, rPagePropSet, "IsBackgroundDark", true ) ) + aAny >>= bIsDark; + nBulletColor = bIsDark ? 0xffffff : 0x000000; + } + nBulletColor &= 0xffffff; + nBulletColor |= 0xfe000000; + rSt.WriteUInt32( nParaFlags ) + .WriteUInt16( nBulletFlags ) + .WriteUInt16( rLev.mnBulletChar ) + .WriteUInt16( rLev.mnBulletFont ) + .WriteUInt16( rLev.mnBulletHeight ) + .WriteUInt32( nBulletColor ) + .WriteUInt16( rLev.mnAdjust ) + .WriteUInt16( rLev.mnLineFeed ) + .WriteUInt16( rLev.mnUpperDist ) + .WriteUInt16( rLev.mnLowerDist ) + .WriteUInt16( rLev.mnTextOfs ) + .WriteUInt16( rLev.mnBulletOfs ); + + if ( bSimpleText || nLev ) + { + if ( nParaFlags & 0x200000 ) + rSt.WriteUInt16( rLev.mnBiDi ); + } + else + { + rSt.WriteUInt16( rLev.mnDefaultTab ) + .WriteUInt16( 0 ) + .WriteUInt16( 0 ) + .WriteUInt16( rLev.mnAsianSettings ) + .WriteUInt16( rLev.mnBiDi ); + } +} + +PPTExStyleSheet::PPTExStyleSheet( sal_uInt16 nDefaultTab, PPTExBulletProvider* pBuProv ) +{ + for ( int nInstance = EPP_TEXTTYPE_Title; nInstance <= EPP_TEXTTYPE_QuarterBody; nInstance++ ) + { + if (nInstance != EPP_TEXTTYPE_notUsed) + { + mpParaSheet[ nInstance ].reset(new PPTExParaSheet( nInstance, nDefaultTab, pBuProv )); + mpCharSheet[ nInstance ].reset(new PPTExCharSheet( nInstance )); + } + } +} + +PPTExStyleSheet::~PPTExStyleSheet() +{ +} + +void PPTExStyleSheet::SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + FontCollection& rFontCollection, int nInstance, int nLevel ) +{ + if ( nInstance == EPP_TEXTTYPE_notUsed ) + return; + mpCharSheet[ nInstance ]->SetStyleSheet( rXPropSet, rFontCollection, nLevel ); + mpParaSheet[ nInstance ]->SetStyleSheet( rXPropSet, rFontCollection, nLevel, mpCharSheet[ nInstance ]->maCharLevel[ nLevel ] ); +} + +bool PPTExStyleSheet::IsHardAttribute( sal_uInt32 nInstance, sal_uInt32 nLevel, PPTExTextAttr eAttr, sal_uInt32 nValue ) +{ + assert(nInstance < PPTEX_STYLESHEETENTRIES && nLevel < 5); + + const PPTExParaLevel& rPara = mpParaSheet[ nInstance ]->maParaLevel[ nLevel ]; + const PPTExCharLevel& rChar = mpCharSheet[ nInstance ]->maCharLevel[ nLevel ]; + + sal_uInt32 nFlag = 0; + + switch ( eAttr ) + { + case ParaAttr_BulletOn : return ( rPara.mbIsBullet ) ? nValue == 0 : nValue != 0; + case ParaAttr_BuHardFont : + case ParaAttr_BulletFont : return ( rPara.mnBulletFont != nValue ); + case ParaAttr_BuHardColor : + case ParaAttr_BulletColor : return ( rPara.mnBulletColor != nValue ); + case ParaAttr_BuHardHeight : + case ParaAttr_BulletHeight : return ( rPara.mnBulletHeight != nValue ); + case ParaAttr_BulletChar : return ( rPara.mnBulletChar != nValue ); + case ParaAttr_Adjust : return ( rPara.mnAdjust != nValue ); + case ParaAttr_LineFeed : return ( rPara.mnLineFeed != nValue ); + case ParaAttr_UpperDist : return ( rPara.mnUpperDist != nValue ); + case ParaAttr_LowerDist : return ( rPara.mnLowerDist != nValue ); + case ParaAttr_TextOfs : return ( rPara.mnTextOfs != nValue ); + case ParaAttr_BulletOfs : return ( rPara.mnBulletOfs != nValue ); + case ParaAttr_DefaultTab : return ( rPara.mnDefaultTab != nValue ); + case ParaAttr_BiDi : return ( rPara.mnBiDi != nValue ); + case CharAttr_Bold : nFlag = 1; break; + case CharAttr_Italic : nFlag = 2; break; + case CharAttr_Underline : nFlag = 4; break; + case CharAttr_Shadow : nFlag = 16; break; + case CharAttr_Strikeout : nFlag = 256; break; + case CharAttr_Embossed : nFlag = 512; break; + case CharAttr_Font : return ( rChar.mnFont != nValue ); + case CharAttr_AsianOrComplexFont : return ( rChar.mnAsianOrComplexFont != nValue ); + case CharAttr_Symbol : return true; + case CharAttr_FontHeight : return ( rChar.mnFontHeight != nValue ); + case CharAttr_FontColor : return ( rChar.mnFontColor != Color(ColorTransparency, nValue) ); + case CharAttr_Escapement : return ( rChar.mnEscapement != nValue ); + default: + break; + } + if ( nFlag ) + { + if ( rChar.mnFlags & nFlag ) + return ( ( nValue & nFlag ) == 0 ); + else + return ( ( nValue & nFlag ) != 0 ); + } + return true; +} + +// the TxCFStyleAtom stores the text properties that are used +// when creating new objects in PowerPoint. + +void PPTExStyleSheet::WriteTxCFStyleAtom( SvStream& rSt ) +{ + const PPTExCharLevel& rCharStyle = mpCharSheet[ EPP_TEXTTYPE_Other ]->maCharLevel[ 0 ]; + + sal_uInt16 const nFlags = 0x60 // ?? + | 0x02 // fontsize; + | 0x04; // fontcolor + + sal_uInt32 nCharFlags = rCharStyle.mnFlags; + nCharFlags &= CharAttr_Italic | CharAttr_Bold | CharAttr_Underline | CharAttr_Shadow; + + rSt.WriteUInt32( EPP_TxCFStyleAtom << 16 ) // recordheader + .WriteUInt32( SizeOfTxCFStyleAtom() - 8 ) + .WriteUInt16( 0x80 | nCharFlags ) + .WriteUInt16( nFlags ) + .WriteUInt16( nCharFlags ) + .WriteInt32( -1 ) // ? + .WriteUInt16( rCharStyle.mnFontHeight ) + .WriteUInt32( sal_uInt32(rCharStyle.mnFontColor) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-text.cxx b/sd/source/filter/eppt/pptx-text.cxx new file mode 100644 index 000000000..85c37f77d --- /dev/null +++ b/sd/source/filter/eppt/pptx-text.cxx @@ -0,0 +1,1400 @@ +/* -*- 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 <memory> +#include "text.hxx" + +#include <com/sun/star/awt/CharSet.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XIndexReplace.hpp> +#include <com/sun/star/i18n/BreakIterator.hpp> +#include <com/sun/star/i18n/ScriptDirection.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/text/FontRelief.hpp> +#include <com/sun/star/text/XTextField.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/style/LineSpacing.hpp> +#include <com/sun/star/style/LineSpacingMode.hpp> +#include <com/sun/star/style/ParagraphAdjust.hpp> +#include <com/sun/star/style/TabStop.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> + +#include <comphelper/processfactory.hxx> +#include <editeng/svxenum.hxx> +#include <editeng/frmdir.hxx> +#include <filter/msfilter/util.hxx> +#include <i18nutil/scripttypedetector.hxx> +#include <o3tl/any.hxx> +#include <svl/languageoptions.hxx> +#include <osl/diagnose.h> +#include <i18nlangtag/languagetag.hxx> +#include <tools/UnitConversion.hxx> + +#include <vcl/settings.hxx> +#include <vcl/metric.hxx> +#include <vcl/virdev.hxx> +#include <vcl/svapp.hxx> + +using namespace css; + +static css::uno::Reference< css::i18n::XBreakIterator > xPPTBreakIter; + +PortionObj::PortionObj(const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + FontCollection& rFontCollection) + : meCharColor(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meCharHeight(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meFontName(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meAsianOrComplexFont(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meCharEscapement(css::beans::PropertyState_AMBIGUOUS_VALUE) + , mnCharAttrHard(0) + , mnCharColor(0) + , mnCharAttr(0) + , mnFont(0) + , mnAsianOrComplexFont(0xffff) + , mnTextSize(0) + , mbLastPortion(true) +{ + mXPropSet = rXPropSet; + + ImplGetPortionValues( rFontCollection, false ); +} + +PortionObj::PortionObj(css::uno::Reference< css::text::XTextRange > & rXTextRange, + bool bLast, FontCollection& rFontCollection) + : meCharColor(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meCharHeight(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meFontName(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meAsianOrComplexFont(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meCharEscapement(css::beans::PropertyState_AMBIGUOUS_VALUE) + , mnCharAttrHard(0) + , mnCharColor(0) + , mnCharAttr(0) + , mnCharHeight(0) + , mnFont(0) + , mnAsianOrComplexFont(0xffff) + , mnCharEscapement(0) + , mbLastPortion(bLast) +{ + OUString aString( rXTextRange->getString() ); + OUString aURL; + + mnTextSize = aString.getLength(); + if ( bLast ) + mnTextSize++; + + if ( !mnTextSize ) + return; + + bool bRTL_endingParen = false; + mpFieldEntry = nullptr; + sal_uInt32 nFieldType = 0; + + mXPropSet.set( rXTextRange, css::uno::UNO_QUERY ); + mXPropState.set( rXTextRange, css::uno::UNO_QUERY ); + + bool bPropSetsValid = ( mXPropSet.is() && mXPropState.is() ); + if ( bPropSetsValid ) + nFieldType = ImplGetTextField( rXTextRange, mXPropSet, aURL ); + if ( nFieldType ) + { + mpFieldEntry.reset( new FieldEntry( nFieldType, 0, mnTextSize ) ); + if ( nFieldType >> 28 == 4 ) + { + mpFieldEntry->aRepresentation = aString; + mpFieldEntry->aFieldUrl = aURL; + } + } + bool bSymbol = false; + + if ( bPropSetsValid && ImplGetPropertyValue( "CharFontCharSet", false ) ) + { + sal_Int16 nCharset = 0; + mAny >>= nCharset; + if ( nCharset == css::awt::CharSet::SYMBOL ) + bSymbol = true; + } + if ( mpFieldEntry && ( nFieldType & 0x800000 ) ) // placeholder ? + { + mnTextSize = 1; + if ( bLast ) + mnTextSize++; + mpText.reset( new sal_uInt16[ mnTextSize ] ); + mpText[ 0 ] = 0x2a; + } + else + { + // For i39516 - a closing parenthesis that ends an RTL string is displayed backwards by PPT + // Solution: add a Unicode Right-to-Left Mark, following the method described in i18024 + if (bLast && !aString.isEmpty() + && aString[aString.getLength() - 1] == ')' + && FontCollection::GetScriptDirection(aString) == css::i18n::ScriptDirection::RIGHT_TO_LEFT) + { + mnTextSize++; + bRTL_endingParen = true; + } + mpText.reset( new sal_uInt16[ mnTextSize ] ); + sal_uInt16 nChar; + for ( sal_Int32 i = 0; i < aString.getLength(); i++ ) + { + nChar = static_cast<sal_uInt16>(aString[ i ]); + if ( nChar == 0xa ) + nChar++; + else if ( !bSymbol ) + { + switch ( nChar ) + { + // Currency + case 128: nChar = 0x20AC; break; + // Punctuation and other + case 130: nChar = 0x201A; break;// SINGLE LOW-9 QUOTATION MARK + case 131: nChar = 0x0192; break;// LATIN SMALL LETTER F WITH HOOK + case 132: nChar = 0x201E; break;// DOUBLE LOW-9 QUOTATION MARK + // LOW DOUBLE PRIME QUOTATION MARK + case 133: nChar = 0x2026; break;// HORIZONTAL ELLIPSES + case 134: nChar = 0x2020; break;// DAGGER + case 135: nChar = 0x2021; break;// DOUBLE DAGGER + case 136: nChar = 0x02C6; break;// MODIFIER LETTER CIRCUMFLEX ACCENT + case 137: nChar = 0x2030; break;// PER MILLE SIGN + case 138: nChar = 0x0160; break;// LATIN CAPITAL LETTER S WITH CARON + case 139: nChar = 0x2039; break;// SINGLE LEFT-POINTING ANGLE QUOTATION MARK + case 140: nChar = 0x0152; break;// LATIN CAPITAL LIGATURE OE + case 142: nChar = 0x017D; break;// LATIN CAPITAL LETTER Z WITH CARON + case 145: nChar = 0x2018; break;// LEFT SINGLE QUOTATION MARK + // MODIFIER LETTER TURNED COMMA + case 146: nChar = 0x2019; break;// RIGHT SINGLE QUOTATION MARK + // MODIFIER LETTER APOSTROPHE + case 147: nChar = 0x201C; break;// LEFT DOUBLE QUOTATION MARK + // REVERSED DOUBLE PRIME QUOTATION MARK + case 148: nChar = 0x201D; break;// RIGHT DOUBLE QUOTATION MARK + // REVERSED DOUBLE PRIME QUOTATION MARK + case 149: nChar = 0x2022; break;// BULLET + case 150: nChar = 0x2013; break;// EN DASH + case 151: nChar = 0x2014; break;// EM DASH + case 152: nChar = 0x02DC; break;// SMALL TILDE + case 153: nChar = 0x2122; break;// TRADE MARK SIGN + case 154: nChar = 0x0161; break;// LATIN SMALL LETTER S WITH CARON + case 155: nChar = 0x203A; break;// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + case 156: nChar = 0x0153; break;// LATIN SMALL LIGATURE OE + case 158: nChar = 0x017E; break;// LATIN SMALL LETTER Z WITH CARON + case 159: nChar = 0x0178; break;// LATIN CAPITAL LETTER Y WITH DIAERESIS + } + } + mpText[ i ] = nChar; + } + } + if ( bRTL_endingParen ) + mpText[ mnTextSize - 2 ] = 0x200F; // Unicode Right-to-Left mark + + if ( bLast ) + mpText[ mnTextSize - 1 ] = 0xd; + + if ( bPropSetsValid ) + ImplGetPortionValues( rFontCollection, true ); +} + +PortionObj::PortionObj( const PortionObj& rPortionObj ) +: PropStateValue( rPortionObj ) +{ + ImplConstruct( rPortionObj ); +} + +PortionObj::~PortionObj() +{ + ImplClear(); +} + +void PortionObj::Write( SvStream* pStrm, bool bLast ) +{ + sal_uInt32 nCount = mnTextSize; + if ( bLast && mbLastPortion ) + nCount--; + for ( sal_uInt32 i = 0; i < nCount; i++ ) + pStrm->WriteUInt16( mpText[ i ] ); +} + +void PortionObj::ImplGetPortionValues( FontCollection& rFontCollection, bool bGetPropStateValue ) +{ + + bool bOk = ImplGetPropertyValue( "CharFontName", bGetPropStateValue ); + meFontName = ePropState; + if ( bOk ) + { + FontCollectionEntry aFontDesc( *o3tl::doAccess<OUString>(mAny) ); + sal_uInt32 nCount = rFontCollection.GetCount(); + mnFont = static_cast<sal_uInt16>(rFontCollection.GetId( aFontDesc )); + if ( mnFont == nCount ) + { + FontCollectionEntry& rFontDesc = rFontCollection.GetLast(); + if ( ImplGetPropertyValue( "CharFontCharSet", false ) ) + mAny >>= rFontDesc.CharSet; + if ( ImplGetPropertyValue( "CharFontFamily", false ) ) + mAny >>= rFontDesc.Family; + if ( ImplGetPropertyValue( "CharFontPitch", false ) ) + mAny >>= rFontDesc.Pitch; + } + } + + sal_Int16 nScriptType = SvtLanguageOptions::FromSvtScriptTypeToI18N( SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ) ); + if ( mpText && mnTextSize && xPPTBreakIter.is() ) + { + OUString sT( reinterpret_cast<sal_Unicode *>(mpText.get()), mnTextSize ); + nScriptType = xPPTBreakIter->getScriptType( sT, 0 ); + } + if ( nScriptType != css::i18n::ScriptType::COMPLEX ) + { + bOk = ImplGetPropertyValue( "CharFontNameAsian", bGetPropStateValue ); + meAsianOrComplexFont = ePropState; + if ( bOk ) + { + FontCollectionEntry aFontDesc( *o3tl::doAccess<OUString>(mAny) ); + sal_uInt32 nCount = rFontCollection.GetCount(); + mnAsianOrComplexFont = static_cast<sal_uInt16>(rFontCollection.GetId( aFontDesc )); + if ( mnAsianOrComplexFont == nCount ) + { + FontCollectionEntry& rFontDesc = rFontCollection.GetLast(); + if ( ImplGetPropertyValue( "CharFontCharSetAsian", false ) ) + mAny >>= rFontDesc.CharSet; + if ( ImplGetPropertyValue( "CharFontFamilyAsian", false ) ) + mAny >>= rFontDesc.Family; + if ( ImplGetPropertyValue( "CharFontPitchAsian", false ) ) + mAny >>= rFontDesc.Pitch; + } + } + } + else + { + bOk = ImplGetPropertyValue( "CharFontNameComplex", bGetPropStateValue ); + meAsianOrComplexFont = ePropState; + if ( bOk ) + { + FontCollectionEntry aFontDesc( *o3tl::doAccess<OUString>(mAny) ); + sal_uInt32 nCount = rFontCollection.GetCount(); + mnAsianOrComplexFont = static_cast<sal_uInt16>(rFontCollection.GetId( aFontDesc )); + if ( mnAsianOrComplexFont == nCount ) + { + FontCollectionEntry& rFontDesc = rFontCollection.GetLast(); + if ( ImplGetPropertyValue( "CharFontCharSetComplex", false ) ) + mAny >>= rFontDesc.CharSet; + if ( ImplGetPropertyValue( "CharFontFamilyComplex", false ) ) + mAny >>= rFontDesc.Family; + if ( ImplGetPropertyValue( "CharFontPitchComplex", false ) ) + mAny >>= rFontDesc.Pitch; + } + } + } + + OUString aCharHeightName, aCharWeightName, aCharLocaleName, aCharPostureName; + switch( nScriptType ) + { + case css::i18n::ScriptType::ASIAN : + { + aCharHeightName = "CharHeightAsian"; + aCharWeightName = "CharWeightAsian"; + aCharLocaleName = "CharLocaleAsian"; + aCharPostureName = "CharPostureAsian"; + break; + } + case css::i18n::ScriptType::COMPLEX : + { + aCharHeightName = "CharHeightComplex"; + aCharWeightName = "CharWeightComplex"; + aCharLocaleName = "CharLocaleComplex"; + aCharPostureName = "CharPostureComplex"; + break; + } + default: + { + aCharHeightName = "CharHeight"; + aCharWeightName = "CharWeight"; + aCharLocaleName = "CharLocale"; + aCharPostureName = "CharPosture"; + break; + } + } + + mnCharHeight = 24; + if ( GetPropertyValue( mAny, mXPropSet, aCharHeightName ) ) + { + float fVal(0.0); + if ( mAny >>= fVal ) + { + mnCharHeight = static_cast<sal_uInt16>( fVal + 0.5 ); + meCharHeight = GetPropertyState( mXPropSet, aCharHeightName ); + } + } + if ( GetPropertyValue( mAny, mXPropSet, aCharWeightName ) ) + { + float fFloat(0.0); + if ( mAny >>= fFloat ) + { + if ( fFloat >= css::awt::FontWeight::SEMIBOLD ) + mnCharAttr |= 1; + if ( GetPropertyState( mXPropSet, aCharWeightName ) == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 1; + } + } + if ( GetPropertyValue( mAny, mXPropSet, aCharLocaleName ) ) + { + css::lang::Locale eLocale; + if ( mAny >>= eLocale ) + meCharLocale = eLocale; + } + if ( GetPropertyValue( mAny, mXPropSet, aCharPostureName ) ) + { + css::awt::FontSlant aFS; + if ( mAny >>= aFS ) + { + switch( aFS ) + { + case css::awt::FontSlant_OBLIQUE : + case css::awt::FontSlant_ITALIC : + mnCharAttr |= 2; + break; + default: + break; + } + if ( GetPropertyState( mXPropSet, aCharPostureName ) == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 2; + } + } + + if ( ImplGetPropertyValue( "CharUnderline", bGetPropStateValue ) ) + { + sal_Int16 nVal(0); + mAny >>= nVal; + switch ( nVal ) + { + case css::awt::FontUnderline::SINGLE : + case css::awt::FontUnderline::DOUBLE : + case css::awt::FontUnderline::DOTTED : + mnCharAttr |= 4; + } + } + if ( ePropState == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 4; + + if ( ImplGetPropertyValue( "CharShadowed", bGetPropStateValue ) ) + { + bool bBool(false); + mAny >>= bBool; + if ( bBool ) + mnCharAttr |= 0x10; + } + if ( ePropState == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 16; + + if ( ImplGetPropertyValue( "CharRelief", bGetPropStateValue ) ) + { + sal_Int16 nVal(0); + mAny >>= nVal; + if ( nVal != css::text::FontRelief::NONE ) + mnCharAttr |= 512; + } + if ( ePropState == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 512; + + if ( ImplGetPropertyValue( "CharColor", bGetPropStateValue ) ) + { + sal_uInt32 nSOColor = *( o3tl::doAccess<sal_uInt32>(mAny) ); + mnCharColor = nSOColor & 0xff00ff00; // green and hibyte + mnCharColor |= static_cast<sal_uInt8>(nSOColor) << 16; // red and blue is switched + mnCharColor |= static_cast<sal_uInt8>( nSOColor >> 16 ); + } + meCharColor = ePropState; + + mnCharEscapement = 0; + if ( ImplGetPropertyValue( "CharEscapement", bGetPropStateValue ) ) + { + mAny >>= mnCharEscapement; + if ( mnCharEscapement > 100 ) + mnCharEscapement = 33; + else if ( mnCharEscapement < -100 ) + mnCharEscapement = -33; + } + meCharEscapement = ePropState; +} + +void PortionObj::ImplClear() +{ + mpFieldEntry.reset(); + mpText.reset(); +} + +void PortionObj::ImplConstruct( const PortionObj& rPortionObj ) +{ + meCharColor = rPortionObj.meCharColor; + meCharHeight = rPortionObj.meCharHeight; + meFontName = rPortionObj.meFontName; + meAsianOrComplexFont = rPortionObj.meAsianOrComplexFont; + meCharEscapement = rPortionObj.meCharEscapement; + meCharLocale = rPortionObj.meCharLocale; + mnCharAttrHard = rPortionObj.mnCharAttrHard; + + mbLastPortion = rPortionObj.mbLastPortion; + mnTextSize = rPortionObj.mnTextSize; + mnCharColor = rPortionObj.mnCharColor; + mnCharEscapement = rPortionObj.mnCharEscapement; + mnCharAttr = rPortionObj.mnCharAttr; + mnCharHeight = rPortionObj.mnCharHeight; + mnFont = rPortionObj.mnFont; + mnAsianOrComplexFont = rPortionObj.mnAsianOrComplexFont; + + if ( rPortionObj.mpText ) + { + mpText.reset( new sal_uInt16[ mnTextSize ] ); + memcpy( mpText.get(), rPortionObj.mpText.get(), mnTextSize << 1 ); + } + + if ( rPortionObj.mpFieldEntry ) + mpFieldEntry.reset( new FieldEntry( *( rPortionObj.mpFieldEntry ) ) ); +} + +sal_uInt32 PortionObj::ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition ) +{ + if ( mpFieldEntry && ( !mpFieldEntry->nFieldStartPos ) ) + { + mpFieldEntry->nFieldStartPos += nCurrentTextPosition; + mpFieldEntry->nFieldEndPos += nCurrentTextPosition; + } + return mnTextSize; +} + +// Return: 0 = no TextField +// bit28->31 text field type : +// 1 = Date +// 2 = Time +// 3 = SlideNumber +// 4 = Url +// 5 = DateTime +// 6 = header +// 7 = footer +// bit24->27 text field sub type (optional) +// 23-> PPT Textfield needs a placeholder + +sal_uInt32 PortionObj::ImplGetTextField( css::uno::Reference< css::text::XTextRange > & , + const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, OUString& rURL ) +{ + sal_uInt32 nRetValue = 0; + sal_Int32 nFormat; + css::uno::Any aAny; + if ( GetPropertyValue( aAny, rXPropSet, "TextPortionType", true ) ) + { + auto aTextFieldType = o3tl::doAccess<OUString>(aAny); + if ( *aTextFieldType == "TextField" ) + { + if ( GetPropertyValue( aAny, rXPropSet, *aTextFieldType, true ) ) + { + css::uno::Reference< css::text::XTextField > aXTextField; + if ( aAny >>= aXTextField ) + { + if ( aXTextField.is() ) + { + css::uno::Reference< css::beans::XPropertySet > xFieldPropSet( aXTextField, css::uno::UNO_QUERY ); + if ( xFieldPropSet.is() ) + { + OUString aFieldKind( aXTextField->getPresentation( true ) ); + if ( aFieldKind == "Date" ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) ) + { + bool bBool = false; + aAny >>= bBool; + if ( !bBool ) // Fixed DateFields does not exist in PPT + { + if ( GetPropertyValue( aAny, xFieldPropSet, "Format", true ) ) + { + nFormat = *o3tl::doAccess<sal_Int32>(aAny); + switch ( nFormat ) + { + default: + case 5 : + case 4 : + case 2 : nFormat = 0; break; + case 8 : + case 9 : + case 3 : nFormat = 1; break; + case 7 : + case 6 : nFormat = 2; break; + } + nRetValue |= ( ( ( 1 << 4 ) | nFormat ) << 24 ) | 0x800000; + } + } + } + } + else if ( aFieldKind == "URL" ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "URL", true ) ) + rURL = *o3tl::doAccess<OUString>(aAny); + nRetValue = 4 << 28; + } + else if ( aFieldKind == "Page" ) + { + nRetValue = 3 << 28 | 0x800000; + } + else if ( aFieldKind == "Pages" ) + { + + } + else if ( aFieldKind == "Time" ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) ) + { + bool bBool = false; + aAny >>= bBool; + if ( !bBool ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) ) + { + nFormat = *o3tl::doAccess<sal_Int32>(aAny); + nRetValue |= ( ( ( 2 << 4 ) | nFormat ) << 24 ) | 0x800000; + } + } + } + } + else if ( aFieldKind == "File" ) + { + + } + else if ( aFieldKind == "Table" ) + { + + } + else if ( aFieldKind == "ExtTime" ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) ) + { + bool bBool = false; + aAny >>= bBool; + if ( !bBool ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "Format", true ) ) + { + nFormat = *o3tl::doAccess<sal_Int32>(aAny); + switch ( nFormat ) + { + default: + case 6 : + case 7 : + case 8 : + case 2 : nFormat = 12; break; + case 3 : nFormat = 9; break; + case 5 : + case 4 : nFormat = 10; break; + + } + nRetValue |= ( ( ( 2 << 4 ) | nFormat ) << 24 ) | 0x800000; + } + } + } + } + else if ( aFieldKind == "ExtFile" ) + { + + } + else if ( aFieldKind == "Author" ) + { + + } + else if ( aFieldKind == "DateTime" ) + { + nRetValue = 5 << 28 | 0x800000; + } + else if ( aFieldKind == "Header" ) + { + nRetValue = 6 << 28 | 0x800000; + } + else if ( aFieldKind == "Footer" ) + { + nRetValue = 7 << 28 | 0x800000; + } + } + } + } + } + } + } + return nRetValue; +} + +PortionObj& PortionObj::operator=( const PortionObj& rPortionObj ) +{ + if ( this != &rPortionObj ) + { + ImplClear(); + ImplConstruct( rPortionObj ); + } + return *this; +} + +ParagraphObj::ParagraphObj(const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + PPTExBulletProvider* pProv) + : mnTextSize(0) + , mbFirstParagraph(false) + , mbLastParagraph(false) + , meBullet(css::beans::PropertyState_AMBIGUOUS_VALUE) + , mnTextAdjust(0) + , mnLineSpacing(0) + , mbFixedLineSpacing(false) + , mnLineSpacingTop(0) + , mnLineSpacingBottom(0) + , mbForbiddenRules(false) + , mbParagraphPunctation(false) + , mnBiDi(0) +{ + mXPropSet = rXPropSet; + + bExtendedParameters = false; + + nDepth = 0; + nBulletFlags = 0; + nParaFlags = 0; + + ImplGetParagraphValues( pProv, false ); +} + +ParagraphObj::ParagraphObj(css::uno::Reference< css::text::XTextContent > const & rXTextContent, + ParaFlags aParaFlags, FontCollection& rFontCollection, PPTExBulletProvider& rProv ) + : mnTextSize(0) + , mbIsBullet(false) + , mbFirstParagraph( aParaFlags.bFirstParagraph ) + , mbLastParagraph( aParaFlags.bLastParagraph ) + , meBullet(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meTextAdjust(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meLineSpacing(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meLineSpacingTop(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meLineSpacingBottom(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meForbiddenRules(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meParagraphPunctation(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meBiDi(css::beans::PropertyState_AMBIGUOUS_VALUE) + , mnTextAdjust(0) + , mnLineSpacing(0) + , mbFixedLineSpacing(false) + , mnLineSpacingTop(0) + , mnLineSpacingBottom(0) + , mbForbiddenRules(false) + , mbParagraphPunctation(false) + , mnBiDi(0) +{ + bExtendedParameters = false; + + nDepth = 0; + nBulletFlags = 0; + nParaFlags = 0; + + mXPropSet.set( rXTextContent, css::uno::UNO_QUERY ); + + mXPropState.set( rXTextContent, css::uno::UNO_QUERY ); + + if ( !(mXPropSet.is() && mXPropState.is()) ) + return; + + css::uno::Reference< css::container::XEnumerationAccess > aXTextPortionEA( rXTextContent, css::uno::UNO_QUERY ); + if ( aXTextPortionEA.is() ) + { + css::uno::Reference< css::container::XEnumeration > aXTextPortionE( aXTextPortionEA->createEnumeration() ); + if ( aXTextPortionE.is() ) + { + while ( aXTextPortionE->hasMoreElements() ) + { + css::uno::Reference< css::text::XTextRange > aXCursorText; + css::uno::Any aAny( aXTextPortionE->nextElement() ); + if ( aAny >>= aXCursorText ) + { + std::unique_ptr<PortionObj> pPortionObj(new PortionObj( aXCursorText, !aXTextPortionE->hasMoreElements(), rFontCollection )); + if ( pPortionObj->Count() ) + mvPortions.push_back( std::move(pPortionObj) ); + } + } + } + } + ImplGetParagraphValues( &rProv, true ); +} + +ParagraphObj::~ParagraphObj() +{ + ImplClear(); +} + +void ParagraphObj::Write( SvStream* pStrm ) +{ + for ( std::vector<std::unique_ptr<PortionObj> >::iterator it = mvPortions.begin(); it != mvPortions.end(); ++it ) + (*it)->Write( pStrm, mbLastParagraph ); +} + +void ParagraphObj::ImplClear() +{ + mvPortions.clear(); +} + +void ParagraphObj::CalculateGraphicBulletSize( sal_uInt16 nFontHeight ) +{ + if ( ( nNumberingType != SVX_NUM_BITMAP ) || ( nBulletId == 0xffff ) ) + return; + + // calculate the bullet real size for this graphic + if ( aBuGraSize.Width() && aBuGraSize.Height() ) + { + double fCharHeight = nFontHeight; + double fLen = aBuGraSize.Height(); + fCharHeight = fCharHeight * 0.2540; + double fQuo = fLen / fCharHeight; + nBulletRealSize = static_cast<sal_Int16>( fQuo + 0.5 ); + if ( static_cast<sal_uInt16>(nBulletRealSize) > 400 ) + nBulletRealSize = 400; + } +} + +void ParagraphObj::ImplGetNumberingLevel( PPTExBulletProvider* pBuProv, sal_Int16 nNumberingDepth, bool bIsBullet, bool bGetPropStateValue ) +{ + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "ParaLeftMargin" ) ) + { + sal_Int32 nVal(0); + if ( aAny >>= nVal ) + nTextOfs = convertMm100ToMasterUnit(nVal); + } + if ( GetPropertyValue( aAny, mXPropSet, "ParaFirstLineIndent" ) ) + { + if ( aAny >>= nBulletOfs ) + nBulletOfs = convertMm100ToMasterUnit(nBulletOfs); + } + if ( GetPropertyValue( aAny, mXPropSet, "NumberingIsNumber" ) ) + aAny >>= bNumberingIsNumber; + + css::uno::Reference< css::container::XIndexReplace > aXIndexReplace; + + if ( bIsBullet && ImplGetPropertyValue( "NumberingRules", bGetPropStateValue ) ) + { + if ( ( mAny >>= aXIndexReplace ) && nNumberingDepth < aXIndexReplace->getCount() ) + { + mAny = aXIndexReplace->getByIndex( nNumberingDepth ); + auto aPropertySequence = o3tl::doAccess<css::uno::Sequence<css::beans::PropertyValue>>(mAny); + + if ( aPropertySequence->hasElements() ) + { + bExtendedParameters = true; + nBulletRealSize = 100; + nMappedNumType = 0; + + uno::Reference<graphic::XGraphic> xGraphic; + for ( const css::beans::PropertyValue& rPropValue : *aPropertySequence ) + { + OUString aPropName( rPropValue.Name ); + if ( aPropName == "NumberingType" ) + nNumberingType = static_cast<SvxNumType>(*o3tl::doAccess<sal_Int16>(rPropValue.Value)); + else if ( aPropName == "Adjust" ) + nHorzAdjust = *o3tl::doAccess<sal_Int16>(rPropValue.Value); + else if ( aPropName == "BulletChar" ) + { + OUString aString( *o3tl::doAccess<OUString>(rPropValue.Value) ); + if ( !aString.isEmpty() ) + cBulletId = aString[ 0 ]; + } + else if ( aPropName == "BulletFont" ) + { + aFontDesc = *o3tl::doAccess<css::awt::FontDescriptor>(rPropValue.Value); + + // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font, + // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used. + // Because there might exist a lot of damaged documents I added this two lines + // which fixes the bullet problem for the export. + if ( aFontDesc.Name.equalsIgnoreAsciiCase("StarSymbol") ) + aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252; + + } + else if ( aPropName == "GraphicBitmap" ) + { + auto xBitmap = rPropValue.Value.get<uno::Reference<awt::XBitmap>>(); + xGraphic.set(xBitmap, uno::UNO_QUERY); + } + else if ( aPropName == "GraphicSize" ) + { + if (auto aSize = o3tl::tryAccess<css::awt::Size>(rPropValue.Value)) + { + // don't cast awt::Size to Size as on 64-bits they are not the same. + aBuGraSize.setWidth( aSize->Width ); + aBuGraSize.setHeight( aSize->Height ); + } + } + else if ( aPropName == "StartWith" ) + nStartWith = *o3tl::doAccess<sal_Int16>(rPropValue.Value); + else if ( aPropName == "LeftMargin" ) + nTextOfs += convertMm100ToMasterUnit(*o3tl::doAccess<sal_Int32>(rPropValue.Value)); + else if ( aPropName == "FirstLineOffset" ) + nBulletOfs += convertMm100ToMasterUnit(*o3tl::doAccess<sal_Int32>(rPropValue.Value)); + else if ( aPropName == "BulletColor" ) + { + sal_uInt32 nSOColor = *o3tl::doAccess<sal_uInt32>(rPropValue.Value); + nBulletColor = nSOColor & 0xff00ff00; // green and hibyte + nBulletColor |= static_cast<sal_uInt8>(nSOColor) << 16; // red + nBulletColor |= static_cast<sal_uInt8>( nSOColor >> 16 ) | 0xfe000000; // blue + } + else if ( aPropName == "BulletRelSize" ) + { + nBulletRealSize = *o3tl::doAccess<sal_Int16>(rPropValue.Value); + nParaFlags |= 0x40; + nBulletFlags |= 8; + } + else if ( aPropName == "Prefix" ) + sPrefix = *o3tl::doAccess<OUString>(rPropValue.Value); + else if ( aPropName == "Suffix" ) + sSuffix = *o3tl::doAccess<OUString>(rPropValue.Value); +#ifdef DBG_UTIL + else if ( aPropName != "SymbolTextDistance" && aPropName != "GraphicBitmap" ) + { + OSL_FAIL( "Unknown Property" ); + } +#endif + } + + if (xGraphic.is()) + { + if ( aBuGraSize.Width() && aBuGraSize.Height() ) + { + nBulletId = pBuProv->GetId(xGraphic, aBuGraSize ); + if ( nBulletId != 0xffff ) + bExtendedBulletsUsed = true; + } + else + { + nNumberingType = SVX_NUM_NUMBER_NONE; + } + } + + CalculateGraphicBulletSize( ( mvPortions.empty() ) ? 24 : mvPortions.front()->mnCharHeight ); + + switch( nNumberingType ) + { + case SVX_NUM_NUMBER_NONE : nParaFlags |= 0xf; break; + + case SVX_NUM_CHAR_SPECIAL : // Bullet + { + if ( IsStarSymbol(aFontDesc.Name) ) + { + rtl_TextEncoding eChrSet = aFontDesc.CharSet; + cBulletId = msfilter::util::bestFitOpenSymbolToMSFont(cBulletId, eChrSet, aFontDesc.Name); + aFontDesc.CharSet = eChrSet; + } + + if ( !aFontDesc.Name.isEmpty() ) + { + nParaFlags |= 0x90; // we define the font and charset + } + + [[fallthrough]]; + } + case SVX_NUM_CHARS_UPPER_LETTER : // count from a-z, aa - az, ba - bz, ... + case SVX_NUM_CHARS_LOWER_LETTER : + case SVX_NUM_ROMAN_UPPER : + case SVX_NUM_ROMAN_LOWER : + case SVX_NUM_ARABIC : + case SVX_NUM_PAGEDESC : // numbering from the page template + case SVX_NUM_BITMAP : + case SVX_NUM_CHARS_UPPER_LETTER_N : // count from a-z, aa-zz, aaa-zzz + case SVX_NUM_CHARS_LOWER_LETTER_N : + case SVX_NUM_NUMBER_UPPER_ZH: + case SVX_NUM_CIRCLE_NUMBER: + case SVX_NUM_NUMBER_UPPER_ZH_TW: + case SVX_NUM_NUMBER_LOWER_ZH: + case SVX_NUM_FULL_WIDTH_ARABIC: + { + if ( nNumberingType != SVX_NUM_CHAR_SPECIAL ) + { + bExtendedBulletsUsed = true; + if ( nNumberingDepth & 1 ) + cBulletId = 0x2013; // defaulting bullet characters for ppt97 + else if ( nNumberingDepth == 4 ) + cBulletId = 0xbb; + else + cBulletId = 0x2022; + + switch( nNumberingType ) + { + case SVX_NUM_CHARS_UPPER_LETTER : + case SVX_NUM_CHARS_UPPER_LETTER_N : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0xa0001; // (A) + else + nMappedNumType = 0xb0001; // A) + } + else + nMappedNumType = 0x10001; // A. + } + break; + case SVX_NUM_CHARS_LOWER_LETTER : + case SVX_NUM_CHARS_LOWER_LETTER_N : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0x80001; // (a) + else + nMappedNumType = 0x90001; // a) + } + else + nMappedNumType = 0x00001; // a. + } + break; + case SVX_NUM_ROMAN_UPPER : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0xe0001; // (I) + else + nMappedNumType = 0xf0001; // I) + } + else + nMappedNumType = 0x70001; // I. + } + break; + case SVX_NUM_ROMAN_LOWER : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0x40001; // (i) + else + nMappedNumType = 0x50001; // i) + } + else + nMappedNumType = 0x60001; // i. + } + break; + case SVX_NUM_ARABIC : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0xc0001; // (1) + else + nMappedNumType = 0x20001; // 1) + } + else + { + if ( sSuffix.isEmpty() && sPrefix.isEmpty() ) + nMappedNumType = 0xd0001; // 1 + else + nMappedNumType = 0x30001; // 1. + } + } + break; + case SVX_NUM_NUMBER_UPPER_ZH : + { + if ( !sSuffix.isEmpty() ) + nMappedNumType = 0x110001; // Simplified Chinese with single-byte period. + else + nMappedNumType = 0x100001; // Simplified Chinese. + } + break; + case SVX_NUM_CIRCLE_NUMBER : + { + nMappedNumType = 0x120001; // Double byte circle numbers. + } + break; + case SVX_NUM_NUMBER_UPPER_ZH_TW : + { + if ( !sSuffix.isEmpty() ) + nMappedNumType = 0x160001; // Traditional Chinese with single-byte period. + else + nMappedNumType = 0x150001; // Traditional Chinese. + } + break; + case SVX_NUM_NUMBER_LOWER_ZH : + { + if ( sSuffix == u"\uff0e" ) + nMappedNumType = 0x260001; // Japanese with double-byte period. + else if ( !sSuffix.isEmpty() ) + nMappedNumType = 0x1B0001; // Japanese/Korean with single-byte period. + else + nMappedNumType = 0x1A0001; // Japanese/Korean. + } + break; + case SVX_NUM_FULL_WIDTH_ARABIC : + { + if ( !sSuffix.isEmpty() ) + nMappedNumType = 0x1D0001; // Double-byte Arabic numbers with double-byte period. + else + nMappedNumType = 0x1C0001; // Double-byte Arabic numbers. + } + break; + default: + break; + } + } + nParaFlags |= 0x2f; + nBulletFlags |= 6; + if ( mbIsBullet && bNumberingIsNumber ) + nBulletFlags |= 1; + break; + } + default: + break; + } + } + } + } + nBulletOfs = nTextOfs + nBulletOfs; + if ( nBulletOfs < 0 ) + nBulletOfs = 0; +} + +void ParagraphObj::ImplGetParagraphValues( PPTExBulletProvider* pBuProv, bool bGetPropStateValue ) +{ + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "NumberingLevel", true ) ) + { + if ( bGetPropStateValue ) + meBullet = GetPropertyState( mXPropSet, "NumberingLevel" ); + nDepth = *o3tl::doAccess<sal_Int16>(aAny); + + if ( nDepth < 0 ) + { + mbIsBullet = false; + nDepth = 0; + } + else + { + if ( nDepth > 4 ) + nDepth = 4; + mbIsBullet = true; + } + } + else + { + nDepth = 0; + mbIsBullet = false; + } + ImplGetNumberingLevel( pBuProv, nDepth, mbIsBullet, bGetPropStateValue ); + + if ( ImplGetPropertyValue( "ParaTabStops", bGetPropStateValue ) ) + maTabStop = *o3tl::doAccess<css::uno::Sequence<css::style::TabStop>>(mAny); + sal_Int16 eTextAdjust = sal_Int16(css::style::ParagraphAdjust_LEFT); + if ( GetPropertyValue( aAny, mXPropSet, "ParaAdjust", bGetPropStateValue ) ) + aAny >>= eTextAdjust; + switch ( static_cast<css::style::ParagraphAdjust>(eTextAdjust) ) + { + case css::style::ParagraphAdjust_CENTER : + mnTextAdjust = 1; + break; + case css::style::ParagraphAdjust_RIGHT : + mnTextAdjust = 2; + break; + case css::style::ParagraphAdjust_BLOCK : + mnTextAdjust = 3; + break; + default : + case css::style::ParagraphAdjust_LEFT : + mnTextAdjust = 0; + break; + } + meTextAdjust = ePropState; + + if ( ImplGetPropertyValue( "ParaLineSpacing", bGetPropStateValue ) ) + { + css::style::LineSpacing aLineSpacing + = *o3tl::doAccess<css::style::LineSpacing>(mAny); + switch ( aLineSpacing.Mode ) + { + case css::style::LineSpacingMode::FIX : + mnLineSpacing = static_cast<sal_Int16>(-( aLineSpacing.Height ) ); + mbFixedLineSpacing = true; + break; + case css::style::LineSpacingMode::MINIMUM : + case css::style::LineSpacingMode::LEADING : + mnLineSpacing = static_cast<sal_Int16>(-( aLineSpacing.Height ) ); + mbFixedLineSpacing = false; + break; + + case css::style::LineSpacingMode::PROP : + default: + mnLineSpacing = aLineSpacing.Height; + break; + } + } + meLineSpacing = ePropState; + + if ( ImplGetPropertyValue( "ParaBottomMargin", bGetPropStateValue ) ) + { + double fSpacing = *o3tl::doAccess<sal_uInt32>(mAny) + convertMasterUnitToMm100(1.0) - 1; + mnLineSpacingBottom = std::round(-convertMm100ToMasterUnit(fSpacing)); + } + meLineSpacingBottom = ePropState; + + if ( ImplGetPropertyValue( "ParaTopMargin", bGetPropStateValue ) ) + { + double fSpacing = *o3tl::doAccess<sal_uInt32>(mAny) + convertMasterUnitToMm100(1.0) - 1; + mnLineSpacingTop = std::round(-convertMm100ToMasterUnit(fSpacing)); + } + meLineSpacingTop = ePropState; + + if ( ImplGetPropertyValue( "ParaIsForbiddenRules", bGetPropStateValue ) ) + mAny >>= mbForbiddenRules; + meForbiddenRules = ePropState; + + if ( ImplGetPropertyValue( "ParaIsHangingPunctuation", bGetPropStateValue ) ) + mAny >>= mbParagraphPunctation; + meParagraphPunctation = ePropState; + + mnBiDi = 0; + if ( ImplGetPropertyValue( "WritingMode", bGetPropStateValue ) ) + { + sal_Int16 nWritingMode = 0; + mAny >>= nWritingMode; + + SvxFrameDirection eWritingMode = static_cast<SvxFrameDirection>(nWritingMode); + if ( ( eWritingMode == SvxFrameDirection::Horizontal_RL_TB ) + || ( eWritingMode == SvxFrameDirection::Vertical_RL_TB ) ) + { + mnBiDi = 1; + } + } + meBiDi = ePropState; +} + +void ParagraphObj::ImplConstruct( const ParagraphObj& rParagraphObj ) +{ + mbIsBullet = rParagraphObj.mbIsBullet; + meBullet = rParagraphObj.meBullet; + meTextAdjust = rParagraphObj.meTextAdjust; + meLineSpacing = rParagraphObj.meLineSpacing; + meLineSpacingTop = rParagraphObj.meLineSpacingTop; + meLineSpacingBottom = rParagraphObj.meLineSpacingBottom; + meForbiddenRules = rParagraphObj.meForbiddenRules; + meParagraphPunctation = rParagraphObj.meParagraphPunctation; + meBiDi =rParagraphObj.meBiDi; + mbFixedLineSpacing = rParagraphObj.mbFixedLineSpacing; + mnTextSize = rParagraphObj.mnTextSize; + mnTextAdjust = rParagraphObj.mnTextAdjust; + mnLineSpacing = rParagraphObj.mnLineSpacing; + mnLineSpacingTop = rParagraphObj.mnLineSpacingTop; + mnLineSpacingBottom = rParagraphObj.mnLineSpacingBottom; + mbFirstParagraph = rParagraphObj.mbFirstParagraph; + mbLastParagraph = rParagraphObj.mbLastParagraph; + mbParagraphPunctation = rParagraphObj.mbParagraphPunctation; + mbForbiddenRules = rParagraphObj.mbForbiddenRules; + mnBiDi = rParagraphObj.mnBiDi; + + for ( std::vector<std::unique_ptr<PortionObj> >::const_iterator it = rParagraphObj.begin(); it != rParagraphObj.end(); ++it ) + mvPortions.push_back( std::make_unique<PortionObj>( **it ) ); + + maTabStop = rParagraphObj.maTabStop; + bExtendedParameters = rParagraphObj.bExtendedParameters; + nParaFlags = rParagraphObj.nParaFlags; + nBulletFlags = rParagraphObj.nBulletFlags; + sPrefix = rParagraphObj.sPrefix; + sSuffix = rParagraphObj.sSuffix; + sGraphicUrl = rParagraphObj.sGraphicUrl; // String to a graphic + aBuGraSize = rParagraphObj.aBuGraSize; + nNumberingType = rParagraphObj.nNumberingType; // this is actually a SvxEnum + nHorzAdjust = rParagraphObj.nHorzAdjust; + nBulletColor = rParagraphObj.nBulletColor; + nBulletOfs = rParagraphObj.nBulletOfs; + nStartWith = rParagraphObj.nStartWith; // start of numbering + nTextOfs = rParagraphObj.nTextOfs; + nBulletRealSize = rParagraphObj.nBulletRealSize; // scale in percent + nDepth = rParagraphObj.nDepth; // actual depth + cBulletId = rParagraphObj.cBulletId; // if Numbering Type == CharSpecial + aFontDesc = rParagraphObj.aFontDesc; + + bExtendedBulletsUsed = rParagraphObj.bExtendedBulletsUsed; + nBulletId = rParagraphObj.nBulletId; +} + +sal_uInt32 ParagraphObj::ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition ) +{ + mnTextSize = 0; + for ( std::vector<std::unique_ptr<PortionObj> >::iterator it = mvPortions.begin(); it != mvPortions.end(); ++it ) + mnTextSize += (*it)->ImplCalculateTextPositions( nCurrentTextPosition + mnTextSize ); + return mnTextSize; +} + +ParagraphObj& ParagraphObj::operator=( const ParagraphObj& rParagraphObj ) +{ + if ( this != &rParagraphObj ) + { + ImplClear(); + ImplConstruct( rParagraphObj ); + } + return *this; +} + +struct ImplTextObj +{ + sal_uInt32 mnTextSize; + int mnInstance; + std::vector<std::unique_ptr<ParagraphObj>> maList; + bool mbHasExtendedBullets; + + explicit ImplTextObj( int nInstance ); +}; + +ImplTextObj::ImplTextObj( int nInstance ) + : mnTextSize(0), + mnInstance(nInstance), + mbHasExtendedBullets(false) +{ +} + +TextObj::TextObj( css::uno::Reference< css::text::XSimpleText > const & rXTextRef, + int nInstance, FontCollection& rFontCollection, PPTExBulletProvider& rProv ): + mpImplTextObj(std::make_shared<ImplTextObj>(nInstance)) +{ + css::uno::Reference< css::container::XEnumerationAccess > aXTextParagraphEA( rXTextRef, css::uno::UNO_QUERY ); + + if ( aXTextParagraphEA.is() ) + { + css::uno::Reference< css::container::XEnumeration > aXTextParagraphE( aXTextParagraphEA->createEnumeration() ); + if ( aXTextParagraphE.is() ) + { + ParaFlags aParaFlags; + while ( aXTextParagraphE->hasMoreElements() ) + { + css::uno::Reference< css::text::XTextContent > aXParagraph; + css::uno::Any aAny( aXTextParagraphE->nextElement() ); + if ( aAny >>= aXParagraph ) + { + if ( !aXTextParagraphE->hasMoreElements() ) + aParaFlags.bLastParagraph = true; + std::unique_ptr<ParagraphObj> pPara(new ParagraphObj( aXParagraph, aParaFlags, rFontCollection, rProv )); + mpImplTextObj->mbHasExtendedBullets |= pPara->bExtendedBulletsUsed; + mpImplTextObj->maList.push_back( std::move(pPara) ); + aParaFlags.bFirstParagraph = false; + } + } + } + } + ImplCalculateTextPositions(); +} + +void TextObj::ImplCalculateTextPositions() +{ + mpImplTextObj->mnTextSize = 0; + for ( sal_uInt32 i = 0; i < ParagraphCount(); ++i ) + mpImplTextObj->mnTextSize += GetParagraph(i)->ImplCalculateTextPositions( mpImplTextObj->mnTextSize ); +} + +ParagraphObj* TextObj::GetParagraph(int idx) +{ + return mpImplTextObj->maList[idx].get(); +} + +sal_uInt32 TextObj::ParagraphCount() const +{ + return mpImplTextObj->maList.size(); +} + +sal_uInt32 TextObj::Count() const +{ + return mpImplTextObj->mnTextSize; +} + +int TextObj::GetInstance() const +{ + return mpImplTextObj->mnInstance; +} + +bool TextObj::HasExtendedBullets() const +{ + return mpImplTextObj->mbHasExtendedBullets; +} + +void FontCollectionEntry::ImplInit( const OUString& rName ) +{ + OUString aSubstName( GetSubsFontName( rName, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) ); + if ( !aSubstName.isEmpty() ) + { + Name = aSubstName; + } + else + { + Name = rName; + } +} + +FontCollection::~FontCollection() +{ + pVDev.disposeAndClear(); + xPPTBreakIter = nullptr; +} + +FontCollection::FontCollection() : + pVDev ( nullptr ) +{ + xPPTBreakIter = css::i18n::BreakIterator::create( ::comphelper::getProcessComponentContext() ); +} + +short FontCollection::GetScriptDirection( std::u16string_view rString ) +{ + short nRet = ScriptTypeDetector::getScriptDirection( rString, 0, css::i18n::ScriptDirection::NEUTRAL ); + return nRet; +} + +sal_uInt32 FontCollection::GetId( FontCollectionEntry& rEntry ) +{ + if( !rEntry.Name.isEmpty() ) + { + const sal_uInt32 nFonts = maFonts.size(); + + for( sal_uInt32 i = 0; i < nFonts; i++ ) + { + const FontCollectionEntry* pEntry = GetById( i ); + if( pEntry->Name == rEntry.Name ) + return i; + } + vcl::Font aFont; + aFont.SetCharSet( rEntry.CharSet ); + aFont.SetFamilyName( rEntry.Original ); + aFont.SetFontHeight( 100 ); + + if ( !pVDev ) + pVDev = VclPtr<VirtualDevice>::Create(); + + pVDev->SetFont( aFont ); + FontMetric aMetric( pVDev->GetFontMetric() ); + + sal_uInt16 nTxtHeight = static_cast<sal_uInt16>(aMetric.GetAscent()) + static_cast<sal_uInt16>(aMetric.GetDescent()); + + if ( nTxtHeight ) + { + double fScaling = static_cast<double>(nTxtHeight) / 120.0; + if ( ( fScaling > 0.50 ) && ( fScaling < 1.5 ) ) + rEntry.Scaling = fScaling; + } + + maFonts.push_back(rEntry); + return nFonts; + } + return 0; +} + +const FontCollectionEntry* FontCollection::GetById( sal_uInt32 nId ) +{ + return nId < maFonts.size() ? &maFonts[nId] : nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/text.hxx b/sd/source/filter/eppt/text.hxx new file mode 100644 index 000000000..ee2fc537c --- /dev/null +++ b/sd/source/filter/eppt/text.hxx @@ -0,0 +1,254 @@ +/* -*- 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 "epptbase.hxx" + +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <editeng/svxenum.hxx> +#include <vector> +#include <memory> + +namespace com::sun::star { + namespace awt { struct FontDescriptor; } + namespace beans { class XPropertyState; } + namespace text { class XTextRange; class XTextContent; class XSimpleText; } + namespace style { struct TabStop; } +} + +struct SOParagraph +{ + bool bExtendedParameters; + sal_uInt32 nParaFlags; + sal_Int16 nBulletFlags; + OUString sPrefix; + OUString sSuffix; + OUString sGraphicUrl; // String to a graphic + Size aBuGraSize; + SvxNumType nNumberingType; + sal_uInt32 nHorzAdjust; + sal_uInt32 nBulletColor; + sal_Int32 nBulletOfs; + sal_Int16 nStartWith; // start of numbering + sal_Int16 nTextOfs; + sal_Int16 nBulletRealSize; // scale in percent + sal_Int16 nDepth; // actual depth + sal_Unicode cBulletId; // if Numbering Type == CharSpecial + css::awt::FontDescriptor aFontDesc; + + bool bExtendedBulletsUsed; + sal_uInt16 nBulletId; + sal_uInt32 nMappedNumType; + bool bNumberingIsNumber; + + SOParagraph() + : bExtendedParameters(false) + , nParaFlags(0) + , nBulletFlags(0) + , nNumberingType(SVX_NUM_NUMBER_NONE) + , nHorzAdjust(0) + , nBulletColor(0) + , nBulletOfs(0) + , nStartWith(0) + , nTextOfs(0) + , nBulletRealSize(0) + , nDepth(0) + , cBulletId(0) + , bExtendedBulletsUsed(false) + , nBulletId(0xffff) + , nMappedNumType(0) + , bNumberingIsNumber(true) + { + } +}; + +class PropStateValue : public PropValue +{ +public: + PropStateValue() + : PropValue() + , ePropState(css::beans::PropertyState_AMBIGUOUS_VALUE) + { + } +protected: + css::beans::PropertyState ePropState; + css::uno::Reference < css::beans::XPropertyState > mXPropState; + + bool ImplGetPropertyValue( const OUString& rString, bool bGetPropertyState ); +}; + +struct FieldEntry +{ + sal_uInt32 nFieldType; + sal_uInt32 nFieldStartPos; + sal_uInt32 nFieldEndPos; + OUString aRepresentation; + OUString aFieldUrl; + + FieldEntry( sal_uInt32 nType, sal_uInt32 nStart, sal_uInt32 nEnd ) + : nFieldType(nType), + nFieldStartPos(nStart), + nFieldEndPos(nEnd) + { + } +}; + +class PortionObj final : public PropStateValue +{ + + friend class ParagraphObj; + + void ImplClear(); + void ImplConstruct( const PortionObj& rPortionObj ); + static sal_uInt32 ImplGetTextField( css::uno::Reference< css::text::XTextRange > & rXTextRangeRef, + const css::uno::Reference< css::beans::XPropertySet > & rXPropSetRef, OUString& rURL ); + sal_uInt32 ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition ); + void ImplGetPortionValues( FontCollection& rFontCollection, bool bGetPropStateValue ); + + public: + + css::beans::PropertyState meCharColor; + css::beans::PropertyState meCharHeight; + css::beans::PropertyState meFontName; + css::beans::PropertyState meAsianOrComplexFont; + css::beans::PropertyState meCharEscapement; + css::lang::Locale meCharLocale; + sal_uInt16 mnCharAttrHard; + + sal_uInt32 mnCharColor; + sal_uInt16 mnCharAttr; + sal_uInt16 mnCharHeight; + sal_uInt16 mnFont; + sal_uInt16 mnAsianOrComplexFont; + sal_Int16 mnCharEscapement; + + sal_uInt32 mnTextSize; + bool mbLastPortion; + + std::unique_ptr<sal_uInt16[]> mpText; + std::unique_ptr<FieldEntry> mpFieldEntry; + + PortionObj( css::uno::Reference< css::text::XTextRange > & rXTextRangeRef, + bool bLast, FontCollection& rFontCollection ); + PortionObj( const css::uno::Reference< css::beans::XPropertySet > & rXPropSetRef, + FontCollection& rFontCollection ); + PortionObj( const PortionObj& rPortionObj ); + ~PortionObj(); + + void Write( SvStream* pStrm, bool bLast ); + sal_uInt32 Count() const { return mnTextSize; }; + + PortionObj& operator=( const PortionObj& rPortionObj ); +}; + +struct ParaFlags +{ + bool bFirstParagraph : 1; + bool bLastParagraph : 1; + + ParaFlags() { bFirstParagraph = true; bLastParagraph = false; }; +}; + +class ParagraphObj : public PropStateValue, public SOParagraph +{ + friend class TextObj; + friend struct PPTExParaSheet; + + std::vector<std::unique_ptr<PortionObj> > mvPortions; + + protected: + + void ImplConstruct( const ParagraphObj& rParagraphObj ); + void ImplClear(); + sal_uInt32 ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition ); + void ImplGetParagraphValues( PPTExBulletProvider* pBuProv, bool bGetPropStateValue ); + void ImplGetNumberingLevel( PPTExBulletProvider* pBuProv, sal_Int16 nDepth, bool bIsBullet, bool bGetPropStateValue ); + + public: + + css::uno::Sequence< css::style::TabStop > maTabStop; + + sal_uInt32 mnTextSize; + + bool mbIsBullet; + bool mbFirstParagraph; + bool mbLastParagraph; + + css::beans::PropertyState meBullet; + css::beans::PropertyState meTextAdjust; + css::beans::PropertyState meLineSpacing; + css::beans::PropertyState meLineSpacingTop; + css::beans::PropertyState meLineSpacingBottom; + css::beans::PropertyState meForbiddenRules; + css::beans::PropertyState meParagraphPunctation; + css::beans::PropertyState meBiDi; + + sal_uInt16 mnTextAdjust; + sal_Int16 mnLineSpacing; + bool mbFixedLineSpacing; + sal_Int16 mnLineSpacingTop; + sal_Int16 mnLineSpacingBottom; + bool mbForbiddenRules; + bool mbParagraphPunctation; + sal_uInt16 mnBiDi; + + ParagraphObj( css::uno::Reference< css::text::XTextContent > const & rXTextContentRef, + ParaFlags, FontCollection& rFontCollection, + PPTExBulletProvider& rBuProv ); + ParagraphObj( const ParagraphObj& rParargraphObj ) = delete; + ParagraphObj( const css::uno::Reference< css::beans::XPropertySet > & rXPropSetRef, + PPTExBulletProvider* pBuProv ); + + bool empty() const { return mvPortions.empty(); } + + const PortionObj& front() const { return *mvPortions.front(); } + + std::vector<std::unique_ptr<PortionObj> >::const_iterator begin() const { return mvPortions.begin(); } + std::vector<std::unique_ptr<PortionObj> >::const_iterator end() const { return mvPortions.end(); } + + void CalculateGraphicBulletSize( sal_uInt16 nFontHeight ); + ~ParagraphObj(); + + void Write( SvStream* pStrm ); + sal_uInt32 CharacterCount() const { return mnTextSize; }; + + ParagraphObj& operator=( const ParagraphObj& rParagraphObj ); +}; + +struct ImplTextObj; + +class TextObj +{ + std::shared_ptr<ImplTextObj> mpImplTextObj; + void ImplCalculateTextPositions(); + +public: + TextObj( css::uno::Reference< css::text::XSimpleText > const & + rXText, int nInstance, FontCollection& rFontCollection, PPTExBulletProvider& rBuProv ); + + ParagraphObj* GetParagraph(int idx); + sal_uInt32 ParagraphCount() const; + sal_uInt32 Count() const; + int GetInstance() const; + bool HasExtendedBullets() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/grf/sdgrffilter.cxx b/sd/source/filter/grf/sdgrffilter.cxx new file mode 100644 index 000000000..46ed24f3b --- /dev/null +++ b/sd/source/filter/grf/sdgrffilter.cxx @@ -0,0 +1,304 @@ +/* -*- 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 <com/sun/star/drawing/GraphicExportFilter.hpp> + +#include <vcl/errinf.hxx> +#include <vcl/weld.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/sfxuno.hxx> +#include <svx/svdograf.hxx> + +#include <strings.hrc> +#include <DrawViewShell.hxx> +#include <DrawDocShell.hxx> + +#include <comphelper/processfactory.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/svapp.hxx> + +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <sdresid.hxx> +#include <sdgrffilter.hxx> +#include <ViewShellBase.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/PropertyValues.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/task/XInteractionRequest.hpp> +#include <com/sun/star/drawing/GraphicFilterRequest.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::graphic; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::ucb; +using namespace com::sun::star::ui::dialogs; +using namespace ::sfx2; + +namespace { + +class SdGRFFilter_ImplInteractionHdl : public ::cppu::WeakImplHelper< css::task::XInteractionHandler > +{ + css::uno::Reference< css::task::XInteractionHandler > m_xInter; + ErrCode nFilterError; + + public: + + explicit SdGRFFilter_ImplInteractionHdl( css::uno::Reference< css::task::XInteractionHandler > const & xInteraction ) : + m_xInter( xInteraction ), + nFilterError( ERRCODE_NONE ) + {} + + ErrCode const & GetErrorCode() const { return nFilterError; }; + + virtual void SAL_CALL handle( const css::uno::Reference< css::task::XInteractionRequest >& ) override; +}; + +} + +void SdGRFFilter_ImplInteractionHdl::handle( const css::uno::Reference< css::task::XInteractionRequest >& xRequest ) +{ + if( !m_xInter.is() ) + return; + + css::drawing::GraphicFilterRequest aErr; + if ( xRequest->getRequest() >>= aErr ) + nFilterError = ErrCode(aErr.ErrCode); + else + m_xInter->handle( xRequest ); +} + + +SdGRFFilter::SdGRFFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell ) : + SdFilter( rMedium, rDocShell ) +{ +} + +SdGRFFilter::~SdGRFFilter() +{ +} + +void SdGRFFilter::HandleGraphicFilterError( ErrCode nFilterError, ErrCode nStreamError ) +{ + if (ERRCODE_NONE != nStreamError) + { + ErrorHandler::HandleError(nStreamError); + return; + } + + TranslateId pId; + + if( nFilterError == ERRCODE_GRFILTER_OPENERROR ) + pId = STR_IMPORT_GRFILTER_OPENERROR; + else if( nFilterError == ERRCODE_GRFILTER_IOERROR ) + pId = STR_IMPORT_GRFILTER_IOERROR; + else if( nFilterError == ERRCODE_GRFILTER_FORMATERROR ) + pId = STR_IMPORT_GRFILTER_FORMATERROR; + else if( nFilterError == ERRCODE_GRFILTER_VERSIONERROR ) + pId = STR_IMPORT_GRFILTER_VERSIONERROR; + else if( nFilterError == ERRCODE_GRFILTER_TOOBIG ) + pId = STR_IMPORT_GRFILTER_TOOBIG; + else if( nFilterError == ERRCODE_NONE ) + ; + else + pId = STR_IMPORT_GRFILTER_FILTERERROR; + + if (pId && pId == STR_IMPORT_GRFILTER_IOERROR) + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + else + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::Ok, pId ? SdResId(pId) : OUString())); + xErrorBox->run(); + } +} + +bool SdGRFFilter::Import() +{ + Graphic aGraphic; + const OUString aFileName( mrMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); + const sal_uInt16 nFilter = rGraphicFilter.GetImportFormatNumberForTypeName( mrMedium.GetFilter()->GetTypeName() ); + bool bRet = false; + + SvStream* pIStm = mrMedium.GetInStream(); + ErrCode nReturn = pIStm ? rGraphicFilter.ImportGraphic( aGraphic, aFileName, *pIStm, nFilter ) : ErrCode(1); + + if( nReturn ) + HandleGraphicFilterError( nReturn, rGraphicFilter.GetLastError() ); + else + { + if( mrDocument.GetPageCount() == 0 ) + mrDocument.CreateFirstPages(); + + SdPage* pPage = mrDocument.GetSdPage( 0, PageKind::Standard ); + Point aPos; + Size aPagSize( pPage->GetSize() ); + Size aGrfSize( OutputDevice::LogicToLogic( aGraphic.GetPrefSize(), + aGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM))); + + aPagSize.AdjustWidth( -(pPage->GetLeftBorder() + pPage->GetRightBorder()) ); + aPagSize.AdjustHeight( -(pPage->GetUpperBorder() + pPage->GetLowerBorder()) ); + + // scale to fit page + if ( ( ( aGrfSize.Height() > aPagSize.Height() ) || ( aGrfSize.Width() > aPagSize.Width() ) ) && + aGrfSize.Height() && aPagSize.Height() ) + { + double fGrfWH = static_cast<double>(aGrfSize.Width()) / aGrfSize.Height(); + double fWinWH = static_cast<double>(aPagSize.Width()) / aPagSize.Height(); + + // adjust graphic to page size (scales) + if( fGrfWH < fWinWH ) + { + aGrfSize.setWidth( static_cast<tools::Long>( aPagSize.Height() * fGrfWH ) ); + aGrfSize.setHeight( aPagSize.Height() ); + } + else if( fGrfWH > 0.F ) + { + aGrfSize.setWidth( aPagSize.Width() ); + aGrfSize.setHeight( static_cast<tools::Long>( aPagSize.Width() / fGrfWH ) ); + } + } + + // set output rectangle for graphic + aPos.setX( ( ( aPagSize.Width() - aGrfSize.Width() ) >> 1 ) + pPage->GetLeftBorder() ); + aPos.setY( ( ( aPagSize.Height() - aGrfSize.Height() ) >> 1 ) + pPage->GetUpperBorder() ); + + pPage->InsertObject( + new SdrGrafObj( + pPage->getSdrModelFromSdrPage(), + aGraphic, + ::tools::Rectangle(aPos, aGrfSize))); + bRet = true; + } + + return bRet; +} + +bool SdGRFFilter::Export() +{ + // SJ: todo: error handling, the GraphicExportFilter does not support proper errorhandling + bool bRet = false; + + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< drawing::XGraphicExportFilter > xExporter = drawing::GraphicExportFilter::create( xContext ); + + SdPage* pPage = nullptr; + sd::DrawViewShell* pDrawViewShell = dynamic_cast<::sd::DrawViewShell* >(mrDocShell.GetViewShell() ); + + PageKind ePageKind = PageKind::Standard; + if( pDrawViewShell ) + { + ePageKind = pDrawViewShell->GetPageKind(); + if( PageKind::Handout == ePageKind ) + pPage = mrDocument.GetSdPage( 0, PageKind::Handout ); + else + pPage = pDrawViewShell->GetActualPage(); + } + else + pPage = mrDocument.GetSdPage( 0, PageKind::Standard ); + + if ( pPage ) + { + // taking the 'correct' page number, seems that there might exist a better method to archive this + pPage = mrDocument.GetSdPage( pPage->GetPageNum() ? ( pPage->GetPageNum() - 1 ) >> 1 : 0, ePageKind ); + if ( pPage ) + { + uno::Reference< lang::XComponent > xSource( pPage->getUnoPage(), uno::UNO_QUERY ); + SfxItemSet* pSet = mrMedium.GetItemSet(); + if ( pSet && xSource.is() ) + { + const OUString aTypeName( mrMedium.GetFilter()->GetTypeName() ); + GraphicFilter &rGraphicFilter = GraphicFilter::GetGraphicFilter(); + const sal_uInt16 nFilter = rGraphicFilter.GetExportFormatNumberForTypeName( aTypeName ); + if ( nFilter != GRFILTER_FORMAT_NOTFOUND ) + { + uno::Reference< task::XInteractionHandler > xInteractionHandler; + + beans::PropertyValues aArgs; + TransformItems( SID_SAVEASDOC, *pSet, aArgs ); + + static const OUStringLiteral sFilterName( u"FilterName" ); + OUString sShortName( rGraphicFilter.GetExportFormatShortName( nFilter ) ); + + bool bFilterNameFound = false; + for ( auto& rArg : asNonConstRange(aArgs) ) + { + OUString& rStr = rArg.Name; + if ( rStr == sFilterName ) + { + bFilterNameFound = true; + rArg.Value <<= sShortName; + } + else if ( rStr == "InteractionHandler" ) + { + uno::Reference< task::XInteractionHandler > xHdl; + if ( rArg.Value >>= xHdl ) + { + xInteractionHandler = new SdGRFFilter_ImplInteractionHdl( xHdl ); + rArg.Value <<= xInteractionHandler; + } + } + } + if ( !bFilterNameFound ) + { + sal_Int32 nCount = aArgs.getLength(); + aArgs.realloc( nCount + 1 ); + auto pArgs = aArgs.getArray(); + pArgs[ nCount ].Name = sFilterName; + pArgs[ nCount ].Value <<= sShortName; + } + + // take selection if needed + if( ( SfxItemState::SET == pSet->GetItemState( SID_SELECTION ) ) + && pSet->Get( SID_SELECTION ).GetValue() + && pDrawViewShell ) + { + uno::Reference< view::XSelectionSupplier > xSelectionSupplier( + pDrawViewShell->GetViewShellBase().GetController(), uno::UNO_QUERY ); + if ( xSelectionSupplier.is() ) + { + uno::Any aSelection( xSelectionSupplier->getSelection() ); + uno::Reference< lang::XComponent > xSelection; + if ( aSelection >>= xSelection ) + xSource = xSelection; + } + } + xExporter->setSourceDocument( xSource ); + bRet = xExporter->filter( aArgs ); + if ( !bRet && xInteractionHandler.is() ) + SdGRFFilter::HandleGraphicFilterError( + static_cast< SdGRFFilter_ImplInteractionHdl* >( xInteractionHandler.get() )->GetErrorCode(), + rGraphicFilter.GetLastError() ); + } + } + } + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/HtmlOptionsDialog.cxx b/sd/source/filter/html/HtmlOptionsDialog.cxx new file mode 100644 index 000000000..78939dc4d --- /dev/null +++ b/sd/source/filter/html/HtmlOptionsDialog.cxx @@ -0,0 +1,203 @@ +/* -*- 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 <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/uno/Any.h> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/beans/XPropertyAccess.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <vcl/svapp.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::document; +using namespace com::sun::star::beans; +using namespace com::sun::star::ui::dialogs; + +#include <pres.hxx> +#include <sdabstdlg.hxx> + +namespace { + +class SdHtmlOptionsDialog : public cppu::WeakImplHelper +< + XExporter, + XExecutableDialog, + XPropertyAccess, + XInitialization, + XServiceInfo +> +{ + Sequence< PropertyValue > maMediaDescriptor; + Sequence< PropertyValue > maFilterDataSequence; + DocumentType meDocType; + +public: + + SdHtmlOptionsDialog(); + + // XInterface + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any > & aArguments ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XPropertyAccess + virtual Sequence< PropertyValue > SAL_CALL getPropertyValues() override; + virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue > & aProps ) override; + + // XExecuteDialog + virtual sal_Int16 SAL_CALL execute() override; + virtual void SAL_CALL setTitle( const OUString& aTitle ) override; + + // XExporter + virtual void SAL_CALL setSourceDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) override; + +}; + +} + +SdHtmlOptionsDialog::SdHtmlOptionsDialog() : + meDocType ( DocumentType::Draw ) +{ +} + +void SAL_CALL SdHtmlOptionsDialog::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL SdHtmlOptionsDialog::release() noexcept +{ + OWeakObject::release(); +} + +// XInitialization +void SAL_CALL SdHtmlOptionsDialog::initialize( const Sequence< Any > & ) +{ +} + +// XServiceInfo +OUString SAL_CALL SdHtmlOptionsDialog::getImplementationName() +{ + return "com.sun.star.comp.draw.SdHtmlOptionsDialog"; +} + +sal_Bool SAL_CALL SdHtmlOptionsDialog::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SAL_CALL SdHtmlOptionsDialog::getSupportedServiceNames() +{ + return { "com.sun.star.ui.dialog.FilterOptionsDialog" }; +} + +// XPropertyAccess +Sequence< PropertyValue > SdHtmlOptionsDialog::getPropertyValues() +{ + auto pProp = std::find_if(std::cbegin(maMediaDescriptor), std::cend(maMediaDescriptor), + [](const PropertyValue& rProp) { return rProp.Name == "FilterData"; }); + auto i = static_cast<sal_Int32>(std::distance(std::cbegin(maMediaDescriptor), pProp)); + sal_Int32 nCount = maMediaDescriptor.getLength(); + if ( i == nCount ) + maMediaDescriptor.realloc( ++nCount ); + + // the "FilterData" Property is an Any that will contain our PropertySequence of Values + auto& el = maMediaDescriptor.getArray()[ i ]; + el.Name = "FilterData"; + el.Value <<= maFilterDataSequence; + return maMediaDescriptor; +} + +void SdHtmlOptionsDialog::setPropertyValues( const Sequence< PropertyValue > & aProps ) +{ + maMediaDescriptor = aProps; + + auto pProp = std::find_if(std::cbegin(maMediaDescriptor), std::cend(maMediaDescriptor), + [](const PropertyValue& rProp) { return rProp.Name == "FilterData"; }); + if (pProp != std::cend(maMediaDescriptor)) + pProp->Value >>= maFilterDataSequence; +} + +// XExecutableDialog +void SdHtmlOptionsDialog::setTitle( const OUString& ) +{ +} + +sal_Int16 SdHtmlOptionsDialog::execute() +{ + sal_Int16 nRet = ExecutableDialogResults::CANCEL; + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSdPublishingDlg> pDlg(pFact->CreateSdPublishingDlg(nullptr /*TODO*/, meDocType)); + if( pDlg->Execute() ) + { + pDlg->GetParameterSequence( maFilterDataSequence ); + nRet = ExecutableDialogResults::OK; + } + else + { + nRet = ExecutableDialogResults::CANCEL; + } + return nRet; +} + +// XEmporter +void SdHtmlOptionsDialog::setSourceDocument( const Reference< XComponent >& xDoc ) +{ + // try to set the corresponding metric unit + Reference< XServiceInfo > xServiceInfo(xDoc, UNO_QUERY); + if ( xServiceInfo.is() ) + { + if ( xServiceInfo->supportsService( "com.sun.star.presentation.PresentationDocument" ) ) + { + meDocType = DocumentType::Impress; + return; + } + else if ( xServiceInfo->supportsService( "com.sun.star.drawing.DrawingDocument" ) ) + { + meDocType = DocumentType::Draw; + return; + } + } + throw IllegalArgumentException(); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_draw_SdHtmlOptionsDialog_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SdHtmlOptionsDialog()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/buttonset.cxx b/sd/source/filter/html/buttonset.cxx new file mode 100644 index 000000000..3929f7422 --- /dev/null +++ b/sd/source/filter/html/buttonset.cxx @@ -0,0 +1,290 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/io/XStream.hpp> + +#include <o3tl/safeint.hxx> +#include <osl/file.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/oslfile2streamwrap.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <vcl/graph.hxx> +#include <vcl/virdev.hxx> +#include <vcl/image.hxx> +#include <unotools/pathoptions.hxx> +#include <tools/diagnose_ex.h> + +#include <memory> + +#include "buttonset.hxx" + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::graphic; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; + +namespace { + +class ButtonsImpl +{ +public: + explicit ButtonsImpl( const OUString& rURL ); + + Reference< XInputStream > getInputStream( const OUString& rName ); + + bool getGraphic( const Reference< XGraphicProvider >& xGraphicProvider, const OUString& rName, Graphic& rGraphic ); + + bool copyGraphic( const OUString& rName, const OUString& rPath ); + +private: + Reference< XStorage > mxStorage; +}; + +} + +ButtonsImpl::ButtonsImpl( const OUString& rURL ) +{ + try + { + mxStorage = comphelper::OStorageHelper::GetStorageOfFormatFromURL( ZIP_STORAGE_FORMAT_STRING, rURL, ElementModes::READ ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::ButtonsImpl::ButtonsImpl()" ); + } +} + +Reference< XInputStream > ButtonsImpl::getInputStream( const OUString& rName ) +{ + Reference< XInputStream > xInputStream; + if( mxStorage.is() ) try + { + Reference< XStream > xStream( mxStorage->openStreamElement( rName, ElementModes::READ ) ); + if( xStream.is() ) + xInputStream = xStream->getInputStream(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::ButtonsImpl::getInputStream()" ); + } + return xInputStream; +} + +bool ButtonsImpl::getGraphic( const Reference< XGraphicProvider >& xGraphicProvider, const OUString& rName, Graphic& rGraphic ) +{ + Reference< XInputStream > xInputStream( getInputStream( rName ) ); + if( xInputStream.is() && xGraphicProvider.is() ) try + { + Sequence< PropertyValue > aMediaProperties{ comphelper::makePropertyValue( + "InputStream", xInputStream) }; + Reference< XGraphic > xGraphic( xGraphicProvider->queryGraphic( aMediaProperties ) ); + + if( xGraphic.is() ) + { + rGraphic = Graphic( xGraphic ); + return true; + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::ButtonsImpl::getGraphic()" ); + } + return false; +} + +bool ButtonsImpl::copyGraphic( const OUString& rName, const OUString& rPath ) +{ + Reference< XInputStream > xInput( getInputStream( rName ) ); + if( xInput.is() ) try + { + osl::File::remove( rPath ); + osl::File aOutputFile( rPath ); + if( aOutputFile.open( osl_File_OpenFlag_Write|osl_File_OpenFlag_Create ) == osl::FileBase::E_None ) + { + Reference< XOutputStream > xOutput( new comphelper::OSLOutputStreamWrapper( aOutputFile ) ); + comphelper::OStorageHelper::CopyInputToOutput( xInput, xOutput ); + return true; + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::ButtonsImpl::copyGraphic()" ); + } + + return false; +} + +class ButtonSetImpl +{ +public: + ButtonSetImpl(); + + int getCount() const; + + bool getPreview( int nSet, const std::vector< OUString >& rButtons, Image& rImage ); + bool exportButton( int nSet, const OUString& rPath, const OUString& rName ); + + void scanForButtonSets( const OUString& rPath ); + + Reference< XGraphicProvider > const & getGraphicProvider(); + + std::vector< std::shared_ptr< ButtonsImpl > > maButtons; + Reference< XGraphicProvider > mxGraphicProvider; +}; + +ButtonSetImpl::ButtonSetImpl() +{ + static const char sSubPath[] = "/wizard/web/buttons" ; + + OUString sSharePath = SvtPathOptions().GetConfigPath() + + sSubPath; + scanForButtonSets( sSharePath ); + + OUString sUserPath = SvtPathOptions().GetUserConfigPath() + + sSubPath; + scanForButtonSets( sUserPath ); +} + +void ButtonSetImpl::scanForButtonSets( const OUString& rPath ) +{ + osl::Directory aDirectory( rPath ); + if( aDirectory.open() != osl::FileBase::E_None ) + return; + + osl::DirectoryItem aItem; + while( aDirectory.getNextItem( aItem, 2211 ) == osl::FileBase::E_None ) + { + osl::FileStatus aStatus( osl_FileStatus_Mask_FileName|osl_FileStatus_Mask_FileURL ); + if( aItem.getFileStatus( aStatus ) == osl::FileBase::E_None ) + { + OUString sFileName( aStatus.getFileName() ); + if( sFileName.endsWithIgnoreAsciiCase( ".zip" ) ) + maButtons.push_back( std::make_shared< ButtonsImpl >( aStatus.getFileURL() ) ); + } + } +} + +int ButtonSetImpl::getCount() const +{ + return maButtons.size(); +} + +bool ButtonSetImpl::getPreview( int nSet, const std::vector< OUString >& rButtons, Image& rImage ) +{ + if( (nSet >= 0) && (o3tl::make_unsigned(nSet) < maButtons.size())) + { + ButtonsImpl& rSet = *maButtons[nSet]; + + std::vector< Graphic > aGraphics; + + ScopedVclPtrInstance< VirtualDevice > pDev; + pDev->SetMapMode(MapMode(MapUnit::MapPixel)); + + Size aSize; + std::vector< OUString >::const_iterator aIter( rButtons.begin() ); + while( aIter != rButtons.end() ) + { + Graphic aGraphic; + if( !rSet.getGraphic( getGraphicProvider(), (*aIter++), aGraphic ) ) + return false; + + aGraphics.push_back(aGraphic); + + Size aGraphicSize( aGraphic.GetSizePixel( pDev ) ); + aSize.AdjustWidth(aGraphicSize.Width() ); + + if( aSize.Height() < aGraphicSize.Height() ) + aSize.setHeight( aGraphicSize.Height() ); + + if( aIter != rButtons.end() ) + aSize.AdjustWidth(3 ); + } + + pDev->SetOutputSizePixel( aSize ); + + Point aPos; + + for( const Graphic& aGraphic : aGraphics ) + { + aGraphic.Draw(*pDev, aPos); + + aPos.AdjustX(aGraphic.GetSizePixel().Width() + 3 ); + } + + rImage = Image( pDev->GetBitmapEx( Point(), aSize ) ); + return true; + } + return false; +} + +bool ButtonSetImpl::exportButton( int nSet, const OUString& rPath, const OUString& rName ) +{ + if( (nSet >= 0) && (o3tl::make_unsigned(nSet) < maButtons.size())) + { + ButtonsImpl& rSet = *maButtons[nSet]; + + return rSet.copyGraphic( rName, rPath ); + } + return false; +} + +Reference< XGraphicProvider > const & ButtonSetImpl::getGraphicProvider() +{ + if( !mxGraphicProvider.is() ) + { + Reference< XComponentContext > xComponentContext = ::comphelper::getProcessComponentContext(); + mxGraphicProvider = GraphicProvider::create(xComponentContext); + } + return mxGraphicProvider; +} + +ButtonSet::ButtonSet() +: mpImpl( new ButtonSetImpl() ) +{ +} + +ButtonSet::~ButtonSet() +{ +} + +int ButtonSet::getCount() const +{ + return mpImpl->getCount(); +} + +bool ButtonSet::getPreview( int nSet, const std::vector< OUString >& rButtons, Image& rImage ) +{ + return mpImpl->getPreview( nSet, rButtons, rImage ); +} + +bool ButtonSet::exportButton( int nSet, const OUString& rPath, const OUString& rName ) +{ + return mpImpl->exportButton( nSet, rPath, rName ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/buttonset.hxx b/sd/source/filter/html/buttonset.hxx new file mode 100644 index 000000000..4289c10e9 --- /dev/null +++ b/sd/source/filter/html/buttonset.hxx @@ -0,0 +1,46 @@ +/* -*- 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 <sddllapi.h> + +#include <rtl/ustring.hxx> +#include <vector> +#include <memory> + +class Image; +class ButtonSetImpl; + +class SD_DLLPUBLIC ButtonSet +{ +public: + ButtonSet(); + ~ButtonSet(); + + int getCount() const; + + bool getPreview(int nSet, const std::vector<OUString>& rButtons, Image& rImage); + bool exportButton(int nSet, const OUString& rPath, const OUString& rName); + +private: + std::unique_ptr<ButtonSetImpl> mpImpl; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlattr.cxx b/sd/source/filter/html/htmlattr.cxx new file mode 100644 index 000000000..b89ac9b6b --- /dev/null +++ b/sd/source/filter/html/htmlattr.cxx @@ -0,0 +1,72 @@ +/* -*- 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 "htmlattr.hxx" +#include <sdresid.hxx> +#include <strings.hrc> +#include <vcl/outdev.hxx> + +SdHtmlAttrPreview::SdHtmlAttrPreview() +{ +} + +SdHtmlAttrPreview::~SdHtmlAttrPreview() +{ +} + +void SdHtmlAttrPreview::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) +{ + ::tools::Rectangle aTextRect; + aTextRect.SetSize(GetOutputSizePixel()); + + rRenderContext.SetLineColor(m_aBackColor); + rRenderContext.SetFillColor(m_aBackColor); + rRenderContext.DrawRect(rRect); + rRenderContext.SetFillColor(); + + int nHeight = (aTextRect.Bottom() - aTextRect.Top()) >> 2; + aTextRect.SetBottom( nHeight + aTextRect.Top() ); + + rRenderContext.SetTextColor(m_aTextColor); + rRenderContext.DrawText(aTextRect, SdResId(STR_HTMLATTR_TEXT), DrawTextFlags::Center | DrawTextFlags::VCenter); + + aTextRect.Move(0,nHeight); + rRenderContext.SetTextColor(m_aLinkColor); + rRenderContext.DrawText(aTextRect, SdResId(STR_HTMLATTR_LINK), DrawTextFlags::Center | DrawTextFlags::VCenter); + + aTextRect.Move(0,nHeight); + rRenderContext.SetTextColor(m_aALinkColor); + rRenderContext.DrawText(aTextRect, SdResId(STR_HTMLATTR_ALINK), DrawTextFlags::Center | DrawTextFlags::VCenter); + + aTextRect.Move(0,nHeight); + rRenderContext.SetTextColor(m_aVLinkColor); + rRenderContext.DrawText(aTextRect, SdResId(STR_HTMLATTR_VLINK), DrawTextFlags::Center | DrawTextFlags::VCenter); +} + +void SdHtmlAttrPreview::SetColors(Color const & aBack, Color const & aText, Color const & aLink, + Color const & aVLink, Color const & aALink) +{ + m_aBackColor = aBack; + m_aTextColor = aText; + m_aLinkColor = aLink; + m_aVLinkColor = aVLink; + m_aALinkColor = aALink; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlattr.hxx b/sd/source/filter/html/htmlattr.hxx new file mode 100644 index 000000000..bf80b9e4b --- /dev/null +++ b/sd/source/filter/html/htmlattr.hxx @@ -0,0 +1,40 @@ +/* -*- 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 <vcl/customweld.hxx> +#include <tools/color.hxx> + +class SdHtmlAttrPreview final : public weld::CustomWidgetController +{ + Color m_aBackColor, m_aTextColor, m_aLinkColor; + Color m_aVLinkColor, m_aALinkColor; + +public: + SdHtmlAttrPreview(); + virtual ~SdHtmlAttrPreview() override; + + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override; + + void SetColors( Color const & aBack, Color const & aText, Color const & aLink, + Color const & aVLink, Color const & aALink ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlex.cxx b/sd/source/filter/html/htmlex.cxx new file mode 100644 index 000000000..072ac3c27 --- /dev/null +++ b/sd/source/filter/html/htmlex.cxx @@ -0,0 +1,3186 @@ +/* -*- 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/drawing/GraphicExportFilter.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + +#include <sal/log.hxx> +#include <rtl/tencinfo.h> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <o3tl/safeint.hxx> +#include <osl/file.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <com/sun/star/frame/XStorable.hpp> +#include <sfx2/frmhtmlw.hxx> +#include <sfx2/progress.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svx/svditer.hxx> +#include <vcl/imaprect.hxx> +#include <vcl/imapcirc.hxx> +#include <vcl/imappoly.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editobj.hxx> +#include <svx/svdopath.hxx> +#include <svtools/htmlout.hxx> +#include <svtools/colorcfg.hxx> +#include <editeng/colritem.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 <svl/style.hxx> +#include <editeng/frmdiritem.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdotable.hxx> +#include <svx/ImageMapInfo.hxx> +#include <tools/urlobj.hxx> +#include <svtools/sfxecode.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include "htmlpublishmode.hxx" +#include <Outliner.hxx> +#include <sdpage.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <anminfo.hxx> +#include <sdresid.hxx> +#include "buttonset.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; + +// get parameter from Itemset +#define RESTOHTML( res ) StringToHTMLString(SdResId(res)) + +const char * const pButtonNames[] = +{ + "first-inactive.png", + "first.png", + "left-inactive.png", + "left.png", + "right-inactive.png", + "right.png", + "last-inactive.png", + "last.png", + "home.png", + "text.png", + "expand.png", + "collapse.png", +}; + +#define BTN_FIRST_0 0 +#define BTN_FIRST_1 1 +#define BTN_PREV_0 2 +#define BTN_PREV_1 3 +#define BTN_NEXT_0 4 +#define BTN_NEXT_1 5 +#define BTN_LAST_0 6 +#define BTN_LAST_1 7 +#define BTN_INDEX 8 +#define BTN_TEXT 9 +#define BTN_MORE 10 +#define BTN_LESS 11 + +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(); +}; + +} + +// 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=\"" + HtmlExport::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=\"" + aLink; + if (!aTarget.isEmpty()) + { + aStr += "\" target=\"" + 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 + "\">"); +} + +} // anonymous 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( + const OUString& aPath, + const Sequence< PropertyValue >& rParams, + SdDrawDocument* pExpDoc, + sd::DrawDocShell* pDocShell ) + : maPath( aPath ), + mpDoc(pExpDoc), + mpDocSh( pDocShell ), + meMode( PUBLISH_SINGLE_DOCUMENT ), + mbContentsPage(false), + mnButtonThema(-1), + mnWidthPixel( PUB_MEDRES_WIDTH ), + meFormat( FORMAT_JPG ), + mbNotes(false), + mnCompression( -1 ), + mbDownload( false ), + mbSlideSound(true), + mbHiddenSlides(true), + mbUserAttr(false), + maTextColor(COL_BLACK), + maBackColor(COL_WHITE), + mbDocColors(false), + maIndexUrl("index"), + meScript( SCRIPT_ASP ), + mpButtonSet( new ButtonSet() ) +{ + bool bChange = mpDoc->IsChanged(); + + maIndexUrl += gaHTMLExtension; + + InitExportParameters( rParams ); + + switch( meMode ) + { + case PUBLISH_HTML: + case PUBLISH_FRAMES: + ExportHtml(); + break; + case PUBLISH_WEBCAST: + ExportWebCast(); + break; + case PUBLISH_KIOSK: + ExportKiosk(); + break; + case PUBLISH_SINGLE_DOCUMENT: + ExportSingleDocument(); + break; + } + + mpDoc->SetChanged(bChange); +} + +HtmlExport::~HtmlExport() +{ +} + +// get common export parameters from item set +void HtmlExport::InitExportParameters( const Sequence< PropertyValue >& rParams ) +{ + mbImpress = mpDoc->GetDocumentType() == DocumentType::Impress; + + OUString aStr; + for( const PropertyValue& rParam : rParams ) + { + if ( rParam.Name == "PublishMode" ) + { + sal_Int32 temp = 0; + rParam.Value >>= temp; + meMode = static_cast<HtmlPublishMode>(temp); + } + else if ( rParam.Name == "IndexURL" ) + { + rParam.Value >>= aStr; + maIndexUrl = aStr; + } + else if ( rParam.Name == "Format" ) + { + sal_Int32 temp = 0; + rParam.Value >>= temp; + meFormat = static_cast<PublishingFormat>(temp); + } + else if ( rParam.Name == "Compression" ) + { + rParam.Value >>= aStr; + OUString aTmp( aStr ); + if(!aTmp.isEmpty()) + { + aTmp = aTmp.replaceFirst("%", ""); + mnCompression = static_cast<sal_Int16>(aTmp.toInt32()); + } + } + else if ( rParam.Name == "Width" ) + { + sal_Int32 temp = 0; + rParam.Value >>= temp; + mnWidthPixel = static_cast<sal_uInt16>(temp); + } + else if ( rParam.Name == "UseButtonSet" ) + { + sal_Int32 temp = 0; + rParam.Value >>= temp; + mnButtonThema = static_cast<sal_Int16>(temp); + } + else if ( rParam.Name == "IsExportNotes" ) + { + if( mbImpress ) + { + bool temp = false; + rParam.Value >>= temp; + mbNotes = temp; + } + } + else if ( rParam.Name == "IsExportContentsPage" ) + { + bool temp = false; + rParam.Value >>= temp; + mbContentsPage = temp; + } + else if ( rParam.Name == "Author" ) + { + rParam.Value >>= aStr; + maAuthor = aStr; + } + else if ( rParam.Name == "EMail" ) + { + rParam.Value >>= aStr; + maEMail = aStr; + } + else if ( rParam.Name == "HomepageURL" ) + { + rParam.Value >>= aStr; + maHomePage = aStr; + } + else if ( rParam.Name == "UserText" ) + { + rParam.Value >>= aStr; + maInfo = aStr; + } + else if ( rParam.Name == "EnableDownload" ) + { + bool temp = false; + rParam.Value >>= temp; + mbDownload = temp; + } + else if ( rParam.Name == "SlideSound" ) + { + bool temp = true; + rParam.Value >>= temp; + mbSlideSound = temp; + } + else if ( rParam.Name == "HiddenSlides" ) + { + bool temp = true; + rParam.Value >>= temp; + mbHiddenSlides = temp; + } + else if ( rParam.Name == "BackColor" ) + { + Color temp; + rParam.Value >>= temp; + maBackColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "TextColor" ) + { + Color temp; + rParam.Value >>= temp; + maTextColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "LinkColor" ) + { + Color temp ; + rParam.Value >>= temp; + maLinkColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "VLinkColor" ) + { + Color temp; + rParam.Value >>= temp; + maVLinkColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "ALinkColor" ) + { + Color temp; + rParam.Value >>= temp; + maALinkColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "IsUseDocumentColors" ) + { + bool temp = false; + rParam.Value >>= temp; + mbDocColors = temp; + } + else if ( rParam.Name == "KioskSlideDuration" ) + { + double temp = 0.0; + rParam.Value >>= temp; + mfSlideDuration = temp; + mbAutoSlide = true; + } + else if ( rParam.Name == "KioskEndless" ) + { + bool temp = false; + rParam.Value >>= temp; + mbEndless = temp; + } + else if ( rParam.Name == "WebCastCGIURL" ) + { + rParam.Value >>= aStr; + maCGIPath = aStr; + } + else if ( rParam.Name == "WebCastTargetURL" ) + { + rParam.Value >>= aStr; + maURLPath = aStr; + } + else if ( rParam.Name == "WebCastScriptLanguage" ) + { + rParam.Value >>= aStr; + if ( aStr == "asp" ) + { + meScript = SCRIPT_ASP; + } + else + { + meScript = SCRIPT_PERL; + } + } + else + { + OSL_FAIL("Unknown property for html export detected!"); + } + } + + if( meMode == PUBLISH_KIOSK ) + { + mbContentsPage = false; + mbNotes = false; + + } + + // calculate image sizes + SdPage* pPage = mpDoc->GetSdPage(0, PageKind::Standard); + Size aTmpSize( pPage->GetSize() ); + double dRatio=static_cast<double>(aTmpSize.Width())/aTmpSize.Height(); + + mnHeightPixel = static_cast<sal_uInt16>(mnWidthPixel/dRatio); + + // 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 ); + + if( mbHiddenSlides || !pPage->IsExcluded() ) + { + maPages.push_back( pPage ); + maNotesPages.push_back( mpDoc->GetSdPage( nPage, PageKind::Notes ) ); + } + } + mnSdPageCount = maPages.size(); + + mbFrames = meMode == PUBLISH_FRAMES; + + maDocFileName = maIndex; +} + +void HtmlExport::ExportSingleDocument() +{ + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + + maPageNames.resize(mnSdPageCount); + + mnPagesWritten = 0; + InitProgress(mnSdPageCount); + + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(DocumentMetadata()); + aStr.append("\r\n"); + aStr.append("</head>\r\n"); + aStr.append(CreateBodyTag()); + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; ++nSdPage) + { + SdPage* pPage = maPages[nSdPage]; + maPageNames[nSdPage] = pPage->GetName(); + + if( mbDocColors ) + { + SetDocColors( pPage ); + } + + // page title + OUString sTitleText(CreateTextForTitle(pOutliner, pPage, pPage->GetPageBackgroundColor())); + 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); + aStr.append("</h1>\r\n"); + + // write outline text + aStr.append(CreateTextForPage( pOutliner, pPage, true, pPage->GetPageBackgroundColor() )); + + // notes + if(mbNotes) + { + SdPage* pNotesPage = maNotesPages[ nSdPage ]; + OUString aNotesStr( CreateTextForNotesPage( pOutliner, pNotesPage, maBackColor) ); + + if (!aNotesStr.isEmpty()) + { + aStr.append("<br>\r\n<h3>"); + aStr.append(RESTOHTML(STR_HTMLEXP_NOTES)); + aStr.append(":</h3>\r\n"); + + aStr.append(aNotesStr); + } + } + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + } + + // close page + aStr.append("</body>\r\n</html>"); + + WriteHtml(maDocFileName, false, aStr.makeStringAndClear()); + + pOutliner->Clear(); + ResetProgress(); +} + +// exports the (in the c'tor specified impress document) to html +void HtmlExport::ExportHtml() +{ + if(mbUserAttr) + { + if( maTextColor == COL_AUTO ) + { + if( !maBackColor.IsDark() ) + maTextColor = COL_BLACK; + } + } + else if( mbDocColors ) + { + // default colors for the color schema 'From Document' + SetDocColors(); + maFirstPageColor = maBackColor; + } + + // get name for downloadable presentation if needed + if( mbDownload ) + { + // fade out separator search and extension + sal_Int32 nSepPos = maDocFileName.indexOf('.'); + if (nSepPos != -1) + maDocFileName = maDocFileName.copy(0, nSepPos); + + maDocFileName += ".odp"; + } + + sal_uInt16 nProgrCount = mnSdPageCount; + nProgrCount += mbImpress?mnSdPageCount:0; + nProgrCount += mbContentsPage?1:0; + nProgrCount += (mbFrames && mbNotes)?mnSdPageCount:0; + nProgrCount += mbFrames ? 8 : 0; + InitProgress( nProgrCount ); + + mpDocSh->SetWaitCursor( true ); + + // Exceptions are cool... + + CreateFileNames(); + + // this is not a true while + while( true ) + { + if( checkForExistingFiles() ) + break; + + if( !CreateImagesForPresPages() ) + break; + + if( mbContentsPage && + !CreateImagesForPresPages( true ) ) + break; + + if( !CreateHtmlForPresPages() ) + break; + + if( mbImpress ) + if( !CreateHtmlTextForPresPages() ) + break; + + if( mbFrames ) + { + if( !CreateFrames() ) + break; + + if( !CreateOutlinePages() ) + break; + + if( !CreateNavBarFrames() ) + break; + + if( mbNotes && mbImpress ) + if( !CreateNotesPages() ) + break; + + } + + if( mbContentsPage ) + if( !CreateContentPage() ) + break; + + CreateBitmaps(); + + mpDocSh->SetWaitCursor( false ); + ResetProgress(); + + if( mbDownload ) + SavePresentation(); + + return; + } + + // if we get to this point the export was + // canceled by the user after an error + mpDocSh->SetWaitCursor( false ); + ResetProgress(); +} + +void HtmlExport::SetDocColors( SdPage* pPage ) +{ + if( pPage == nullptr ) + pPage = mpDoc->GetSdPage(0, PageKind::Standard); + + svtools::ColorConfig aConfig; + maVLinkColor = aConfig.GetColorValue(svtools::LINKSVISITED).nColor; + maALinkColor = aConfig.GetColorValue(svtools::LINKS).nColor; + maLinkColor = aConfig.GetColorValue(svtools::LINKS).nColor; + maTextColor = COL_BLACK; + + SfxStyleSheet* pSheet = nullptr; + + if( mpDoc->GetDocumentType() == DocumentType::Impress ) + { + // default text color from the outline template of the first page + pSheet = pPage->GetStyleSheetForPresObj(PresObjKind::Outline); + if(pSheet == nullptr) + pSheet = pPage->GetStyleSheetForPresObj(PresObjKind::Text); + if(pSheet == nullptr) + pSheet = pPage->GetStyleSheetForPresObj(PresObjKind::Title); + } + + if(pSheet == nullptr) + pSheet = mpDoc->GetDefaultStyleSheet(); + + if(pSheet) + { + SfxItemSet& rSet = pSheet->GetItemSet(); + if(rSet.GetItemState(EE_CHAR_COLOR) == SfxItemState::SET) + maTextColor = rSet.GetItem<SvxColorItem>(EE_CHAR_COLOR)->GetValue(); + } + + // default background from the background of the master page of the first page + maBackColor = pPage->GetPageBackgroundColor(); + + if( maTextColor == COL_AUTO ) + { + if( !maBackColor.IsDark() ) + maTextColor = COL_BLACK; + } +} + +void HtmlExport::InitProgress( sal_uInt16 nProgrCount ) +{ + mpProgress.reset(new SfxProgress( mpDocSh, SdResId(STR_CREATE_PAGES), nProgrCount )); +} + +void HtmlExport::ResetProgress() +{ + mpProgress.reset(); +} + +void HtmlExport::ExportKiosk() +{ + mnPagesWritten = 0; + InitProgress( 2*mnSdPageCount ); + + CreateFileNames(); + if( !checkForExistingFiles() ) + { + if( CreateImagesForPresPages() ) + CreateHtmlForPresPages(); + } + + ResetProgress(); +} + +// Export Document with WebCast (TM) Technology +void HtmlExport::ExportWebCast() +{ + mnPagesWritten = 0; + InitProgress( mnSdPageCount + 9 ); + + mpDocSh->SetWaitCursor( true ); + + CreateFileNames(); + + if (maCGIPath.isEmpty()) + maCGIPath = "."; + + if (!maCGIPath.endsWith("/")) + maCGIPath += "/"; + + if( meScript == SCRIPT_ASP ) + { + maURLPath = "./"; + } + else + { + if (maURLPath.isEmpty()) + maURLPath = "."; + + if (!maURLPath.endsWith("/")) + maURLPath += "/"; + } + + // this is not a true while + while(true) + { + if( checkForExistingFiles() ) + break; + + if(!CreateImagesForPresPages()) + break; + + if( meScript == SCRIPT_ASP ) + { + if(!CreateASPScripts()) + break; + } + else + { + if(!CreatePERLScripts()) + break; + } + + if(!CreateImageFileList()) + break; + + if(!CreateImageNumberFile()) + break; + + break; + } + + mpDocSh->SetWaitCursor( false ); + ResetProgress(); +} + +// Save the presentation as a downloadable file in the dest directory +bool HtmlExport::SavePresentation() +{ + meEC.SetContext( STR_HTMLEXP_ERROR_CREATE_FILE, maDocFileName ); + + OUString aURL(maExportPath + maDocFileName); + + mpDocSh->EnableSetModified(); + + try + { + uno::Reference< frame::XStorable > xStorable( mpDoc->getUnoModel(), uno::UNO_QUERY ); + if( xStorable.is() ) + { + uno::Sequence< beans::PropertyValue > aProperties{ + comphelper::makePropertyValue("Overwrite", true), + comphelper::makePropertyValue("FilterName", OUString("impress8")) + }; + xStorable->storeToURL( aURL, aProperties ); + + mpDocSh->EnableSetModified( false ); + + return true; + } + } + catch( Exception& ) + { + } + + mpDocSh->EnableSetModified( false ); + + return false; +} + +// create image files +bool HtmlExport::CreateImagesForPresPages( bool bThumbnail) +{ + try + { + Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + + Reference< drawing::XGraphicExportFilter > xGraphicExporter = drawing::GraphicExportFilter::create( xContext ); + + Sequence< PropertyValue > aFilterData(((meFormat==FORMAT_JPG)&&(mnCompression != -1))? 3 : 2); + auto pFilterData = aFilterData.getArray(); + pFilterData[0].Name = "PixelWidth"; + pFilterData[0].Value <<= static_cast<sal_Int32>(bThumbnail ? PUB_THUMBNAIL_WIDTH : mnWidthPixel ); + pFilterData[1].Name = "PixelHeight"; + pFilterData[1].Value <<= static_cast<sal_Int32>(bThumbnail ? PUB_THUMBNAIL_HEIGHT : mnHeightPixel); + if((meFormat==FORMAT_JPG)&&(mnCompression != -1)) + { + pFilterData[2].Name = "Quality"; + pFilterData[2].Value <<= static_cast<sal_Int32>(mnCompression); + } + + OUString sFormat; + if( meFormat == FORMAT_PNG ) + sFormat = "PNG"; + else if( meFormat == FORMAT_GIF ) + sFormat = "GIF"; + else + sFormat = "JPG"; + + Sequence< PropertyValue > aDescriptor{ + comphelper::makePropertyValue("URL", Any()), + comphelper::makePropertyValue("FilterName", sFormat), + comphelper::makePropertyValue("FilterData", aFilterData) + }; + auto pDescriptor = aDescriptor.getArray(); + + for (sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + SdPage* pPage = maPages[ nSdPage ]; + + OUString aFull(maExportPath); + if (bThumbnail) + aFull += maThumbnailFiles[nSdPage]; + else + aFull += maImageFiles[nSdPage]; + + pDescriptor[0].Value <<= aFull; + + Reference< XComponent > xPage( pPage->getUnoPage(), UNO_QUERY ); + xGraphicExporter->setSourceDocument( xPage ); + xGraphicExporter->filter( aDescriptor ); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + } + catch( Exception& ) + { + return false; + } + + return true; +} + +// get SdrTextObject with layout text of this page +SdrTextObj* HtmlExport::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; +} + +// create HTML text version of impress pages +OUString HtmlExport::CreateMetaCharset() +{ + OUString aStr; + const char *pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 ); + if ( pCharSet ) + { + aStr = " <meta HTTP-EQUIV=CONTENT-TYPE CONTENT=\"text/html; charset=" + + OUString::createFromAscii(pCharSet) + "\">\r\n"; + } + return aStr; +} + +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"); + OString aData(static_cast<const char*>(aStream.GetData()), static_cast<sal_Int32>(nLen)); + + return OStringToOUString(aData, RTL_TEXTENCODING_UTF8); +} + +bool HtmlExport::CreateHtmlTextForPresPages() +{ + bool bOk = true; + + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount && bOk; nSdPage++) + { + SdPage* pPage = maPages[ nSdPage ]; + + if( mbDocColors ) + { + SetDocColors( pPage ); + } + + // HTML head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" <title>"); + aStr.append(StringToHTMLString(maPageNames[nSdPage])); + aStr.append("</title>\r\n"); + aStr.append("</head>\r\n"); + aStr.append(CreateBodyTag()); + + // navigation bar + aStr.append(CreateNavBar(nSdPage, true)); + + // page title + OUString sTitleText( CreateTextForTitle(pOutliner,pPage, pPage->GetPageBackgroundColor()) ); + lclAppendStyle(aStr, u"h1", getParagraphStyle(pOutliner, 0)); + aStr.append(sTitleText); + aStr.append("</h1>\r\n"); + + // write outline text + aStr.append(CreateTextForPage( pOutliner, pPage, true, pPage->GetPageBackgroundColor() )); + + // notes + if(mbNotes) + { + SdPage* pNotesPage = maNotesPages[ nSdPage ]; + OUString aNotesStr( CreateTextForNotesPage( pOutliner, pNotesPage, maBackColor) ); + + if (!aNotesStr.isEmpty()) + { + aStr.append("<br>\r\n<h3>"); + aStr.append(RESTOHTML(STR_HTMLEXP_NOTES)); + aStr.append(":</h3>\r\n"); + + aStr.append(aNotesStr); + } + } + + // close page + aStr.append("</body>\r\n</html>"); + + bOk = WriteHtml(maTextFiles[nSdPage], false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + } + + pOutliner->Clear(); + + return bOk; +} + +/** 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; + + meEC.SetContext( STR_HTMLEXP_ERROR_CREATE_FILE, rFileName ); + 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; +} + +/** creates an outliner text for the title objects of a page + */ +OUString HtmlExport::CreateTextForTitle( SdrOutliner* pOutliner, SdPage* pPage, const Color& rBackgroundColor ) +{ + 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, rBackgroundColor); + } + } + + return OUString(); +} + +// creates an outliner text for a page +OUString HtmlExport::CreateTextForPage(SdrOutliner* pOutliner, SdPage const * pPage, + bool bHeadLine, const Color& rBackgroundColor) +{ + 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, rBackgroundColor, false); + } + else if (pObject->GetObjIdentifier() == SdrObjKind::Table) + { + SdrTableObj* pTableObject = static_cast<SdrTableObj*>(pObject); + WriteTable(aStr, pTableObject, pOutliner, rBackgroundColor); + } + else + { + if (pObject->GetOutlinerParaObject()) + { + WriteOutlinerParagraph(aStr, pOutliner, pObject->GetOutlinerParaObject(), rBackgroundColor, false); + } + } + } + break; + + case PresObjKind::Table: + { + SdrTableObj* pTableObject = static_cast<SdrTableObj*>(pObject); + WriteTable(aStr, pTableObject, pOutliner, rBackgroundColor); + } + break; + + case PresObjKind::Text: + case PresObjKind::Outline: + { + SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject); + if (pTextObject->IsEmptyPresObj()) + continue; + WriteOutlinerParagraph(aStr, pOutliner, pTextObject->GetOutlinerParaObject(), rBackgroundColor, bHeadLine); + } + break; + + default: + break; + } + } + return aStr.makeStringAndClear(); +} + +void HtmlExport::WriteTable(OUStringBuffer& aStr, SdrTableObj const * pTableObject, SdrOutliner* pOutliner, const Color& rBackgroundColor) +{ + 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(), rBackgroundColor, false); + aStr.append(" </td>\r\n"); + } + aStr.append(" </tr>\r\n"); + } + aStr.append("</table>\r\n"); +} + +void HtmlExport::WriteObjectGroup(OUStringBuffer& aStr, SdrObjGroup const * pObjectGroup, SdrOutliner* pOutliner, + const Color& rBackgroundColor, 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, rBackgroundColor, bHeadLine); + } + else + { + OutlinerParaObject* pOutlinerParagraphObject = pCurrentObject->GetOutlinerParaObject(); + if (pOutlinerParagraphObject != nullptr) + { + WriteOutlinerParagraph(aStr, pOutliner, pOutlinerParagraphObject, rBackgroundColor, bHeadLine); + } + } + } +} + +void HtmlExport::WriteOutlinerParagraph(OUStringBuffer& aStr, SdrOutliner* pOutliner, + OutlinerParaObject const * pOutlinerParagraphObject, + const Color& rBackgroundColor, 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, rBackgroundColor); + + 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(); +} + +// creates an outliner text for a note page +OUString HtmlExport::CreateTextForNotesPage( SdrOutliner* pOutliner, + SdPage* pPage, + const Color& rBackgroundColor ) +{ + OUStringBuffer aStr; + + SdrTextObj* pTO = static_cast<SdrTextObj*>(pPage->GetPresObj(PresObjKind::Notes)); + + if (pTO && !pTO->IsEmptyPresObj()) + { + OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject(); + if (pOPO) + { + pOutliner->Clear(); + pOutliner->SetText( *pOPO ); + + sal_Int32 nCount = pOutliner->GetParagraphCount(); + for (sal_Int32 nPara = 0; nPara < nCount; nPara++) + { + lclAppendStyle(aStr, u"p", getParagraphStyle(pOutliner, nPara)); + aStr.append(ParagraphToHTMLString(pOutliner, nPara, rBackgroundColor)); + aStr.append("</p>\r\n"); + } + } + } + + return aStr.makeStringAndClear(); +} + +// converts a paragraph of the outliner to html +OUString HtmlExport::ParagraphToHTMLString( SdrOutliner const * pOutliner, sal_Int32 nPara, const Color& rBackgroundColor ) +{ + 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( (mbUserAttr || mbDocColors) ? maTextColor : 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 ) ); + + OUString aPortion(StringToHTMLString(rEditEngine.GetText( aSelection ))); + + aStr.append(TextAttribToHTMLString( &aSet, &aState, rBackgroundColor )); + aStr.append(aPortion); + + nPos1 = nPos2; + } + aStr.append(aState.Flush()); + rEditEngine.SetUpdateLayout(bOldUpdateMode); + + return aStr.makeStringAndClear(); +} + +// 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 HtmlExport::TextAttribToHTMLString( SfxItemSet const * pSet, HtmlState* pState, const Color& rBackgroundColor ) +{ + 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(mbDocColors) + { + if ( pSet->GetItemState( EE_CHAR_COLOR ) == SfxItemState::SET ) + { + Color aTextColor = pSet->Get( EE_CHAR_COLOR ).GetValue(); + if( aTextColor == COL_AUTO ) + { + if( !rBackgroundColor.IsDark() ) + aTextColor = COL_BLACK; + } + aStr.append(pState->SetColor( aTextColor )); + } + } + + if (!aLink.isEmpty()) + aStr.insert(0, pState->SetLink(aLink, aTarget)); + else + aStr.append(pState->SetLink(aLink, aTarget)); + + return aStr.makeStringAndClear(); +} + +// create HTML wrapper for picture files +bool HtmlExport::CreateHtmlForPresPages() +{ + bool bOk = true; + + std::vector<SdrObject*> aClickableObjects; + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount && bOk; nSdPage++) + { + // find clickable objects (also on the master page) and put it in the + // list. This in reverse order character order since in html the first + // area is taken in the case they overlap. + SdPage* pPage = maPages[ nSdPage ]; + + if( mbDocColors ) + { + SetDocColors( pPage ); + } + + bool bMasterDone = false; + + while (!bMasterDone) + { + // sal_True = backwards + SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups, true); + + SdrObject* pObject = aIter.Next(); + while (pObject) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetAnimationInfo(pObject); + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObject); + + if ((pInfo && + (pInfo->meClickAction == presentation::ClickAction_BOOKMARK || + pInfo->meClickAction == presentation::ClickAction_DOCUMENT || + pInfo->meClickAction == presentation::ClickAction_PREVPAGE || + pInfo->meClickAction == presentation::ClickAction_NEXTPAGE || + pInfo->meClickAction == presentation::ClickAction_FIRSTPAGE || + pInfo->meClickAction == presentation::ClickAction_LASTPAGE)) || + pIMapInfo) + { + aClickableObjects.push_back(pObject); + } + + pObject = aIter.Next(); + } + // now to the master page or finishing + if (!pPage->IsMasterPage()) + pPage = static_cast<SdPage*>(&(pPage->TRG_GetMasterPage())); + else + bMasterDone = true; + } + + // HTML Head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" <title>" + StringToHTMLString(maPageNames[nSdPage]) + "</title>\r\n"); + + // insert timing information + pPage = maPages[ nSdPage ]; + if( meMode == PUBLISH_KIOSK ) + { + double fSecs = 0; + bool bEndless = false; + if( !mbAutoSlide ) + { + if( pPage->GetPresChange() != PresChange::Manual ) + { + fSecs = pPage->GetTime(); + bEndless = mpDoc->getPresentationSettings().mbEndless; + } + } + else + { + fSecs = mfSlideDuration; + bEndless = mbEndless; + } + + if( fSecs != 0 ) + { + if( nSdPage < (mnSdPageCount-1) || bEndless ) + { + aStr.append("<meta http-equiv=\"refresh\" content=\""); + aStr.append(fSecs); + aStr.append("; URL="); + + int nPage = nSdPage + 1; + if( nPage == mnSdPageCount ) + nPage = 0; + + aStr.append(maHTMLFiles[nPage]); + + aStr.append("\">\r\n"); + } + } + } + + aStr.append("</head>\r\n"); + + // HTML Body + aStr.append(CreateBodyTag()); + + if( mbSlideSound && pPage->IsSoundOn() ) + aStr.append(InsertSound(pPage->GetSoundFile())); + + // navigation bar + if(!mbFrames ) + aStr.append(CreateNavBar(nSdPage, false)); + // Image + aStr.append("<center>"); + aStr.append("<img src=\""); + aStr.append(maImageFiles[nSdPage]); + aStr.append("\" alt=\"\""); + + if (!aClickableObjects.empty()) + aStr.append(" USEMAP=\"#map0\""); + + aStr.append("></center>\r\n"); + + // notes + if(mbNotes && !mbFrames) + { + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + SdPage* pNotesPage = maNotesPages[ nSdPage ]; + OUString aNotesStr( CreateTextForNotesPage( pOutliner, pNotesPage, maBackColor) ); + pOutliner->Clear(); + + if (!aNotesStr.isEmpty()) + { + aStr.append("<h3>"); + aStr.append(RESTOHTML(STR_HTMLEXP_NOTES)); + aStr.append(":</h3><br>\r\n\r\n<p>"); + + aStr.append(aNotesStr); + aStr.append("\r\n</p>\r\n"); + } + } + + // create Imagemap if necessary + if (!aClickableObjects.empty()) + { + aStr.append("<map name=\"map0\">\r\n"); + + for (SdrObject* pObject : aClickableObjects) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetAnimationInfo(pObject); + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObject); + + ::tools::Rectangle aRect(pObject->GetCurrentBoundRect()); + Point aLogPos(aRect.TopLeft()); + bool bIsSquare = aRect.GetWidth() == aRect.GetHeight(); + + sal_uLong nPageWidth = pPage->GetSize().Width() - pPage->GetLeftBorder() - + pPage->GetRightBorder(); + + // BoundRect is relative to the physical page origin, not the + // origin of ordinates + aRect.Move(-pPage->GetLeftBorder(), -pPage->GetUpperBorder()); + + double fLogicToPixel = static_cast<double>(mnWidthPixel) / nPageWidth; + aRect.SetLeft( static_cast<tools::Long>(aRect.Left() * fLogicToPixel) ); + aRect.SetTop( static_cast<tools::Long>(aRect.Top() * fLogicToPixel) ); + aRect.SetRight( static_cast<tools::Long>(aRect.Right() * fLogicToPixel) ); + aRect.SetBottom( static_cast<tools::Long>(aRect.Bottom() * fLogicToPixel) ); + tools::Long nRadius = aRect.GetWidth() / 2; + + /** + insert areas into Imagemap of the object, if the object has + such an Imagemap + */ + if (pIMapInfo) + { + const ImageMap& rIMap = pIMapInfo->GetImageMap(); + sal_uInt16 nAreaCount = rIMap.GetIMapObjectCount(); + for (sal_uInt16 nArea = 0; nArea < nAreaCount; nArea++) + { + IMapObject* pArea = rIMap.GetIMapObject(nArea); + IMapObjectType nType = pArea->GetType(); + OUString aURL( pArea->GetURL() ); + + // if necessary, convert page and object names into the + // corresponding names of the html file + bool bIsMasterPage; + sal_uInt16 nPgNum = mpDoc->GetPageByName( aURL, bIsMasterPage ); + + if (nPgNum == SDRPAGE_NOTFOUND) + { + // is the bookmark an object? + SdrObject* pObj = mpDoc->GetObj( aURL ); + if (pObj) + nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum(); + } + if (nPgNum != SDRPAGE_NOTFOUND) + { + nPgNum = (nPgNum - 1) / 2; // SdrPageNum --> SdPageNum + aURL = CreatePageURL(nPgNum); + } + + switch(nType) + { + case IMapObjectType::Rectangle: + { + ::tools::Rectangle aArea(static_cast<IMapRectangleObject*>(pArea)-> + GetRectangle(false)); + + // conversion into pixel coordinates + aArea.Move(aLogPos.X() - pPage->GetLeftBorder(), + aLogPos.Y() - pPage->GetUpperBorder()); + aArea.SetLeft( static_cast<tools::Long>(aArea.Left() * fLogicToPixel) ); + aArea.SetTop( static_cast<tools::Long>(aArea.Top() * fLogicToPixel) ); + aArea.SetRight( static_cast<tools::Long>(aArea.Right() * fLogicToPixel) ); + aArea.SetBottom( static_cast<tools::Long>(aArea.Bottom() * fLogicToPixel) ); + + aStr.append(CreateHTMLRectArea(aArea, aURL)); + } + break; + + case IMapObjectType::Circle: + { + Point aCenter(static_cast<IMapCircleObject*>(pArea)-> + GetCenter(false)); + aCenter += Point(aLogPos.X() - pPage->GetLeftBorder(), + aLogPos.Y() - pPage->GetUpperBorder()); + aCenter.setX( static_cast<tools::Long>(aCenter.X() * fLogicToPixel) ); + aCenter.setY( static_cast<tools::Long>(aCenter.Y() * fLogicToPixel) ); + + sal_uLong nCircleRadius = static_cast<IMapCircleObject*>(pArea)-> + GetRadius(false); + nCircleRadius = static_cast<sal_uLong>(nCircleRadius * fLogicToPixel); + aStr.append(CreateHTMLCircleArea(nCircleRadius, + aCenter.X(), aCenter.Y(), + aURL)); + } + break; + + case IMapObjectType::Polygon: + { + tools::Polygon aArea(static_cast<IMapPolygonObject*>(pArea)->GetPolygon(false)); + aStr.append(CreateHTMLPolygonArea(::basegfx::B2DPolyPolygon(aArea.getB2DPolygon()), + Size(aLogPos.X() - pPage->GetLeftBorder(), + aLogPos.Y() - pPage->GetUpperBorder()), + fLogicToPixel, aURL)); + } + break; + + default: + { + SAL_INFO("sd", "unknown IMapObjectType"); + } + break; + } + } + } + + /** + if there is a presentation::ClickAction, determine bookmark + and create area for the whole object + */ + if( pInfo ) + { + OUString aHRef; + presentation::ClickAction eClickAction = pInfo->meClickAction; + + switch( eClickAction ) + { + case presentation::ClickAction_BOOKMARK: + { + bool bIsMasterPage; + sal_uInt16 nPgNum = mpDoc->GetPageByName( pInfo->GetBookmark(), bIsMasterPage ); + + if( nPgNum == SDRPAGE_NOTFOUND ) + { + // is the bookmark an object? + SdrObject* pObj = mpDoc->GetObj(pInfo->GetBookmark()); + if (pObj) + nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum(); + } + + if( SDRPAGE_NOTFOUND != nPgNum ) + aHRef = CreatePageURL(( nPgNum - 1 ) / 2 ); + } + break; + + case presentation::ClickAction_DOCUMENT: + aHRef = pInfo->GetBookmark(); + break; + + case presentation::ClickAction_PREVPAGE: + { + sal_uLong nPage; + + if (nSdPage == 0) + nPage = 0; + else + nPage = nSdPage - 1; + + aHRef = CreatePageURL( static_cast<sal_uInt16>(nPage)); + } + break; + + case presentation::ClickAction_NEXTPAGE: + { + sal_uLong nPage; + if (nSdPage == mnSdPageCount - 1) + nPage = mnSdPageCount - 1; + else + nPage = nSdPage + 1; + + aHRef = CreatePageURL( static_cast<sal_uInt16>(nPage)); + } + break; + + case presentation::ClickAction_FIRSTPAGE: + aHRef = CreatePageURL(0); + break; + + case presentation::ClickAction_LASTPAGE: + aHRef = CreatePageURL(mnSdPageCount - 1); + break; + + default: + break; + } + + // and now the areas + if (!aHRef.isEmpty()) + { + // a circle? + if (pObject->GetObjInventor() == SdrInventor::Default && + pObject->GetObjIdentifier() == SdrObjKind::CircleOrEllipse && + bIsSquare ) + { + aStr.append(CreateHTMLCircleArea(aRect.GetWidth() / 2, + aRect.Left() + nRadius, + aRect.Top() + nRadius, + aHRef)); + } + // a polygon? + else if (pObject->GetObjInventor() == SdrInventor::Default && + (pObject->GetObjIdentifier() == SdrObjKind::PathLine || + pObject->GetObjIdentifier() == SdrObjKind::PolyLine || + pObject->GetObjIdentifier() == SdrObjKind::Polygon)) + { + aStr.append(CreateHTMLPolygonArea(static_cast<SdrPathObj*>(pObject)->GetPathPoly(), Size(-pPage->GetLeftBorder(), -pPage->GetUpperBorder()), fLogicToPixel, aHRef)); + } + // something completely different: use the BoundRect + else + { + aStr.append(CreateHTMLRectArea(aRect, aHRef)); + } + + } + } + } + + aStr.append("</map>\r\n"); + } + aClickableObjects.clear(); + + aStr.append("</body>\r\n</html>"); + + bOk = WriteHtml(maHTMLFiles[nSdPage], false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + return bOk; +} + +// create overview pages +bool HtmlExport::CreateContentPage() +{ + if( mbDocColors ) + SetDocColors(); + + // html head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" <title>"); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("</title>\r\n</head>\r\n"); + aStr.append(CreateBodyTag()); + + // page head + aStr.append("<center>\r\n"); + + if(mbHeader) + { + aStr.append("<h1>"); + aStr.append(getDocumentTitle()); + aStr.append("</h1><br>\r\n"); + } + + aStr.append("<h2>"); + + // Solaris compiler bug workaround + if( mbFrames ) + aStr.append(CreateLink(maFramePage, + RESTOHTML(STR_HTMLEXP_CLICKSTART))); + else + aStr.append(CreateLink(StringToHTMLString(maHTMLFiles[0]), + RESTOHTML(STR_HTMLEXP_CLICKSTART))); + + aStr.append("</h2>\r\n</center>\r\n"); + + aStr.append("<center><table width=\"90%\"><tr>\r\n"); + + // table of content + aStr.append("<td valign=\"top\" align=\"left\" width=\"25%\">\r\n"); + aStr.append("<h3>"); + aStr.append(RESTOHTML(STR_HTMLEXP_CONTENTS)); + aStr.append("</h3>"); + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + OUString aPageName = maPageNames[nSdPage]; + aStr.append("<div align=\"left\">"); + if(mbFrames) + aStr.append(StringToHTMLString(aPageName)); + else + aStr.append(CreateLink(maHTMLFiles[nSdPage], aPageName)); + aStr.append("</div>\r\n"); + } + aStr.append("</td>\r\n"); + + // document information + aStr.append("<td valign=\"top\" align=\"left\" width=\"75%\">\r\n"); + + if (!maAuthor.isEmpty()) + { + aStr.append("<p><strong>"); + aStr.append(RESTOHTML(STR_HTMLEXP_AUTHOR)); + aStr.append(":</strong> "); + aStr.append(StringToHTMLString(maAuthor)); + aStr.append("</p>\r\n"); + } + + if (!maEMail.isEmpty()) + { + aStr.append("<p><strong>"); + aStr.append(RESTOHTML(STR_HTMLEXP_EMAIL)); + aStr.append(":</strong> <a href=\"mailto:"); + aStr.append(maEMail); + aStr.append("\">"); + aStr.append(StringToHTMLString(maEMail)); + aStr.append("</a></p>\r\n"); + } + + if (!maHomePage.isEmpty()) + { + aStr.append("<p><strong>"); + aStr.append(RESTOHTML(STR_HTMLEXP_HOMEPAGE)); + aStr.append(":</strong> <a href=\""); + aStr.append(maHomePage); + aStr.append("\">"); + aStr.append(StringToHTMLString(maHomePage)); + aStr.append("</a> </p>\r\n"); + } + + if (!maInfo.isEmpty()) + { + aStr.append("<p><strong>"); + aStr.append(RESTOHTML(STR_HTMLEXP_INFO)); + aStr.append(":</strong><br>\r\n"); + aStr.append(StringToHTMLString(maInfo)); + aStr.append("</p>\r\n"); + } + + if(mbDownload) + { + aStr.append("<p><a href=\""); + aStr.append(maDocFileName); + aStr.append("\">"); + aStr.append(RESTOHTML(STR_HTMLEXP_DOWNLOAD)); + aStr.append("</a></p>\r\n"); + } + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + OUString aText( + "<img src=\"" + + maThumbnailFiles[nSdPage] + + "\" width=\"256\" height=\"192\" alt=\"" + + StringToHTMLString(maPageNames[nSdPage]) + + "\">"); + + aStr.append(CreateLink(maHTMLFiles[nSdPage], aText)); + aStr.append("\r\n"); + } + + aStr.append("</td></tr></table></center>\r\n"); + + aStr.append("</body>\r\n</html>"); + + bool bOk = WriteHtml(maIndex, false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + return bOk; +} + +// create note pages (for frames) + +bool HtmlExport::CreateNotesPages() +{ + bool bOk = true; + + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + for( sal_uInt16 nSdPage = 0; bOk && nSdPage < mnSdPageCount; nSdPage++ ) + { + SdPage* pPage = maNotesPages[nSdPage]; + if( mbDocColors ) + SetDocColors( pPage ); + + // Html head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" <title>"); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("</title>\r\n</head>\r\n"); + aStr.append(CreateBodyTag()); + + if(pPage) + aStr.append(CreateTextForNotesPage( pOutliner, pPage, maBackColor )); + + aStr.append("</body>\r\n</html>"); + + OUString aFileName("note" + OUString::number(nSdPage)); + bOk = WriteHtml(aFileName, true, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + pOutliner->Clear(); + + return bOk; +} + +// create outline pages (for frames) + +bool HtmlExport::CreateOutlinePages() +{ + bool bOk = true; + + if( mbDocColors ) + { + SetDocColors(); + } + + // page 0 will be the closed outline, page 1 the opened + for (sal_Int32 nPage = 0; nPage < (mbImpress?2:1) && bOk; ++nPage) + { + // Html head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" <title>"); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("</title>\r\n</head>\r\n"); + aStr.append(CreateBodyTag()); + + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + SdPage* pPage = maPages[ nSdPage ]; + + aStr.append("<div align=\"left\">"); + OUString aLink("JavaScript:parent.NavigateAbs(" + + OUString::number(nSdPage) + ")"); + + OUString aTitle = CreateTextForTitle(pOutliner, pPage, maBackColor); + if (aTitle.isEmpty()) + aTitle = maPageNames[nSdPage]; + + lclAppendStyle(aStr, u"p", getParagraphStyle(pOutliner, 0)); + aStr.append(CreateLink(aLink, aTitle)); + aStr.append("</p>"); + + if(nPage==1) + { + aStr.append(CreateTextForPage( pOutliner, pPage, false, maBackColor )); + } + aStr.append("</div>\r\n"); + } + pOutliner->Clear(); + + aStr.append("</body>\r\n</html>"); + + OUString aFileName("outline" + OUString::number(nPage)); + bOk = WriteHtml(aFileName, true, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + return bOk; +} + +// set file name +void HtmlExport::CreateFileNames() +{ + // create lists with new file names + maHTMLFiles.resize(mnSdPageCount); + maImageFiles.resize(mnSdPageCount); + maThumbnailFiles.resize(mnSdPageCount); + maPageNames.resize(mnSdPageCount); + maTextFiles.resize(mnSdPageCount); + + mbHeader = false; // headline on overview page? + + for (sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + OUString aHTMLFileName; + if(nSdPage == 0 && !mbContentsPage && !mbFrames ) + aHTMLFileName = maIndex; + else + { + aHTMLFileName = "img" + OUString::number(nSdPage) + gaHTMLExtension; + } + + maHTMLFiles[nSdPage] = aHTMLFileName; + + OUString aImageFileName = "img" + OUString::number(nSdPage); + if( meFormat==FORMAT_GIF ) + aImageFileName += ".gif"; + else if( meFormat==FORMAT_JPG ) + aImageFileName += ".jpg"; + else + aImageFileName += ".png"; + + maImageFiles[nSdPage] = aImageFileName; + + OUString aThumbnailFileName = "thumb" + OUString::number(nSdPage); + if( meFormat!=FORMAT_JPG ) + aThumbnailFileName += ".png"; + else + aThumbnailFileName += ".jpg"; + + maThumbnailFiles[nSdPage] = aThumbnailFileName; + + maTextFiles[nSdPage] = "text" + OUString::number(nSdPage) + gaHTMLExtension; + + SdPage* pSdPage = maPages[ nSdPage ]; + + // get slide title from page name + maPageNames[nSdPage] = pSdPage->GetName(); + } + + if(!mbContentsPage && mbFrames) + maFramePage = maIndex; + else + { + maFramePage = "siframes" + gaHTMLExtension; + } +} + +OUString const & HtmlExport::getDocumentTitle() +{ + // check for a title object in this page, if it's the first + // title it becomes this documents title for the content + // page + if( !mbHeader ) + { + if(mbImpress) + { + // if there is a non-empty title object, use their first passage + // as page title + SdPage* pSdPage = mpDoc->GetSdPage(0, PageKind::Standard); + SdrObject* pTitleObj = pSdPage->GetPresObj(PresObjKind::Title); + if (pTitleObj && !pTitleObj->IsEmptyPresObj()) + { + OutlinerParaObject* pParaObject = pTitleObj->GetOutlinerParaObject(); + if (pParaObject) + { + const EditTextObject& rEditTextObject = + pParaObject->GetTextObject(); + OUString aTest(rEditTextObject.GetText(0)); + if (!aTest.isEmpty()) + mDocTitle = aTest; + } + } + + mDocTitle = mDocTitle.replace(0xff, ' '); + } + + if (mDocTitle.isEmpty()) + { + mDocTitle = maDocFileName; + sal_Int32 nDot = mDocTitle.indexOf('.'); + if (nDot > 0) + mDocTitle = mDocTitle.copy(0, nDot); + } + mbHeader = true; + } + + return mDocTitle; +} + +constexpr OUStringLiteral JS_NavigateAbs = + u"function NavigateAbs( nPage )\r\n" + "{\r\n" + " frames[\"show\"].location.href = \"img\" + nPage + \".$EXT\";\r\n" + " //frames[\"notes\"].location.href = \"note\" + nPage + \".$EXT\";\r\n" + " nCurrentPage = nPage;\r\n" + " if(nCurrentPage==0)\r\n" + " {\r\n" + " frames[\"navbar1\"].location.href = \"navbar0.$EXT\";\r\n" + " }\r\n" + " else if(nCurrentPage==nPageCount-1)\r\n" + " {\r\n" + " frames[\"navbar1\"].location.href = \"navbar2.$EXT\";\r\n" + " }\r\n" + " else\r\n" + " {\r\n" + " frames[\"navbar1\"].location.href = \"navbar1.$EXT\";\r\n" + " }\r\n" + "}\r\n\r\n"; + +constexpr OUStringLiteral JS_NavigateRel = + u"function NavigateRel( nDelta )\r\n" + "{\r\n" + " var nPage = parseInt(nCurrentPage) + parseInt(nDelta);\r\n" + " if( (nPage >= 0) && (nPage < nPageCount) )\r\n" + " {\r\n" + " NavigateAbs( nPage );\r\n" + " }\r\n" + "}\r\n\r\n"; + +constexpr OUStringLiteral JS_ExpandOutline = + u"function ExpandOutline()\r\n" + "{\r\n" + " frames[\"navbar2\"].location.href = \"navbar4.$EXT\";\r\n" + " frames[\"outline\"].location.href = \"outline1.$EXT\";\r\n" + "}\r\n\r\n"; + +constexpr OUStringLiteral JS_CollapseOutline = + u"function CollapseOutline()\r\n" + "{\r\n" + " frames[\"navbar2\"].location.href = \"navbar3.$EXT\";\r\n" + " frames[\"outline\"].location.href = \"outline0.$EXT\";\r\n" + "}\r\n\r\n"; + +// create page with the frames + +bool HtmlExport::CreateFrames() +{ + OUString aTmp; + OUStringBuffer aStr( + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\"\r\n" + " \"http://www.w3.org/TR/html4/frameset.dtd\">\r\n" + "<html>\r\n<head>\r\n"); + + aStr.append(CreateMetaCharset()); + aStr.append(" <title>"); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("</title>\r\n"); + + aStr.append("<script type=\"text/javascript\">\r\n<!--\r\n"); + + aStr.append("var nCurrentPage = 0;\r\nvar nPageCount = "); + aStr.append(static_cast<sal_Int32>(mnSdPageCount)); + aStr.append(";\r\n\r\n"); + + OUString aFunction = JS_NavigateAbs; + + if(mbNotes) + { + aFunction = aFunction.replaceAll("//", ""); + } + + // substitute HTML file extension + OUString aPlaceHolder(".$EXT"); + aFunction = aFunction.replaceAll(aPlaceHolder, gaHTMLExtension); + aStr.append(aFunction); + + aTmp = JS_NavigateRel; + aTmp = aTmp.replaceAll(aPlaceHolder, gaHTMLExtension); + aStr.append(aTmp); + + if(mbImpress) + { + aTmp = JS_ExpandOutline; + aTmp = aTmp.replaceAll(aPlaceHolder, gaHTMLExtension); + aStr.append(aTmp); + + aTmp = JS_CollapseOutline; + aTmp = aTmp.replaceAll(aPlaceHolder, gaHTMLExtension); + aStr.append(aTmp); + } + aStr.append("// -->\r\n</script>\r\n"); + + aStr.append("</head>\r\n"); + + aStr.append("<frameset cols=\"*,"); + aStr.append(static_cast<sal_Int32>(mnWidthPixel + 16)); + aStr.append("\">\r\n"); + if(mbImpress) + { + aStr.append(" <frameset rows=\"42,*\">\r\n"); + aStr.append(" <frame src=\"navbar3"); + aStr.append(gaHTMLExtension); + aStr.append("\" name=\"navbar2\" marginwidth=\"4\" marginheight=\"4\" scrolling=\"no\">\r\n"); + } + aStr.append(" <frame src=\"outline0"); + aStr.append(gaHTMLExtension); + aStr.append("\" name=\"outline\">\r\n"); + if(mbImpress) + aStr.append(" </frameset>\r\n"); + + if(mbNotes) + { + aStr.append(" <frameset rows=\"42,"); + aStr.append(static_cast<sal_Int32>(static_cast<double>(mnWidthPixel) * 0.75) + 16); + aStr.append(",*\">\r\n"); + } + else + aStr.append(" <frameset rows=\"42,*\">\r\n"); + + aStr.append(" <frame src=\"navbar0"); + aStr.append(gaHTMLExtension); + aStr.append("\" name=\"navbar1\" marginwidth=\"4\" marginheight=\"4\" scrolling=\"no\">\r\n"); + + aStr.append(" <frame src=\""); + aStr.append(maHTMLFiles[0]); + aStr.append("\" name=\"show\" marginwidth=\"4\" marginheight=\"4\">\r\n"); + + if(mbNotes) + { + aStr.append(" <frame src=\"note0"); + aStr.append(gaHTMLExtension); + aStr.append("\" name=\"notes\">\r\n"); + } + aStr.append(" </frameset>\r\n"); + + aStr.append("<noframes>\r\n"); + aStr.append(CreateBodyTag()); + aStr.append(RESTOHTML(STR_HTMLEXP_NOFRAMES)); + aStr.append("\r\n</noframes>\r\n</frameset>\r\n</html>"); + + bool bOk = WriteHtml(maFramePage, false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + return bOk; +} + +// create button bar for standard +// we create the following html files +// navbar0.htm navigation bar graphic for the first page +// navbar1.htm navigation bar graphic for the second until second last page +// navbar2.htm navigation bar graphic for the last page +// navbar3.htm navigation outline closed +// navbar4.htm navigation outline open +bool HtmlExport::CreateNavBarFrames() +{ + bool bOk = true; + OUString aButton; + + if( mbDocColors ) + { + SetDocColors(); + maBackColor = maFirstPageColor; + } + + for( int nFile = 0; nFile < 3 && bOk; nFile++ ) + { + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" <title>"); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("</title>\r\n</head>\r\n"); + aStr.append(CreateBodyTag()); + aStr.append("<center>\r\n"); + + // first page + aButton = SdResId(STR_HTMLEXP_FIRSTPAGE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(nFile == 0 || mnSdPageCount == 1 ? BTN_FIRST_0 : BTN_FIRST_1), + aButton); + + if(nFile != 0 && mnSdPageCount > 1) + aButton = CreateLink(u"JavaScript:parent.NavigateAbs(0)", aButton); + + aStr.append(aButton); + aStr.append("\r\n"); + + // to the previous page + aButton = SdResId(STR_PUBLISH_BACK); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(nFile == 0 || mnSdPageCount == 1? + BTN_PREV_0:BTN_PREV_1), + aButton); + + if(nFile != 0 && mnSdPageCount > 1) + aButton = CreateLink(u"JavaScript:parent.NavigateRel(-1)", aButton); + + aStr.append(aButton); + aStr.append("\r\n"); + + // to the next page + aButton = SdResId(STR_PUBLISH_NEXT); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(nFile ==2 || mnSdPageCount == 1? + BTN_NEXT_0:BTN_NEXT_1), + aButton); + + if(nFile != 2 && mnSdPageCount > 1) + aButton = CreateLink(u"JavaScript:parent.NavigateRel(1)", aButton); + + aStr.append(aButton); + aStr.append("\r\n"); + + // to the last page + aButton = SdResId(STR_HTMLEXP_LASTPAGE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(nFile ==2 || mnSdPageCount == 1? + BTN_LAST_0:BTN_LAST_1), + aButton); + + if(nFile != 2 && mnSdPageCount > 1) + { + OUString aLink("JavaScript:parent.NavigateAbs(" + + OUString::number(mnSdPageCount-1) + ")"); + + aButton = CreateLink(aLink, aButton); + } + + aStr.append(aButton); + aStr.append("\r\n"); + + // content + if (mbContentsPage) + { + aButton = SdResId(STR_PUBLISH_OUTLINE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(BTN_INDEX), aButton); + + // to the overview + aStr.append(CreateLink(maIndex, aButton, u"_top")); + aStr.append("\r\n"); + } + + // text mode + if(mbImpress) + { + aButton = SdResId(STR_HTMLEXP_SETTEXT); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(BTN_TEXT), aButton); + + OUString aText0("text0" + gaHTMLExtension); + aStr.append(CreateLink(aText0, aButton, u"_top")); + aStr.append("\r\n"); + } + + // and finished... + aStr.append("</center>\r\n"); + aStr.append("</body>\r\n</html>"); + + OUString aFileName("navbar" + OUString::number(nFile)); + + bOk = WriteHtml(aFileName, true, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + // the navigation bar outliner closed... + if(bOk) + { + aButton = SdResId(STR_HTMLEXP_OUTLINE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(BTN_MORE), aButton); + + bOk = WriteHtml( + "navbar3", true, + OUStringConcatenation( + gaHTMLHeader + CreateMetaCharset() + " <title>" + + StringToHTMLString(maPageNames[0]) + "</title>\r\n</head>\r\n" + CreateBodyTag() + + CreateLink(u"JavaScript:parent.ExpandOutline()", aButton) + + "</body>\r\n</html>")); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + // ... and the outliner open + if( bOk ) + { + aButton = SdResId(STR_HTMLEXP_NOOUTLINE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(BTN_LESS), aButton); + + bOk = WriteHtml( + "navbar4", true, + OUStringConcatenation( + gaHTMLHeader + CreateMetaCharset() + " <title>" + + StringToHTMLString(maPageNames[0]) + "</title>\r\n</head>\r\n" + CreateBodyTag() + + CreateLink(u"JavaScript:parent.CollapseOutline()", aButton) + + "</body>\r\n</html>")); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + } + + return bOk; +} + +// create button bar for standard +OUString HtmlExport::CreateNavBar( sal_uInt16 nSdPage, bool bIsText ) const +{ + // prepare button bar + OUString aStrNavFirst(SdResId(STR_HTMLEXP_FIRSTPAGE)); + OUString aStrNavPrev(SdResId(STR_PUBLISH_BACK)); + OUString aStrNavNext(SdResId(STR_PUBLISH_NEXT)); + OUString aStrNavLast(SdResId(STR_HTMLEXP_LASTPAGE)); + OUString aStrNavContent(SdResId(STR_PUBLISH_OUTLINE)); + OUString aStrNavText; + if( bIsText ) + { + aStrNavText = SdResId(STR_HTMLEXP_SETGRAPHIC); + } + else + { + aStrNavText = SdResId(STR_HTMLEXP_SETTEXT); + } + + if(!bIsText && mnButtonThema != -1) + { + if(nSdPage<1 || mnSdPageCount == 1) + { + aStrNavFirst = CreateImage(GetButtonName(BTN_FIRST_0), aStrNavFirst); + aStrNavPrev = CreateImage(GetButtonName(BTN_PREV_0), aStrNavPrev); + } + else + { + aStrNavFirst = CreateImage(GetButtonName(BTN_FIRST_1), aStrNavFirst); + aStrNavPrev = CreateImage(GetButtonName(BTN_PREV_1), aStrNavPrev); + } + + if(nSdPage == mnSdPageCount-1 || mnSdPageCount == 1) + { + aStrNavNext = CreateImage(GetButtonName(BTN_NEXT_0), aStrNavNext); + aStrNavLast = CreateImage(GetButtonName(BTN_LAST_0), aStrNavLast); + } + else + { + aStrNavNext = CreateImage(GetButtonName(BTN_NEXT_1), aStrNavNext); + aStrNavLast = CreateImage(GetButtonName(BTN_LAST_1), aStrNavLast); + } + + aStrNavContent = CreateImage(GetButtonName(BTN_INDEX), aStrNavContent); + aStrNavText = CreateImage(GetButtonName(BTN_TEXT), aStrNavText); + } + + OUStringBuffer aStr("<center>\r\n"); //<table><tr>\r\n"); + + // first page + if(nSdPage > 0) + aStr.append(CreateLink( bIsText ? maTextFiles[0] : maHTMLFiles[0],aStrNavFirst)); + else + aStr.append(aStrNavFirst); + aStr.append(' '); + + // to Previous page + if(nSdPage > 0) + aStr.append(CreateLink( bIsText ? maTextFiles[nSdPage-1] + : maHTMLFiles[nSdPage-1], aStrNavPrev)); + else + aStr.append(aStrNavPrev); + aStr.append(' '); + + // to Next page + if(nSdPage < mnSdPageCount-1) + aStr.append(CreateLink( bIsText ? maTextFiles[nSdPage+1] + : maHTMLFiles[nSdPage+1], aStrNavNext)); + else + aStr.append(aStrNavNext); + aStr.append(' '); + + // to Last page + if(nSdPage < mnSdPageCount-1) + aStr.append(CreateLink( bIsText ? maTextFiles[mnSdPageCount-1] + : maHTMLFiles[mnSdPageCount-1], + aStrNavLast)); + else + aStr.append(aStrNavLast); + aStr.append(' '); + + // to Index page + if (mbContentsPage) + { + aStr.append(CreateLink(maIndex, aStrNavContent)); + aStr.append(' '); + } + + // Text/Graphics + if(mbImpress) + { + aStr.append(CreateLink( bIsText ? (mbFrames ? maFramePage : maHTMLFiles[nSdPage]) + : maTextFiles[nSdPage], aStrNavText)); + + } + + aStr.append("</center><br>\r\n"); + + return aStr.makeStringAndClear(); +} + +// export navigation graphics from button set +void HtmlExport::CreateBitmaps() +{ + if(mnButtonThema == -1 || !mpButtonSet) + return; + + for( int nButton = 0; nButton != SAL_N_ELEMENTS(pButtonNames); nButton++ ) + { + if(!mbFrames && (nButton == BTN_MORE || nButton == BTN_LESS)) + continue; + + if(!mbImpress && (nButton == BTN_TEXT || nButton == BTN_MORE || nButton == BTN_LESS )) + continue; + + OUString aFull = maExportPath + GetButtonName(nButton); + mpButtonSet->exportButton( mnButtonThema, aFull, GetButtonName(nButton) ); + } +} + +// creates the <body> tag, including the specified color attributes +OUString HtmlExport::CreateBodyTag() const +{ + OUStringBuffer aStr( "<body" ); + + if( mbUserAttr || mbDocColors ) + { + Color aTextColor( maTextColor ); + if( (aTextColor == COL_AUTO) && (!maBackColor.IsDark()) ) + aTextColor = COL_BLACK; + + aStr.append(" text=\""); + aStr.append(ColorToHTMLString( aTextColor )); + aStr.append("\" bgcolor=\""); + aStr.append(ColorToHTMLString( maBackColor )); + aStr.append("\" link=\""); + aStr.append(ColorToHTMLString( maLinkColor )); + aStr.append("\" vlink=\""); + aStr.append(ColorToHTMLString( maVLinkColor )); + aStr.append("\" alink=\""); + aStr.append(ColorToHTMLString( maALinkColor )); + aStr.append("\""); + } + + aStr.append(">\r\n"); + + return aStr.makeStringAndClear(); +} + +// creates a hyperlink +OUString HtmlExport::CreateLink( std::u16string_view aLink, + std::u16string_view aText, + std::u16string_view aTarget ) +{ + OUStringBuffer aStr( "<a href=\"" ); + aStr.append(aLink); + if (!aTarget.empty()) + { + aStr.append("\" target=\""); + aStr.append(aTarget); + } + aStr.append("\">"); + aStr.append(aText); + aStr.append("</a>"); + + return aStr.makeStringAndClear(); +} + +// creates an image tag +OUString HtmlExport::CreateImage( std::u16string_view aImage, std::u16string_view aAltText ) +{ + OUStringBuffer aStr( "<img src=\""); + aStr.append(aImage); + aStr.append("\" border=0"); + + if (!aAltText.empty()) + { + aStr.append(" alt=\""); + aStr.append(aAltText); + aStr.append('"'); + } + else + { + // Agerskov: HTML 4.01 has to have an alt attribute even if it is an empty string + aStr.append(" alt=\"\""); + } + + aStr.append('>'); + + return aStr.makeStringAndClear(); +} + +// create area for a circle; we expect pixel coordinates +OUString HtmlExport::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(); +} + +// create area for a circle; we expect pixel coordinates +OUString HtmlExport::CreateHTMLCircleArea( sal_uLong nRadius, + sal_uLong nCenterX, + sal_uLong nCenterY, + std::u16string_view rHRef ) +{ + OUString aStr( + "<area shape=\"circle\" alt=\"\" coords=\"" + + OUString::number(nCenterX) + "," + + OUString::number(nCenterY) + "," + + OUString::number(nRadius) + + "\" href=\"" + rHRef + "\">\n"); + + return aStr; +} + +// create area for a polygon; we expect pixel coordinates +OUString HtmlExport::CreateHTMLPolygonArea( const ::basegfx::B2DPolyPolygon& rPolyPolygon, + Size aShift, double fFactor, std::u16string_view rHRef ) +{ + OUStringBuffer aStr; + const sal_uInt32 nNoOfPolygons(rPolyPolygon.count()); + + for ( sal_uInt32 nXPoly = 0; nXPoly < nNoOfPolygons; nXPoly++ ) + { + const ::basegfx::B2DPolygon& aPolygon = rPolyPolygon.getB2DPolygon(nXPoly); + const sal_uInt32 nNoOfPoints(aPolygon.count()); + + aStr.append("<area shape=\"polygon\" alt=\"\" coords=\""); + + for ( sal_uInt32 nPoint = 0; nPoint < nNoOfPoints; nPoint++ ) + { + const ::basegfx::B2DPoint aB2DPoint(aPolygon.getB2DPoint(nPoint)); + Point aPnt(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY())); + // coordinates are relative to the physical page origin, not the + // origin of ordinates + aPnt.Move(aShift.Width(), aShift.Height()); + + aPnt.setX( static_cast<tools::Long>(aPnt.X() * fFactor) ); + aPnt.setY( static_cast<tools::Long>(aPnt.Y() * fFactor) ); + aStr.append( OUString::number(aPnt.X()) + "," + OUString::number(aPnt.Y()) ); + + if (nPoint < nNoOfPoints - 1) + aStr.append(','); + } + aStr.append(OUString::Concat("\" href=\"") + rHRef + "\">\n"); + } + + return aStr.makeStringAndClear(); +} + +// create area for a rectangle; we expect pixel coordinates +OUString HtmlExport::CreateHTMLRectArea( const ::tools::Rectangle& rRect, + std::u16string_view rHRef ) +{ + OUString aStr( + "<area shape=\"rect\" alt=\"\" coords=\"" + + OUString::number(rRect.Left()) + "," + + OUString::number(rRect.Top()) + "," + + OUString::number(rRect.Right()) + "," + + OUString::number(rRect.Bottom()) + + "\" href=\"" + rHRef + "\">\n"); + + return aStr; +} + +// escapes a string for html +OUString HtmlExport::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 ); +} + +// creates a URL for a specific page +OUString HtmlExport::CreatePageURL( sal_uInt16 nPgNum ) +{ + if(mbFrames) + { + return OUString("JavaScript:parent.NavigateAbs(" + + OUString::number(nPgNum) + ")"); + } + else + return maHTMLFiles[nPgNum]; +} + +bool HtmlExport::CopyScript( std::u16string_view rPath, const OUString& rSource, const OUString& rDest, bool bUnix /* = false */ ) +{ + INetURLObject aURL( SvtPathOptions().GetConfigPath() ); + OUStringBuffer aScriptBuf; + + aURL.Append( u"webcast" ); + aURL.Append( rSource ); + + meEC.SetContext( STR_HTMLEXP_ERROR_OPEN_FILE, rSource ); + + ErrCode nErr = ERRCODE_NONE; + std::unique_ptr<SvStream> pIStm = ::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ); + + if( pIStm ) + { + OStringBuffer aLine; + + while( pIStm->ReadLine( aLine ) ) + { + aScriptBuf.appendAscii( aLine.getStr(), aLine.getLength() ); + if( bUnix ) + { + aScriptBuf.append("\n"); + } + else + { + aScriptBuf.append("\r\n"); + } + } + + nErr = pIStm->GetError(); + pIStm.reset(); + } + + if( nErr != ERRCODE_NONE ) + { + ErrorHandler::HandleError( nErr ); + return static_cast<bool>(nErr); + } + + OUString aScript(aScriptBuf.makeStringAndClear()); + aScript = aScript.replaceAll("$$1", getDocumentTitle()); + aScript = aScript.replaceAll("$$2", RESTOHTML(STR_WEBVIEW_SAVE)); + aScript = aScript.replaceAll("$$3", maCGIPath); + aScript = aScript.replaceAll("$$4", OUString::number(mnWidthPixel)); + aScript = aScript.replaceAll("$$5", OUString::number(mnHeightPixel)); + + OUString aDest(rPath + rDest); + + meEC.SetContext( STR_HTMLEXP_ERROR_CREATE_FILE, rDest ); + // write script file + { + EasyFile aFile; + SvStream* pStr; + nErr = aFile.createStream(aDest, pStr); + if(nErr == ERRCODE_NONE) + { + OString aStr(OUStringToOString(aScript, RTL_TEXTENCODING_UTF8)); + pStr->WriteOString( aStr ); + aFile.close(); + } + } + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + if( nErr != ERRCODE_NONE ) + ErrorHandler::HandleError( nErr ); + + return nErr == ERRCODE_NONE; +} + +static const char * ASP_Scripts[] = { "common.inc", "webcast.asp", "show.asp", "savepic.asp", "poll.asp", "editpic.asp" }; + +/** creates and saves the ASP scripts for WebShow */ +bool HtmlExport::CreateASPScripts() +{ + for(const char * p : ASP_Scripts) + { + OUString aScript = OUString::createFromAscii(p); + + if(!CopyScript(maExportPath, aScript, aScript)) + return false; + } + + return CopyScript(maExportPath, "edit.asp", maIndex); +} + +static const char *PERL_Scripts[] = { "webcast.pl", "common.pl", "editpic.pl", "poll.pl", "savepic.pl", "show.pl" }; + +// creates and saves the PERL scripts for WebShow +bool HtmlExport::CreatePERLScripts() +{ + for(const char * p : PERL_Scripts) + { + OUString aScript = OUString::createFromAscii(p); + + if(!CopyScript(maExportPath, aScript, aScript, true)) + return false; + } + + if (!CopyScript(maExportPath, "edit.pl", maIndex, true)) + return false; + + if (!CopyScript(maExportPath, "index.pl", maIndexUrl, true)) + return false; + + return true; +} + +// creates a list with names of the saved images +bool HtmlExport::CreateImageFileList() +{ + OUStringBuffer aStr; + for( sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + aStr.append(static_cast<sal_Int32>(nSdPage + 1)); + aStr.append(';'); + aStr.append(maURLPath); + aStr.append(maImageFiles[nSdPage]); + aStr.append("\r\n"); + } + + bool bOk = WriteHtml("picture.txt", false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + return bOk; +} + +// creates a file with the actual page number +bool HtmlExport::CreateImageNumberFile() +{ + OUString aFileName("currpic.txt"); + OUString aFull(maExportPath + aFileName); + + meEC.SetContext( STR_HTMLEXP_ERROR_CREATE_FILE, aFileName ); + EasyFile aFile; + SvStream* pStr; + ErrCode nErr = aFile.createStream(aFull, pStr); + if(nErr == ERRCODE_NONE) + { + pStr->WriteCharPtr( "1" ); + aFile.close(); + } + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + if( nErr != ERRCODE_NONE ) + ErrorHandler::HandleError( nErr ); + + return nErr == ERRCODE_NONE; +} + +OUString HtmlExport::InsertSound( const OUString& rSoundFile ) +{ + if (rSoundFile.isEmpty()) + return rSoundFile; + + INetURLObject aURL( rSoundFile ); + OUString aSoundFileName = aURL.getName(); + + DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" ); + + OUString aStr("<embed src=\"" + aSoundFileName + + "\" hidden=\"true\" autostart=\"true\">"); + + CopyFile(rSoundFile, maExportPath + aSoundFileName); + + return aStr; +} + +bool HtmlExport::CopyFile( const OUString& rSourceFile, const OUString& rDestFile ) +{ + meEC.SetContext( STR_HTMLEXP_ERROR_COPY_FILE, rSourceFile, rDestFile ); + osl::FileBase::RC Error = osl::File::copy( rSourceFile, rDestFile ); + + if( Error != osl::FileBase::E_None ) + { + ErrorHandler::HandleError(ErrCode(Error)); + return false; + } + else + { + return true; + } +} + +bool HtmlExport::checkFileExists( Reference< css::ucb::XSimpleFileAccess3 > const & xFileAccess, std::u16string_view aFileName ) +{ + try + { + OUString url = maExportPath + aFileName; + return xFileAccess->exists( url ); + } + catch( css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::HtmlExport::checkFileExists()" ); + } + + return false; +} + +bool HtmlExport::checkForExistingFiles() +{ + bool bFound = false; + + try + { + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference<ucb::XSimpleFileAccess3> xFA(ucb::SimpleFileAccess::create(xContext)); + + sal_uInt16 nSdPage; + for( nSdPage = 0; !bFound && (nSdPage < mnSdPageCount); nSdPage++) + { + if( checkFileExists( xFA, maImageFiles[nSdPage] ) || + checkFileExists( xFA, maHTMLFiles[nSdPage] ) || + checkFileExists( xFA, maThumbnailFiles[nSdPage] ) || + checkFileExists( xFA, maPageNames[nSdPage] ) || + checkFileExists( xFA, maTextFiles[nSdPage] ) ) + { + bFound = true; + } + } + + if( !bFound && mbDownload ) + bFound = checkFileExists( xFA, maDocFileName ); + + if( !bFound && mbFrames ) + bFound = checkFileExists( xFA, maFramePage ); + + if( bFound ) + { + OUString aSystemPath; + osl::FileBase::getSystemPathFromFileURL( maExportPath, aSystemPath ); + OUString aMsg(SdResId(STR_OVERWRITE_WARNING)); + aMsg = aMsg.replaceFirst( "%FILENAME", aSystemPath ); + + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::YesNo, + aMsg)); + xWarn->set_default_response(RET_YES); + bFound = (RET_NO == xWarn->run()); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::HtmlExport::checkForExistingFiles()" ); + bFound = false; + } + + return bFound; +} + +OUString HtmlExport::GetButtonName( int nButton ) +{ + return OUString::createFromAscii(pButtonNames[nButton]); +} + +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; +} + +// This class helps reporting errors during file i/o +HtmlErrorContext::HtmlErrorContext() + : ErrorContext(nullptr) +{ +} + +bool HtmlErrorContext::GetString( ErrCode, OUString& rCtxStr ) +{ + DBG_ASSERT(mpResId, "No error context set"); + if (!mpResId) + return false; + + rCtxStr = SdResId(mpResId); + + rCtxStr = rCtxStr.replaceAll( "$(URL1)", maURL1 ); + rCtxStr = rCtxStr.replaceAll( "$(URL2)", maURL2 ); + + return true; +} + +void HtmlErrorContext::SetContext(TranslateId pResId, const OUString& rURL) +{ + mpResId = pResId; + maURL1 = rURL; + maURL2.clear(); +} + +void HtmlErrorContext::SetContext(TranslateId pResId, const OUString& rURL1, const OUString& rURL2 ) +{ + mpResId = pResId; + maURL1 = rURL1; + maURL2 = rURL2; +} + +/* 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 000000000..5f6f06e49 --- /dev/null +++ b/sd/source/filter/html/htmlex.hxx @@ -0,0 +1,237 @@ +/* -*- 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 "htmlpublishmode.hxx" + +#include <memory> +#include <string_view> +#include <vector> + +namespace basegfx { class B2DPolyPolygon; } +namespace com::sun::star::beans { struct PropertyValue; } +namespace com::sun::star::ucb { class XSimpleFileAccess3; } +namespace sd { class DrawDocShell; } +namespace tools { class Rectangle; } + +#define PUB_LOWRES_WIDTH 640 +#define PUB_MEDRES_WIDTH 800 +#define PUB_HIGHRES_WIDTH 1024 +#define PUB_FHDRES_WIDTH 1920 + +#define PUB_THUMBNAIL_WIDTH 256 +#define PUB_THUMBNAIL_HEIGHT 192 + +class ErrCode; +class OutlinerParaObject; +class SfxItemSet; +class Size; +class SfxProgress; +class SdrOutliner; +class SdPage; +class HtmlState; +class SdrTextObj; +class SdrObjGroup; +namespace sdr::table { class SdrTableObj; } +class SdrPage; +class SdDrawDocument; +class ButtonSet; + +class HtmlErrorContext : public ErrorContext +{ +private: + TranslateId mpResId; + OUString maURL1; + OUString maURL2; + +public: + explicit HtmlErrorContext(); + + virtual bool GetString( ErrCode nErrId, OUString& rCtxStr ) override; + + void SetContext(TranslateId pResId, const OUString& rURL); + void SetContext(TranslateId pResId, const OUString& rURL1, const OUString& rURL2); +}; + +/// this class exports an Impress Document as a HTML Presentation. +class HtmlExport final +{ + std::vector< SdPage* > maPages; + std::vector< SdPage* > maNotesPages; + + OUString maPath; + + SdDrawDocument* mpDoc; + ::sd::DrawDocShell* mpDocSh; + + HtmlErrorContext meEC; + + HtmlPublishMode meMode; + std::unique_ptr<SfxProgress> mpProgress; + bool mbImpress; + sal_uInt16 mnSdPageCount; + sal_uInt16 mnPagesWritten; + bool mbContentsPage; + sal_Int16 mnButtonThema; + sal_uInt16 mnWidthPixel; + sal_uInt16 mnHeightPixel; + PublishingFormat meFormat; + bool mbHeader; + bool mbNotes; + bool mbFrames; + OUString maIndex; + OUString maEMail; + OUString maAuthor; + OUString maHomePage; + OUString maInfo; + sal_Int16 mnCompression; + OUString maDocFileName; + OUString maFramePage; + OUString mDocTitle; + bool mbDownload; + + bool mbAutoSlide; + double mfSlideDuration; + bool mbSlideSound; + bool mbHiddenSlides; + bool mbEndless; + + bool mbUserAttr; + Color maTextColor; ///< The following colors are used for the <body> tag if mbUserAttr is true. + Color maBackColor; + Color maLinkColor; + Color maVLinkColor; + Color maALinkColor; + Color maFirstPageColor; + bool mbDocColors; + + std::vector<OUString> maHTMLFiles; + std::vector<OUString> maImageFiles; + std::vector<OUString> maThumbnailFiles; + std::vector<OUString> maPageNames; + std::vector<OUString> maTextFiles; + + OUString maExportPath; ///< output directory or URL. + OUString maIndexUrl; + OUString maURLPath; + OUString maCGIPath; + PublishingScript meScript; + + std::unique_ptr< ButtonSet > mpButtonSet; + + static SdrTextObj* GetLayoutTextObject(SdrPage const * pPage); + + void SetDocColors( SdPage* pPage = nullptr ); + + bool CreateImagesForPresPages( bool bThumbnails = false ); + bool CreateHtmlTextForPresPages(); + bool CreateHtmlForPresPages(); + bool CreateContentPage(); + void CreateFileNames(); + void CreateBitmaps(); + bool CreateOutlinePages(); + bool CreateFrames(); + bool CreateNotesPages(); + bool CreateNavBarFrames(); + + bool CreateASPScripts(); + bool CreatePERLScripts(); + bool CreateImageFileList(); + bool CreateImageNumberFile(); + + bool checkForExistingFiles(); + bool checkFileExists( css::uno::Reference< css::ucb::XSimpleFileAccess3 > const & xFileAccess, std::u16string_view aFileName ); + + OUString const & getDocumentTitle(); + bool SavePresentation(); + + static OUString CreateLink( std::u16string_view aLink, std::u16string_view aText, + std::u16string_view aTarget = std::u16string_view()); + static OUString CreateImage( std::u16string_view aImage, std::u16string_view aAltText ); + OUString CreateNavBar( sal_uInt16 nSdPage, bool bIsText ) const; + OUString CreateBodyTag() const; + + OUString ParagraphToHTMLString( SdrOutliner const * pOutliner, sal_Int32 nPara, const Color& rBackgroundColor ); + OUString TextAttribToHTMLString( SfxItemSet const * pSet, HtmlState* pState, const Color& rBackgroundColor ); + + OUString CreateTextForTitle( SdrOutliner* pOutliner, SdPage* pPage, const Color& rBackgroundColor ); + OUString CreateTextForPage( SdrOutliner* pOutliner, SdPage const * pPage, bool bHeadLine, const Color& rBackgroundColor ); + OUString CreateTextForNotesPage( SdrOutliner* pOutliner, SdPage* pPage, const Color& rBackgroundColor ); + + static OUString CreateHTMLCircleArea( sal_uLong nRadius, sal_uLong nCenterX, + sal_uLong nCenterY, std::u16string_view rHRef ); + static OUString CreateHTMLPolygonArea( const ::basegfx::B2DPolyPolygon& rPolyPoly, Size aShift, double fFactor, std::u16string_view rHRef ); + static OUString CreateHTMLRectArea( const ::tools::Rectangle& rRect, + std::u16string_view rHRef ); + + OUString CreatePageURL( sal_uInt16 nPgNum ); + + OUString InsertSound( const OUString& rSoundFile ); + bool CopyFile( const OUString& rSourceFile, const OUString& rDestFile ); + bool CopyScript( std::u16string_view rPath, const OUString& rSource, const OUString& rDest, bool bUnix = false ); + + void InitProgress( sal_uInt16 nProgrCount ); + void ResetProgress(); + + /// Output only the charset metadata, title etc. will be handled separately. + static OUString CreateMetaCharset(); + + /// Output document metadata. + OUString DocumentMetadata() const; + + void InitExportParameters( const css::uno::Sequence< css::beans::PropertyValue >& rParams); + void ExportHtml(); + void ExportKiosk(); + void ExportWebCast(); + void ExportSingleDocument(); + + bool WriteHtml( const OUString& rFileName, bool bAddExtension, std::u16string_view rHtmlData ); + static OUString GetButtonName( int nButton ); + + void WriteOutlinerParagraph(OUStringBuffer& aStr, SdrOutliner* pOutliner, + OutlinerParaObject const * pOutlinerParagraphObject, + const Color& rBackgroundColor, bool bHeadLine); + + void WriteObjectGroup(OUStringBuffer& aStr, SdrObjGroup const * pObjectGroup, + SdrOutliner* pOutliner, const Color& rBackgroundColor, bool bHeadLine); + + void WriteTable(OUStringBuffer& aStr, sdr::table::SdrTableObj const * pTableObject, + SdrOutliner* pOutliner, const Color& rBackgroundColor); + + public: + HtmlExport(const OUString& aPath, + const css::uno::Sequence<css::beans::PropertyValue>& rParams, + SdDrawDocument* pExpDoc, + sd::DrawDocShell* pDocShell); + + ~HtmlExport(); + + static OUString ColorToHTMLString( Color aColor ); + static OUString StringToHTMLString( const OUString& rString ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlpublishmode.hxx b/sd/source/filter/html/htmlpublishmode.hxx new file mode 100644 index 000000000..3ba7eeb80 --- /dev/null +++ b/sd/source/filter/html/htmlpublishmode.hxx @@ -0,0 +1,31 @@ +/* -*- 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 + +enum HtmlPublishMode +{ + PUBLISH_HTML, + PUBLISH_FRAMES, + PUBLISH_WEBCAST, + PUBLISH_KIOSK, + PUBLISH_SINGLE_DOCUMENT +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/pubdlg.cxx b/sd/source/filter/html/pubdlg.cxx new file mode 100644 index 000000000..257021d96 --- /dev/null +++ b/sd/source/filter/html/pubdlg.cxx @@ -0,0 +1,1539 @@ +/* -*- 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 <com/sun/star/beans/PropertyValue.hpp> +#include <comphelper/sequence.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/FilterConfigItem.hxx> +#include <vcl/image.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sal/log.hxx> +#include <svtools/valueset.hxx> +#include <svtools/colrdlg.hxx> +#include <tools/debug.hxx> +#include <tools/urlobj.hxx> +#include <tools/GenericTypeSerializer.hxx> +#include <sdiocmpt.hxx> +#include <sfx2/docfile.hxx> +#include <pres.hxx> +#include <unotools/useroptions.hxx> +#include <unotools/pathoptions.hxx> + +#include <sdresid.hxx> +#include <strings.hrc> +#include <pubdlg.hxx> +#include "htmlattr.hxx" +#include "htmlex.hxx" +#include "htmlpublishmode.hxx" +#include <helpids.h> +#include "buttonset.hxx" +#include <strings.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; + +#define NOOFPAGES 6 + +//ID for the config-data with the HTML-settings +const sal_uInt16 nMagic = sal_uInt16(0x1977); + +// Key for the soffice.ini +constexpr OUStringLiteral KEY_QUALITY = u"JPG-EXPORT-QUALITY"; + +// The Help-IDs of the pages +const char* const aPageHelpIds[NOOFPAGES] = +{ + HID_SD_HTMLEXPORT_PAGE1, + HID_SD_HTMLEXPORT_PAGE2, + HID_SD_HTMLEXPORT_PAGE3, + HID_SD_HTMLEXPORT_PAGE4, + HID_SD_HTMLEXPORT_PAGE5, + HID_SD_HTMLEXPORT_PAGE6 +}; + +static SvStream& operator >> (SvStream& rIn, SdPublishingDesign& rDesign); + +static SvStream& WriteSdPublishingDesign(SvStream& rOut, const SdPublishingDesign& rDesign); + +// This class has all the settings for the HTML-export autopilot +class SdPublishingDesign +{ +public: + OUString m_aDesignName; + + HtmlPublishMode m_eMode; + + // special WebCast options + PublishingScript m_eScript; + OUString m_aCGI; + OUString m_aURL; + + // special Kiosk options + bool m_bAutoSlide; + sal_uInt32 m_nSlideDuration; + bool m_bEndless; + + // special HTML options + bool m_bContentPage; + bool m_bNotes; + + // misc options + sal_uInt16 m_nResolution; + OUString m_aCompression; + PublishingFormat m_eFormat; + bool m_bSlideSound; + bool m_bHiddenSlides; + + // title page information + OUString m_aAuthor; + OUString m_aEMail; + OUString m_aWWW; + OUString m_aMisc; + bool m_bDownload; + bool m_bCreated; // not used + + // buttons and colorscheme + sal_Int16 m_nButtonThema; + bool m_bUserAttr; + Color m_aBackColor; + Color m_aTextColor; + Color m_aLinkColor; + Color m_aVLinkColor; + Color m_aALinkColor; + bool m_bUseAttribs; + bool m_bUseColor; + + SdPublishingDesign(); + + bool operator ==(const SdPublishingDesign & rDesign) const; + friend SvStream& operator >> (SvStream& rIn, SdPublishingDesign& rDesign); + friend SvStream& WriteSdPublishingDesign(SvStream& rOut, const SdPublishingDesign& rDesign); +}; + +// load Default-settings +SdPublishingDesign::SdPublishingDesign() + : m_eMode(PUBLISH_HTML) + , m_eScript(SCRIPT_ASP) + , m_bAutoSlide(true) + , m_nSlideDuration(15) + , m_bEndless(true) + , m_bContentPage(true) + , m_bNotes(true) + , m_nResolution(PUB_LOWRES_WIDTH) + , m_eFormat(FORMAT_PNG) + , m_bSlideSound(true) + , m_bHiddenSlides(false) + , m_bDownload(false) + , m_bCreated(false) + , m_nButtonThema(-1) + , m_bUserAttr(false) + , m_aBackColor(COL_WHITE) + , m_aTextColor(COL_BLACK) + , m_aLinkColor(COL_BLUE) + , m_aVLinkColor(COL_LIGHTGRAY) + , m_aALinkColor(COL_GRAY) + , m_bUseAttribs(true) + , m_bUseColor(true) +{ + FilterConfigItem aFilterConfigItem(u"Office.Common/Filter/Graphic/Export/JPG"); + sal_Int32 nCompression = aFilterConfigItem.ReadInt32( KEY_QUALITY, 75 ); + m_aCompression = OUString::number(nCompression) + "%"; + + SvtUserOptions aUserOptions; + m_aAuthor = aUserOptions.GetFirstName(); + if (!m_aAuthor.isEmpty() && !aUserOptions.GetLastName().isEmpty()) + m_aAuthor += " "; + m_aAuthor += aUserOptions.GetLastName(); + m_aEMail = aUserOptions.GetEmail(); +} + +// Compares the values without paying attention to the name +bool SdPublishingDesign::operator ==(const SdPublishingDesign & rDesign) const +{ + return + ( + m_eMode == rDesign.m_eMode && + m_nResolution == rDesign.m_nResolution && + m_aCompression == rDesign.m_aCompression && + m_eFormat == rDesign.m_eFormat && + m_bHiddenSlides == rDesign.m_bHiddenSlides && + ( // compare html options + (m_eMode != PUBLISH_HTML && m_eMode != PUBLISH_FRAMES) || + ( + m_bContentPage == rDesign.m_bContentPage && + m_bNotes == rDesign.m_bNotes && + m_aAuthor == rDesign.m_aAuthor && + m_aEMail == rDesign.m_aEMail && + m_aWWW == rDesign.m_aWWW && + m_aMisc == rDesign.m_aMisc && + m_bDownload == rDesign.m_bDownload && + m_nButtonThema == rDesign.m_nButtonThema && + m_bUserAttr == rDesign.m_bUserAttr && + m_aBackColor == rDesign.m_aBackColor && + m_aTextColor == rDesign.m_aTextColor && + m_aLinkColor == rDesign.m_aLinkColor && + m_aVLinkColor == rDesign.m_aVLinkColor && + m_aALinkColor == rDesign.m_aALinkColor && + m_bUseAttribs == rDesign.m_bUseAttribs && + m_bSlideSound == rDesign.m_bSlideSound && + m_bUseColor == rDesign.m_bUseColor + ) + ) && + ( // compare kiosk options + (m_eMode != PUBLISH_KIOSK) || + ( + m_bAutoSlide == rDesign.m_bAutoSlide && + m_bSlideSound == rDesign.m_bSlideSound && + ( + !m_bAutoSlide || + ( + m_nSlideDuration == rDesign.m_nSlideDuration && + m_bEndless == rDesign.m_bEndless + ) + ) + ) + ) && + ( // compare WebCast options + (m_eMode != PUBLISH_WEBCAST) || + ( + m_eScript == rDesign.m_eScript && + ( + m_eScript != SCRIPT_PERL || + ( + m_aURL == rDesign.m_aURL && + m_aCGI == rDesign.m_aCGI + ) + ) + ) + ) + ); +} + +// Load the design from the stream +SvStream& operator >> (SvStream& rIn, SdPublishingDesign& rDesign) +{ + SdIOCompat aIO(rIn, StreamMode::READ); + + sal_uInt16 nTemp16; + tools::GenericTypeSerializer aSerializer(rIn); + + rDesign.m_aDesignName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rIn.ReadUInt16( nTemp16 ); + rDesign.m_eMode = static_cast<HtmlPublishMode>(nTemp16); + rIn.ReadCharAsBool( rDesign.m_bContentPage ); + rIn.ReadCharAsBool( rDesign.m_bNotes ); + rIn.ReadUInt16( rDesign.m_nResolution ); + rDesign.m_aCompression = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rIn.ReadUInt16( nTemp16 ); + rDesign.m_eFormat = static_cast<PublishingFormat>(nTemp16); + rDesign.m_aAuthor = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rDesign.m_aEMail = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rDesign.m_aWWW = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rDesign.m_aMisc = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rIn.ReadCharAsBool( rDesign.m_bDownload ); + rIn.ReadCharAsBool( rDesign.m_bCreated ); // not used + rIn.ReadInt16( rDesign.m_nButtonThema ); + rIn.ReadCharAsBool( rDesign.m_bUserAttr ); + aSerializer.readColor(rDesign.m_aBackColor); + aSerializer.readColor(rDesign.m_aTextColor); + aSerializer.readColor(rDesign.m_aLinkColor); + aSerializer.readColor(rDesign.m_aVLinkColor); + aSerializer.readColor(rDesign.m_aALinkColor); + rIn.ReadCharAsBool( rDesign.m_bUseAttribs ); + rIn.ReadCharAsBool( rDesign.m_bUseColor ); + + rIn.ReadUInt16( nTemp16 ); + rDesign.m_eScript = static_cast<PublishingScript>(nTemp16); + rDesign.m_aURL = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rDesign.m_aCGI = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + + rIn.ReadCharAsBool( rDesign.m_bAutoSlide ); + rIn.ReadUInt32( rDesign.m_nSlideDuration ); + rIn.ReadCharAsBool( rDesign.m_bEndless ); + rIn.ReadCharAsBool( rDesign.m_bSlideSound ); + rIn.ReadCharAsBool( rDesign.m_bHiddenSlides ); + + return rIn; +} + +// Set the design to the stream +SvStream& WriteSdPublishingDesign(SvStream& rOut, const SdPublishingDesign& rDesign) +{ + // The last parameter is the versionnumber of the code + SdIOCompat aIO(rOut, StreamMode::WRITE, 0); + + tools::GenericTypeSerializer aSerializer(rOut); + + // Name + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aDesignName, + RTL_TEXTENCODING_UTF8); + + rOut.WriteUInt16( rDesign.m_eMode ); + rOut.WriteBool( rDesign.m_bContentPage ); + rOut.WriteBool( rDesign.m_bNotes ); + rOut.WriteUInt16( rDesign.m_nResolution ); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aCompression, + RTL_TEXTENCODING_UTF8); + rOut.WriteUInt16( rDesign.m_eFormat ); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aAuthor, + RTL_TEXTENCODING_UTF8); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aEMail, + RTL_TEXTENCODING_UTF8); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aWWW, + RTL_TEXTENCODING_UTF8); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aMisc, + RTL_TEXTENCODING_UTF8); + rOut.WriteBool( rDesign.m_bDownload ); + rOut.WriteBool( rDesign.m_bCreated ); // not used + rOut.WriteInt16( rDesign.m_nButtonThema ); + rOut.WriteBool( rDesign.m_bUserAttr ); + aSerializer.writeColor(rDesign.m_aBackColor); + aSerializer.writeColor(rDesign.m_aTextColor); + aSerializer.writeColor(rDesign.m_aLinkColor); + aSerializer.writeColor(rDesign.m_aVLinkColor); + aSerializer.writeColor(rDesign.m_aALinkColor); + rOut.WriteBool( rDesign.m_bUseAttribs ); + rOut.WriteBool( rDesign.m_bUseColor ); + + rOut.WriteUInt16( rDesign.m_eScript ); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aURL, + RTL_TEXTENCODING_UTF8); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aCGI, + RTL_TEXTENCODING_UTF8); + + rOut.WriteBool( rDesign.m_bAutoSlide ); + rOut.WriteUInt32( rDesign.m_nSlideDuration ); + rOut.WriteBool( rDesign.m_bEndless ); + rOut.WriteBool( rDesign.m_bSlideSound ); + rOut.WriteBool( rDesign.m_bHiddenSlides ); + + return rOut; +} + +namespace { + +// Dialog for the entry of the name of the design +class SdDesignNameDlg : public weld::GenericDialogController +{ +private: + std::unique_ptr<weld::Entry> m_xEdit; + std::unique_ptr<weld::Button> m_xBtnOK; + +public: + SdDesignNameDlg(weld::Window* pWindow, const OUString& aName ); + OUString GetDesignName() const; + DECL_LINK(ModifyHdl, weld::Entry&, void); +}; + +} + +// SdPublishingDlg Methods + +SdPublishingDlg::SdPublishingDlg(weld::Window* pWindow, DocumentType eDocType) + : GenericDialogController(pWindow, "modules/simpress/ui/publishingdialog.ui", "PublishingDialog") + , m_xPage1_Designs(m_xBuilder->weld_tree_view("designsTreeview")) + , m_xPage2_Standard_FB(m_xBuilder->weld_image("standardFBImage")) + , m_xPage2_Frames_FB(m_xBuilder->weld_image("framesFBImage")) + , m_xPage2_Kiosk_FB(m_xBuilder->weld_image("kioskFBImage")) + , m_xPage2_WebCast_FB(m_xBuilder->weld_image("webCastFBImage")) + , m_xPage4_Misc(m_xBuilder->weld_text_view("miscTextview")) + , m_xButtonSet(new ButtonSet()) + , m_xLastPageButton(m_xBuilder->weld_button("lastPageButton")) + , m_xNextPageButton(m_xBuilder->weld_button("nextPageButton")) + , m_xFinishButton(m_xBuilder->weld_button("finishButton")) + , aAssistentFunc(NOOFPAGES) + , m_bButtonsDirty(true) + , m_bDesignListDirty(false) + , m_pDesign(nullptr) +{ + m_bImpress = eDocType == DocumentType::Impress; + + Size aSize(m_xPage2_Standard_FB->get_approximate_digit_width() * 12, + m_xPage2_Standard_FB->get_text_height() * 6); + m_xPage2_Standard_FB->set_size_request(aSize.Width(), aSize.Height()); + m_xPage2_Frames_FB->set_size_request(aSize.Width(), aSize.Height()); + m_xPage2_Kiosk_FB->set_size_request(aSize.Width(), aSize.Height()); + m_xPage2_WebCast_FB->set_size_request(aSize.Width(), aSize.Height()); + + m_xPage4_Misc->set_size_request(m_xPage4_Misc->get_approximate_digit_width() * 40, + m_xPage4_Misc->get_height_rows(8)); + + m_xPage1_Designs->set_size_request(m_xPage4_Misc->get_approximate_digit_width() * 40, + m_xPage4_Misc->get_height_rows(8)); + + //Lock down the preferred size based on the + //initial max-size configuration + aSize = m_xDialog->get_preferred_size(); + m_xDialog->set_size_request(aSize.Width(), aSize.Height()); + + CreatePages(); + Load(); + + // sets the output page + aAssistentFunc.GotoPage(1); + m_xLastPageButton->set_sensitive(false); + + // button assignment + m_xFinishButton->connect_clicked(LINK(this,SdPublishingDlg,FinishHdl)); + m_xLastPageButton->connect_clicked(LINK(this,SdPublishingDlg,LastPageHdl)); + m_xNextPageButton->connect_clicked(LINK(this,SdPublishingDlg,NextPageHdl)); + + m_xPage1_NewDesign->connect_toggled(LINK(this,SdPublishingDlg,DesignHdl)); + m_xPage1_OldDesign->connect_toggled(LINK(this,SdPublishingDlg,DesignHdl)); + m_xPage1_Designs->connect_changed(LINK(this,SdPublishingDlg,DesignSelectHdl)); + m_xPage1_DelDesign->connect_clicked(LINK(this,SdPublishingDlg,DesignDeleteHdl)); + + m_xPage2_Standard->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + m_xPage2_Frames->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + m_xPage2_SingleDocument->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + m_xPage2_Kiosk->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + m_xPage2_WebCast->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + + m_xPage2_Content->connect_toggled(LINK(this,SdPublishingDlg,ContentHdl)); + + m_xPage2_ASP->connect_toggled(LINK(this,SdPublishingDlg,WebServerHdl)); + m_xPage2_PERL->connect_toggled(LINK(this,SdPublishingDlg,WebServerHdl)); + m_xPage2_Index->set_text("index" STR_HTMLEXP_DEFAULT_EXTENSION); + m_xPage2_CGI->set_text("/cgi-bin/"); + + m_xPage3_Png->connect_toggled(LINK(this,SdPublishingDlg, GfxFormatHdl)); + m_xPage3_Gif->connect_toggled(LINK(this,SdPublishingDlg, GfxFormatHdl)); + m_xPage3_Jpg->connect_toggled(LINK(this,SdPublishingDlg, GfxFormatHdl)); + m_xPage3_Quality->set_sensitive(false); + + m_xPage3_Resolution_1->connect_toggled(LINK(this,SdPublishingDlg, ResolutionHdl )); + m_xPage3_Resolution_2->connect_toggled(LINK(this,SdPublishingDlg, ResolutionHdl )); + m_xPage3_Resolution_3->connect_toggled(LINK(this,SdPublishingDlg, ResolutionHdl )); + m_xPage3_Resolution_4->connect_toggled(LINK(this, SdPublishingDlg, ResolutionHdl)); + + m_xPage2_ChgDefault->connect_toggled(LINK(this,SdPublishingDlg, SlideChgHdl)); + m_xPage2_ChgAuto->connect_toggled(LINK(this,SdPublishingDlg, SlideChgHdl)); + + m_xPage5_Buttons->SetSelectHdl(LINK(this,SdPublishingDlg, ButtonsHdl )); + m_xPage5_Buttons->SetStyle( m_xPage5_Buttons->GetStyle() | WB_VSCROLL ); + + m_xPage6_Back->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + m_xPage6_Text->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + m_xPage6_Link->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + m_xPage6_VLink->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + m_xPage6_ALink->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + + m_xPage6_DocColors->set_active(true); + + m_xPage3_Quality->append_text( "25%" ); + m_xPage3_Quality->append_text( "50%" ); + m_xPage3_Quality->append_text( "75%" ); + m_xPage3_Quality->append_text( "100%" ); + + m_xPage5_Buttons->SetColCount(); + m_xPage5_Buttons->SetLineCount( 4 ); + m_xPage5_Buttons->SetExtraSpacing( 1 ); + + for( const auto& rDesign : m_aDesignList ) + m_xPage1_Designs->append_text(rDesign.m_aDesignName); + + SetDefaults(); + + m_xDialog->set_help_id(aPageHelpIds[0]); + + m_xNextPageButton->grab_focus(); +} + +SdPublishingDlg::~SdPublishingDlg() +{ +} + +// Generate dialog controls and embed them in the pages +void SdPublishingDlg::CreatePages() +{ + // Page 1 + m_xPage1 = m_xBuilder->weld_container("page1"); + m_xPage1_Title = m_xBuilder->weld_label("assignLabel"); + m_xPage1_NewDesign = m_xBuilder->weld_radio_button("newDesignRadiobutton"); + m_xPage1_OldDesign = m_xBuilder->weld_radio_button("oldDesignRadiobutton"); + m_xPage1_DelDesign = m_xBuilder->weld_button("delDesingButton"); + m_xPage1_Desc = m_xBuilder->weld_label("descLabel"); + aAssistentFunc.InsertControl(1, m_xPage1.get()); + aAssistentFunc.InsertControl(1, m_xPage1_Title.get()); + aAssistentFunc.InsertControl(1, m_xPage1_NewDesign.get()); + aAssistentFunc.InsertControl(1, m_xPage1_OldDesign.get()); + aAssistentFunc.InsertControl(1, m_xPage1_Designs.get()); + aAssistentFunc.InsertControl(1, m_xPage1_DelDesign.get()); + aAssistentFunc.InsertControl(1, m_xPage1_Desc.get()); + + // Page 2 + m_xPage2 = m_xBuilder->weld_container("page2"); + m_xPage2Frame2 = m_xBuilder->weld_container("page2.2"); + m_xPage2Frame3 = m_xBuilder->weld_container("page2.3"); + m_xPage2Frame4 = m_xBuilder->weld_container("page2.4"); + m_xPage2_Title = m_xBuilder->weld_label("publicationLabel"); + m_xPage2_Standard = m_xBuilder->weld_radio_button("standardRadiobutton"); + m_xPage2_Frames = m_xBuilder->weld_radio_button("framesRadiobutton"); + m_xPage2_SingleDocument = m_xBuilder->weld_radio_button("singleDocumentRadiobutton"); + m_xPage2_Kiosk = m_xBuilder->weld_radio_button("kioskRadiobutton"); + m_xPage2_WebCast = m_xBuilder->weld_radio_button("webCastRadiobutton"); + aAssistentFunc.InsertControl(2, m_xPage2.get()); + aAssistentFunc.InsertControl(2, m_xPage2Frame2.get()); + aAssistentFunc.InsertControl(2, m_xPage2Frame3.get()); + aAssistentFunc.InsertControl(2, m_xPage2Frame4.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Title.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Standard.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Frames.get()); + aAssistentFunc.InsertControl(2, m_xPage2_SingleDocument.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Kiosk.get()); + aAssistentFunc.InsertControl(2, m_xPage2_WebCast.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Standard_FB.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Frames_FB.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Kiosk_FB.get()); + aAssistentFunc.InsertControl(2, m_xPage2_WebCast_FB.get()); + + m_xPage2_Title_Html = m_xBuilder->weld_label( "htmlOptionsLabel"); + m_xPage2_Content = m_xBuilder->weld_check_button("contentCheckbutton"); + m_xPage2_Notes = m_xBuilder->weld_check_button("notesCheckbutton"); + aAssistentFunc.InsertControl(2, m_xPage2_Title_Html.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Content.get()); + if (m_bImpress) + aAssistentFunc.InsertControl(2, m_xPage2_Notes.get()); + + m_xPage2_Title_WebCast = m_xBuilder->weld_label("webCastLabel"); + m_xPage2_ASP = m_xBuilder->weld_radio_button("ASPRadiobutton"); + m_xPage2_PERL = m_xBuilder->weld_radio_button("perlRadiobutton"); + m_xPage2_URL_txt = m_xBuilder->weld_label("URLTxtLabel"); + m_xPage2_URL = m_xBuilder->weld_entry("URLEntry"); + m_xPage2_CGI_txt = m_xBuilder->weld_label("CGITxtLabel"); + m_xPage2_CGI = m_xBuilder->weld_entry("CGIEntry"); + m_xPage2_Index_txt = m_xBuilder->weld_label("indexTxtLabel"); + m_xPage2_Index = m_xBuilder->weld_entry("indexEntry"); + m_xPage2_Title_Kiosk = m_xBuilder->weld_label("kioskLabel"); + m_xPage2_ChgDefault = m_xBuilder->weld_radio_button("chgDefaultRadiobutton"); + m_xPage2_ChgAuto = m_xBuilder->weld_radio_button("chgAutoRadiobutton"); + m_xPage2_Duration_txt = m_xBuilder->weld_label("durationTxtLabel"); + m_xPage2_Duration = m_xBuilder->weld_formatted_spin_button("durationSpinbutton"); + m_xFormatter.reset(new weld::TimeFormatter(*m_xPage2_Duration)); + m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration); + m_xPage2_Endless = m_xBuilder->weld_check_button("endlessCheckbutton"); + aAssistentFunc.InsertControl(2, m_xPage2_Title_WebCast.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Index_txt.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Index.get()); + aAssistentFunc.InsertControl(2, m_xPage2_ASP.get()); + aAssistentFunc.InsertControl(2, m_xPage2_PERL.get()); + aAssistentFunc.InsertControl(2, m_xPage2_URL_txt.get()); + aAssistentFunc.InsertControl(2, m_xPage2_URL.get()); + aAssistentFunc.InsertControl(2, m_xPage2_CGI_txt.get()); + aAssistentFunc.InsertControl(2, m_xPage2_CGI.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Title_Kiosk.get()); + aAssistentFunc.InsertControl(2, m_xPage2_ChgDefault.get()); + aAssistentFunc.InsertControl(2, m_xPage2_ChgAuto.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Duration_txt.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Duration.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Endless.get()); + + // Page 3 + m_xPage3 = m_xBuilder->weld_container("page3"); + m_xPage3_Title1 = m_xBuilder->weld_label("saveImgAsLabel"); + m_xPage3_Png = m_xBuilder->weld_radio_button("pngRadiobutton"); + m_xPage3_Gif = m_xBuilder->weld_radio_button("gifRadiobutton"); + m_xPage3_Jpg = m_xBuilder->weld_radio_button("jpgRadiobutton"); + m_xPage3_Quality_txt = m_xBuilder->weld_label("qualityTxtLabel"); + m_xPage3_Quality= m_xBuilder->weld_combo_box("qualityCombobox"); + m_xPage3_Title2 = m_xBuilder->weld_label("monitorResolutionLabel"); + m_xPage3_Resolution_1 = m_xBuilder->weld_radio_button("resolution1Radiobutton"); + m_xPage3_Resolution_2 = m_xBuilder->weld_radio_button("resolution2Radiobutton"); + m_xPage3_Resolution_3 = m_xBuilder->weld_radio_button("resolution3Radiobutton"); + m_xPage3_Resolution_4 = m_xBuilder->weld_radio_button("resolution4Radiobutton"); + m_xPage3_Title3 = m_xBuilder->weld_label("effectsLabel"); + m_xPage3_SldSound = m_xBuilder->weld_check_button("sldSoundCheckbutton"); + m_xPage3_HiddenSlides = m_xBuilder->weld_check_button("hiddenSlidesCheckbutton"); + aAssistentFunc.InsertControl(3, m_xPage3.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Title1.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Png.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Gif.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Jpg.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Quality_txt.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Quality.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Title2.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Resolution_1.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Resolution_2.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Resolution_3.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Resolution_4.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Title3.get()); + aAssistentFunc.InsertControl(3, m_xPage3_SldSound.get()); + aAssistentFunc.InsertControl(3, m_xPage3_HiddenSlides.get()); + + // Page 4 + m_xPage4 = m_xBuilder->weld_container("page4"); + m_xPage4_Title1 = m_xBuilder->weld_label("infTitlePageLabel"); + m_xPage4_Author_txt = m_xBuilder->weld_label("authorTxtLabel"); + m_xPage4_Author = m_xBuilder->weld_entry("authorEntry"); + m_xPage4_Email_txt = m_xBuilder->weld_label("emailTxtLabel"); + m_xPage4_Email = m_xBuilder->weld_entry("emailEntry"); + m_xPage4_WWW_txt = m_xBuilder->weld_label("wwwTxtLabel"); + m_xPage4_WWW = m_xBuilder->weld_entry("wwwEntry"); + m_xPage4_Title2 = m_xBuilder->weld_label("addInformLabel"); + m_xPage4_Download = m_xBuilder->weld_check_button("downloadCheckbutton"); + aAssistentFunc.InsertControl(4, m_xPage4.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Title1.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Author_txt.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Author.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Email_txt.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Email.get()); + aAssistentFunc.InsertControl(4, m_xPage4_WWW_txt.get()); + aAssistentFunc.InsertControl(4, m_xPage4_WWW.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Title2.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Misc.get()); + if(m_bImpress) + aAssistentFunc.InsertControl(4, m_xPage4_Download.get()); + + // Page 5 + m_xPage5 = m_xBuilder->weld_container("page5"); + m_xPage5_Title = m_xBuilder->weld_label("buttonStyleLabel"); + m_xPage5_TextOnly = m_xBuilder->weld_check_button("textOnlyCheckbutton"); + m_xPage5_Buttons.reset(new ValueSet(m_xBuilder->weld_scrolled_window("buttonsDrawingareawin", true))); + m_xPage5_ButtonsWnd.reset(new weld::CustomWeld(*m_xBuilder, "buttonsDrawingarea", *m_xPage5_Buttons)); + aAssistentFunc.InsertControl(5, m_xPage5.get()); + aAssistentFunc.InsertControl(5, m_xPage5_Title.get()); + aAssistentFunc.InsertControl(5, m_xPage5_TextOnly.get()); + aAssistentFunc.InsertControl(5, m_xPage5_Buttons->GetDrawingArea()); + + // Page 6 + m_xPage6 = m_xBuilder->weld_container("page6"); + m_xPage6_Title = m_xBuilder->weld_label("selectColorLabel"); + m_xPage6_Default = m_xBuilder->weld_radio_button("defaultRadiobutton"); + m_xPage6_User = m_xBuilder->weld_radio_button("userRadiobutton"); + m_xPage6_Back = m_xBuilder->weld_button("backButton"); + m_xPage6_Text = m_xBuilder->weld_button("textButton"); + m_xPage6_Link = m_xBuilder->weld_button("linkButton"); + m_xPage6_VLink = m_xBuilder->weld_button("vLinkButton"); + m_xPage6_ALink = m_xBuilder->weld_button("aLinkButton"); + m_xPage6_DocColors = m_xBuilder->weld_radio_button("docColorsRadiobutton"); + m_xPage6_Preview.reset(new SdHtmlAttrPreview); + m_xPage6_PreviewWnd.reset(new weld::CustomWeld(*m_xBuilder, "previewDrawingarea", *m_xPage6_Preview)); + aAssistentFunc.InsertControl(6, m_xPage6.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Title.get()); + aAssistentFunc.InsertControl(6, m_xPage6_DocColors.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Default.get()); + aAssistentFunc.InsertControl(6, m_xPage6_User.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Text.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Link.get()); + aAssistentFunc.InsertControl(6, m_xPage6_ALink.get()); + aAssistentFunc.InsertControl(6, m_xPage6_VLink.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Back.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Preview->GetDrawingArea()); +} + +// Initialize dialog with default-values +void SdPublishingDlg::SetDefaults() +{ + SdPublishingDesign aDefault; + SetDesign(&aDefault); + + m_xPage1_NewDesign->set_active(true); + m_xPage1_OldDesign->set_active(false); + UpdatePage(); +} + +// Feed the SfxItemSet with the settings of the dialog +void SdPublishingDlg::GetParameterSequence( Sequence< PropertyValue >& rParams ) +{ + std::vector< PropertyValue > aProps; + + PropertyValue aValue; + + // Page 2 + aValue.Name = "PublishMode"; + + HtmlPublishMode ePublishMode; + if (m_xPage2_Frames->get_active()) + ePublishMode = PUBLISH_FRAMES; + else if (m_xPage2_SingleDocument->get_active()) + ePublishMode = PUBLISH_SINGLE_DOCUMENT; + else if (m_xPage2_Kiosk->get_active()) + ePublishMode = PUBLISH_KIOSK; + else if (m_xPage2_WebCast->get_active()) + ePublishMode = PUBLISH_WEBCAST; + else + ePublishMode = PUBLISH_HTML; + + aValue.Value <<= static_cast<sal_Int32>(ePublishMode); + aProps.push_back( aValue ); + + aValue.Name = "IsExportContentsPage"; + aValue.Value <<= m_xPage2_Content->get_active(); + aProps.push_back( aValue ); + + if(m_bImpress) + { + aValue.Name = "IsExportNotes"; + aValue.Value <<= m_xPage2_Notes->get_active(); + aProps.push_back( aValue ); + } + + if( m_xPage2_WebCast->get_active() ) + { + aValue.Name = "WebCastScriptLanguage"; + if( m_xPage2_ASP->get_active() ) + aValue.Value <<= OUString( "asp" ); + else + aValue.Value <<= OUString( "perl" ); + aProps.push_back( aValue ); + + aValue.Name = "WebCastCGIURL"; + aValue.Value <<= m_xPage2_CGI->get_text(); + aProps.push_back( aValue ); + + aValue.Name = "WebCastTargetURL"; + aValue.Value <<= m_xPage2_URL->get_text(); + aProps.push_back( aValue ); + } + aValue.Name = "IndexURL"; + aValue.Value <<= m_xPage2_Index->get_text(); + aProps.push_back( aValue ); + + if( m_xPage2_Kiosk->get_active() && m_xPage2_ChgAuto->get_active() ) + { + aValue.Name = "KioskSlideDuration"; + aValue.Value <<= static_cast<sal_uInt32>(m_xFormatter->GetTime().GetMSFromTime()) / 1000; + aProps.push_back( aValue ); + + aValue.Name = "KioskEndless"; + aValue.Value <<= m_xPage2_Endless->get_active(); + aProps.push_back( aValue ); + } + + // Page 3 + + aValue.Name = "Width"; + sal_Int32 nTmpWidth = PUB_LOWRES_WIDTH; + if( m_xPage3_Resolution_2->get_active() ) + nTmpWidth = PUB_MEDRES_WIDTH; + else if( m_xPage3_Resolution_3->get_active() ) + nTmpWidth = PUB_HIGHRES_WIDTH; + else if (m_xPage3_Resolution_4->get_active()) + nTmpWidth = PUB_FHDRES_WIDTH; + + aValue.Value <<= nTmpWidth; + aProps.push_back( aValue ); + + aValue.Name = "Compression"; + aValue.Value <<= m_xPage3_Quality->get_active_text(); + aProps.push_back( aValue ); + + aValue.Name = "Format"; + sal_Int32 nFormat; + if( m_xPage3_Png->get_active() ) + nFormat = static_cast<sal_Int32>(FORMAT_PNG); + else if( m_xPage3_Gif->get_active() ) + nFormat = static_cast<sal_Int32>(FORMAT_GIF); + else + nFormat = static_cast<sal_Int32>(FORMAT_JPG); + aValue.Value <<= nFormat; + aProps.push_back( aValue ); + + aValue.Name = "SlideSound"; + aValue.Value <<= m_xPage3_SldSound->get_active(); + aProps.push_back( aValue ); + + aValue.Name = "HiddenSlides"; + aValue.Value <<= m_xPage3_HiddenSlides->get_active(); + aProps.push_back( aValue ); + + // Page 4 + aValue.Name = "Author"; + aValue.Value <<= m_xPage4_Author->get_text(); + aProps.push_back( aValue ); + + aValue.Name = "EMail"; + aValue.Value <<= m_xPage4_Email->get_text(); + aProps.push_back( aValue ); + + // try to guess protocol for user's homepage + INetURLObject aHomeURL( m_xPage4_WWW->get_text(), + INetProtocol::Http, // default proto is HTTP + INetURLObject::EncodeMechanism::All ); + + aValue.Name = "HomepageURL"; + aValue.Value <<= aHomeURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + aProps.push_back( aValue ); + + aValue.Name = "UserText"; + aValue.Value <<= m_xPage4_Misc->get_text(); + aProps.push_back( aValue ); + + if( m_bImpress ) + { + aValue.Name = "EnableDownload"; + aValue.Value <<= m_xPage4_Download->get_active(); + aProps.push_back( aValue ); + } + + // Page 5 + if( !m_xPage5_TextOnly->get_active() ) + { + aValue.Name = "UseButtonSet"; + aValue.Value <<= static_cast<sal_Int32>(m_xPage5_Buttons->GetSelectedItemId() - 1); + aProps.push_back( aValue ); + } + + // Page 6 + if( m_xPage6_User->get_active() ) + { + aValue.Name = "BackColor"; + aValue.Value <<= m_aBackColor; + aProps.push_back( aValue ); + + aValue.Name = "TextColor"; + aValue.Value <<= m_aTextColor; + aProps.push_back( aValue ); + + aValue.Name = "LinkColor"; + aValue.Value <<= m_aLinkColor; + aProps.push_back( aValue ); + + aValue.Name = "VLinkColor"; + aValue.Value <<= m_aVLinkColor; + aProps.push_back( aValue ); + + aValue.Name = "ALinkColor"; + aValue.Value <<= m_aALinkColor; + aProps.push_back( aValue ); + } + + if( m_xPage6_DocColors->get_active() ) + { + aValue.Name = "IsUseDocumentColors"; + aValue.Value <<= true; + aProps.push_back( aValue ); + } + + rParams = comphelper::containerToSequence(aProps); +} + +// Clickhandler for the radiobuttons of the design-selection +IMPL_LINK( SdPublishingDlg, DesignHdl, weld::Toggleable&, rButton, void ) +{ + if (!rButton.get_active()) + return; + + if (m_xPage1_NewDesign->get_active()) + { + m_xPage1_NewDesign->set_active(true); // because of DesignDeleteHdl + m_xPage1_OldDesign->set_active(false); + m_xPage1_Designs->set_sensitive(false); + m_xPage1_DelDesign->set_sensitive(false); + m_pDesign = nullptr; + + SdPublishingDesign aDefault; + SetDesign(&aDefault); + } + else + { + m_xPage1_NewDesign->set_active(false); + m_xPage1_Designs->set_sensitive(true); + m_xPage1_DelDesign->set_sensitive(true); + + if (m_xPage1_Designs->get_selected_index() == -1) + m_xPage1_Designs->select(0); + + const sal_Int32 nPos = m_xPage1_Designs->get_selected_index(); + m_pDesign = &m_aDesignList[nPos]; + DBG_ASSERT(m_pDesign, "No Design? That's not allowed (CL)"); + + if(m_pDesign) + SetDesign(m_pDesign); + } +} + +// Clickhandler for the choice of one design +IMPL_LINK_NOARG(SdPublishingDlg, DesignSelectHdl, weld::TreeView&, void) +{ + const sal_Int32 nPos = m_xPage1_Designs->get_selected_index(); + m_pDesign = &m_aDesignList[nPos]; + DBG_ASSERT(m_pDesign, "No Design? That's not allowed (CL)"); + + if(m_pDesign) + SetDesign(m_pDesign); + + UpdatePage(); +} + +// Clickhandler for the delete of one design +IMPL_LINK_NOARG(SdPublishingDlg, DesignDeleteHdl, weld::Button&, void) +{ + const sal_Int32 nPos = m_xPage1_Designs->get_selected_index(); + + std::vector<SdPublishingDesign>::iterator iter = m_aDesignList.begin()+nPos; + + DBG_ASSERT(iter != m_aDesignList.end(), "No Design? That's not allowed (CL)"); + + m_xPage1_Designs->remove(nPos); + + if(m_pDesign == &(*iter)) + DesignHdl(*m_xPage1_NewDesign); + + m_aDesignList.erase(iter); + + m_bDesignListDirty = true; + + UpdatePage(); +} + +// Clickhandler for the other servertypes +IMPL_LINK(SdPublishingDlg, WebServerHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + + bool bASP = m_xPage2_ASP->get_active(); + m_xPage2_ASP->set_sensitive( bASP ); + m_xPage2_PERL->set_sensitive( !bASP ); + UpdatePage(); +} + +// Clickhandler for the Radiobuttons of the graphicformat choice +IMPL_LINK(SdPublishingDlg, GfxFormatHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + + m_xPage3_Png->set_sensitive(m_xPage3_Png->get_active()); + m_xPage3_Gif->set_sensitive(m_xPage3_Gif->get_active()); + m_xPage3_Jpg->set_sensitive(m_xPage3_Jpg->get_active()); + m_xPage3_Quality->set_sensitive(m_xPage3_Jpg->get_active()); +} + +// Clickhandler for the Radiobuttons Standard/Frames +IMPL_LINK(SdPublishingDlg, BaseHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + + UpdatePage(); +} + +// Clickhandler for the Checkbox of the Title page +IMPL_LINK_NOARG(SdPublishingDlg, ContentHdl, weld::Toggleable&, void) +{ + if(m_xPage2_Content->get_active()) + { + if(!aAssistentFunc.IsEnabled(4)) + { + aAssistentFunc.EnablePage(4); + UpdatePage(); + } + } + else + { + if(aAssistentFunc.IsEnabled(4)) + { + aAssistentFunc.DisablePage(4); + UpdatePage(); + } + } +} + +// Clickhandler for the Resolution Radiobuttons +IMPL_LINK( SdPublishingDlg, ResolutionHdl, weld::Toggleable&, rButton, void ) +{ + if (!rButton.get_active()) + return; + m_xPage3_Resolution_1->set_sensitive(m_xPage3_Resolution_1->get_active()); + m_xPage3_Resolution_2->set_sensitive(m_xPage3_Resolution_2->get_active()); + m_xPage3_Resolution_3->set_sensitive(m_xPage3_Resolution_3->get_active()); + m_xPage3_Resolution_4->set_sensitive(m_xPage3_Resolution_4->get_active()); +} + +// Clickhandler for the ValueSet with the bitmap-buttons +IMPL_LINK_NOARG(SdPublishingDlg, ButtonsHdl, ValueSet*, void) +{ + // if one bitmap-button is chosen, then disable TextOnly + m_xPage5_TextOnly->set_active(false); +} + +// Fill the SfxItemSet with the settings of the dialog +IMPL_LINK( SdPublishingDlg, ColorHdl, weld::Button&, rButton, void) +{ + SvColorDialog aDlg; + + if (&rButton == m_xPage6_Back.get()) + { + aDlg.SetColor( m_aBackColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aBackColor = aDlg.GetColor(); + } + else if (&rButton == m_xPage6_Text.get()) + { + aDlg.SetColor( m_aTextColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aTextColor = aDlg.GetColor(); + } + else if (&rButton == m_xPage6_Link.get()) + { + aDlg.SetColor( m_aLinkColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aLinkColor = aDlg.GetColor(); + } + else if (&rButton == m_xPage6_VLink.get()) + { + aDlg.SetColor( m_aVLinkColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aVLinkColor = aDlg.GetColor(); + } + else if (&rButton == m_xPage6_ALink.get()) + { + aDlg.SetColor( m_aALinkColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aALinkColor = aDlg.GetColor(); + } + + m_xPage6_User->set_active(true); + m_xPage6_Preview->SetColors( m_aBackColor, m_aTextColor, m_aLinkColor, + m_aVLinkColor, m_aALinkColor ); + m_xPage6_Preview->Invalidate(); +} + +IMPL_LINK(SdPublishingDlg, SlideChgHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + UpdatePage(); +} + +// Clickhandler for the Ok Button +IMPL_LINK_NOARG(SdPublishingDlg, FinishHdl, weld::Button&, void) +{ + //End + SdPublishingDesign aDesign; + GetDesign(&aDesign); + + bool bSave = false; + + if(m_xPage1_OldDesign->get_active() && m_pDesign) + { + // are there changes? + if(!(aDesign == *m_pDesign)) + bSave = true; + } + else + { + SdPublishingDesign aDefaultDesign; + if(!(aDefaultDesign == aDesign)) + bSave = true; + } + + if(bSave) + { + OUString aName; + if(m_pDesign) + aName = m_pDesign->m_aDesignName; + + bool bRetry; + do + { + bRetry = false; + + SdDesignNameDlg aDlg(m_xDialog.get(), aName); + + if (aDlg.run() == RET_OK) + { + aDesign.m_aDesignName = aDlg.GetDesignName(); + + auto iter = std::find_if(m_aDesignList.begin(), m_aDesignList.end(), + [&aDesign](const SdPublishingDesign& rDesign) { return rDesign.m_aDesignName == aDesign.m_aDesignName; }); + + if (iter != m_aDesignList.end()) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Error, VclButtonsType::YesNo, + SdResId(STR_PUBDLG_SAMENAME))); + bRetry = xErrorBox->run() == RET_NO; + + if(!bRetry) + m_aDesignList.erase(iter); + } + + if(!bRetry) + { + m_aDesignList.push_back(aDesign); + m_bDesignListDirty = true; + } + } + } + while(bRetry); + } + + if(m_bDesignListDirty) + Save(); + + m_xDialog->response(RET_OK); +} + +// Refresh the dialogs when changing from pages +void SdPublishingDlg::ChangePage() +{ + int nPage = aAssistentFunc.GetCurrentPage(); + m_xDialog->set_help_id(aPageHelpIds[nPage-1]); + + UpdatePage(); + + if (m_xNextPageButton->get_sensitive()) + m_xNextPageButton->grab_focus(); + else + m_xFinishButton->grab_focus(); +} + +void SdPublishingDlg::UpdatePage() +{ + m_xNextPageButton->set_sensitive(!aAssistentFunc.IsLastPage()); + m_xLastPageButton->set_sensitive(!aAssistentFunc.IsFirstPage()); + + int nPage = aAssistentFunc.GetCurrentPage(); + + switch( nPage ) + { + case 1: + if(m_xPage1_NewDesign->get_active()) + { + m_xPage1_Designs->set_sensitive(false); + m_xPage1_DelDesign->set_sensitive(false); + } + + if(m_aDesignList.empty()) + m_xPage1_OldDesign->set_sensitive(false); + break; + case 2: + m_xPage2_Frames_FB->set_visible(m_xPage2_Frames->get_active()); + m_xPage2_Standard_FB->set_visible(m_xPage2_Standard->get_active()); + m_xPage2_Kiosk_FB->set_visible(m_xPage2_Kiosk->get_active()); + m_xPage2_WebCast_FB->set_visible(m_xPage2_WebCast->get_active()); + + if( m_xPage2_WebCast->get_active() ) + { + m_xPage2Frame4->show(); + m_xPage2_Title_WebCast->show(); + m_xPage2_ASP->show(); + m_xPage2_PERL->show(); + m_xPage2_URL_txt->show(); + m_xPage2_URL->show(); + m_xPage2_CGI_txt->show(); + m_xPage2_CGI->show(); + m_xPage2_Index_txt->show(); + m_xPage2_Index->show(); + + bool bPerl = m_xPage2_PERL->get_active(); + m_xPage2_Index->set_sensitive(bPerl); + m_xPage2_Index_txt->set_sensitive(bPerl); + m_xPage2_URL_txt->set_sensitive(bPerl); + m_xPage2_URL->set_sensitive(bPerl); + m_xPage2_CGI_txt->set_sensitive(bPerl); + m_xPage2_CGI->set_sensitive(bPerl); + } + else + { + m_xPage2Frame4->hide(); + m_xPage2_Title_WebCast->hide(); + m_xPage2_ASP->hide(); + m_xPage2_PERL->hide(); + m_xPage2_URL_txt->hide(); + m_xPage2_URL->hide(); + m_xPage2_CGI_txt->hide(); + m_xPage2_CGI->hide(); + m_xPage2_Index->hide(); + m_xPage2_Index_txt->hide(); + } + + if( m_xPage2_Kiosk->get_active() ) + { + m_xPage2Frame3->show(); + m_xPage2_Title_Kiosk->show(); + m_xPage2_ChgDefault->show(); + m_xPage2_ChgAuto->show(); + m_xPage2_Duration_txt->show(); + m_xPage2_Duration->show(); + m_xPage2_Endless->show(); + bool bAuto = m_xPage2_ChgAuto->get_active(); + m_xPage2_Duration->set_sensitive(bAuto); + m_xPage2_Endless->set_sensitive(bAuto); + } + else + { + m_xPage2Frame3->hide(); + m_xPage2_Title_Kiosk->hide(); + m_xPage2_ChgDefault->hide(); + m_xPage2_ChgAuto->hide(); + m_xPage2_Duration->hide(); + m_xPage2_Duration_txt->hide(); + m_xPage2_Endless->hide(); + } + + if( m_xPage2_Standard->get_active() || m_xPage2_Frames->get_active() ) + { + m_xPage2Frame2->show(); + m_xPage2_Title_Html->show(); + m_xPage2_Content->show(); + if(m_bImpress) + m_xPage2_Notes->show(); + } + else + { + m_xPage2Frame2->hide(); + m_xPage2_Title_Html->hide(); + m_xPage2_Content->hide(); + if(m_bImpress) + m_xPage2_Notes->hide(); + } + break; + case 3: + if( m_xPage2_Kiosk->get_active() || m_xPage2_WebCast->get_active() ) + m_xNextPageButton->set_sensitive(false); + + if( m_xPage2_WebCast->get_active() ) + m_xPage3_SldSound->set_sensitive(false); + + m_xPage3_Quality->set_sensitive(m_xPage3_Jpg->get_active()); + + break; + case 5: + if( m_bButtonsDirty ) + LoadPreviewButtons(); + break; + } +} + +/** loads the html buttons from the button sets, creates a preview and fills the + itemset for page 5 + */ +void SdPublishingDlg::LoadPreviewButtons() +{ + if (!m_xButtonSet) + return; + + const int nButtonCount = 8; + static const char *pButtonNames[nButtonCount] = + { + "first.png", + "left.png", + "right.png", + "last.png", + "home.png", + "text.png", + "expand.png", + "collapse.png", + }; + + std::vector< OUString > aButtonNames; + for(const char * p : pButtonNames) + aButtonNames.push_back( OUString::createFromAscii( p ) ); + + int nSetCount = m_xButtonSet->getCount(); + + int nHeight = 32; + Image aImage; + for( int nSet = 0; nSet < nSetCount; ++nSet ) + { + if( m_xButtonSet->getPreview( nSet, aButtonNames, aImage ) ) + { + m_xPage5_Buttons->InsertItem( static_cast<sal_uInt16>(nSet)+1, aImage ); + if( nHeight < aImage.GetSizePixel().Height() ) + nHeight = aImage.GetSizePixel().Height(); + } + } + + m_xPage5_Buttons->SetItemHeight( nHeight ); + m_bButtonsDirty = false; +} + +// Clickhandler for the Forward Button +IMPL_LINK_NOARG(SdPublishingDlg, NextPageHdl, weld::Button&, void) +{ + aAssistentFunc.NextPage(); + ChangePage(); +} + +// Sets the Controls in the dialog to the settings in the design +void SdPublishingDlg::SetDesign( SdPublishingDesign const * pDesign ) +{ + if(!pDesign) + return; + + m_xPage2_Standard->set_sensitive(pDesign->m_eMode == PUBLISH_HTML); + m_xPage2_Frames->set_sensitive(pDesign->m_eMode == PUBLISH_FRAMES); + m_xPage2_Kiosk->set_sensitive(pDesign->m_eMode == PUBLISH_KIOSK ); + m_xPage2_WebCast->set_sensitive(pDesign->m_eMode == PUBLISH_WEBCAST ); + + m_xPage2_Content->set_sensitive(pDesign->m_bContentPage); + if(pDesign->m_bContentPage) + aAssistentFunc.EnablePage(4); + else + aAssistentFunc.DisablePage(4); + + if(m_bImpress) + m_xPage2_Notes->set_sensitive(pDesign->m_bNotes); + + m_xPage2_ASP->set_sensitive(pDesign->m_eScript == SCRIPT_ASP); + m_xPage2_PERL->set_sensitive(pDesign->m_eScript == SCRIPT_PERL); + m_xPage2_CGI->set_text(pDesign->m_aCGI); + m_xPage2_URL->set_text(pDesign->m_aURL); + + m_xPage2_ChgDefault->set_sensitive( !pDesign->m_bAutoSlide ); + m_xPage2_ChgAuto->set_sensitive( pDesign->m_bAutoSlide ); + + tools::Time aTime( tools::Time::EMPTY ); + aTime.MakeTimeFromMS( pDesign->m_nSlideDuration * 1000 ); + m_xFormatter->SetTime(aTime); + + m_xPage2_Endless->set_sensitive( pDesign->m_bEndless ); + + m_xPage3_Png->set_sensitive(pDesign->m_eFormat == FORMAT_PNG); + m_xPage3_Gif->set_sensitive(pDesign->m_eFormat == FORMAT_GIF); + m_xPage3_Jpg->set_sensitive(pDesign->m_eFormat == FORMAT_JPG); + m_xPage3_Quality->set_sensitive(pDesign->m_eFormat == FORMAT_JPG); + + m_xPage3_Quality->set_entry_text(pDesign->m_aCompression); + m_xPage3_Resolution_1->set_sensitive(pDesign->m_nResolution == PUB_LOWRES_WIDTH); + m_xPage3_Resolution_2->set_sensitive(pDesign->m_nResolution == PUB_MEDRES_WIDTH); + m_xPage3_Resolution_3->set_sensitive(pDesign->m_nResolution == PUB_HIGHRES_WIDTH); + m_xPage3_Resolution_4->set_sensitive(pDesign->m_nResolution == PUB_FHDRES_WIDTH); + + m_xPage3_SldSound->set_sensitive( pDesign->m_bSlideSound ); + m_xPage3_HiddenSlides->set_sensitive( pDesign->m_bHiddenSlides ); + + m_xPage4_Author->set_text(pDesign->m_aAuthor); + m_xPage4_Email->set_text(pDesign->m_aEMail); + m_xPage4_WWW->set_text(pDesign->m_aWWW); + m_xPage4_Misc->set_text(pDesign->m_aMisc); + if(m_bImpress) + m_xPage4_Download->set_sensitive(pDesign->m_bDownload); + + m_xPage5_TextOnly->set_sensitive(pDesign->m_nButtonThema == -1); + if(pDesign->m_nButtonThema != -1) + { + if(m_bButtonsDirty) + LoadPreviewButtons(); + m_xPage5_Buttons->SelectItem(pDesign->m_nButtonThema + 1); + } + else + m_xPage5_Buttons->SetNoSelection(); + + m_xPage6_User->set_sensitive(pDesign->m_bUserAttr); + m_aBackColor = pDesign->m_aBackColor; + m_aTextColor = pDesign->m_aTextColor; + m_aLinkColor = pDesign->m_aLinkColor; + m_aVLinkColor = pDesign->m_aVLinkColor; + m_aALinkColor = pDesign->m_aALinkColor; + + m_xPage6_DocColors->set_sensitive(pDesign->m_bUseColor); + + m_xPage6_Preview->SetColors( m_aBackColor, m_aTextColor, m_aLinkColor, + m_aVLinkColor, m_aALinkColor ); + m_xPage6_Preview->Invalidate(); + + UpdatePage(); +} + +// Transfer the status of the Design Dialog Controls +void SdPublishingDlg::GetDesign( SdPublishingDesign* pDesign ) +{ + if(!pDesign) + return; + + pDesign->m_eMode = m_xPage2_Standard->get_active()?PUBLISH_HTML: + m_xPage2_Frames->get_active()?PUBLISH_FRAMES: + m_xPage2_Kiosk->get_active()?PUBLISH_KIOSK: + PUBLISH_WEBCAST; + + pDesign->m_bContentPage = m_xPage2_Content->get_active(); + if(m_bImpress) + pDesign->m_bNotes = m_xPage2_Notes->get_active(); + + if( m_xPage3_Gif->get_active() ) + pDesign->m_eFormat = FORMAT_GIF; + else if( m_xPage3_Jpg->get_active() ) + pDesign->m_eFormat = FORMAT_JPG; + else + pDesign->m_eFormat = FORMAT_PNG; + + pDesign->m_aCompression = m_xPage3_Quality->get_active_text(); + + if (m_xPage3_Resolution_1->get_active()) + pDesign->m_nResolution = PUB_LOWRES_WIDTH; + else if (m_xPage3_Resolution_2->get_active()) + pDesign->m_nResolution = PUB_MEDRES_WIDTH; + else if (m_xPage3_Resolution_3->get_active()) + pDesign->m_nResolution = PUB_HIGHRES_WIDTH; + else + pDesign->m_nResolution = PUB_FHDRES_WIDTH; + + pDesign->m_bSlideSound = m_xPage3_SldSound->get_active(); + pDesign->m_bHiddenSlides = m_xPage3_HiddenSlides->get_active(); + + pDesign->m_aAuthor = m_xPage4_Author->get_text(); + pDesign->m_aEMail = m_xPage4_Email->get_text(); + pDesign->m_aWWW = m_xPage4_WWW->get_text(); + pDesign->m_aMisc = m_xPage4_Misc->get_text(); + pDesign->m_bDownload = m_bImpress && m_xPage4_Download->get_active(); + + if(m_xPage5_TextOnly->get_active()) + pDesign->m_nButtonThema = -1; + else + pDesign->m_nButtonThema = m_xPage5_Buttons->GetSelectedItemId() - 1; + + pDesign->m_bUserAttr = m_xPage6_User->get_active(); + pDesign->m_aBackColor = m_aBackColor; + pDesign->m_aTextColor = m_aTextColor; + pDesign->m_aLinkColor = m_aLinkColor; + pDesign->m_aVLinkColor = m_aVLinkColor; + pDesign->m_aALinkColor = m_aALinkColor; + pDesign->m_bUseColor = m_xPage6_DocColors->get_active(); + + pDesign->m_eScript = m_xPage2_ASP->get_active()?SCRIPT_ASP:SCRIPT_PERL; + pDesign->m_aCGI = m_xPage2_CGI->get_text(); + pDesign->m_aURL = m_xPage2_URL->get_text(); + + pDesign->m_bAutoSlide = m_xPage2_ChgAuto->get_active(); + pDesign->m_nSlideDuration = static_cast<sal_uInt32>(m_xFormatter->GetTime().GetMSFromTime()) / 1000; + pDesign->m_bEndless = m_xPage2_Endless->get_active(); +} + +// Clickhandler for the back Button +IMPL_LINK_NOARG(SdPublishingDlg, LastPageHdl, weld::Button&, void) +{ + aAssistentFunc.PreviousPage(); + ChangePage(); +} + +// Load Designs +void SdPublishingDlg::Load() +{ + m_bDesignListDirty = false; + + INetURLObject aURL( SvtPathOptions().GetUserConfigPath() ); + aURL.Append( u"designs.sod" ); + + // check if file exists, SfxMedium shows an errorbox else + { + std::unique_ptr<SvStream> pIStm = ::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ); + + bool bOk = pIStm && ( pIStm->GetError() == ERRCODE_NONE); + + if( !bOk ) + return; + } + + SfxMedium aMedium( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ | StreamMode::NOCREATE ); + + SvStream* pStream = aMedium.GetInStream(); + + if( !pStream ) + return; + + sal_uInt16 aCheck; + pStream->ReadUInt16( aCheck ); + + if(aCheck != nMagic) + return; + + SdIOCompat aIO(*pStream, StreamMode::READ); + + sal_uInt16 nDesigns(0); + pStream->ReadUInt16(nDesigns); + + // there has to at least be a sal_uInt16 header in each design + const size_t nMaxRecords = pStream->remainingSize() / sizeof(sal_uInt16); + if (nDesigns > nMaxRecords) + { + SAL_WARN("sd", "Parsing error: " << nMaxRecords << + " max possible entries, but " << nDesigns << " claimed, truncating"); + nDesigns = nMaxRecords; + } + + for( sal_uInt16 nIndex = 0; + pStream->GetError() == ERRCODE_NONE && nIndex < nDesigns; + nIndex++ ) + { + SdPublishingDesign aDesign; + *pStream >> aDesign; + + m_aDesignList.push_back(aDesign); + } +} + +// Save Designs +bool SdPublishingDlg::Save() +{ + INetURLObject aURL( SvtPathOptions().GetUserConfigPath() ); + aURL.Append( u"designs.sod" ); + SfxMedium aMedium( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::TRUNC ); + + SvStream* pStream = aMedium.GetOutStream(); + + if( !pStream ) + return false; + + pStream->WriteUInt16( nMagic ); + + // Destroys the SdIOCompat before the Stream is being distributed + { + SdIOCompat aIO(*pStream, StreamMode::WRITE, 0); + + sal_uInt16 nDesigns = static_cast<sal_uInt16>(m_aDesignList.size()); + pStream->WriteUInt16( nDesigns ); + + for( sal_uInt16 nIndex = 0; + pStream->GetError() == ERRCODE_NONE && nIndex < nDesigns; + nIndex++ ) + WriteSdPublishingDesign( *pStream, m_aDesignList[nIndex] ); + } + + aMedium.Close(); + aMedium.Commit(); + + return( aMedium.GetError() == ERRCODE_NONE ); +} + +// SdDesignNameDlg Methods +SdDesignNameDlg::SdDesignNameDlg(weld::Window* pWindow, const OUString& rName) + : GenericDialogController(pWindow, "modules/sdraw/ui/namedesign.ui", "NameDesignDialog") + , m_xEdit(m_xBuilder->weld_entry("entry")) + , m_xBtnOK(m_xBuilder->weld_button("ok")) +{ + m_xEdit->connect_changed(LINK(this, SdDesignNameDlg, ModifyHdl )); + m_xEdit->set_text(rName); + m_xBtnOK->set_sensitive(!rName.isEmpty()); +} + +OUString SdDesignNameDlg::GetDesignName() const +{ + return m_xEdit->get_text(); +} + +IMPL_LINK_NOARG(SdDesignNameDlg, ModifyHdl, weld::Entry&, void) +{ + m_xBtnOK->set_sensitive(!m_xEdit->get_text().isEmpty()); +} + +/* 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 000000000..f7a3bc10f --- /dev/null +++ b/sd/source/filter/html/sdhtmlfilter.cxx @@ -0,0 +1,51 @@ +/* -*- 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(); + + SfxItemSet* pSet = mrMedium.GetItemSet(); + + css::uno::Sequence<css::beans::PropertyValue> aParams; + + if (const SfxUnoAnyItem* pItem = pSet->GetItemIfSet(SID_FILTER_DATA, false)) + pItem->GetValue() >>= aParams; + + HtmlExport aExport(mrMedium.GetName(), aParams, &mrDocument, &mrDocShell); + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/pdf/sdpdffilter.cxx b/sd/source/filter/pdf/sdpdffilter.cxx new file mode 100644 index 000000000..002c1c5db --- /dev/null +++ b/sd/source/filter/pdf/sdpdffilter.cxx @@ -0,0 +1,201 @@ +/* -*- 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 <sal/config.h> + +#include <sfx2/docfile.hxx> +#include <svx/svdograf.hxx> +#include <o3tl/safeint.hxx> + +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <sdpdffilter.hxx> + +#include <vcl/graph.hxx> +#include <vcl/pdfread.hxx> + +#include <Annotation.hxx> + +#include <com/sun/star/office/XAnnotation.hpp> +#include <com/sun/star/text/XText.hpp> + +#include <basegfx/polygon/b2dpolygontools.hxx> + +using namespace css; + +SdPdfFilter::SdPdfFilter(SfxMedium& rMedium, sd::DrawDocShell& rDocShell) + : SdFilter(rMedium, rDocShell) +{ +} + +SdPdfFilter::~SdPdfFilter() {} + +bool SdPdfFilter::Import() +{ + const OUString aFileName( + mrMedium.GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE)); + + std::vector<vcl::PDFGraphicResult> aGraphics; + if (vcl::ImportPDFUnloaded(aFileName, aGraphics) == 0) + return false; + + bool bWasLocked = mrDocument.isLocked(); + mrDocument.setLock(true); + const bool bSavedUndoEnabled = mrDocument.IsUndoEnabled(); + mrDocument.EnableUndo(false); + + // Add as many pages as we need up-front. + mrDocument.CreateFirstPages(); + for (size_t i = 0; i < aGraphics.size() - 1; ++i) + { + mrDocument.DuplicatePage(0); + } + + for (vcl::PDFGraphicResult const& rPDFGraphicResult : aGraphics) + { + const Graphic& rGraphic = rPDFGraphicResult.GetGraphic(); + const Size& aSizeHMM = rPDFGraphicResult.GetSize(); + + const sal_Int32 nPageNumber = rGraphic.getPageNumber(); + assert(nPageNumber >= 0 && o3tl::make_unsigned(nPageNumber) < aGraphics.size()); + + // Create the page and insert the Graphic. + SdPage* pPage = mrDocument.GetSdPage(nPageNumber, PageKind::Standard); + if (!pPage) // failed to duplicate page, out of memory? + return false; + + // Make the page size match the rendered image. + pPage->SetSize(aSizeHMM); + + SdrGrafObj* pSdrGrafObj = new SdrGrafObj(pPage->getSdrModelFromSdrPage(), rGraphic, + tools::Rectangle(Point(), aSizeHMM)); + + pSdrGrafObj->SetResizeProtect(true); + pSdrGrafObj->SetMoveProtect(true); + + pPage->InsertObject(pSdrGrafObj); + + for (auto const& rPDFAnnotation : rPDFGraphicResult.GetAnnotations()) + { + uno::Reference<office::XAnnotation> xAnnotation; + pPage->createAnnotation(xAnnotation); + + xAnnotation->setAuthor(rPDFAnnotation.maAuthor); + + uno::Reference<text::XText> xText(xAnnotation->getTextRange()); + xText->setString(rPDFAnnotation.maText); + // position is in mm not 100thmm + geometry::RealPoint2D aUnoPosition(rPDFAnnotation.maRectangle.getMinX() / 100.0, + rPDFAnnotation.maRectangle.getMinY() / 100.00); + geometry::RealSize2D aUnoSize(rPDFAnnotation.maRectangle.getWidth() / 100.0, + rPDFAnnotation.maRectangle.getHeight() / 100.00); + xAnnotation->setPosition(aUnoPosition); + xAnnotation->setSize(aUnoSize); + xAnnotation->setDateTime(rPDFAnnotation.maDateTime); + + if (rPDFAnnotation.mpMarker) + { + auto* pAnnotation = static_cast<sd::Annotation*>(xAnnotation.get()); + pAnnotation->createCustomAnnotationMarker(); + sd::CustomAnnotationMarker& rCustomAnnotationMarker + = pAnnotation->getCustomAnnotationMarker(); + + rCustomAnnotationMarker.maLineColor = rPDFAnnotation.maColor; + + if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Polygon) + { + auto* pMarker = static_cast<vcl::pdf::PDFAnnotationMarkerPolygon*>( + rPDFAnnotation.mpMarker.get()); + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = pMarker->maFillColor; + rCustomAnnotationMarker.maPolygons.push_back(pMarker->maPolygon); + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Square) + { + auto* pMarker = static_cast<vcl::pdf::PDFAnnotationMarkerSquare*>( + rPDFAnnotation.mpMarker.get()); + basegfx::B2DPolygon aPoly + = basegfx::utils::createPolygonFromRect(rPDFAnnotation.maRectangle); + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = pMarker->maFillColor; + rCustomAnnotationMarker.maPolygons.push_back(aPoly); + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Circle) + { + auto* pMarker = static_cast<vcl::pdf::PDFAnnotationMarkerCircle*>( + rPDFAnnotation.mpMarker.get()); + + basegfx::B2DPoint rCenter = rPDFAnnotation.maRectangle.getCenter(); + double fRadiusX = rPDFAnnotation.maRectangle.getWidth() / 2; + double fRadiusY = rPDFAnnotation.maRectangle.getHeight() / 2; + + basegfx::B2DPolygon aPoly + = basegfx::utils::createPolygonFromEllipse(rCenter, fRadiusX, fRadiusY); + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = pMarker->maFillColor; + rCustomAnnotationMarker.maPolygons.push_back(aPoly); + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Ink) + { + auto* pMarker = static_cast<vcl::pdf::PDFAnnotationMarkerInk*>( + rPDFAnnotation.mpMarker.get()); + rCustomAnnotationMarker.maPolygons.insert( + rCustomAnnotationMarker.maPolygons.end(), pMarker->maStrokes.begin(), + pMarker->maStrokes.end()); + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = pMarker->maFillColor; + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Highlight) + { + if (!rCustomAnnotationMarker.maLineColor.IsTransparent()) + rCustomAnnotationMarker.maLineColor.SetAlpha(255 - 0x90); + auto* pMarker = static_cast<vcl::pdf::PDFAnnotationMarkerHighlight*>( + rPDFAnnotation.mpMarker.get()); + rCustomAnnotationMarker.maPolygons.insert( + rCustomAnnotationMarker.maPolygons.end(), pMarker->maQuads.begin(), + pMarker->maQuads.end()); + rCustomAnnotationMarker.mnLineWidth = 1; + rCustomAnnotationMarker.maFillColor = rPDFAnnotation.maColor; + if (!rCustomAnnotationMarker.maFillColor.IsTransparent()) + rCustomAnnotationMarker.maFillColor.SetAlpha(255 - 0x90); + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Line) + { + auto* pMarker = static_cast<vcl::pdf::PDFAnnotationMarkerLine*>( + rPDFAnnotation.mpMarker.get()); + + basegfx::B2DPolygon aPoly; + aPoly.append(pMarker->maLineStart); + aPoly.append(pMarker->maLineEnd); + rCustomAnnotationMarker.maPolygons.push_back(aPoly); + + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = COL_TRANSPARENT; + } + } + } + } + mrDocument.setLock(bWasLocked); + mrDocument.EnableUndo(bSavedUndoEnabled); + return true; +} + +bool SdPdfFilter::Export() { return false; } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/ppt97animations.cxx b/sd/source/filter/ppt/ppt97animations.cxx new file mode 100644 index 000000000..db3a960a7 --- /dev/null +++ b/sd/source/filter/ppt/ppt97animations.cxx @@ -0,0 +1,682 @@ +/* -*- 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 "ppt97animations.hxx" + +#include <svx/svdobj.hxx> +#include <sdpage.hxx> +#include <tools/stream.hxx> +#include <svx/unoapi.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <CustomAnimationPreset.hxx> +#include <com/sun/star/presentation/TextAnimationType.hpp> +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/presentation/ShapeAnimationSubType.hpp> + +using namespace ::com::sun::star; + +void Ppt97AnimationInfoAtom::ReadStream( SvStream& rIn ) +{ + sal_uInt32 nTmp; + rIn.ReadUInt32( nTmp ); + nDimColor = Color(ColorTransparency, nTmp); + rIn.ReadUInt32( nFlags ); + rIn.ReadUInt32( nSoundRef ); + rIn.ReadInt32( nDelayTime ); + rIn.ReadUInt16( nOrderID ); + rIn.ReadUInt16( nSlideCount ); + rIn.ReadUChar( nBuildType ); + rIn.ReadUChar( nFlyMethod ); + rIn.ReadUChar( nFlyDirection ); + rIn.ReadUChar( nAfterEffect ); + rIn.ReadUChar( nSubEffect ); + rIn.ReadUChar( nOLEVerb ); + rIn.ReadUChar( nUnknown1 ); + rIn.ReadUChar( nUnknown2 ); +} + +Ppt97Animation::Ppt97Animation( SvStream& rInputStream ) + : m_aAtom() + , m_bDirtyCache(true) + , m_bHasSpecialDuration(false) + , m_fDurationInSeconds(0.001) +{ + m_aAtom.ReadStream( rInputStream ); +} + +bool Ppt97Animation::operator < ( const Ppt97Animation& rAnimation ) const +{ + return m_aAtom.nOrderID < rAnimation.m_aAtom.nOrderID; +} +bool Ppt97Animation::operator > ( const Ppt97Animation& rAnimation ) const +{ + return m_aAtom.nOrderID > rAnimation.m_aAtom.nOrderID; +} +bool Ppt97Animation::HasEffect() const +{ + return m_aAtom.nBuildType != 0; +} +bool Ppt97Animation::HasParagraphEffect() const +{ + return m_aAtom.nBuildType > 1; +} +sal_Int32 Ppt97Animation::GetParagraphLevel() const +{ + sal_Int32 nParagraphLevel = 0; + if(m_aAtom.nBuildType>1) + nParagraphLevel = m_aAtom.nBuildType-1; + return nParagraphLevel; +} +bool Ppt97Animation::HasSoundEffect() const +{ + return m_aAtom.nSoundRef && m_aAtom.nFlags & 0x0010; +} +bool Ppt97Animation::HasStopPreviousSound() const +{ + return m_aAtom.nFlags & 0x0040; +} +bool Ppt97Animation::HasReverseOrder() const +{ + return m_aAtom.nFlags & 0x001; +} +bool Ppt97Animation::HasAnimateAssociatedShape() const +{ + return m_aAtom.nFlags & 0x004000; +} +bool Ppt97Animation::HasAfterEffect() const +{ + return m_aAtom.nAfterEffect != 0; +} +bool Ppt97Animation::HasAfterEffect_ChangeColor() const +{ + return m_aAtom.nAfterEffect == 1; +} +bool Ppt97Animation::HasAfterEffect_DimAtNextEffect() const +{ + return m_aAtom.nAfterEffect == 2; +} +#ifdef FUTURE +bool Ppt97Animation::HasAfterEffect_DimAfterEffect() const +{ + return m_aAtom.nAfterEffect == 3; +} +#endif +void Ppt97Animation::SetSoundFileUrl( const OUString& rSoundFileUrl ) +{ + m_aSoundFileUrl = rSoundFileUrl; +} + +double Ppt97Animation::GetDelayTimeInSeconds() const +{ + return m_aAtom.nDelayTime != 0X7FFFFFFF ? m_aAtom.nDelayTime/1000.0 : 0.0; +} + +bool Ppt97Animation::GetSpecialDuration( double& rfDurationInSeconds ) const +{ + UpdateCacheData(); + if( m_bHasSpecialDuration ) + rfDurationInSeconds = m_fDurationInSeconds; + return m_bHasSpecialDuration; +} + +bool Ppt97Animation::GetSpecialTextIterationDelay( double& rfTextIterationDelay ) const +{ + bool bRet = false; + switch(GetTextAnimationType()) + { + case presentation::TextAnimationType::BY_LETTER: + rfTextIterationDelay = 0.075; + bRet = true; + break; + case presentation::TextAnimationType::BY_WORD: + rfTextIterationDelay = 0.3; + bRet = true; + break; + default: + break; + } + return bRet; +} + +void Ppt97Animation::SetDimColor( Color nDimColor ) +{ + m_aAtom.nDimColor = nDimColor; +} +void Ppt97Animation::SetAnimateAssociatedShape( bool bAnimate ) +{ + if( !bAnimate ) + { + //the appear effect cannot be animated without text + if( GetPresetId() == "ooo-entrance-appear" ) + return; + //the random effect may be the appear effect and then has the same problem + if( GetPresetId() == "ooo-entrance-random" ) + { + //this case is not 100% correct -> feel free to complete + //i consider this case as seldom and not that problematic and a simple correct fix is not in sight + SAL_INFO("sd", "you tried to deselect the animation of the form for random animation-> this has been refused"); + return; + } + + } + + if(bAnimate) + m_aAtom.nFlags = m_aAtom.nFlags | 0x004000; + else if( HasAnimateAssociatedShape() ) + { + m_aAtom.nFlags = m_aAtom.nFlags ^ 0x004000; + } +} + +sal_Int16 Ppt97Animation::GetEffectNodeType() const //see css::presentation::EffectNodeType +{ + sal_Int16 nRet = presentation::EffectNodeType::ON_CLICK; + if( m_aAtom.nFlags & 0x04 ) + { + nRet = presentation::EffectNodeType::AFTER_PREVIOUS; + } + return nRet; +} + +sal_Int16 Ppt97Animation::GetTextAnimationType() const +{ + sal_Int16 nRet = presentation::TextAnimationType::BY_PARAGRAPH; + switch( m_aAtom.nSubEffect ) + { + case 0: + break; + case 2: + nRet = presentation::TextAnimationType::BY_LETTER; + break; + default: + nRet = presentation::TextAnimationType::BY_WORD; + break; + } + return nRet; +} +OUString const & Ppt97Animation::GetPresetId() const +{ + UpdateCacheData(); + return m_aPresetId; +} +OUString const & Ppt97Animation::GetPresetSubType() const +{ + UpdateCacheData(); + return m_aSubType; +} + +void Ppt97Animation::ClearCacheData() const +{ + m_aPresetId.clear(); + m_aSubType.clear(); + m_bHasSpecialDuration = false; + m_fDurationInSeconds = 0.001; +} +void Ppt97Animation::UpdateCacheData() const +{ + if( !m_bDirtyCache ) + return; + + ClearCacheData(); + + if( !HasEffect() ) + { + m_bDirtyCache = false; + return; + } + + switch( m_aAtom.nFlyMethod ) + { + case 0x0: + m_aPresetId = "ooo-entrance-appear"; // --- appear --- + break; + case 0x01: + m_aPresetId = "ooo-entrance-random"; // --- random --- + break; + case 0x02: // --- blinds effect --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-venetian-blinds"; + m_aSubType = "horizontal"; // horizontal + break; + case 0x1: + m_aPresetId = "ooo-entrance-venetian-blinds"; + m_aSubType = "vertical"; // vertical + break; + } + } + break; + case 0x03: // --- (hor/ver) shifted appear --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-checkerboard"; + m_aSubType = "across"; // vertical ??? + break; + case 0x1: + m_aPresetId = "ooo-entrance-checkerboard"; + m_aSubType = "downward"; // horizontal ??? + break; + } + } + break; + case 0x05: + m_aPresetId = "ooo-entrance-dissolve-in"; + break; + case 0x08: // --- (hor/ver) lines --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-random-bars"; + m_aSubType = "vertical"; // horizontal ??? + break; + case 0x1: + m_aPresetId = "ooo-entrance-random-bars"; + m_aSubType = "horizontal"; // vertical ??? + break; + } + } + break; + case 0x09: // --- diagonal --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x4: + m_aPresetId = "ooo-entrance-diagonal-squares"; + m_aSubType = "left-to-top"; // to left top + break; + case 0x5: + m_aPresetId = "ooo-entrance-diagonal-squares"; + m_aSubType = "right-to-top"; // to right top + break; + case 0x6: + m_aPresetId = "ooo-entrance-diagonal-squares"; + m_aSubType = "left-to-bottom"; // to left bottom + break; + case 0x7: + m_aPresetId = "ooo-entrance-diagonal-squares"; + m_aSubType = "right-to-bottom"; // to right bottom + break; + } + } + break; + case 0x0a: // --- roll/wipe --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-wipe"; + m_aSubType = "from-right"; // from right + break; + case 0x1: + m_aPresetId = "ooo-entrance-wipe"; + m_aSubType = "from-bottom"; // from bottom + break; + case 0x2: + m_aPresetId = "ooo-entrance-wipe"; + m_aSubType = "from-left"; // from left + break; + case 0x3: + m_aPresetId = "ooo-entrance-wipe"; + m_aSubType = "from-top"; // from top + break; + } + } + break; + case 0x0b: //--- fade in --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-box"; + m_aSubType = "out"; // from center + break; + case 0x1: + m_aPresetId = "ooo-entrance-box"; + m_aSubType = "in"; // to center + break; + } + } + break; + case 0x0c: // --- text effects --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-left"; + + break; + case 0x1: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-top"; + break; + case 0x2: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-right"; + break; + case 0x3: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-bottom"; + break; + case 0x4: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-top-left"; + break; + case 0x5: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-top-right"; + break; + case 0x6: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-bottom-left"; + break; + case 0x7: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-bottom-right"; + break; + case 0x8: // -- short text effects -- + m_aPresetId = "ooo-entrance-peek-in"; + m_aSubType = "from-left"; + break; + case 0x9: + m_aPresetId = "ooo-entrance-peek-in"; + m_aSubType = "from-bottom"; + break; + case 0xa: + m_aPresetId = "ooo-entrance-peek-in"; + m_aSubType = "from-right"; + break; + case 0xb: + m_aPresetId = "ooo-entrance-peek-in"; + m_aSubType = "from-top"; + break; + case 0xc: // -- slow text effects -- + { + m_aPresetId = "ooo-entrance-fly-in-slow"; + m_aSubType = "from-left"; + } + break; + case 0xd: + { + m_aPresetId = "ooo-entrance-fly-in-slow"; + m_aSubType = "from-top"; + } + break; + case 0xe: + { + m_aPresetId = "ooo-entrance-fly-in-slow"; + m_aSubType = "from-right"; + } + break; + case 0xf: + { + m_aPresetId = "ooo-entrance-fly-in-slow"; + m_aSubType = "from-bottom"; + } + break; + case 0x10: // --- zoom --- + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "in"; + break; + case 0x11: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "in-slightly"; + break; + case 0x12: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "out"; + break; + case 0x13: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "out-slightly"; + break; + case 0x14: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "in-from-screen-center"; + break; + case 0x15: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "out-from-screen-center"; + break; + case 0x16: // --- stretch --- + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "across"; + break; + case 0x17: + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "from-left"; + break; + case 0x18: + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "from-top"; + break; + case 0x19: + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "from-right"; + break; + case 0x1a: + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "from-bottom"; + break; + case 0x1b: // --- rotate --- + m_aPresetId = "ooo-entrance-swivel"; + m_aSubType = "vertical"; + break; + case 0x1c: // --- spirale --- + m_aPresetId = "ooo-entrance-spiral-in"; + break; + } + } + break; + case 0x0d: // --- open/close --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-split"; + m_aSubType = "horizontal-out"; //horizontal open + break; + case 0x1: + m_aPresetId = "ooo-entrance-split"; + m_aSubType = "horizontal-in"; //horizontal close + break; + case 0x2: + m_aPresetId = "ooo-entrance-split"; + m_aSubType = "vertical-out"; // vertical open + break; + case 0x3: + m_aPresetId = "ooo-entrance-split"; + m_aSubType = "vertical-in"; // vertical close + break; + } + } + break; + case 0x0e: // --- blink --- + { + m_aPresetId = "ooo-entrance-flash-once"; + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: //fast + m_fDurationInSeconds = 0.075; + m_bHasSpecialDuration = true; + break; + case 0x1: //medium + m_fDurationInSeconds = 0.5; + m_bHasSpecialDuration = true; + break; + case 0x2: //slow + m_fDurationInSeconds = 1.0; + m_bHasSpecialDuration = true; + break; + } + } + break; + default: + { + m_aPresetId = "ooo-entrance-appear"; + OSL_FAIL("no effect mapped"); + } + break; + } + m_bDirtyCache = false; +} + +void Ppt97Animation::createAndSetCustomAnimationEffect( SdrObject* pObj ) +{ + + if( !HasEffect() ) + return; + if( !pObj || !pObj->getSdrPageFromSdrObject() ) + { + OSL_FAIL("no valid SdrObject or page found for ppt import"); + return; + } + + uno::Reference< drawing::XShape > xShape = GetXShapeForSdrObject( pObj ); + if( !xShape.is() ) + { + OSL_FAIL("no XShape interface found for ppt import"); + return; + } + ::sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + if( !pMainSequence ) + { + OSL_FAIL("no MainSequence found for ppt import"); + return; + } + + const ::sd::CustomAnimationPresets& rPresets( ::sd::CustomAnimationPresets::getCustomAnimationPresets() ); + ::sd::CustomAnimationPresetPtr pPreset( rPresets.getEffectDescriptor( GetPresetId() ) ); + if( !pPreset ) + { + OSL_FAIL("no suitable preset found for ppt import"); + return; + } + + //--------------start doing something + + //1. ------ create an effect from the presets ------ + ::sd::CustomAnimationEffectPtr pEffect = std::make_shared<::sd::CustomAnimationEffect>( pPreset->create( GetPresetSubType() ) ); + + //2. ------ adapt the created effect ------ + + // set the shape targeted by this effect + pEffect->setTarget( css::uno::Any( xShape ) ); + + pEffect->setBegin( GetDelayTimeInSeconds() ); + + // some effects need a different duration than that of the mapped preset effect + double fDurationInSeconds = 1.0; //in seconds + if( GetSpecialDuration( fDurationInSeconds ) ) + pEffect->setDuration( fDurationInSeconds ); + + // set after effect + if( HasAfterEffect() ) + { + pEffect->setHasAfterEffect( true ); + if( HasAfterEffect_ChangeColor() ) + pEffect->setDimColor( uno::Any( GetDimColor() ) ); + else + pEffect->setAfterEffectOnNext( HasAfterEffect_DimAtNextEffect() ); + } + + // set sound effect + if( HasSoundEffect() ) + pEffect->createAudio( uno::Any( m_aSoundFileUrl ) ); + + // text iteration + pEffect->setIterateType( GetTextAnimationType() ); + + // some effects need a different delay between text iteration than that of the mapped preset effect + double fTextIterationDelay = 1.0; + if( GetSpecialTextIterationDelay( fTextIterationDelay ) ) + pEffect->setIterateInterval( fTextIterationDelay ); + + // is the effect started on click or after the last effect (Another possible value is EffectNodeType::WITH_PREVIOUS ) + pEffect->setNodeType( GetEffectNodeType() ); + + //set stop sound effect + if( HasStopPreviousSound() ) + pEffect->setStopAudio(); + + // append the effect to the main sequence + if( !HasParagraphEffect() ) + { + // TODO: !HasAnimateAssociatedShape() can possibly have this set to ONLY_TEXT - see i#42737 + pEffect->setTargetSubItem( presentation::ShapeAnimationSubType::AS_WHOLE ); + } + + //3. ------ put the created effect to the model and do some last changes fro paragraph effects ------ + pMainSequence->append( pEffect ); + if( HasParagraphEffect() ) + { + sal_Int32 nParagraphLevel = GetParagraphLevel(); + double fDelaySeconds = GetDelayTimeInSeconds(); + bool bAnimateAssociatedShape = HasAnimateAssociatedShape();//or only text + bool bTextReverse = HasReverseOrder(); + + // now create effects for each paragraph + ::sd::CustomAnimationTextGroupPtr pGroup = pMainSequence-> + createTextGroup( pEffect, nParagraphLevel, fDelaySeconds, bAnimateAssociatedShape, bTextReverse ); + + if( pGroup ) + { + const ::sd::EffectSequence& rEffects = pGroup->getEffects(); + + ::sd::CustomAnimationEffectPtr pLastEffect; + sal_Int32 nIndex = 0; + for( const auto& rxEffect : rEffects ) + { + ::sd::CustomAnimationEffectPtr pGroupEffect(rxEffect); + + ////todo? if( nIndex > 1 && pLastEffect && HasSoundEffect() ) + //// pLastEffect->setStopAudio(); + if( nIndex < 2 ) + { + pGroupEffect->setNodeType( GetEffectNodeType() ); + } + else if( nIndex > 0 ) + { + bool bAtParagraphBegin = false; + if(!bTextReverse) + bAtParagraphBegin = pGroupEffect->getParaDepth() < nParagraphLevel; + else + bAtParagraphBegin = !pLastEffect || pLastEffect->getParaDepth() < nParagraphLevel; + if( bAtParagraphBegin ) + pGroupEffect->setNodeType( GetEffectNodeType() ); + else if( GetTextAnimationType() == presentation::TextAnimationType::BY_PARAGRAPH ) + pGroupEffect->setNodeType( presentation::EffectNodeType::WITH_PREVIOUS ); + else + pGroupEffect->setNodeType( presentation::EffectNodeType::AFTER_PREVIOUS ); + } + pLastEffect = pGroupEffect; + nIndex++; + } + } + } + pMainSequence->rebuild(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/ppt97animations.hxx b/sd/source/filter/ppt/ppt97animations.hxx new file mode 100644 index 000000000..1811d93a3 --- /dev/null +++ b/sd/source/filter/ppt/ppt97animations.hxx @@ -0,0 +1,156 @@ +/* -*- 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 <rtl/ustring.hxx> +#include <sal/types.h> +#include <tools/color.hxx> + +class SdrObject; +class SvStream; + +/// helper class for reading PPT AnimationInfoAtom +class Ppt97AnimationInfoAtom +{ + friend class Ppt97Animation; + +//-- member + Color nDimColor; + sal_uInt32 nFlags; ///< 0x0004: time instead of click + sal_uInt32 nSoundRef; + sal_Int32 nDelayTime; ///< 1/1000 sec + sal_uInt16 nOrderID; + sal_uInt16 nSlideCount; + sal_uInt8 nBuildType; + sal_uInt8 nFlyMethod; + sal_uInt8 nFlyDirection; + sal_uInt8 nAfterEffect; ///< nAfterEffect: 0: none; 1: change color; 2: dim on next effect; 3: dim after effect; + sal_uInt8 nSubEffect; + sal_uInt8 nOLEVerb; + + // unknown, because whole size needs to be 28 + sal_uInt8 nUnknown1; + sal_uInt8 nUnknown2; + +//-- methods + void ReadStream( SvStream& rIn ); +/* + nFlags: + decimal / hexadecimal / binary + 1040 0x00000410 10000010000 mouseclick + 17428 0x00004414 100010000010100 after previous 0 sec (animate form) + 17412 0x00004404 100010000000100 after previous 0 sec + 1088 0x00000440 10001000000 stop previous sound and mouseclick + 1044 0x00000414 10000010100 play sound automatic + 1041 0x00000411 10000010001 + | | | | | | + | | | | | reverse order + | | | | after previous + | | | sound + | | stop previous sound + | ? + animate form + + nAfterEffect: + 1: color + 0: nothing + 3: hide after animation + 2: hide at next mouse click +*/ +}; + +/** this is a helping class for import of PPT 97 animations + 1. use the constructor Ppt97Animation( SvStream& rIn ) to import information from the stream + 2. use the set methods to modify and complete the data + 3. use the method createAndSetCustomAnimationEffect( ) to create an effect in sd model + */ +class Ppt97Animation +{ + +public: //public methods + explicit Ppt97Animation( SvStream& rIn ); + + bool operator < ( const Ppt97Animation& rAnimation ) const;//later is greater + bool operator > ( const Ppt97Animation& rAnimation ) const;//later is greater + + //get methods + bool HasEffect() const; + bool HasParagraphEffect() const; + bool HasSoundEffect() const; + sal_Int32 GetDimColor() const { return static_cast<sal_Int32>(m_aAtom.nDimColor);} + sal_uInt32 GetSoundRef() const { return m_aAtom.nSoundRef;} + /// @return true if the shape should be animated in addition to the text + bool HasAnimateAssociatedShape() const; + + //set methods + void SetDimColor( Color nDimColor ); + void SetSoundFileUrl( const OUString& rSoundFileUrl ); + void SetAnimateAssociatedShape( bool bAnimate ); //true if the shape should be animated in addition to the text + + //action methods + /** this method creates a CustomAnimationEffect for the given SdrObject + from internal data and stores the created effect at the draw model + */ + void createAndSetCustomAnimationEffect( SdrObject* pObj ); + +private: //private methods + + //read methods + OUString const & GetPresetId() const; + OUString const & GetPresetSubType() const; + bool HasAfterEffect() const; + bool HasAfterEffect_ChangeColor() const; + bool HasAfterEffect_DimAtNextEffect() const; + bool HasStopPreviousSound() const; + + /// @return true if the text paragraphs should be animated in reverse order + bool HasReverseOrder() const; + + ///paragraph level that is animated ( that paragraph and higher levels ) + sal_Int32 GetParagraphLevel() const; + + ///@see css::presentation::TextAnimationType + sal_Int16 GetTextAnimationType() const; + + ///@see css::presentation::EffectNodeType + sal_Int16 GetEffectNodeType() const; + + /// @return -1 for start on mouseclick or >= 0 for a delay in seconds for automatic start + double GetDelayTimeInSeconds() const; + bool GetSpecialDuration( double& rfDurationInSeconds ) const; + bool GetSpecialTextIterationDelay( double& rfTextIterationDelay ) const; + + void UpdateCacheData() const; + void ClearCacheData() const; + +private: //private member + //input information: + Ppt97AnimationInfoAtom m_aAtom; ///< pure input from stream + OUString m_aSoundFileUrl; ///< this needs to be set in addition from outside as this class has not the knowledge to translate the sound bits to a file url + + //cached generated output information: + mutable bool m_bDirtyCache; + mutable OUString m_aPresetId; // m_aPresetId and m_aSubType match to the values in sd/xml/effects.xml + mutable OUString m_aSubType; + mutable bool m_bHasSpecialDuration; + mutable double m_fDurationInSeconds; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptanimations.hxx b/sd/source/filter/ppt/pptanimations.hxx new file mode 100644 index 000000000..7fda1bb68 --- /dev/null +++ b/sd/source/filter/ppt/pptanimations.hxx @@ -0,0 +1,209 @@ +/* -*- 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 <com/sun/star/uno/Any.h> + +#include <map> +#include <sal/types.h> + +class SvStream; + +namespace ppt +{ + +// old transition types +#define PPT_TRANSITION_TYPE_NONE 0 +#define PPT_TRANSITION_TYPE_RANDOM 1 +#define PPT_TRANSITION_TYPE_BLINDS 2 +#define PPT_TRANSITION_TYPE_CHECKER 3 +#define PPT_TRANSITION_TYPE_COVER 4 +#define PPT_TRANSITION_TYPE_DISSOLVE 5 +#define PPT_TRANSITION_TYPE_FADE 6 +#define PPT_TRANSITION_TYPE_PULL 7 // Uncover in MS-PPT Specs +#define PPT_TRANSITION_TYPE_RANDOM_BARS 8 +#define PPT_TRANSITION_TYPE_STRIPS 9 +#define PPT_TRANSITION_TYPE_WIPE 10 +#define PPT_TRANSITION_TYPE_ZOOM 11 // Box In/Out in MS-PPT Specs +#define PPT_TRANSITION_TYPE_SPLIT 13 + +// effects, new in xp +#define PPT_TRANSITION_TYPE_DIAMOND 17 +#define PPT_TRANSITION_TYPE_PLUS 18 +#define PPT_TRANSITION_TYPE_WEDGE 19 +#define PPT_TRANSITION_TYPE_PUSH 20 +#define PPT_TRANSITION_TYPE_COMB 21 +#define PPT_TRANSITION_TYPE_NEWSFLASH 22 +#define PPT_TRANSITION_TYPE_SMOOTHFADE 23 // Alpha Fade in MS-PPT Specs +#define PPT_TRANSITION_TYPE_WHEEL 26 +#define PPT_TRANSITION_TYPE_CIRCLE 27 + +// undocumented(?) +#define PPT_TRANSITION_TYPE_FLASH 30 + +// atoms +#define DFF_msofbtAnimEvent 0xf125 +#define DFF_msofbtAnimNode 0xf127 +#define DFF_msofbtAnimTrigger 0xf128 +#define DFF_msofbtAnimValue 0xf129 +#define DFF_msofbtAnimateTarget 0xf12a +#define DFF_msofbtAnimate 0xf12b +#define DFF_msofbtAnimateColor 0xf12c +#define DFF_msofbtAnimateFilter 0xf12d +#define DFF_msofbtAnimateMotion 0xf12e +#define DFF_msofbtAnimateRotation 0xf12f +#define DFF_msofbtAnimateScale 0xf130 +#define DFF_msofbtAnimateSet 0xf131 +#define DFF_msofbtAnimCommand 0xf132 +#define DFF_msofbtAnimateTargetSettings 0xf133 +#define DFF_msofbtAnimateData 0xf134 +#define DFF_msofbtAnimateColorData 0xf135 +#define DFF_msofbtAnimateFilterData 0xf136 +#define DFF_msofbtAnimateMotionData 0xf137 +#define DFF_msofbtAnimateScaleData 0xf139 +#define DFF_msofbtAnimateSetData 0xf13a +#define DFF_msofbtCommandData 0xf13b +#define DFF_msofbtAnimateTargetElement 0xf13c +#define DFF_msofbtAnimPropertySet 0xf13d +#define DFF_msofbtAnimateAttributeNames 0xf13e +#define DFF_msofbtAnimKeyPoints 0xf13f +#define DFF_msofbtAnimIteration 0xf140 +#define DFF_msofbtAnimAction 0xf141 // correct name?? +#define DFF_msofbtAnimAttributeValue 0xf142 +#define DFF_msofbtAnimKeyTime 0xf143 +#define DFF_msofbtAnimGroup 0xf144 +#define DFF_msofbtAnimSubGoup 0xf145 +#define DFF_msofbtAnimateRotationData 0xf138 +#define DFF_msofbtAnimReference 0x2afb + +// property ids +#define DFF_ANIM_ID 1 +#define DFF_ANIM_RUNTIMECONTEXT 2 +#define DFF_ANIM_PATH_EDIT_MODE 3 +#define DFF_ANIM_COLORSPACE 4 +#define DFF_ANIM_DIRECTION 5 // TODO: Conflict? +#define DFF_ANIM_MASTERREL 5 // TODO: Conflict? +#define DFF_ANIM_OVERRIDE 6 +#define DFF_ANIM_PRESET_ID 9 +#define DFF_ANIM_PRESET_SUB_TYPE 10 +#define DFF_ANIM_PRESET_CLASS 11 +#define DFF_ANIM_AFTEREFFECT 13 +#define DFF_ANIM_ENDAFTERSLIDE 15 +#define DFF_ANIM_TIMEFILTER 16 +#define DFF_ANIM_EVENT_FILTER 17 +#define DFF_ANIM_GROUP_ID 19 +#define DFF_ANIM_NODE_TYPE 20 +#define DFF_ANIM_VOLUME 22 +#define DFF_ANIM_PROPERTY_ID_COUNT (DFF_ANIM_VOLUME + 1) + +// property types +#define DFF_ANIM_PROP_TYPE_BYTE 0 +#define DFF_ANIM_PROP_TYPE_INT32 1 +#define DFF_ANIM_PROP_TYPE_FLOAT 2 +#define DFF_ANIM_PROP_TYPE_UNISTRING 3 + +#define DFF_ANIM_PRESS_CLASS_USER_DEFINED 0 +#define DFF_ANIM_PRESS_CLASS_ENTRANCE 1 +#define DFF_ANIM_PRESS_CLASS_EXIT 2 +#define DFF_ANIM_PRESS_CLASS_EMPHASIS 3 +#define DFF_ANIM_PRESS_CLASS_MOTIONPATH 4 +#define DFF_ANIM_PRESS_CLASS_OLE_ACTION 5 +#define DFF_ANIM_PRESS_CLASS_MEDIACALL 6 + +// Effect node type. +#define DFF_ANIM_NODE_TYPE_ON_CLICK 1 +#define DFF_ANIM_NODE_TYPE_WITH_PREVIOUS 2 +#define DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS 3 +#define DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE 4 +#define DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ 5 +#define DFF_ANIM_NODE_TYPE_CLICK_PARALLEL 6 +#define DFF_ANIM_NODE_TYPE_WITH_GROUP 7 +#define DFF_ANIM_NODE_TYPE_AFTER_GROUP 8 +#define DFF_ANIM_NODE_TYPE_TIMING_ROOT 9 + +/* constants for fill entry in AnimationNode */ +const sal_Int32 mso_Anim_GroupType_PAR = 0; +const sal_Int32 mso_Anim_GroupType_SEQ = 1; +const sal_Int32 mso_Anim_GroupType_NODE = 3; +const sal_Int32 mso_Anim_GroupType_MEDIA = 4; + +/* constants for fill entry in AnimationNode */ +const sal_Int32 mso_Anim_Fill_ALWAYS = 1; +const sal_Int32 mso_Anim_Fill_WHENOFF = 2; +const sal_Int32 mso_Anim_Fill_NEVER = 3; + +/* constants for fill entry in AnimationNode */ +const sal_Int32 mso_Anim_Fill_REMOVE = 1; +const sal_Int32 mso_Anim_Fill_FREEZE = 2; +const sal_Int32 mso_Anim_Fill_HOLD = 3; + +/* constants for behaviour entry in PPTAnimationNode */ +const sal_Int32 mso_Anim_Behaviour_FILTER = 24; +const sal_Int32 mso_Anim_Behaviour_ANIMATION= 25; + +typedef ::std::map< sal_Int32, css::uno::Any > PropertySetMap_t; + +class PropertySet +{ +public: + PropertySetMap_t maProperties; + + bool hasProperty( sal_Int32 nProperty ) const; + css::uno::Any getProperty( sal_Int32 nProperty ) const; +}; + +/** this atom is the first entry in each animation group */ +struct AnimationNode +{ +public: + /** see mso_Anim_GroupType_? */ + sal_Int32 mnGroupType; + + /** see mso_Anim_Restart_? */ + sal_Int32 mnRestart; + + /** see mso_Anim_Fill_? */ + sal_Int32 mnFill; + + /** see mso_Anim_Behaviour_? */ + sal_Int32 mnNodeType; + + /** duration of this group in 1000th seconds */ + sal_Int32 mnDuration; + + sal_Int32 mnU1, mnU3, mnU4; + + AnimationNode() + : mnGroupType(0) + , mnRestart(0) + , mnFill(0) + , mnNodeType(0) + , mnDuration(0) + , mnU1(0), mnU3(0), mnU4(0) + { + } +public: + + friend SvStream& WriteAnimationNode(SvStream& rOut, AnimationNode const & rAtom); +}; + +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptatom.cxx b/sd/source/filter/ppt/pptatom.cxx new file mode 100644 index 000000000..24d87f040 --- /dev/null +++ b/sd/source/filter/ppt/pptatom.cxx @@ -0,0 +1,104 @@ +/* -*- 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 <tools/stream.hxx> +#include "pptatom.hxx" + +using namespace ppt; + +Atom::Atom( const DffRecordHeader& rRecordHeader, SvStream& rStream ) +: mrStream( rStream ) +, maRecordHeader( rRecordHeader ) +, mpFirstChild( nullptr ) +, mpNextAtom( nullptr ) +{ + if( isContainer() ) + { + if( seekToContent() ) + { + DffRecordHeader aChildHeader; + + Atom* pLastAtom = nullptr; + + // retrieve file size (to allow sanity checks) + sal_uInt64 const nStreamSize = mrStream.TellEnd(); + + while( mrStream.good() + && ( mrStream.Tell() < nStreamSize ) + && ( mrStream.Tell() < maRecordHeader.GetRecEndFilePos() ) ) + { + if (ReadDffRecordHeader(mrStream, aChildHeader)) + { + Atom* pAtom = new Atom( aChildHeader, mrStream ); + + if( pLastAtom ) + pLastAtom->mpNextAtom = pAtom; + if( mpFirstChild == nullptr ) + mpFirstChild = pAtom; + + pLastAtom = pAtom; + } + } + } + } + + if (!maRecordHeader.SeekToEndOfRecord(mrStream)) + mrStream.SetError(SVSTREAM_FILEFORMAT_ERROR); +} + +Atom::~Atom() +{ + Atom* pChild = mpFirstChild; + while( pChild ) + { + Atom* pNextChild = pChild->mpNextAtom; + delete pChild; + pChild = pNextChild; + } +} + +/** imports this atom and its child atoms */ +Atom* Atom::import( const DffRecordHeader& rRootRecordHeader, SvStream& rStCtrl ) +{ + Atom* pRootAtom = new Atom( rRootRecordHeader, rStCtrl ); + + if( rStCtrl.GetError() == ERRCODE_NONE ) + { + return pRootAtom; + } + else + { + delete pRootAtom; + return nullptr; + } +} + +/** returns the next child atom after pLast with nRecType or NULL */ +const Atom* Atom::findNextChildAtom( sal_uInt16 nRecType, const Atom* pLast ) const +{ + Atom* pChild = pLast != nullptr ? pLast->mpNextAtom : mpFirstChild; + while( pChild && pChild->maRecordHeader.nRecType != nRecType ) + { + pChild = pChild->mpNextAtom; + } + + return pChild; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptatom.hxx b/sd/source/filter/ppt/pptatom.hxx new file mode 100644 index 000000000..55ee7f687 --- /dev/null +++ b/sd/source/filter/ppt/pptatom.hxx @@ -0,0 +1,106 @@ +/* -*- 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 <filter/msfilter/dffrecordheader.hxx> + +namespace ppt +{ +class Atom +{ +public: + ~Atom(); + + /** imports this atom and its child atoms */ + static Atom* import(const DffRecordHeader& rRootRecordHeader, SvStream& rStCtrl); + + /** @return true if at least one atom with the given nRecType is found */ + inline bool hasChildAtom(sal_uInt16 nRecType) const; + + /** @return the first child atom with nRecType or NULL */ + inline const Atom* findFirstChildAtom(sal_uInt16 nRecType) const; + + /** @return the next child atom after pLast with nRecType or NULL */ + const Atom* findNextChildAtom(sal_uInt16 nRecType, const Atom* pLast) const; + + /** @return the first child atom or NULL */ + inline const Atom* findFirstChildAtom() const; + + /** @return the next child atom after pLast or NULL */ + static inline const Atom* findNextChildAtom(const Atom* pLast); + + /** @return true if this atom is a container */ + inline bool isContainer() const; + + /** seeks to the contents of this atom */ + inline bool seekToContent() const; + + /** @return the record type */ + inline sal_uInt16 getType() const; + + /** @return the record instance */ + inline sal_uInt16 getInstance() const; + + /** @return the record length */ + inline sal_uInt32 getLength() const; + +private: + Atom(const DffRecordHeader& rRecordHeader, SvStream& rStCtrl); + + SvStream& mrStream; + DffRecordHeader maRecordHeader; + Atom* mpFirstChild; + Atom* mpNextAtom; +}; + +inline bool Atom::hasChildAtom(sal_uInt16 nRecType) const +{ + return findFirstChildAtom(nRecType) != nullptr; +} + +inline const Atom* Atom::findFirstChildAtom(sal_uInt16 nRecType) const +{ + return findNextChildAtom(nRecType, nullptr); +} + +inline const Atom* Atom::findFirstChildAtom() const { return mpFirstChild; } + +inline const Atom* Atom::findNextChildAtom(const Atom* pLast) +{ + return pLast ? pLast->mpNextAtom : pLast; +} + +inline bool Atom::isContainer() const { return maRecordHeader.IsContainer(); } + +inline bool Atom::seekToContent() const +{ + maRecordHeader.SeekToContent(mrStream); + return mrStream.GetError() == ERRCODE_NONE; +} + +inline sal_uInt16 Atom::getType() const { return maRecordHeader.nRecType; } + +inline sal_uInt16 Atom::getInstance() const { return maRecordHeader.nRecInstance; } + +inline sal_uInt32 Atom::getLength() const { return maRecordHeader.nRecLen; } + +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptin.cxx b/sd/source/filter/ppt/pptin.cxx new file mode 100644 index 000000000..305e1813a --- /dev/null +++ b/sd/source/filter/ppt/pptin.cxx @@ -0,0 +1,2821 @@ +/* -*- 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 <osl/file.hxx> +#include <sal/log.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <svl/urihelper.hxx> +#include <svx/svxids.hrc> +#include <filter/msfilter/svdfppt.hxx> +#include <svx/svditer.hxx> +#include <sfx2/docfile.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdlayer.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/sdtmfitm.hxx> +#include <svx/sdtagitm.hxx> +#include <svl/style.hxx> +#include <svl/intitem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editeng.hxx> +#include <svx/svdoutl.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> + +#include <sfx2/docinf.hxx> +#include <o3tl/string_view.hxx> + +#include <strings.hrc> +#include <strings.hxx> +#include "pptin.hxx" +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <pres.hxx> +#include <stlpool.hxx> +#include <anminfo.hxx> +#include <svx/gallery.hxx> +#include <tools/debug.hxx> +#include <tools/urlobj.hxx> +#include <svx/svdopage.hxx> +#include <svx/svdomedia.hxx> +#include <svx/svdogrp.hxx> +#include "propread.hxx" +#include <cusshow.hxx> +#include <xmloff/autolayout.hxx> + +#include <customshowlist.hxx> +#include <sddll.hxx> + +#include <DrawDocShell.hxx> +#include <FrameView.hxx> +#include <unokywds.hxx> + +#include <unotools/fltrcfg.hxx> +#include <sfx2/progress.hxx> +#include <editeng/editstat.hxx> +#include <unotools/pathoptions.hxx> + +#define MAX_USER_MOVE 2 + +#include "pptanimations.hxx" +#include "pptinanimations.hxx" +#include "ppt97animations.hxx" + +#include <com/sun/star/animations/TransitionSubType.hpp> +#include <com/sun/star/animations/TransitionType.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <comphelper/string.hxx> +#include <oox/ole/olehelper.hxx> + +#include <optional> + +#include <cassert> +#include <memory> +#include <string_view> + +using namespace ::com::sun::star; + +SdPPTImport::SdPPTImport( SdDrawDocument* pDocument, SvStream& rDocStream, SotStorage& rStorage, SfxMedium& rMedium ) + : maParam(rDocStream) +{ +#ifdef DBG_UTIL + std::unique_ptr<PropRead> pSummaryInformation(new PropRead( rStorage, "\005SummaryInformation" )); + if ( pSummaryInformation->IsValid() ) + { + pSummaryInformation->Read(); + sal_uInt8 const aPropSetGUID[ 16 ] + { + 0xe0, 0x85, 0x9f, 0xf2, 0xf9, 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9 + }; + Section* pSection = const_cast<Section*>(pSummaryInformation->GetSection( aPropSetGUID )); + if ( pSection ) + { + PropItem aPropItem; + if ( pSection->GetProperty( PID_COMMENTS, aPropItem ) ) + { + OUString aComment; + aPropItem.Read( aComment ); + if ( aComment.indexOf( "Applixware" ) >= 0 ) + { + maParam.nImportFlags |= PPT_IMPORTFLAGS_NO_TEXT_ASSERT; + } + } + } + } + pSummaryInformation.reset(); +#endif + + tools::SvRef<SotStorageStream> pCurrentUserStream(rStorage.OpenSotStream("Current User", StreamMode::STD_READ)); + if (pCurrentUserStream) + { + ReadPptCurrentUserAtom(*pCurrentUserStream, maParam.aCurrentUserAtom); + } + + if( pDocument ) + { + // iterate over all styles + SdStyleSheetPool* pStyleSheetPool = pDocument->GetSdStyleSheetPool(); + std::shared_ptr<SfxStyleSheetIterator> aIter = + std::make_shared<SfxStyleSheetIterator>(pStyleSheetPool, SfxStyleFamily::All); + + for (SfxStyleSheetBase *pSheet = aIter->First(); pSheet; pSheet = aIter->Next()) + { + SfxItemSet& rSet = pSheet->GetItemSet(); + // if autokerning is set in style, override it, ppt has no autokerning + if( rSet.GetItemState( EE_CHAR_PAIRKERNING, false ) == SfxItemState::SET ) + rSet.ClearItem( EE_CHAR_PAIRKERNING ); + } + } + + pFilter.reset(new ImplSdPPTImport(pDocument, rStorage, rMedium, maParam)); +} + +bool SdPPTImport::Import() +{ + return pFilter->Import(); +} + +SdPPTImport::~SdPPTImport() +{ +} + +ImplSdPPTImport::ImplSdPPTImport( SdDrawDocument* pDocument, SotStorage& rStorage_, SfxMedium& rMedium, PowerPointImportParam& rParam ) + : SdrPowerPointImport(rParam, rMedium.GetBaseURL()) + , mrMed(rMedium) + , mrStorage(rStorage_) + , mbDocumentFound(false) + , mnFilterOptions(0) + , mpDoc(pDocument) + , mePresChange(PresChange::Manual) + , mnBackgroundObjectsLayerID(0) +{ + if ( !m_bOk ) + return; + + mbDocumentFound = SeekToDocument( &maDocHd ); // maDocHd = the latest DocumentHeader + while ( SeekToRec( rStCtrl, PPT_PST_Document, nStreamLen, &maDocHd ) ) + mbDocumentFound = true; + + sal_uInt32 nDggContainerOfs = 0; + + if ( mbDocumentFound ) + { + sal_uInt64 nOldPos = rStCtrl.Tell(); + + mxPicturesStream = rStorage_.OpenSotStream( "Pictures", StreamMode::STD_READ ); + pStData = mxPicturesStream.get(); + + rStCtrl.Seek( maDocHd.GetRecBegFilePos() + 8 ); + sal_uLong nDocLen = maDocHd.GetRecEndFilePos(); + DffRecordHeader aPPDGHd; + if ( SeekToRec( rStCtrl, PPT_PST_PPDrawingGroup, nDocLen, &aPPDGHd ) ) + { + sal_uLong nPPDGLen = aPPDGHd.GetRecEndFilePos(); + if ( SeekToRec( rStCtrl, DFF_msofbtDggContainer, nPPDGLen ) ) + nDggContainerOfs = rStCtrl.Tell(); + } + rStCtrl.Seek( nOldPos ); + } + sal_uInt32 nSvxMSDffOLEConvFlags2 = 0; + + const SvtFilterOptions& rBasOpt = SvtFilterOptions::Get(); + if ( rBasOpt.IsLoadPPointBasicCode() ) + mnFilterOptions |= 1; + if ( rBasOpt.IsMathType2Math() ) + nSvxMSDffOLEConvFlags2 |= OLE_MATHTYPE_2_STARMATH; + if ( rBasOpt.IsWinWord2Writer() ) + nSvxMSDffOLEConvFlags2 |= OLE_WINWORD_2_STARWRITER; + if ( rBasOpt.IsExcel2Calc() ) + nSvxMSDffOLEConvFlags2 |= OLE_EXCEL_2_STARCALC; + if ( rBasOpt.IsPowerPoint2Impress() ) + nSvxMSDffOLEConvFlags2 |= OLE_POWERPOINT_2_STARIMPRESS; + + InitSvxMSDffManager( nDggContainerOfs, pStData, nSvxMSDffOLEConvFlags2 ); + SetSvxMSDffSettings( SVXMSDFF_SETTINGS_CROP_BITMAPS + | SVXMSDFF_SETTINGS_IMPORT_PPT ); + SetModel( mpDoc, 576 ); +} + +// Dtor +ImplSdPPTImport::~ImplSdPPTImport() +{ + pStData = nullptr; + mxPicturesStream.clear(); +} + +// Import +bool ImplSdPPTImport::Import() +{ + if ( !m_bOk ) + return false; + + bool bWasLocked = pSdrModel->isLocked(); + pSdrModel->setLock(true); + const bool bSavedUndoEnabled = pSdrModel->IsUndoEnabled(); + pSdrModel->EnableUndo(false); + + SdrOutliner& rOutl = mpDoc->GetDrawOutliner(); + EEControlBits nControlWord = rOutl.GetEditEngine().GetControlWord(); + nControlWord |= EEControlBits::ULSPACESUMMATION; + const_cast<EditEngine&>(rOutl.GetEditEngine()).SetControlWord( nControlWord ); + + SdrLayerAdmin& rAdmin = mpDoc->GetLayerAdmin(); + mnBackgroundObjectsLayerID = rAdmin.GetLayerID( sUNO_LayerName_background_objects ); + + ::sd::DrawDocShell* pDocShell = mpDoc->GetDocSh(); + if ( pDocShell ) + SeekOle( pDocShell, mnFilterOptions ); + + // hyperlinks + std::unique_ptr<PropRead> pDInfoSec2(new PropRead( mrStorage, "\005DocumentSummaryInformation" )); + if ( pDInfoSec2->IsValid() ) + { + PropItem aPropItem; + + sal_uInt32 nType(0), nPropCount(0); + + pDInfoSec2->Read(); + + sal_uInt8 const aPropSetGUID[ 16 ] + { + 0x02, 0xd5, 0xcd, 0xd5, 0x9c, 0x2e, 0x1b, 0x10, 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae + }; + Section* pSection = const_cast<Section*>(pDInfoSec2->GetSection( aPropSetGUID )); + if ( pSection ) + { + if ( pSection->GetProperty( PID_SLIDECOUNT, aPropItem ) ) + { + aPropItem.ReadUInt32( nType ); + if ( ( nType == VT_I4 ) || ( nType == VT_UI4 ) ) + { + // examine PID_HEADINGPAIR to get the correct entry for PID_DOCPARTS + sal_uInt32 nSlideCount(0), nVecCount(0); + aPropItem.ReadUInt32( nSlideCount ); + if ( nSlideCount && pSection->GetProperty( PID_HEADINGPAIR, aPropItem ) ) + { + sal_uInt32 nSlideTitleIndex = 0, nSlideTitleCount = 0; + + OUString aUString; + + aPropItem.ReadUInt32( nType ) + .ReadUInt32( nVecCount ); + + if ( ( nType == ( VT_VARIANT | VT_VECTOR ) ) && ( nVecCount ^ 1 ) ) + { + nVecCount >>= 1; + sal_uInt32 nEntryCount = 0; + for (sal_uInt32 i = 0; i < nVecCount; ++i) + { + if ( !aPropItem.Read( aUString, VT_EMPTY, false ) ) + break; + aPropItem.ReadUInt32( nType ); + if ( ( nType != VT_I4 ) && ( nType != VT_UI4 ) ) + break; + sal_uInt32 nTemp(0); + aPropItem.ReadUInt32( nTemp ); + if ( aUString == "Slide Titles" || aUString == "Folientitel" ) + { + nSlideTitleCount = nTemp; + nSlideTitleIndex = nEntryCount; + } + nEntryCount += nTemp; + } + } + if ( ( nSlideCount == nSlideTitleCount ) && pSection->GetProperty( PID_DOCPARTS, aPropItem ) ) + { + aPropItem.ReadUInt32( nType ) + .ReadUInt32( nVecCount ); + + bool bVecOk = ( ( nVecCount >= (nSlideTitleIndex + nSlideTitleCount) ) + && ( nType == ( VT_LPSTR | VT_VECTOR ) ) ); + + if (bVecOk) + { + for (sal_uInt32 i = 0; i != nSlideTitleIndex; ++i) + { + sal_uInt32 nTemp(0); + aPropItem.ReadUInt32(nTemp); + if (!aPropItem.good()) + { + bVecOk = false; + break; + } + auto nPos = aPropItem.Tell() + nTemp; + if (!checkSeek(aPropItem, nPos)) + { + bVecOk = false; + break; + } + } + } + if (bVecOk) + { + for (sal_uInt32 i = 0; i < nSlideTitleCount; ++i) + { + if (!aPropItem.Read(aUString, nType, false)) + break; + + OUString aString( aUString ); + if ( aString == "No Slide Title" ) + aString.clear(); + else + { + std::vector<OUString>::const_iterator pIter = + std::find(maSlideNameList.begin(),maSlideNameList.end(),aString); + + if (pIter != maSlideNameList.end()) + aString.clear(); + } + maSlideNameList.push_back( aString ); + } + } + } + } + } + } + + sal_uInt8 const aUserPropSetGUID[ 16 ] + { + 0x05, 0xd5, 0xcd, 0xd5, 0x9c, 0x2e, 0x1b, 0x10, 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae + }; + pSection = const_cast<Section*>(pDInfoSec2->GetSection( aUserPropSetGUID )); + if ( pSection ) + { + PropDictionary aDict; + pSection->GetDictionary(aDict); + if (!aDict.empty()) + { + auto iter = aDict.find( OUString("_PID_HLINKS") ); + + if ( iter != aDict.end() ) + { + if ( pSection->GetProperty( iter->second, aPropItem ) ) + { + aPropItem.Seek( STREAM_SEEK_TO_BEGIN ); + aPropItem.ReadUInt32( nType ); + if ( nType == VT_BLOB ) + { + sal_uInt32 nPropSize; + aPropItem.ReadUInt32( nPropSize ) + .ReadUInt32( nPropCount ); + + if ( ! ( nPropCount % 6 ) ) + { + sal_uInt32 i; + + nPropCount /= 6; // 6 properties per hyperlink + + for ( i = 0; i < nPropCount; i++ ) + { + SdHyperlinkEntry aHyperlink; + aHyperlink.nIndex = 0; + aPropItem.ReadUInt32( nType ); + if ( nType != VT_I4 ) + break; + aPropItem.ReadInt32( aHyperlink.nPrivate1 ) + .ReadUInt32( nType ); + if ( nType != VT_I4 ) + break; + aPropItem.ReadInt32( aHyperlink.nPrivate2 ) + .ReadUInt32( nType ); + if ( nType != VT_I4 ) + break; + aPropItem.ReadInt32( aHyperlink.nPrivate3 ) + .ReadUInt32( nType ); + if ( nType != VT_I4 ) + break; + aPropItem.ReadInt32( aHyperlink.nInfo ); + if ( !aPropItem.Read( aHyperlink.aTarget ) ) + break; + + // Convert '\\' notation to 'smb://' + INetURLObject aUrl( aHyperlink.aTarget, INetProtocol::File ); + aHyperlink.aTarget = aUrl.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if ( !aPropItem.Read( aHyperlink.aSubAddress ) ) + break; + + if ( !aHyperlink.aSubAddress.isEmpty() ) // get the converted subaddress + { + sal_uInt32 nPageNumber = 0; + OUString aString( aHyperlink.aSubAddress ); + OString aStringAry[ 3 ]; + size_t nTokenCount = 0; + sal_Int32 nPos = 0; + do + { + aStringAry[nTokenCount] = + OUStringToOString(o3tl::getToken(aString, 0, ',', nPos ), RTL_TEXTENCODING_UTF8); + } + while ( ++nTokenCount < SAL_N_ELEMENTS(aStringAry) && nPos >= 0 ); + + bool bDocInternalSubAddress = false; + + // first pass, searching for a SlideId + for( size_t nToken = 0; nToken < nTokenCount; ++nToken ) + { + if (comphelper::string::isdigitAsciiString(aStringAry[nToken])) + { + sal_Int32 nNumber = aStringAry[ nToken ].toInt32(); + if ( nNumber & ~0xff ) + { + PptSlidePersistList* pPageList = GetPageList( PPT_SLIDEPAGE ); + if ( pPageList ) + { + sal_uInt16 nPage = pPageList->FindPage( nNumber ); + if ( nPage != PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + { + nPageNumber = nPage; + bDocInternalSubAddress = true; + break; + } + } + } + } + } + if ( !bDocInternalSubAddress ) + { // second pass, searching for a SlideName + for ( size_t nToken = 0; nToken < nTokenCount; ++nToken ) + { + OUString aToken(OStringToOUString(aStringAry[nToken], RTL_TEXTENCODING_UTF8)); + std::vector<OUString>::const_iterator pIter = + std::find(maSlideNameList.begin(),maSlideNameList.end(),aToken); + + if (pIter != maSlideNameList.end()) + { + nPageNumber = pIter - maSlideNameList.begin(); + bDocInternalSubAddress = true; + } + } + } + if ( !bDocInternalSubAddress ) + { // third pass, searching for a slide number + for ( size_t nToken = 0; nToken < nTokenCount; ++nToken ) + { + if (comphelper::string::isdigitAsciiString(aStringAry[nToken])) + { + sal_Int32 nNumber = aStringAry[ nToken ].toInt32(); + if ( ( nNumber & ~0xff ) == 0 ) + { + nPageNumber = static_cast<sal_uInt32>(nNumber) - 1; + bDocInternalSubAddress = true; + break; + } + } + } + } + // if a document internal sub address + if ( bDocInternalSubAddress ) + { + if ( nPageNumber < maSlideNameList.size() ) + aHyperlink.aConvSubString = maSlideNameList[ nPageNumber ]; + if ( aHyperlink.aConvSubString.isEmpty() ) + { + aHyperlink.aConvSubString = SdResId( STR_PAGE ) + " " + mpDoc->CreatePageNumValue( static_cast<sal_uInt16>(nPageNumber) + 1 ); + } + } else { + // if sub address is given but not internal, use it as it is + if ( aHyperlink.aConvSubString.isEmpty() ) + { + aHyperlink.aConvSubString = aString; + } + } + } + m_aHyperList.push_back( aHyperlink ); + } + } + } + } + } + } + } + } + } + pDInfoSec2.reset(); + + if ( mbDocumentFound ) + { + rStCtrl.Seek( maDocHd.GetRecBegFilePos() + 8 ); + // read hyperlist / set indices of the entries + DffRecordHeader aHyperHd; + if ( SeekToRec( rStCtrl, PPT_PST_ExObjList, maDocHd.GetRecEndFilePos(), &aHyperHd ) ) + { + sal_uInt32 nExObjHyperListLen = aHyperHd.GetRecEndFilePos(); + for (SdHyperlinkEntry & entry : m_aHyperList) + { + DffRecordHeader aHyperE; + if ( !SeekToRec( rStCtrl, PPT_PST_ExHyperlink, nExObjHyperListLen, &aHyperE ) ) + break; + if ( !SeekToRec( rStCtrl, PPT_PST_ExHyperlinkAtom, nExObjHyperListLen ) ) + break; + rStCtrl.SeekRel( 8 ); + rStCtrl.ReadUInt32( entry.nIndex ); + if (!aHyperE.SeekToEndOfRecord(rStCtrl)) + break; + } + + if (m_aHyperList.size() == 0) + { + while(true) + { + + DffRecordHeader aHyperE; + if (!SeekToRec(rStCtrl, PPT_PST_ExHyperlink, nExObjHyperListLen, &aHyperE)) + break; + if (!SeekToRec(rStCtrl, PPT_PST_ExHyperlinkAtom, nExObjHyperListLen)) + continue; + + SdHyperlinkEntry aHyperlink; + + OUString aURLText; + OUString aURLLink; + rStCtrl.SeekRel(8); + rStCtrl.ReadUInt32(aHyperlink.nIndex); + + ReadString(aURLText); + ReadString(aURLLink); + aHyperlink.aTarget = aURLLink; + m_aHyperList.push_back(aHyperlink); + } + } + } + } + + if (pDocShell) + { + Size aVisAreaSize; + switch ( m_aUserEditAtom.eLastViewType ) + { + case PptViewTypeEnum::Notes: + case PptViewTypeEnum::NotesMaster: + aVisAreaSize = aDocAtom.GetNotesPageSize(); + break; + default : + aVisAreaSize = aDocAtom.GetSlidesPageSize(); + } + Scale( aVisAreaSize ); + pDocShell->SetVisArea( ::tools::Rectangle( Point(), aVisAreaSize ) ); + } + + // create master pages: + + std::unique_ptr<SfxProgress> xStbMgr; + if (!utl::ConfigManager::IsFuzzing()) + { + xStbMgr.reset(new SfxProgress(pDocShell, + SdResId( STR_POWERPOINT_IMPORT), + m_pMasterPages->size() + + m_pSlidePages->size() + m_pNotePages->size())); + } + + sal_uInt32 nImportedPages = 0; + { + sal_uInt16 nMasterCnt = GetPageCount( PPT_MASTERPAGE ); + + for ( sal_uInt16 nMasterNum = 0; nMasterNum < nMasterCnt; nMasterNum++ ) + { + SetPageNum( nMasterNum, PPT_MASTERPAGE ); + rtl::Reference<SdPage> pPage = static_cast<SdPage*>(MakeBlankPage( true ).get()); + if ( pPage ) + { + bool bNotesMaster = (*GetPageList( m_eCurrentPageKind ) )[ m_nCurrentPageNum ].bNotesMaster; + bool bStarDrawFiller = (*GetPageList( m_eCurrentPageKind ) )[ m_nCurrentPageNum ].bStarDrawFiller; + + PageKind ePgKind = bNotesMaster ? PageKind::Notes : PageKind::Standard; + bool bHandout = (*GetPageList( m_eCurrentPageKind ) )[ m_nCurrentPageNum ].bHandoutMaster; + if ( bHandout ) + ePgKind = PageKind::Handout; + + pPage->SetPageKind( ePgKind ); + pSdrModel->InsertMasterPage( pPage.get() ); + if ( bNotesMaster && bStarDrawFiller ) + pPage->SetAutoLayout( AUTOLAYOUT_NOTES, true ); + if ( nMasterNum ) + { + std::optional< sal_Int16 > oStartNumbering; + SfxStyleSheet* pSheet; + if ( nMasterNum == 1 ) + { + // standardsheet + pSheet = static_cast<SfxStyleSheet*>(mpDoc->GetStyleSheetPool()->Find(SdResId(STR_STANDARD_STYLESHEET_NAME), SfxStyleFamily::Para )); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, TSS_Type::TextInShape, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, TSS_Type::TextInShape, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + } + } + + // PSEUDO + pSheet = static_cast<SfxStyleSheet*>(mpDoc->GetStyleSheetPool()->Find(SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS), SfxStyleFamily::Pseudo )); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, TSS_Type::TextInShape, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, TSS_Type::TextInShape, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + } + + // create layoutstylesheets, set layoutname and stylesheet + // (only on standard and not pages) + + OUString aLayoutName( SdResId( STR_LAYOUT_DEFAULT_NAME ) ); + if ( nMasterNum > 2 ) + { + if ( ePgKind == PageKind::Standard ) + { // standard page: create new presentation layout + aLayoutName = SdResId( STR_LAYOUT_DEFAULT_TITLE_NAME ) + + OUString::number( static_cast<sal_Int32>( ( nMasterNum + 1 ) / 2 - 1 ) ); + static_cast<SdStyleSheetPool*>( mpDoc->GetStyleSheetPool() )->CreateLayoutStyleSheets( aLayoutName ); + } + else // note page: use presentation layout of standard page + aLayoutName = static_cast<SdPage*>( mpDoc->GetMasterPage( nMasterNum - 1 ) )->GetName(); + } + pPage->SetName( aLayoutName ); + aLayoutName += SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + pPage->SetLayoutName( aLayoutName ); + + // set stylesheets + if ( pPage->GetPageKind() == PageKind::Standard ) + { + TSS_Type nTitleInstance = TSS_Type::PageTitle; + TSS_Type nOutlinerInstance = TSS_Type::Body; + const PptSlideLayoutAtom* pSlideLayout = GetSlideLayoutAtom(); + bool bSwapStyleSheet = pSlideLayout->eLayout == PptSlideLayout::TITLEMASTERSLIDE; + if ( bSwapStyleSheet ) + { + nTitleInstance = TSS_Type::Title; + nOutlinerInstance = TSS_Type::Subtitle; + } + + // titlestylesheet + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, nTitleInstance, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, nTitleInstance, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + } + + // outlinerstylesheet + sal_uInt16 nLevel; + PPTParagraphObj* pParagraphs[ 9 ]; + + for ( nLevel = 0; nLevel < 9; nLevel++ ) + { + OUString aName = pPage->GetLayoutName() + + " " + OUString::number( nLevel + 1 ); + SfxStyleSheet* pOutlineSheet = static_cast<SfxStyleSheet*>( mpDoc->GetStyleSheetPool()->Find( aName, SfxStyleFamily::Page ) ); + DBG_ASSERT( pOutlineSheet, "Template for outline object not found" ); + if ( pOutlineSheet ) + { + pParagraphs[ nLevel ] = new PPTParagraphObj( *m_pPPTStyleSheet, nOutlinerInstance, nLevel ); + SfxItemSet& rItemSet = pOutlineSheet->GetItemSet(); + PPTPortionObj aPortion( *m_pPPTStyleSheet, nOutlinerInstance, nLevel ); + pParagraphs[ nLevel ]->AppendPortion( aPortion ); + pParagraphs[ nLevel ]->ApplyTo( rItemSet, oStartNumbering, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + } + else + pParagraphs[ nLevel ] = nullptr; + } + for ( nLevel = 0; nLevel < 9; delete pParagraphs[ nLevel++ ] ) ; + + // subtitle stylesheet + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Text ); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, TSS_Type::Subtitle, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, TSS_Type::Subtitle, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + } + } + else if ( ePgKind == PageKind::Notes ) + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Notes ); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, TSS_Type::Notes, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, TSS_Type::Notes, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast<SdrPowerPointImport&>(*this), TSS_Type::Unknown ); + } + } + } + } + } + } + for (sal_uInt16 i = 0; i < mpDoc->GetMasterPageCount(); ++i) + { + SdPage *const pMPage(static_cast<SdPage*>(mpDoc->GetMasterPage(i))); + if (pMPage == nullptr) + break; + SetPageNum( i, PPT_MASTERPAGE ); + + // importing master page objects + PptSlidePersistList* pList = GetPageList( m_eCurrentPageKind ); + PptSlidePersistEntry* pPersist = ( pList && ( m_nCurrentPageNum < pList->size() ) ) + ? &(*pList)[ m_nCurrentPageNum ] : nullptr; + if ( pPersist ) + { + if ( pPersist->bStarDrawFiller && pPersist->bNotesMaster && ( m_nCurrentPageNum > 2 ) && ( ( m_nCurrentPageNum & 1 ) == 0 ) ) + { + pSdrModel->DeleteMasterPage( m_nCurrentPageNum ); + SdPage* pMasterPage2 = static_cast<SdPage*>(pSdrModel->GetMasterPage( 2 )); + rtl::Reference<SdPage> pNotesClone = static_cast<SdPage*>(pMasterPage2->CloneSdrPage(*pSdrModel).get()); + pSdrModel->InsertMasterPage( pNotesClone.get(), m_nCurrentPageNum ); + if ( pNotesClone ) + { + OUString aLayoutName( static_cast<SdPage*>(pSdrModel->GetMasterPage( m_nCurrentPageNum - 1 ))->GetLayoutName() ); + pNotesClone->SetPresentationLayout( aLayoutName, false, false ); + pNotesClone->SetLayoutName( aLayoutName ); + } + } + else if ( !pPersist->bStarDrawFiller ) + { + PptSlidePersistEntry* pE = pPersist; + while( ( pE->aSlideAtom.nFlags & 4 ) && pE->aSlideAtom.nMasterId ) + { + auto nOrigMasterId = pE->aSlideAtom.nMasterId; + sal_uInt16 nNextMaster = m_pMasterPages->FindPage(nOrigMasterId); + if ( nNextMaster == PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + break; + else + pE = &(*pList)[ nNextMaster ]; + if (pE->aSlideAtom.nMasterId == nOrigMasterId) + { + SAL_WARN("filter.ms", "loop in atom chain"); + break; + } + } + SdrObject* pObj = ImportPageBackgroundObject( *pMPage, pE->nBackgroundOffset ); // import background + if ( pObj ) + pMPage->NbcInsertObject( pObj ); + + bool bNewAnimationsUsed = false; + ProcessData aProcessData( (*pList)[ m_nCurrentPageNum ], SdPageCapsule(pMPage) ); + sal_uInt32 nOldFPos = rStCtrl.Tell(); + DffRecordHeader aPageHd; + if ( SeekToCurrentPage( &aPageHd ) ) + { + auto nEndRecPos = SanitizeEndPos(rStCtrl, aPageHd.GetRecEndFilePos()); + while( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nEndRecPos ) ) + { + DffRecordHeader aHd; + if (!ReadDffRecordHeader( rStCtrl, aHd )) + break; + switch( aHd.nRecType ) + { + case PPT_PST_PPDrawing : + { + aHd.SeekToBegOfRecord( rStCtrl ); + DffRecordHeader aPPDrawHd; + if ( SeekToRec( rStCtrl, PPT_PST_PPDrawing, aHd.GetRecEndFilePos(), &aPPDrawHd ) ) + { + sal_uInt32 nPPDrawEnd = aPPDrawHd.GetRecEndFilePos(); + DffRecordHeader aEscherF002Hd; + if ( SeekToRec( rStCtrl, DFF_msofbtDgContainer, nPPDrawEnd, &aEscherF002Hd ) ) + { + sal_uInt32 nEscherF002End = aEscherF002Hd.GetRecEndFilePos(); + DffRecordHeader aEscherObjListHd; + if ( SeekToRec( rStCtrl, DFF_msofbtSpgrContainer, nEscherF002End, &aEscherObjListHd ) ) + { + sal_uInt32 nObjCount = 0; + auto nListEndRecPos = SanitizeEndPos(rStCtrl, aEscherObjListHd.GetRecEndFilePos()); + while( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nListEndRecPos ) ) + { + DffRecordHeader aHd2; + ReadDffRecordHeader( rStCtrl, aHd2 ); + if ( ( aHd2.nRecType == DFF_msofbtSpContainer ) || ( aHd2.nRecType == DFF_msofbtSpgrContainer ) ) + { + if ( nObjCount++ ) // skipping the first object + { + ::tools::Rectangle aEmpty; + if (!aHd2.SeekToBegOfRecord(rStCtrl)) + break; + SdrObject* pImpObj = ImportObj( rStCtrl, aProcessData, aEmpty, aEmpty, /*nCalledByGroup*/0, /*pShapeId*/ nullptr ); + if ( pImpObj ) + { + pImpObj->SetLayer( mnBackgroundObjectsLayerID ); + pMPage->NbcInsertObject( pImpObj ); + } + } + } + if (!aHd2.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + } + } + break; + + case PPT_PST_ProgTags : + { + DffRecordHeader aProgTagHd; + if ( SeekToContentOfProgTag( 10, rStCtrl, aPageHd, aProgTagHd ) ) + { + auto nTagEndRecPos = SanitizeEndPos(rStCtrl, aProgTagHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nTagEndRecPos ) ) + { + DffRecordHeader aProgTagContentHd; + ReadDffRecordHeader( rStCtrl, aProgTagContentHd ); + switch( aProgTagContentHd.nRecType ) + { + case DFF_msofbtAnimGroup : + { + css::uno::Reference< css::drawing::XDrawPage > xPage( pMPage->getUnoPage(), css::uno::UNO_QUERY ); + ppt::AnimationImporter aImporter( this, rStCtrl ); + bNewAnimationsUsed = aImporter.import( xPage, aProgTagContentHd ) > 0; + } + break; + } + if (!aProgTagContentHd.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + break; + } + bool bSuccess = aHd.SeekToEndOfRecord(rStCtrl); + if (!bSuccess) + { + SAL_WARN("filter.ms", "Could not seek to end of record"); + break; + } + } + } + rStCtrl.Seek( nOldFPos ); + ImportPageEffect( pMPage, bNewAnimationsUsed ); + + // background object + pObj = pMPage->GetObj( 0 ); + if ( pObj && pObj->GetObjIdentifier() == SdrObjKind::Rectangle ) + { + if ( pMPage->GetPageKind() == PageKind::Standard ) + { + // transform data from imported background object to new form + // and delete the object. It was used as container to transport + // the attributes of the MasterPage background fill + SfxStyleSheet* pSheet = pMPage->GetStyleSheetForMasterPageBackground(); + + if(pSheet) + { + // if we have a StyleSheet (for Masterpages), set attributes there and use it + pSheet->GetItemSet().ClearItem(); + pSheet->GetItemSet().Put(pObj->GetMergedItemSet()); + pMPage->getSdrPageProperties().ClearItem(); + pMPage->getSdrPageProperties().SetStyleSheet(pSheet); + } + else + { + // without StyleSheet, set attributes directly. This + // should not be done at all and is an error (will be asserted by SdrPage) + pMPage->getSdrPageProperties().ClearItem(); + pMPage->getSdrPageProperties().PutItemSet(pObj->GetMergedItemSet()); + } + + pMPage->RemoveObject(pObj->GetOrdNum()); + SdrObject::Free(pObj); + } + } + } + } + if (xStbMgr) + xStbMgr->SetState( nImportedPages++ ); + } + + // importing slide pages + { + sal_uInt32 nOldFPos = rStCtrl.Tell(); + PptPageKind ePageKind = m_eCurrentPageKind; + sal_uInt16 nPageNum = m_nCurrentPageNum; + + rtl::Reference<SdPage> pHandoutPage = static_cast<SdPage*>(MakeBlankPage( false ).get()); + pHandoutPage->SetPageKind( PageKind::Handout ); + pSdrModel->InsertPage( pHandoutPage.get() ); + + sal_uInt16 nPageCnt = GetPageCount(); + if ( nPageCnt ) + { + for ( sal_uInt16 nPage = 0; nPage < nPageCnt; nPage++ ) + { + mePresChange = PresChange::SemiAuto; + SetPageNum( nPage ); + rtl::Reference<SdPage> pPage = static_cast<SdPage*>(MakeBlankPage( false ).get()); + PptSlidePersistEntry* pMasterPersist = nullptr; + if ( HasMasterPage( nPage ) ) // try to get the LayoutName from the masterpage + { + sal_uInt16 nMasterNum = GetMasterPageIndex( m_nCurrentPageNum, m_eCurrentPageKind ); + pPage->TRG_SetMasterPage(*pSdrModel->GetMasterPage(nMasterNum)); + PptSlidePersistList* pPageList = GetPageList( PPT_MASTERPAGE ); + if ( pPageList && nMasterNum < pPageList->size() ) + pMasterPersist = &(*pPageList)[ nMasterNum ]; + pPage->SetLayoutName(static_cast<SdPage&>(pPage->TRG_GetMasterPage()).GetLayoutName()); + } + pPage->SetPageKind( PageKind::Standard ); + pSdrModel->InsertPage( pPage.get() ); // SJ: #i29625# because of form controls, the + ImportPage( pPage.get(), pMasterPersist ); // page must be inserted before importing + SetHeaderFooterPageSettings( pPage.get(), pMasterPersist ); + // CWS preseng01: pPage->SetPageKind( PageKind::Standard ); + + DffRecordHeader aPageHd; + if ( SeekToCurrentPage( &aPageHd ) ) + { + bool bNewAnimationsUsed = false; + + aPageHd.SeekToContent( rStCtrl ); + auto nEndRecPos = SanitizeEndPos(rStCtrl, aPageHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nEndRecPos ) ) + { + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + switch ( aHd.nRecType ) + { + case PPT_PST_ProgTags : + { + DffRecordHeader aProgTagHd; + if ( SeekToContentOfProgTag( 10, rStCtrl, aPageHd, aProgTagHd ) ) + { + auto nHdEndRecPos = SanitizeEndPos(rStCtrl, aProgTagHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nHdEndRecPos ) ) + { + DffRecordHeader aProgTagContentHd; + ReadDffRecordHeader( rStCtrl, aProgTagContentHd ); + switch( aProgTagContentHd.nRecType ) + { + case DFF_msofbtAnimGroup : + { + css::uno::Reference< css::drawing::XDrawPage > xPage( pPage->getUnoPage(), css::uno::UNO_QUERY ); + ppt::AnimationImporter aImporter( this, rStCtrl ); + bNewAnimationsUsed = aImporter.import( xPage, aProgTagContentHd ) > 0; + } + break; + + case PPT_PST_HashCodeAtom : // ??? + break; + + case PPT_PST_SlideTime10Atom : // ??? don't know, this atom is always 8 bytes big + break; // and is appearing in nearly every l10 progtag + } + if (!aProgTagContentHd.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + break; + + case PPT_PST_HeadersFooters : + case PPT_PST_PPDrawing : + default: + break; + } + + if (!aHd.SeekToEndOfRecord(rStCtrl)) + break; + } + ImportPageEffect( pPage.get(), bNewAnimationsUsed ); + } + + // creating the corresponding note page + m_eCurrentPageKind = PPT_NOTEPAGE; + rtl::Reference<SdPage> pNotesPage = static_cast<SdPage*>(MakeBlankPage( false ).get()); + sal_uInt16 nNotesMasterNum = GetMasterPageIndex( nPage ) + 1; + sal_uInt32 nNotesPageId = GetNotesPageId( nPage ); + if ( nNotesPageId ) + { + nImportedPages++; + sal_uInt16 nNotesPageIndex = m_pNotePages->FindPage( nNotesPageId ); + if ( nNotesPageIndex == PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + nNotesPageIndex = 0; + SetPageNum( nNotesPageIndex, PPT_NOTEPAGE ); + PptSlidePersistEntry* pMasterPersist2 = nullptr; + if ( HasMasterPage( nNotesPageIndex, PPT_NOTEPAGE ) ) // try to get the LayoutName from the masterpage + { + pNotesPage->TRG_SetMasterPage(*pSdrModel->GetMasterPage(nNotesMasterNum)); + PptSlidePersistList* pPageList = GetPageList( PPT_MASTERPAGE ); + if ( pPageList && nNotesMasterNum < pPageList->size() ) + pMasterPersist2 = &(*pPageList)[ nNotesMasterNum ]; + pNotesPage->SetLayoutName( static_cast<SdPage&>(pNotesPage->TRG_GetMasterPage()).GetLayoutName() ); + } + pNotesPage->SetPageKind( PageKind::Notes ); + pNotesPage->TRG_SetMasterPage(*pSdrModel->GetMasterPage(nNotesMasterNum)); + pSdrModel->InsertPage( pNotesPage.get() ); // SJ: #i29625# because of form controls, the + ImportPage( pNotesPage.get(), pMasterPersist2 ); // page must be inserted before importing + SetHeaderFooterPageSettings( pNotesPage.get(), pMasterPersist2 ); + pNotesPage->SetAutoLayout( AUTOLAYOUT_NOTES ); + } + else + { + pNotesPage->SetPageKind( PageKind::Notes ); + pNotesPage->TRG_SetMasterPage(*pSdrModel->GetMasterPage(nNotesMasterNum)); + pNotesPage->SetAutoLayout( AUTOLAYOUT_NOTES, true ); + pSdrModel->InsertPage( pNotesPage.get() ); + SdrObject* pPageObj = pNotesPage->GetPresObj( PresObjKind::Page ); + if ( pPageObj ) + static_cast<SdrPageObj*>(pPageObj)->SetReferencedPage(pSdrModel->GetPage(( nPage << 1 ) + 1)); + } + + if (xStbMgr) + xStbMgr->SetState( nImportedPages++ ); + } + } + else + { + // that can happen by document templates + m_eCurrentPageKind = PPT_SLIDEPAGE; + rtl::Reference<SdPage> pPage = static_cast<SdPage*>(MakeBlankPage( false ).get()); + pSdrModel->InsertPage( pPage.get() ); + + // #i37397#, trying to set the title master for the first page + sal_uInt16 nMaster, nMasterCount = pSdrModel->GetMasterPageCount(); + SdPage* pFoundMaster = nullptr; + for ( nMaster = 1; nMaster < nMasterCount; nMaster++ ) + { + SdPage* pMaster = static_cast<SdPage*>( pSdrModel->GetMasterPage( nMaster ) ); + if ( pMaster->GetPageKind() == PageKind::Standard ) + { + SetPageNum( nMaster, PPT_MASTERPAGE ); + if ( !pFoundMaster ) + pFoundMaster = pMaster; + else if ( GetSlideLayoutAtom()->eLayout == PptSlideLayout::TITLEMASTERSLIDE ) + pFoundMaster = pMaster; + if ( GetSlideLayoutAtom()->eLayout == PptSlideLayout::TITLEMASTERSLIDE ) + break; + } + } + if ( pFoundMaster ) + { + pPage->TRG_SetMasterPage( *pFoundMaster ); + pPage->SetLayoutName( pFoundMaster->GetLayoutName() ); + } + pPage->SetAutoLayout( AUTOLAYOUT_TITLE, true, true ); + + m_eCurrentPageKind = PPT_NOTEPAGE; + rtl::Reference<SdrPage> pNPage = MakeBlankPage( false ); + pSdrModel->InsertPage( pNPage.get() ); + } + SetPageNum( nPageNum, ePageKind ); + rStCtrl.Seek( nOldFPos ); + } + + // create handout and note pages + m_bOk = mpDoc->CreateMissingNotesAndHandoutPages(); + if ( m_bOk ) + { + for ( sal_uInt16 i = 0; i < mpDoc->GetSdPageCount( PageKind::Standard ); i++ ) + { + + // set AutoLayout + SetPageNum( i ); + SdPage* pPage = mpDoc->GetSdPage( i, PageKind::Standard ); + AutoLayout eAutoLayout = AUTOLAYOUT_NONE; + const PptSlideLayoutAtom* pSlideLayout = GetSlideLayoutAtom(); + if ( pSlideLayout ) + { + switch ( pSlideLayout->eLayout ) // presentation layout for standard pages + { + case PptSlideLayout::TITLEANDBODYSLIDE : + { + eAutoLayout = AUTOLAYOUT_TITLE_CONTENT; + PptPlaceholder nID1 = pSlideLayout->aPlaceholderId[ 1 ]; + switch ( nID1 ) + { + case PptPlaceholder::BODY : + eAutoLayout = AUTOLAYOUT_TITLE_CONTENT; + break; + case PptPlaceholder::TABLE : + eAutoLayout = AUTOLAYOUT_TAB; + break; + case PptPlaceholder::ORGANISZATIONCHART : + eAutoLayout = AUTOLAYOUT_ORG; + break; + case PptPlaceholder::GRAPH : + eAutoLayout = AUTOLAYOUT_CHART; + break; + case PptPlaceholder::OBJECT : + eAutoLayout = AUTOLAYOUT_OBJ; + break; + case PptPlaceholder::VERTICALTEXTBODY : + eAutoLayout = AUTOLAYOUT_TITLE_VCONTENT; + break; + default: break; + } + } + break; + + case PptSlideLayout::TWOCOLUMNSANDTITLE : + { + eAutoLayout = AUTOLAYOUT_TITLE_2CONTENT; + PptPlaceholder nID1 = pSlideLayout->aPlaceholderId[ 1 ]; + PptPlaceholder nID2 = pSlideLayout->aPlaceholderId[ 2 ]; + if ( nID1 == PptPlaceholder::BODY && nID2 == PptPlaceholder::GRAPH ) + eAutoLayout = AUTOLAYOUT_TEXTCHART; + else if ( nID1 == PptPlaceholder::GRAPH && nID2 == PptPlaceholder::BODY ) + eAutoLayout = AUTOLAYOUT_CHARTTEXT; + else if ( nID1 == PptPlaceholder::BODY && nID2 == PptPlaceholder::CLIPART ) + eAutoLayout = AUTOLAYOUT_TEXTCLIP; + else if ( nID1 == PptPlaceholder::CLIPART && nID2 == PptPlaceholder::BODY ) + eAutoLayout = AUTOLAYOUT_CLIPTEXT; + else if ( nID1 == PptPlaceholder::CLIPART && nID2 == PptPlaceholder::VERTICALTEXTBODY ) + eAutoLayout = AUTOLAYOUT_TITLE_2VTEXT; + else if ( ( nID1 == PptPlaceholder::BODY ) + && ( ( nID2 == PptPlaceholder::OBJECT ) || ( nID2 == PptPlaceholder::MEDIACLIP ) ) ) + eAutoLayout = AUTOLAYOUT_TEXTOBJ; + else if ( ( nID2 == PptPlaceholder::BODY ) + && ( ( nID1 == PptPlaceholder::OBJECT ) || ( nID1 == PptPlaceholder::MEDIACLIP ) ) ) + eAutoLayout = AUTOLAYOUT_OBJTEXT; + else if ( ( nID1 == PptPlaceholder::OBJECT ) && ( nID2 == PptPlaceholder::OBJECT ) ) + eAutoLayout = AUTOLAYOUT_OBJ; + } + break; + + case PptSlideLayout::TWOROWSANDTITLE : + { + eAutoLayout = AUTOLAYOUT_TITLE_2CONTENT; + PptPlaceholder nID1 = pSlideLayout->aPlaceholderId[ 1 ]; + PptPlaceholder nID2 = pSlideLayout->aPlaceholderId[ 2 ]; + if ( nID1 == PptPlaceholder::BODY && nID2 == PptPlaceholder::OBJECT ) + eAutoLayout = AUTOLAYOUT_TEXTOVEROBJ; + else if ( nID1 == PptPlaceholder::OBJECT && nID2 == PptPlaceholder::BODY ) + eAutoLayout = AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT; + } + break; + + case PptSlideLayout::TITLESLIDE : + eAutoLayout = AUTOLAYOUT_TITLE; + break; + case PptSlideLayout::ONLYTITLE : + eAutoLayout = AUTOLAYOUT_TITLE_ONLY; + break; + case PptSlideLayout::RIGHTCOLUMN2ROWS : + eAutoLayout = AUTOLAYOUT_TITLE_CONTENT_2CONTENT; + break; + case PptSlideLayout::LEFTCOLUMN2ROWS : + eAutoLayout = AUTOLAYOUT_TITLE_2CONTENT_CONTENT; + break; + case PptSlideLayout::TOPROW2COLUMN : + eAutoLayout = AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT; + break; + case PptSlideLayout::FOUROBJECTS : + eAutoLayout = AUTOLAYOUT_TITLE_4CONTENT; + break; + case PptSlideLayout::BIGOBJECT : + eAutoLayout = AUTOLAYOUT_OBJ; + break; + case PptSlideLayout::TITLERIGHTBODYLEFT : + eAutoLayout = AUTOLAYOUT_VTITLE_VCONTENT; + break; + case PptSlideLayout::TITLERIGHT2BODIESLEFT : + eAutoLayout = AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT; + break; + + case PptSlideLayout::BOTTOMROW2COLUMNS : + case PptSlideLayout::BLANKSLIDE : + case PptSlideLayout::MASTERSLIDE : // layout of the standard and title master page + case PptSlideLayout::TITLEMASTERSLIDE : + case PptSlideLayout::MASTERNOTES : // layout of the note master page + case PptSlideLayout::NOTESTITLEBODY : // presentation layout for note pages + case PptSlideLayout::HANDOUTLAYOUT : // presentation layout for handout + eAutoLayout = AUTOLAYOUT_NONE; + break; + } + if ( eAutoLayout != AUTOLAYOUT_NONE ) + pPage->SetAutoLayout( eAutoLayout ); + } + } + + // handout master page: auto layout + SdPage* pHandoutMPage = mpDoc->GetMasterSdPage( 0, PageKind::Handout ); + pHandoutMPage->SetAutoLayout( AUTOLAYOUT_HANDOUT6, true, true ); + } + + sal_uInt32 nSlideCount = GetPageCount(); + for ( sal_uInt32 i = 0; ( i < nSlideCount) && ( i < maSlideNameList.size() ); i++ ) + { + SdPage* pPage = mpDoc->GetSdPage( i, PageKind::Standard ); + OUString &aName = maSlideNameList[ i ]; + if ( pPage ) + { + if ( !aName.isEmpty() ) + pPage->SetName( aName ); + else + aName = pPage->GetName(); + } + } + if ( mbDocumentFound ) + { + mpDoc->SetSummationOfParagraphs(); + if ( pDocShell ) + { + ::sd::FrameView* pFrameView = mpDoc->GetFrameView( 0 ); + if ( !pFrameView ) + { + std::vector<std::unique_ptr<sd::FrameView>> &rViews = mpDoc->GetFrameViewList(); + pFrameView = new ::sd::FrameView( mpDoc ); + rViews.push_back( std::unique_ptr<sd::FrameView>(pFrameView) ); + } + sal_uInt16 nSelectedPage = 0; + PageKind ePageKind = PageKind::Standard; + EditMode eEditMode = EditMode::Page; + + switch ( m_aUserEditAtom.eLastViewType ) + { + case PptViewTypeEnum::Outline: + { + SfxItemSet* pSet = mrMed.GetItemSet(); + if ( pSet ) + pSet->Put( SfxUInt16Item( SID_VIEW_ID, 3 ) ); + } + break; + case PptViewTypeEnum::SlideSorter: + { + SfxItemSet* pSet = mrMed.GetItemSet(); + if ( pSet ) + pSet->Put( SfxUInt16Item( SID_VIEW_ID, 2 ) ); + } + break; + case PptViewTypeEnum::TitleMaster: + nSelectedPage = 1; + [[fallthrough]]; + case PptViewTypeEnum::SlideMaster: + { + ePageKind = PageKind::Standard; + eEditMode = EditMode::MasterPage; + } + break; + case PptViewTypeEnum::NotesMaster: + eEditMode = EditMode::MasterPage; + [[fallthrough]]; + case PptViewTypeEnum::Notes: + ePageKind = PageKind::Notes; + break; + case PptViewTypeEnum::Handout: + ePageKind = PageKind::Handout; + break; + default : + case PptViewTypeEnum::Slide: + break; + } + pFrameView->SetPageKind( ePageKind ); + pFrameView->SetSelectedPage( nSelectedPage ); + pFrameView->SetViewShEditMode( eEditMode ); + } + DffRecordHeader aCustomShowHeader; + // read and set custom show + rStCtrl.Seek( maDocHd.GetRecBegFilePos() + 8 ); + if ( SeekToRec( rStCtrl, PPT_PST_NamedShows, maDocHd.GetRecEndFilePos(), &aCustomShowHeader ) ) + { + DffRecordHeader aCuHeader; + while( SeekToRec( rStCtrl, PPT_PST_NamedShow, aCustomShowHeader.GetRecEndFilePos(), &aCuHeader ) ) + { + DffRecordHeader aContent; + if ( SeekToRec( rStCtrl, PPT_PST_CString, aCuHeader.GetRecEndFilePos(), &aContent ) ) + { + OUString aCuShow; + aContent.SeekToBegOfRecord( rStCtrl ); + if ( ReadString( aCuShow ) ) + { + if ( SeekToRec( rStCtrl, PPT_PST_NamedShowSlides, aCuHeader.GetRecEndFilePos(), &aContent ) ) + { + PptSlidePersistList* pPageList = GetPageList( PPT_SLIDEPAGE ); + const auto nRemainingSize = rStCtrl.remainingSize(); + sal_uInt32 nBCount = aContent.nRecLen; + if (nBCount > nRemainingSize) + { + SAL_WARN("filter.ms", "page number data len longer than remaining stream size"); + nBCount = nRemainingSize; + } + sal_uInt32 nSCount = nBCount >> 2; + + if ( pPageList && nSCount ) + { + SdCustomShowList* pList = mpDoc->GetCustomShowList( true ); + if ( pList ) + { + std::unique_ptr<SdCustomShow> pSdCustomShow(new SdCustomShow); + pSdCustomShow->SetName( aCuShow ); + sal_uInt32 nFound = 0; + for ( sal_uInt32 nS = 0; nS < nSCount; nS++ ) + { + sal_uInt32 nPageNumber; + rStCtrl.ReadUInt32( nPageNumber ); + sal_uInt16 nPage = pPageList->FindPage( nPageNumber ); + if ( nPage != PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + { + SdPage* pPage = mpDoc->GetSdPage( nPage, PageKind::Standard ); + if ( pPage ) + { + pSdCustomShow->PagesVector().push_back( pPage ); + nFound++; + } + } + } + if ( nFound ) + pList->push_back( std::move(pSdCustomShow) ); + } + } + } + } + } + } + } + // this is defaulted, maybe there is no SSDocInfoAtom + OUStringBuffer aCustomShow; + sal_uInt32 nFlags = 1; // Bit 0: Auto advance + sal_uInt16 nStartSlide = 0; + + // read the pres. configuration + rStCtrl.Seek( maDocHd.GetRecBegFilePos() + 8 ); + if ( SeekToRec( rStCtrl, PPT_PST_SSDocInfoAtom, maDocHd.GetRecEndFilePos(), &aCustomShowHeader ) ) + { + sal_uInt32 nPenColor = 0x1000000; + sal_Int32 nRestartTime = 0x7fffffff; + sal_Int16 nEndSlide = 0; + rStCtrl.ReadUInt32( nPenColor ) + .ReadInt32( nRestartTime ) + .ReadUInt16( nStartSlide ) + .ReadInt16( nEndSlide ); + + sal_Unicode nChar; + for ( sal_uInt32 i2 = 0; i2 < 32; i2++ ) + { + rStCtrl.ReadUtf16( nChar ); + if ( nChar ) + aCustomShow.append( nChar ); + else + { + rStCtrl.SeekRel( ( 31 - i2 ) << 1 ); + break; + } + } + rStCtrl.ReadUInt32( nFlags ); + } + // set the current custom show + if ( !aCustomShow.isEmpty() ) + { + SdCustomShowList* pList = mpDoc->GetCustomShowList(); + if ( pList ) + { + SdCustomShow* pPtr = nullptr; + OUString aCustomShowStr = aCustomShow.makeStringAndClear(); + for( pPtr = pList->First(); pPtr; pPtr = pList->Next() ) + { + if ( pPtr->GetName() == aCustomShowStr ) + break; + } + if ( !pPtr ) + pList->First(); + } + } + sd::PresentationSettings& rPresSettings = mpDoc->getPresentationSettings(); + + rPresSettings.mbManual = ( nFlags & 1 ) == 0; + rPresSettings.mbAnimationAllowed = ( nFlags & 2 ) == 0; + rPresSettings.mbAll = ( nFlags & 4 ) == 0; + rPresSettings.mbCustomShow = ( nFlags & 8 ) != 0; + rPresSettings.mbEndless = ( nFlags & 0x80 ) != 0; + rPresSettings.mbFullScreen = ( nFlags & 0x10 ) == 0; + + if ( nStartSlide && ( nStartSlide <= GetPageCount() ) ) + { + SdPage* pPage = mpDoc->GetSdPage( nStartSlide - 1, PageKind::Standard ); + if ( pPage ) + rPresSettings.maPresPage = pPage->GetName(); + } + } + + xStbMgr.reset(); + + // read DocumentProperties + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + mpDoc->GetObjectShell()->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + sfx2::LoadOlePropertySet(xDocProps, &mrStorage); + xDocProps->setTemplateName(OUString()); + + pSdrModel->setLock(bWasLocked); + pSdrModel->EnableUndo(bSavedUndoEnabled); + return m_bOk; +} + +void ImplSdPPTImport::SetHeaderFooterPageSettings( SdPage* pPage, const PptSlidePersistEntry* pMasterPersist ) +{ + sal_uInt32 i; + PptSlidePersistList* pList = GetPageList( m_eCurrentPageKind ); + if ( ( !pList ) || ( pList->size() <= m_nCurrentPageNum ) ) + return; + PptSlidePersistEntry& rSlidePersist = (*pList)[ m_nCurrentPageNum ]; + HeaderFooterEntry* pHFE = rSlidePersist.xHeaderFooterEntry.get(); + if (!pHFE) + return; + + for ( i = 0; i < 4; i++ ) + { + bool bVisible = pHFE->IsToDisplay( i ); + if ( ( m_eCurrentPageKind == PPT_SLIDEPAGE ) + && ( rSlidePersist.aSlideAtom.aLayout.eLayout == PptSlideLayout::TITLESLIDE ) + && ( aDocAtom.bTitlePlaceholdersOmitted ) ) + { + bVisible = false; + } + if ( bVisible && pMasterPersist ) + { + sal_uInt32 nPosition = pHFE->NeedToImportInstance( i, rSlidePersist ); + if ( nPosition ) + { + ::tools::Rectangle aEmpty; + bVisible = false; + rStCtrl.Seek( nPosition ); + ProcessData aProcessData( rSlidePersist, SdPageCapsule(pPage) ); + SdrObject* pObj = ImportObj( rStCtrl, aProcessData, aEmpty, aEmpty, /*nCalledByGroup*/0, /*pShapeId*/nullptr ); + if ( pObj ) + pPage->NbcInsertObject( pObj, 0 ); + } + } + OUString aPlaceHolderString = pHFE->pPlaceholder[ i ]; + + sd::HeaderFooterSettings rHeaderFooterSettings( pPage->getHeaderFooterSettings() ); + switch( i ) + { + case 0 : + { + rHeaderFooterSettings.mbDateTimeVisible = bVisible; + rHeaderFooterSettings.mbDateTimeIsFixed = ( pHFE->nAtom & 0x20000 ) == 0; + rHeaderFooterSettings.maDateTimeText = aPlaceHolderString; + SvxDateFormat eDateFormat; + SvxTimeFormat eTimeFormat; + PPTFieldEntry::GetDateTime( pHFE->nAtom & 0xff, eDateFormat, eTimeFormat ); + rHeaderFooterSettings.meDateFormat = eDateFormat; + rHeaderFooterSettings.meTimeFormat = eTimeFormat; + } + break; + case 1 : + { + rHeaderFooterSettings.mbHeaderVisible = bVisible; + rHeaderFooterSettings.maHeaderText = aPlaceHolderString; + } + break; + case 2 : + { + rHeaderFooterSettings.mbFooterVisible = bVisible; + rHeaderFooterSettings.maFooterText = aPlaceHolderString; + } + break; + case 3 : + { + rHeaderFooterSettings.mbSlideNumberVisible = bVisible; + } + break; + } + pPage->setHeaderFooterSettings( rHeaderFooterSettings ); + } +} + +namespace { + +// Import of pages +struct Ppt97AnimationStlSortHelper +{ + bool operator()( const std::pair< SdrObject*, Ppt97AnimationPtr >& p1, const std::pair< SdrObject*, Ppt97AnimationPtr >& p2 ); +}; + +} + +bool Ppt97AnimationStlSortHelper::operator()( const std::pair< SdrObject*, Ppt97AnimationPtr >& p1, const std::pair< SdrObject*, Ppt97AnimationPtr >& p2 ) +{ + if( !p1.second || !p2.second ) + return p1.second.get() < p2.second.get(); + if( *p1.second < *p2.second ) + return true; + if( *p1.second > *p2.second ) + return false; + return p1.first->GetOrdNum() < p2.first->GetOrdNum(); +} + +void ImplSdPPTImport::ImportPageEffect( SdPage* pPage, const bool bNewAnimationsUsed ) +{ + sal_uInt64 nOldFilePos = rStCtrl.Tell(); + + // set PageKind at page (up to now only PageKind::Standard or PageKind::Notes) + if ( pPage->GetPageKind() == PageKind::Standard ) + { + PptSlidePersistList* pPersistList = GetPageList( m_eCurrentPageKind ); + PptSlidePersistEntry* pActualSlidePersist = ( pPersistList && ( m_nCurrentPageNum < pPersistList->size() ) ) + ? &(*pPersistList)[ m_nCurrentPageNum ] : nullptr; + + if ( pActualSlidePersist && ( m_eCurrentPageKind == PPT_SLIDEPAGE ) ) + { + if ( ! ( pActualSlidePersist->aSlideAtom.nFlags & 1 ) ) // do not follow master objects ? + { + if(pPage->TRG_HasMasterPage()) + { + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aVisibleLayers.Set(mnBackgroundObjectsLayerID, false); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + } + } + DffRecordHeader aPageRecHd; + if ( SeekToCurrentPage( &aPageRecHd ) ) + { + sal_uLong nPageRecEnd = SanitizeEndPos(rStCtrl, aPageRecHd.GetRecEndFilePos()); + + bool bTryTwice = ( m_eCurrentPageKind == PPT_SLIDEPAGE ); + bool bSSSlideInfoAtom = false; + while ( true ) + { + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nPageRecEnd ) ) + { + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + switch ( aHd.nRecType ) + { + case PPT_PST_SSSlideInfoAtom: + { + bSSSlideInfoAtom = true; + if ( m_eCurrentPageKind == PPT_MASTERPAGE ) + { + if ( pActualSlidePersist ) + pActualSlidePersist->aPersistAtom.nReserved = aHd.GetRecBegFilePos(); + } + else + { + sal_Int8 nDirection, nTransitionType, nByteDummy, nSpeed; + sal_Int16 nBuildFlags; + sal_Int32 nSlideTime, nSoundRef; + rStCtrl.ReadInt32( nSlideTime ) // time to show (in Ticks) + .ReadInt32( nSoundRef ) // Index of SoundCollection + .ReadSChar( nDirection ) // direction of fade effect + .ReadSChar( nTransitionType ) // fade effect + .ReadInt16( nBuildFlags ) // Buildflags (s.u.) + .ReadSChar( nSpeed ) // speed (slow, medium, fast) + .ReadSChar( nByteDummy ).ReadSChar( nByteDummy ).ReadSChar( nByteDummy ); + + switch ( nTransitionType ) + { + case PPT_TRANSITION_TYPE_BLINDS : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_VERTICAL_STRIPES ); // fade vertical + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_HORIZONTAL_STRIPES ); // fade horizontal + } + break; + case PPT_TRANSITION_TYPE_CHECKER : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_HORIZONTAL_CHECKERBOARD ); // fade vertical with offset ?? + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_VERTICAL_CHECKERBOARD ); // fade horizontal with offset ?? + } + break; + case PPT_TRANSITION_TYPE_COVER : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_RIGHT ); // overlay from right + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_BOTTOM ); // overlay from bottom + else if ( nDirection == 2 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_LEFT ); // overlay from left + else if ( nDirection == 3 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_TOP ); // overlay from top + else if ( nDirection == 4 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_LOWERRIGHT ); // overlay from bottom right ?? + else if ( nDirection == 5 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_LOWERLEFT ); // overlay from bottom left ?? + else if ( nDirection == 6 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_UPPERRIGHT ); // overlay from top right + else if ( nDirection == 7 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_UPPERLEFT ); // overlay from top left ?? + } + break; + case PPT_TRANSITION_TYPE_NONE : + { + if ( nBuildFlags ) + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_NONE ); // direct + else if ( nDirection == 1 ) + { + pPage->setTransitionType( animations::TransitionType::BARWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::FADEOVERCOLOR ); + pPage->setTransitionFadeColor( 0 ); + } + } + else + pPage->setTransitionType( 0 ); + } + break; + case PPT_TRANSITION_TYPE_DISSOLVE : + pPage->SetFadeEffect(css::presentation::FadeEffect_DISSOLVE); // dissolve + break; + case PPT_TRANSITION_TYPE_RANDOM_BARS : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_HORIZONTAL_LINES ); // horizontal lines + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_VERTICAL_LINES ); // vertical lines + } + break; + case PPT_TRANSITION_TYPE_SPLIT : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_OPEN_VERTICAL ); // open horizontal ?? + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_CLOSE_VERTICAL ); // close horizontal ?? + else if ( nDirection == 2 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_OPEN_HORIZONTAL ); // open vertical ?? + else if ( nDirection == 3 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_CLOSE_HORIZONTAL ); // close vertical ?? + } + break; + case PPT_TRANSITION_TYPE_STRIPS : + { + if ( nDirection == 4 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_LOWERRIGHT ); // diagonal to top left + else if ( nDirection == 5 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_LOWERLEFT ); // diagonal to top right + else if ( nDirection == 6 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_UPPERRIGHT ); // diagonal to bottom left + else if ( nDirection == 7 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_UPPERLEFT ); // diagonal to bottom right + } + break; + case PPT_TRANSITION_TYPE_PULL : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_LEFT ); // uncover to left + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_TOP ); // uncover to top + else if ( nDirection == 2 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_RIGHT ); // uncover to right + else if ( nDirection == 3 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_BOTTOM ); // uncover to bottom + else if ( nDirection == 4 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_UPPERLEFT ); // uncover to top left + else if ( nDirection == 5 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_UPPERRIGHT ); // uncover to top right + else if ( nDirection == 6 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_LOWERLEFT ); // uncover to bottom left + else if ( nDirection == 7 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_LOWERRIGHT ); // uncover to bottom right + } + break; + case PPT_TRANSITION_TYPE_WIPE : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_RIGHT ); // roll from right + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_BOTTOM ); // roll from bottom + else if ( nDirection == 2 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_LEFT ); // roll from left + else if ( nDirection == 3 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_TOP ); // roll from top + } + break; + case PPT_TRANSITION_TYPE_RANDOM : + pPage->SetFadeEffect( css::presentation::FadeEffect_RANDOM ); // automatic + break; + case PPT_TRANSITION_TYPE_FADE : + { + pPage->setTransitionType( animations::TransitionType::FADE ); + pPage->setTransitionSubtype( animations::TransitionSubType::FADEOVERCOLOR ); + pPage->setTransitionFadeColor( 0 ); + } + break; + case PPT_TRANSITION_TYPE_ZOOM : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_CENTER ); // fade from center + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_TO_CENTER ); // fade from the outside + } + break; + case PPT_TRANSITION_TYPE_DIAMOND : + { + pPage->setTransitionType( animations::TransitionType::IRISWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::DIAMOND ); + } + break; + case PPT_TRANSITION_TYPE_PLUS : + { + pPage->setTransitionType( animations::TransitionType::FOURBOXWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::CORNERSOUT ); + } + break; + case PPT_TRANSITION_TYPE_CIRCLE : + { + pPage->setTransitionType( animations::TransitionType::ELLIPSEWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::CIRCLE ); + } + break; + case PPT_TRANSITION_TYPE_WEDGE : + { + pPage->setTransitionType( animations::TransitionType::FANWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::CENTERTOP ); + } + break; + case PPT_TRANSITION_TYPE_WHEEL : + { + pPage->setTransitionType( animations::TransitionType::PINWHEELWIPE ); + sal_Int16 nSubType; + switch( nDirection ) + { + default: + case 1 : nSubType = animations::TransitionSubType::ONEBLADE; break; + case 2 : nSubType = animations::TransitionSubType::TWOBLADEVERTICAL; break; + case 3 : nSubType = animations::TransitionSubType::THREEBLADE; break; + case 4 : nSubType = animations::TransitionSubType::FOURBLADE; break; + case 8 : nSubType = animations::TransitionSubType::EIGHTBLADE; break; + } + pPage->setTransitionSubtype( nSubType ); + } + break; + case PPT_TRANSITION_TYPE_PUSH : + { + pPage->setTransitionType( animations::TransitionType::PUSHWIPE ); + sal_Int16 nSubType; + switch( nDirection ) + { + default: + case 0 : nSubType = animations::TransitionSubType::FROMRIGHT; break; + case 1 : nSubType = animations::TransitionSubType::FROMBOTTOM; break; + case 2 : nSubType = animations::TransitionSubType::FROMLEFT; break; + case 3 : nSubType = animations::TransitionSubType::FROMTOP; break; + } + pPage->setTransitionSubtype( nSubType ); + } + break; + case PPT_TRANSITION_TYPE_COMB : + { + pPage->setTransitionType( animations::TransitionType::PUSHWIPE ); + pPage->setTransitionSubtype( nDirection ? animations::TransitionSubType::COMBVERTICAL : animations::TransitionSubType::COMBHORIZONTAL ); + } + break; + case PPT_TRANSITION_TYPE_NEWSFLASH : + { + pPage->setTransitionType( animations::TransitionType::ZOOM ); + pPage->setTransitionSubtype( animations::TransitionSubType::ROTATEIN ); + } + break; + case PPT_TRANSITION_TYPE_SMOOTHFADE : + { + pPage->setTransitionType( animations::TransitionType::FADE ); + pPage->setTransitionSubtype( animations::TransitionSubType::CROSSFADE ); + } + break; + } + + if ( nSpeed == 0 ) + pPage->setTransitionDuration( 1.0 ); // slow + else if ( nSpeed == 1 ) + pPage->setTransitionDuration( 0.75 ); // medium + else if ( nSpeed == 2 ) + pPage->setTransitionDuration( 0.5 ); // fast + + if ( nBuildFlags & 0x400 ) // slidechange by time + { // time to show (in Ticks) + pPage->SetPresChange( PresChange::Auto ); + pPage->SetTime( nSlideTime / 1000.0 ); + } + else + pPage->SetPresChange( mePresChange ); + + if ( nBuildFlags & 4 ) + pPage->SetExcluded( true ); // don't show slide + if ( nBuildFlags & 16 ) + { // slide with sound effect + pPage->SetSound( true ); + OUString aSoundFile( ReadSound( nSoundRef ) ); + pPage->SetSoundFile( aSoundFile ); + } + if ( nBuildFlags & ( 1 << 6 ) ) // Loop until next sound + pPage->SetLoopSound( true ); + if ( nBuildFlags & ( 1 << 8 ) ) // Stop the previous sound + pPage->SetStopSound( true ); + break; + } + } + } + if (!aHd.SeekToEndOfRecord(rStCtrl)) + break; + } + if ( bTryTwice && !bSSSlideInfoAtom ) + { + bTryTwice = false; + if ( HasMasterPage( m_nCurrentPageNum, m_eCurrentPageKind ) ) + { + sal_uInt16 nMasterNum = GetMasterPageIndex( m_nCurrentPageNum, m_eCurrentPageKind ); + PptSlidePersistList* pPageList = GetPageList( PPT_MASTERPAGE ); + if ( pPageList && ( nMasterNum < pPageList->size() ) ) + { + assert( !pPageList->is_null( nMasterNum ) ); + const PptSlidePersistEntry& rE = (*pPageList)[ nMasterNum ]; + sal_uInt32 nOfs = rE.aPersistAtom.nReserved; + if ( nOfs ) + { + rStCtrl.Seek( nOfs ); + nPageRecEnd = nOfs + 16; + continue; + } + } + + } + } + break; + } + } + } + + if ( !bNewAnimationsUsed ) + { + std::vector< std::pair< SdrObject*, Ppt97AnimationPtr > > aAnimationsOnThisPage; + + // add effects from page in correct order + SdrObjListIter aSdrIter( pPage, SdrIterMode::Flat ); + while ( aSdrIter.IsMore() ) + { + SdrObject* pObj = aSdrIter.Next(); + tAnimationMap::iterator aFound = maAnimations.find( pObj ); + if( aFound != maAnimations.end() ) + { + std::pair< SdrObject*, Ppt97AnimationPtr > aPair( (*aFound).first, (*aFound).second ); + aAnimationsOnThisPage.push_back( aPair ); + } + } + + std::sort( aAnimationsOnThisPage.begin(), aAnimationsOnThisPage.end(), Ppt97AnimationStlSortHelper() ); + + for( auto& rEntry : aAnimationsOnThisPage ) + { + Ppt97AnimationPtr pPpt97Animation = rEntry.second; + if( pPpt97Animation ) + pPpt97Animation->createAndSetCustomAnimationEffect( rEntry.first ); + } + } + rStCtrl.Seek( nOldFilePos ); +} + +// import of sounds + +// Not only the sounds are imported as string, they are also inserted to +// the gallery if they are not already there. +OUString ImplSdPPTImport::ReadSound(sal_uInt32 nSoundRef) const +{ + OUString aRetval; + sal_uInt32 nOldPos = rStCtrl.Tell(); + DffRecordHeader aDocHd; + if ( SeekToDocument( &aDocHd ) ) + { + sal_uInt32 nSoundLen = aDocHd.GetRecEndFilePos(); + DffRecordHeader aSoundBlockRecHd; + if( SeekToRec( rStCtrl, PPT_PST_SoundCollection, nSoundLen, &aSoundBlockRecHd ) ) + { + sal_uInt32 nDataLen = aSoundBlockRecHd.GetRecEndFilePos(); + DffRecordHeader aSoundRecHd; + bool bRefStrValid = false; + bool bDone = false; + + while( !bDone && SeekToRec( rStCtrl, PPT_PST_Sound, nDataLen, &aSoundRecHd ) ) + { + sal_uInt32 nStrLen = aSoundRecHd.GetRecEndFilePos(); + OUString aRefStr; + sal_uInt32 nOldPos2 = rStCtrl.Tell(); + if ( SeekToRec( rStCtrl, PPT_PST_CString, nStrLen, nullptr, 2 ) ) + { + if ( ReadString( aRefStr ) ) + bRefStrValid = true; + } + if ( bRefStrValid ) + { + if ( std::u16string_view(OUString::number(nSoundRef)) == aRefStr ) + { + rStCtrl.Seek( nOldPos2 ); + if ( SeekToRec( rStCtrl, PPT_PST_CString, nStrLen ) ) + { + ReadString( aRetval ); + bDone = true; + } + } + } + if ( bDone ) + { + // Check if this sound file already exists. + // If not, it is exported to our local sound directory. + bool bSoundExists = false; + ::std::vector< OUString > aSoundList; + + GalleryExplorer::FillObjList( GALLERY_THEME_SOUNDS, aSoundList ); + GalleryExplorer::FillObjList( GALLERY_THEME_USERSOUNDS, aSoundList ); + + for( size_t n = 0; ( n < aSoundList.size() ) && !bSoundExists; ++n ) + { + INetURLObject aURL( aSoundList[ n ] ); + + if (aURL.GetLastName() == aRetval) + { + aRetval = aSoundList[ n ]; + bSoundExists = true; + } + } + + aSoundList.clear(); + + if ( !bSoundExists ) + { + rStCtrl.Seek( nOldPos2 ); + DffRecordHeader aSoundDataRecHd; + if ( SeekToRec( rStCtrl, PPT_PST_SoundData, nStrLen, &aSoundDataRecHd ) ) + { + OUString aGalleryDir; + if (utl::ConfigManager::IsFuzzing()) + osl_getTempDirURL(&aGalleryDir.pData); + else + aGalleryDir = SvtPathOptions().GetGalleryPath(); + // Use last token delimited by ';'. copy(lastIndexOf+1) works whether + // string is empty or not and whether ';' is there or not. + INetURLObject aGalleryUserSound( aGalleryDir.subView(aGalleryDir.lastIndexOf(';')+1) ); + + aGalleryUserSound.Append( aRetval ); + const auto nRemainingSize = rStCtrl.remainingSize(); + sal_uInt32 nSoundDataLen = aSoundDataRecHd.nRecLen; + if (nSoundDataLen > nRemainingSize) + { + SAL_WARN("filter.ms", "sound data len longer than remaining stream size"); + nSoundDataLen = nRemainingSize; + } + std::vector<sal_uInt8> aBuf(nSoundDataLen); + + rStCtrl.ReadBytes(aBuf.data(), nSoundDataLen); + std::unique_ptr<SvStream> pOStm = ::utl::UcbStreamHelper::CreateStream( aGalleryUserSound.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::TRUNC ); + + if( pOStm ) + { + pOStm->WriteBytes(aBuf.data(), nSoundDataLen); + + if( pOStm->GetError() == ERRCODE_NONE ) + { + GalleryExplorer::InsertURL( GALLERY_THEME_USERSOUNDS, aGalleryUserSound.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + aRetval = aGalleryUserSound.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + } + } + } + } + if ( !bDone ) + { + if (!aSoundRecHd.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + } + rStCtrl.Seek( nOldPos ); + return aRetval; +} + +// media object import, the return value is the url to the media object +OUString ImplSdPPTImport::ReadMedia( sal_uInt32 nMediaRef ) const +{ + OUString aRetVal; + DffRecordHeader* pHd( const_cast<ImplSdPPTImport*>(this)->aDocRecManager.GetRecordHeader( PPT_PST_ExObjList ) ); + if ( pHd ) + { + pHd->SeekToContent( rStCtrl ); + auto nEndRecPos = SanitizeEndPos(rStCtrl, pHd->GetRecEndFilePos()); + while ( ( rStCtrl.Tell() < nEndRecPos ) && aRetVal.isEmpty() ) + { + DffRecordHeader aHdMovie; + ReadDffRecordHeader( rStCtrl, aHdMovie ); + switch( aHdMovie.nRecType ) + { + case PPT_PST_ExAviMovie : + case PPT_PST_ExMCIMovie : + { + DffRecordHeader aExVideoHd; + if ( SeekToRec( rStCtrl, PPT_PST_ExVideo, aHdMovie.GetRecEndFilePos(), &aExVideoHd ) ) + { + DffRecordHeader aExMediaAtomHd; + if ( SeekToRec( rStCtrl, PPT_PST_ExMediaAtom, aExVideoHd.GetRecEndFilePos(), &aExMediaAtomHd ) ) + { + sal_uInt32 nRef; + rStCtrl.ReadUInt32( nRef ); + if ( nRef == nMediaRef ) + { + aExVideoHd.SeekToContent( rStCtrl ); + auto nHdEndRecPos = SanitizeEndPos(rStCtrl, aExVideoHd.GetRecEndFilePos()); + while (rStCtrl.Tell() < nHdEndRecPos) + { + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + switch( aHd.nRecType ) + { + case PPT_PST_CString : + { + aHd.SeekToBegOfRecord( rStCtrl ); + OUString aStr; + if ( ReadString( aStr ) ) + { + if( osl::FileBase::getFileURLFromSystemPath( aStr, aRetVal ) + == osl::FileBase::E_None ) + { + aRetVal = INetURLObject( aRetVal ).GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ); + }else{ + aRetVal = aStr; + } + } + } + break; + } + if (!aHd.SeekToEndOfRecord(rStCtrl)) + break; + } + break; + } + } + } + } + break; + } + if (!aHdMovie.SeekToEndOfRecord(rStCtrl)) + break; + } + } + return aRetVal; +} + +// import of objects +void ImplSdPPTImport::FillSdAnimationInfo( SdAnimationInfo* pInfo, PptInteractiveInfoAtom const * pIAtom, const OUString& aMacroName ) +{ + // set local information into pInfo + if( pIAtom->nSoundRef ) + { + pInfo->SetBookmark( ReadSound( pIAtom->nSoundRef ) ); // path to sound file in MS DOS notation + pInfo->meClickAction = css::presentation::ClickAction_SOUND; // RunProgramAction + } + + switch ( pIAtom->nAction ) + { + + case 0x02 : // RunProgramAction + { + pInfo->meClickAction = css::presentation::ClickAction_PROGRAM; + pInfo->SetBookmark( aMacroName ); // program name in aBookmark + } + break; + case 0x03 : // JumpAction + { + switch( pIAtom->nJump ) + { + case 0x01 : + pInfo->meClickAction = css::presentation::ClickAction_NEXTPAGE; // Next slide + break; + case 0x02 : + pInfo->meClickAction = css::presentation::ClickAction_PREVPAGE; // Previous slide + break; + case 0x03 : + pInfo->meClickAction = css::presentation::ClickAction_FIRSTPAGE; // First slide + break; + case 0x04 : + pInfo->meClickAction = css::presentation::ClickAction_LASTPAGE; // last Slide + break; + case 0x05 : + pInfo->meClickAction = css::presentation::ClickAction_PREVPAGE; // Last slide viewed + break; + case 0x06 : + pInfo->meClickAction = css::presentation::ClickAction_STOPPRESENTATION; // End show + break; + default : + pInfo->meClickAction = css::presentation::ClickAction_NONE; // 0x00: no action, else unknown + break; + } + } + break; + case 0x04 : + { + SdHyperlinkEntry* pPtr = nullptr; + for (SdHyperlinkEntry & entry : m_aHyperList) { + if ( entry.nIndex == pIAtom->nExHyperlinkId ) { + pPtr = &entry; + break; + } + } + if ( pPtr ) + { + switch( pIAtom->nHyperlinkType ) + { + case 9: + case 8: // hyperlink : URL + { + if ( !pPtr->aTarget.isEmpty() ) + { + ::sd::DrawDocShell* pDocShell = mpDoc->GetDocSh(); + SfxMedium* pMedium = pDocShell ? pDocShell->GetMedium() : nullptr; + if (pMedium) + { + OUString aBaseURL = pMedium->GetBaseURL(); + OUString aBookmarkURL( pInfo->GetBookmark() ); + INetURLObject aURL( pPtr->aTarget ); + if( INetProtocol::NotValid == aURL.GetProtocol() + && (osl::FileBase::getFileURLFromSystemPath( + pPtr->aTarget, aBookmarkURL) + != osl::FileBase::E_None) ) + aBookmarkURL.clear(); + if( aBookmarkURL.isEmpty() ) + aBookmarkURL = URIHelper::SmartRel2Abs( INetURLObject(aBaseURL), pPtr->aTarget, URIHelper::GetMaybeFileHdl() ); + pInfo->SetBookmark( aBookmarkURL ); + pInfo->meClickAction = css::presentation::ClickAction_PROGRAM; + } + } + } + break; + + case 10: + break; + + case 7: // hyperlink to a page + { + if ( !pPtr->aConvSubString.isEmpty() ) + { + pInfo->meClickAction = css::presentation::ClickAction_BOOKMARK; + pInfo->SetBookmark( pPtr->aConvSubString ); + } + } + break; + } + } + } + break; + case 0x05 : // OLEAction ( OLEVerb to use, 0==first, 1==second, .. ) + case 0x06 : // MediaAction + case 0x07 : // CustomShowAction + default : // 0x00: no action, else unknown action + break; + } +} + +SdrObject* ImplSdPPTImport::ApplyTextObj( PPTTextObj* pTextObj, SdrTextObj* pObj, SdPageCapsule pPageCapsule, + SfxStyleSheet* pSheet, SfxStyleSheet** ppStyleSheetAry ) const +{ + SdPage * pPage = static_cast<SdPage *>(pPageCapsule.page); + SfxStyleSheet* pStyleSheetAry[ 9 ]; + SdrTextObj* pText = pObj; + SdrObject* pRet = pText; + + ppStyleSheetAry = nullptr; + + PresObjKind ePresKind = PresObjKind::NONE; + PptOEPlaceholderAtom* pPlaceHolder = pTextObj->GetOEPlaceHolderAtom(); + OUString aPresentationText; + if ( pPlaceHolder ) + { + switch( pPlaceHolder->nPlaceholderId ) + { + case PptPlaceholder::MASTERNOTESSLIDEIMAGE : + case PptPlaceholder::MASTERCENTEREDTITLE : + case PptPlaceholder::MASTERTITLE : + { + ePresKind = PresObjKind::Title; + aPresentationText = pPage->GetPresObjText( ePresKind ); + } + break; + case PptPlaceholder::MASTERBODY : + { + ePresKind = PresObjKind::Outline; + aPresentationText = pPage->GetPresObjText( ePresKind ); + } + break; + case PptPlaceholder::MASTERSUBTITLE : + { + ePresKind = PresObjKind::Text; + aPresentationText = pPage->GetPresObjText( ePresKind ); + } + break; + case PptPlaceholder::MASTERNOTESBODYIMAGE : + { + ePresKind = PresObjKind::Notes; + aPresentationText = pPage->GetPresObjText( ePresKind ); + } + break; + case PptPlaceholder::MASTERDATE : ePresKind = PresObjKind::DateTime; break; + case PptPlaceholder::MASTERSLIDENUMBER : ePresKind = PresObjKind::SlideNumber;break; + case PptPlaceholder::MASTERFOOTER : ePresKind = PresObjKind::Footer; break; + case PptPlaceholder::MASTERHEADER : ePresKind = PresObjKind::Header; break; + default: break; + } + } + switch ( pTextObj->GetDestinationInstance() ) + { + case TSS_Type::PageTitle : + case TSS_Type::Title : + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + if ( pSheet ) + static_cast<SdrAttrObj*>(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for titleobject (SJ)" ); + } + break; + case TSS_Type::Subtitle : + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Text ); + if ( pSheet ) + static_cast<SdrAttrObj*>(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for subtitleobject (SJ)" ); + } + break; + case TSS_Type::Body : + case TSS_Type::HalfBody : + case TSS_Type::QuarterBody : + { + for ( sal_uInt16 nLevel = 9; nLevel; nLevel-- ) + { + OUString aName = pPage->GetLayoutName() + " " + OUString::number( nLevel ); + pSheet = static_cast<SfxStyleSheet*>(mpDoc->GetStyleSheetPool()->Find( aName, SfxStyleFamily::Page )); + if ( pSheet ) + pText->StartListening( *pSheet ); + pStyleSheetAry[ nLevel - 1 ] = pSheet; + } + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for outlinerobject (SJ)" ); + if ( pSheet ) + static_cast<SdrAttrObj*>(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + ppStyleSheetAry = &pStyleSheetAry[ 0 ]; + } + break; + case TSS_Type::Notes : + { + if ( pPlaceHolder && ( ( pPlaceHolder->nPlaceholderId == PptPlaceholder::NOTESSLIDEIMAGE ) + || ( pPlaceHolder->nPlaceholderId == PptPlaceholder::MASTERNOTESSLIDEIMAGE ) ) ) + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + if ( pSheet ) + static_cast<SdrAttrObj*>(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for titleobject (SJ)" ); + } + else + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Notes ); + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for notesobj (SJ)" ); + if ( pSheet ) + static_cast<SdrAttrObj*>(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + } + } + break; + case TSS_Type::Unused : + case TSS_Type::TextInShape : + { + switch( ePresKind ) + { + case PresObjKind::DateTime : + case PresObjKind::SlideNumber : + case PresObjKind::Footer : + case PresObjKind::Header : + pSheet = static_cast<SfxStyleSheet*>(mpDoc->GetStyleSheetPool()->Find(SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS), SfxStyleFamily::Pseudo )); + break; + default : + pSheet = static_cast<SfxStyleSheet*>(mpDoc->GetStyleSheetPool()->Find(SdResId(STR_STANDARD_STYLESHEET_NAME), SfxStyleFamily::Para )); + } + } + break; + default: break; + } + + pText = static_cast<SdrTextObj*>(SdrPowerPointImport::ApplyTextObj( pTextObj, pText, pPageCapsule, pSheet, ppStyleSheetAry )); + + if ( pPlaceHolder && pPlaceHolder->nPlaceholderId != PptPlaceholder::NONE ) + { + if ( m_eCurrentPageKind == PPT_MASTERPAGE ) + { + bool bCreatePlaceHolder = ( pTextObj->GetInstance() != TSS_Type::Unused ); + bool bIsHeaderFooter = ( ePresKind == PresObjKind::Header) || (ePresKind == PresObjKind::Footer) + || (ePresKind == PresObjKind::DateTime) || (ePresKind == PresObjKind::SlideNumber); + if ( bCreatePlaceHolder && ( pTextObj->GetInstance() == TSS_Type::TextInShape ) ) + bCreatePlaceHolder = bIsHeaderFooter; + if ( bCreatePlaceHolder ) + { + if ( !bIsHeaderFooter ) + { + pText->SetNotVisibleAsMaster( true ); + pText->SetEmptyPresObj( true ); + } + pText->SetUserCall( pPage ); + pPage->InsertPresObj( pText, ePresKind ); + SdrOutliner* pOutl = nullptr; + if ( pTextObj->GetInstance() == TSS_Type::Notes ) + pOutl = GetDrawOutliner( pText ); + if ( !aPresentationText.isEmpty() ) + pPage->SetObjText( pText, pOutl, ePresKind, aPresentationText ); + + if ( pPage->GetPageKind() != PageKind::Notes && pPage->GetPageKind() != PageKind::Handout) + { + SfxStyleSheet* pSheet2( pPage->GetStyleSheetForPresObj( ePresKind ) ); + if ( pSheet2 ) + { + SfxItemSet& rItemSet = pSheet2->GetItemSet(); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_LEFTDIST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_RIGHTDIST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_UPPERDIST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_LOWERDIST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_VERTADJUST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_HORZADJUST ) ); + if ( pTextObj->GetInstance() == TSS_Type::Title + || pTextObj->GetInstance() == TSS_Type::Subtitle) + { + rItemSet.Put( pText->GetMergedItemSet() ); + } + } + } + + SfxItemSet aTempAttr( mpDoc->GetPool() ); + SdrMetricItem aMinHeight( makeSdrTextMinFrameHeightItem(pText->GetLogicRect().GetSize().Height()) ); + aTempAttr.Put( aMinHeight ); + SdrOnOffItem aAutoGrowHeight( makeSdrTextAutoGrowHeightItem(false) ); + aTempAttr.Put( aAutoGrowHeight ); + pText->SetMergedItemSet(aTempAttr); + } + else + { + pRet = nullptr; + } + } + else + { + const PptSlideLayoutAtom* pSlideLayout = GetSlideLayoutAtom(); + if ( pSlideLayout || ( m_eCurrentPageKind == PPT_NOTEPAGE ) ) + { + sal_uInt32 nPlacementId = pPlaceHolder->nPlacementId; + PptPlaceholder nPlaceholderId = pPlaceHolder->nPlaceholderId; + PresObjKind ePresObjKind = PresObjKind::NONE; + bool bEmptyPresObj = true; + bool bVertical = false; + if ( ( pTextObj->GetShapeType() == mso_sptRectangle ) || ( pTextObj->GetShapeType() == mso_sptTextBox ) ) + { + //if a placeholder with some custom attribute,the pTextObj will keep those attr,whose text size is zero, + //so sdPage should renew a PresObj to process placeholder. + bEmptyPresObj = ( pTextObj->Count() == 0 ) || ( pTextObj->Count() == 1 && pTextObj->First()->GetTextSize() == 0 ); + switch ( nPlaceholderId ) + { + case PptPlaceholder::NOTESBODY : ePresObjKind = PresObjKind::Notes; break; + case PptPlaceholder::VERTICALTEXTTITLE : + bVertical = true; + [[fallthrough]]; + case PptPlaceholder::TITLE : ePresObjKind = PresObjKind::Title; break; + case PptPlaceholder::VERTICALTEXTBODY : + bVertical = true; + [[fallthrough]]; + case PptPlaceholder::BODY : ePresObjKind = PresObjKind::Outline; break; + case PptPlaceholder::CENTEREDTITLE : ePresObjKind = PresObjKind::Title; break; + case PptPlaceholder::SUBTITLE : ePresObjKind = PresObjKind::Text; break; // PresObjKind::Outline + + default : + { + if ( pTextObj->Count() == 0 ) + { + switch ( nPlaceholderId ) + { + case PptPlaceholder::MEDIACLIP : + case PptPlaceholder::OBJECT : ePresObjKind = PresObjKind::Object; break; + case PptPlaceholder::GRAPH : ePresObjKind = PresObjKind::Chart; break; + case PptPlaceholder::TABLE : ePresObjKind = PresObjKind::Table; break; + case PptPlaceholder::CLIPART : ePresObjKind = PresObjKind::Graphic; break; + case PptPlaceholder::ORGANISZATIONCHART : ePresObjKind = PresObjKind::OrgChart; break; + default: break; + } + } + }; + } + } + else if ( pTextObj->GetShapeType() == mso_sptPictureFrame ) + { + if ( !pTextObj->Count() && dynamic_cast< const SdrGrafObj *>( pObj ) != nullptr ) + { + bEmptyPresObj = false; + switch ( nPlaceholderId ) + { + case PptPlaceholder::MEDIACLIP : + case PptPlaceholder::OBJECT : ePresObjKind = PresObjKind::Object; break; + case PptPlaceholder::GRAPH : ePresObjKind = PresObjKind::Chart; break; + case PptPlaceholder::TABLE : ePresObjKind = PresObjKind::Calc; break; + case PptPlaceholder::CLIPART : ePresObjKind = PresObjKind::Graphic; break; + case PptPlaceholder::ORGANISZATIONCHART : ePresObjKind = PresObjKind::OrgChart; break; + default: break; + } + } + } + if ( ePresObjKind != PresObjKind::NONE ) + { + if ( !bEmptyPresObj ) + { + pPage->InsertPresObj( pRet, ePresObjKind ); + } + else + { + SdrObject* pPresObj = pPage->CreatePresObj( ePresObjKind, bVertical, pText->GetLogicRect() ); + pPresObj->SetUserCall( pPage ); + + SfxItemSet aSet( pSdrModel->GetItemPool() ); + ApplyAttributes( rStCtrl, aSet ); + pPresObj->SetLogicRect(pText->GetLogicRect()); + ApplyTextAnchorAttributes( *pTextObj, aSet ); + //set custom font attribute of the placeholder + if ( pTextObj->Count() == 1 ) + { + PPTParagraphObj* pPara = pTextObj->First(); + if ( pPara && pPara->GetTextSize() == 0 ) + { + if ( PPTPortionObj * pPor = pPara->First() ) + { + pPor->ApplyTo(aSet, const_cast<SdrPowerPointImport&>(static_cast<SdrPowerPointImport const &>(*this)), pTextObj->GetDestinationInstance()); + } + } + } + pPresObj->SetMergedItemSet(aSet); + + if ((m_eCurrentPageKind != PPT_NOTEPAGE) && (nPlacementId != 0xffffffff) && pPage->TRG_HasMasterPage()) + { + SdrObject* pTitleObj = static_cast<SdPage&>(pPage->TRG_GetMasterPage()).GetPresObj( PresObjKind::Title ); + SdrObject* pOutlineObj = static_cast<SdPage&>(pPage->TRG_GetMasterPage()).GetPresObj( PresObjKind::Outline ); + + ::tools::Rectangle aTitleRect; + ::tools::Rectangle aOutlineRect; + Size aOutlineSize; + + if ( pTitleObj ) + aTitleRect = pTitleObj->GetLogicRect(); + if ( pOutlineObj ) + { + aOutlineRect = pOutlineObj->GetLogicRect(); + aOutlineSize = aOutlineRect.GetSize(); + } + ::tools::Rectangle aLogicRect( pPresObj->GetLogicRect() ); + Size aLogicSize( aLogicRect.GetSize() ); + + switch ( nPlacementId ) + { + case 0 : // position in title area + { + if ( aLogicRect != aTitleRect ) + pPresObj->SetUserCall( nullptr ); + } + break; + + case 1: + { + if ( pSlideLayout->eLayout == PptSlideLayout::TITLEANDBODYSLIDE ) + { // position in outline area + if ( aLogicRect != aOutlineRect ) + pPresObj->SetUserCall( nullptr ); + } + else if ( pSlideLayout->eLayout == PptSlideLayout::TWOCOLUMNSANDTITLE ) + { // position in outline area left + if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE || + aOutlineSize.Width() == 0 || + static_cast<double>(aLogicSize.Width()) / aOutlineSize.Width() < 0.48 || + static_cast<double>(aLogicSize.Width()) / aOutlineSize.Width() > 0.5) + { + pPresObj->SetUserCall(nullptr); + } + } + else if ( pSlideLayout->eLayout == PptSlideLayout::TWOROWSANDTITLE ) + { // position in outline area top + if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE || + std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE) + { + pPresObj->SetUserCall( nullptr ); + } + } + else if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE) + { // position in outline area top left + pPresObj->SetUserCall( nullptr ); + } + } + break; + + case 2: + { + if ( pSlideLayout->eLayout == PptSlideLayout::TWOCOLUMNSANDTITLE ) + { // position in outline area right + if (std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE || + aOutlineSize.Width() == 0 || + static_cast<double>(aLogicSize.Width()) / aOutlineSize.Width() < 0.48 || + static_cast<double>(aLogicSize.Width()) / aOutlineSize.Width() > 0.5) + { + pPresObj->SetUserCall( nullptr ); + } + } + else if ( pSlideLayout->eLayout == PptSlideLayout::TWOROWSANDTITLE ) + { // position in outline area bottom + if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE || + std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE) + { + pPresObj->SetUserCall( nullptr ); + } + } + else if (std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE) + { // position in outline area top right + pPresObj->SetUserCall(nullptr); + } + } + break; + + case 3: + { // position in outline area bottom left + if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE) + { + pPresObj->SetUserCall( nullptr ); + } + } + break; + + case 4: + { // position in outline area bottom right + if (std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE) + { + pObj->SetUserCall( nullptr ); + } + } + break; + } + } + pRet = nullptr; // return zero cause this obj was already inserted by CreatePresObj + } + } + else if ( !pTextObj->Count() ) + pRet = nullptr; + } + } + } + if ( pRet != pText ) + { + SdrObject* pFree( pText ); + SdrObject::Free( pFree ); + } + return pRet; +} + +SdrObject* ImplSdPPTImport::ProcessObj( SvStream& rSt, DffObjData& rData, SvxMSDffClientData& rClientData, ::tools::Rectangle& rTextRect, SdrObject* pRet ) +{ + SdrObject* pObj = SdrPowerPointImport::ProcessObj( rSt, rData, rClientData, rTextRect, pRet ); + + // read animation effect of object + if ( pObj ) + { + // further setup placeholder objects + if (dynamic_cast<const SdrPageObj*>(pObj)) + { + const ProcessData& rProcessData=static_cast<const ProcessData&>(rClientData); + if(rProcessData.pPage.page) + static_cast<SdPage *>(rProcessData.pPage.page)->InsertPresObj( + pObj, PresObjKind::Page ); + } + + DffRecordHeader aMasterShapeHd; + + if ( maShapeRecords.SeekToContent( rSt, DFF_msofbtClientData, SEEK_FROM_CURRENT_AND_RESTART ) ) + { + bool bInhabitanceChecked = false; + bool bAnimationInfoFound = false; + + DffRecordHeader& rHdClientData = *maShapeRecords.Current(); + while( true ) + { + sal_uInt32 nClientDataLen = SanitizeEndPos(rSt, rHdClientData.GetRecEndFilePos()); + DffRecordHeader aHd; + do + { + ReadDffRecordHeader( rSt, aHd ); + sal_uInt32 nHdRecEnd = aHd.GetRecEndFilePos(); + switch ( aHd.nRecType ) + { + case PPT_PST_AnimationInfo : + { + DffRecordHeader aHdAnimInfoAtom; + if ( SeekToRec( rSt, PPT_PST_AnimationInfoAtom, nHdRecEnd, &aHdAnimInfoAtom ) ) + { + // read data from stream + Ppt97AnimationPtr pAnimation = std::make_shared<Ppt97Animation>( rSt ); + // store animation information + if( pAnimation->HasEffect() ) + { + // translate color to RGB + pAnimation->SetDimColor( MSO_CLR_ToColor(pAnimation->GetDimColor()) ); + // translate sound bits to file url + if( pAnimation->HasSoundEffect() ) + pAnimation->SetSoundFileUrl( ReadSound( pAnimation->GetSoundRef() ) ); + + bool bDontAnimateInvisibleShape = false; + { + SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(pObj); + + if( pTextObj && pTextObj->HasText() && + dynamic_cast< SdrObjGroup *>( pObj ) == nullptr && + pAnimation->HasAnimateAssociatedShape() ) + { + const SfxItemSet& rObjItemSet = pObj->GetMergedItemSet(); + + drawing::FillStyle eFillStyle = rObjItemSet.Get(XATTR_FILLSTYLE).GetValue(); + drawing::LineStyle eLineStyle = rObjItemSet.Get(XATTR_LINESTYLE).GetValue(); + + if ( ( eFillStyle == drawing::FillStyle_NONE ) && ( eLineStyle == drawing::LineStyle_NONE ) ) + bDontAnimateInvisibleShape = true; + } + } + if( bDontAnimateInvisibleShape ) + pAnimation->SetAnimateAssociatedShape(false); + + //maybe some actions necessary to ensure that animations on master pages are played before animations on normal pages + //maybe todo in future: bool bIsEffectOnMasterPage = !bInhabitanceChecked;? + + maAnimations[pObj] = pAnimation; + + bAnimationInfoFound = true; + } + } + } + break; + case PPT_PST_InteractiveInfo: + { + sal_uInt32 nOldFilePos2 = rSt.Tell(); + OUString aMacroName; + + if(SeekToRec( rSt, PPT_PST_CString, nHdRecEnd ) ) + ReadString(aMacroName); + + rSt.Seek( nOldFilePos2 ); + DffRecordHeader aHdInteractiveInfoAtom; + if ( SeekToRec( rSt, PPT_PST_InteractiveInfoAtom, nHdRecEnd, &aHdInteractiveInfoAtom ) ) + { + PptInteractiveInfoAtom aInteractiveInfoAtom; + ReadPptInteractiveInfoAtom( rSt, aInteractiveInfoAtom ); + + // interactive object + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj, true); + + FillSdAnimationInfo( pInfo, &aInteractiveInfoAtom, aMacroName ); + if ( aInteractiveInfoAtom.nAction == 6 ) // Sj -> media action + { + rHdClientData.SeekToContent( rStCtrl ); + DffRecordHeader aObjRefAtomHd; + if ( SeekToRec( rSt, PPT_PST_ExObjRefAtom, nHdRecEnd, &aObjRefAtomHd ) ) + { + sal_uInt32 nRef; + rSt.ReadUInt32( nRef ); + OUString aMediaURL( ReadMedia( nRef ) ); + if ( aMediaURL.isEmpty() ) + aMediaURL = ReadSound( nRef ); + if ( !aMediaURL.isEmpty() ) + { + SdrMediaObj* pMediaObj = new SdrMediaObj( + pObj->getSdrModelFromSdrObject(), + pObj->GetSnapRect()); + pMediaObj->SetMergedItemSet( pObj->GetMergedItemSet() ); + + //--remove object from maAnimations list and add the new object instead + Ppt97AnimationPtr pAnimation; + { + tAnimationMap::iterator aFound = maAnimations.find( pObj ); + if( aFound != maAnimations.end() ) + { + pAnimation = (*aFound).second; + maAnimations.erase(aFound); + } + maAnimations[pMediaObj] = pAnimation; + } + + SdrObject::Free( pObj ); + pObj = pMediaObj; // SJ: hoping that pObj is not inserted in any list + pMediaObj->setURL( aMediaURL, ""/*TODO?*/ ); + } + } + } + } + } + break; + } + if (!aHd.SeekToEndOfRecord(rSt)) + break; + } + while( ( rSt.GetError() == ERRCODE_NONE ) && ( rSt.Tell() < nClientDataLen ) ); + + if ( bInhabitanceChecked || bAnimationInfoFound ) + break; + bInhabitanceChecked = true; + if ( ! ( IsProperty( DFF_Prop_hspMaster ) && SeekToShape( rSt, &rClientData, GetPropertyValue( DFF_Prop_hspMaster, 0 ) ) ) ) + break; + ReadDffRecordHeader( rSt, aMasterShapeHd ); + if ( !SeekToRec( rSt, DFF_msofbtClientData, aMasterShapeHd.GetRecEndFilePos(), &aMasterShapeHd ) ) + break; + aMasterShapeHd.SeekToContent( rSt ); + rHdClientData = aMasterShapeHd; + } + } + } + return pObj; +} + +bool +ImplSdPPTImport::ReadFormControl( tools::SvRef<SotStorage>& rSrc1, css::uno::Reference< css::form::XFormComponent > & rFormComp ) const +{ + uno::Reference< frame::XModel > xModel; + if ( mpDoc->GetDocSh() ) + { + xModel = mpDoc->GetDocSh()->GetModel(); + oox::ole::MSConvertOCXControls aCtrlImporter( xModel ); + return aCtrlImporter.ReadOCXStorage( rSrc1, rFormComp ); + } + return false; +} + +// exported function +extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool ImportPPT( + SdDrawDocument* pDocument, SvStream& rDocStream, SotStorage& rStorage, SfxMedium& rMedium ) +{ + std::unique_ptr<SdPPTImport> pImport( new SdPPTImport( pDocument, rDocStream, rStorage, rMedium )); + return pImport->Import(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportPPT(SvStream &rStream) +{ + bool bRet = false; + try + { + tools::SvRef<SotStorage> xStorage(new SotStorage(rStream)); + if (xStorage->GetError()) + return false; + + tools::SvRef<SotStorageStream> xDocStream(xStorage->OpenSotStream( "PowerPoint Document", StreamMode::STD_READ)); + if ( !xDocStream.is() ) + return false; + + SdDLL::Init(); + + SfxMedium aSrcMed("", StreamMode::STD_READ); + + xDocStream->SetVersion(xStorage->GetVersion()); + xDocStream->SetCryptMaskKey(xStorage->GetKey()); + + ::sd::DrawDocShellRef xDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::EMBEDDED, false, DocumentType::Impress); + SdDrawDocument *pDoc = xDocShRef->GetDoc(); + + try + { + bRet = ImportPPT(pDoc, *xDocStream, *xStorage, aSrcMed); + } + catch (...) + { + } + + xDocShRef->DoClose(); + } + catch (...) + { + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptin.hxx b/sd/source/filter/ppt/pptin.hxx new file mode 100644 index 000000000..39eff2890 --- /dev/null +++ b/sd/source/filter/ppt/pptin.hxx @@ -0,0 +1,92 @@ +/* -*- 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 <filter/msfilter/svdfppt.hxx> +#include <diadef.h> +#include <sot/storage.hxx> +#include <svx/svdtypes.hxx> +#include <memory> + +class SdDrawDocument; +class SfxMedium; + +/************************************************************************* +|* +|* local import +|* +\************************************************************************/ + +class SdPage; +class SdAnimationInfo; +class Ppt97Animation; + +typedef std::shared_ptr< Ppt97Animation > Ppt97AnimationPtr; +typedef ::std::map < SdrObject*, Ppt97AnimationPtr > tAnimationMap; + +class ImplSdPPTImport : public SdrPowerPointImport +{ + tools::SvRef<SotStorageStream> mxPicturesStream; + SfxMedium& mrMed; + SotStorage& mrStorage; + DffRecordHeader maDocHd; + std::vector<OUString> maSlideNameList; + bool mbDocumentFound; + sal_uInt32 mnFilterOptions; + SdDrawDocument* mpDoc; + PresChange mePresChange; + SdrLayerID mnBackgroundObjectsLayerID; + + tAnimationMap maAnimations; + void SetHeaderFooterPageSettings( SdPage* pPage, const PptSlidePersistEntry* pMasterPersist ); + void ImportPageEffect( SdPage* pPage, const bool bNewAnimationsUsed ); + + void FillSdAnimationInfo( SdAnimationInfo* pInfo, PptInteractiveInfoAtom const * pIAtom, const OUString& aMacroName ); + + virtual SdrObject* ProcessObj( SvStream& rSt, DffObjData& rData, SvxMSDffClientData& rClientData, ::tools::Rectangle& rTextRect, SdrObject* pObj ) override; + virtual SdrObject* ApplyTextObj( PPTTextObj* pTextObj, SdrTextObj* pText, SdPageCapsule pPage, + SfxStyleSheet*, SfxStyleSheet** ) const override; + +public: + + OUString ReadSound( sal_uInt32 nSoundRef ) const; + OUString ReadMedia( sal_uInt32 nMediaRef ) const; + + ImplSdPPTImport( SdDrawDocument* pDoc, SotStorage& rStorage, SfxMedium& rMed, PowerPointImportParam& ); + virtual ~ImplSdPPTImport() override; + + bool Import(); + virtual bool ReadFormControl( tools::SvRef<SotStorage>& rSrc1, css::uno::Reference< css::form::XFormComponent > & rFormComp ) const override; +}; + +class SdPPTImport +{ + PowerPointImportParam maParam; + std::unique_ptr<ImplSdPPTImport> pFilter; + +public: + + SdPPTImport( SdDrawDocument* pDoc, SvStream& rDocStream, SotStorage& rStorage, SfxMedium& rMed ); + ~SdPPTImport(); + + bool Import(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptinanimations.cxx b/sd/source/filter/ppt/pptinanimations.cxx new file mode 100644 index 000000000..ff49054dd --- /dev/null +++ b/sd/source/filter/ppt/pptinanimations.cxx @@ -0,0 +1,3294 @@ +/* -*- 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 <com/sun/star/animations/XAnimationNodeSupplier.hpp> +#include <com/sun/star/animations/AnimationFill.hpp> +#include <com/sun/star/animations/AnimationRestart.hpp> +#include <com/sun/star/animations/Timing.hpp> +#include <com/sun/star/animations/Event.hpp> +#include <com/sun/star/animations/AnimationEndSync.hpp> +#include <com/sun/star/animations/Command.hpp> +#include <com/sun/star/animations/EventTrigger.hpp> +#include <com/sun/star/animations/AnimationNodeType.hpp> +#include <com/sun/star/animations/AnimationTransformType.hpp> +#include <com/sun/star/animations/AnimationCalcMode.hpp> +#include <com/sun/star/animations/AnimationValueType.hpp> +#include <com/sun/star/animations/AnimationAdditiveMode.hpp> +#include <com/sun/star/animations/XIterateContainer.hpp> +#include <com/sun/star/animations/XAnimateSet.hpp> +#include <com/sun/star/animations/XAnimationNode.hpp> +#include <com/sun/star/animations/XAudio.hpp> +#include <com/sun/star/animations/XCommand.hpp> +#include <com/sun/star/animations/XTransitionFilter.hpp> +#include <com/sun/star/animations/XAnimateColor.hpp> +#include <com/sun/star/animations/XAnimateMotion.hpp> +#include <com/sun/star/animations/XAnimateTransform.hpp> +#include <com/sun/star/animations/ValuePair.hpp> +#include <com/sun/star/animations/AnimationColorSpace.hpp> +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/presentation/EffectPresetClass.hpp> +#include <com/sun/star/presentation/ShapeAnimationSubType.hpp> +#include <com/sun/star/presentation/EffectCommands.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/io/WrongFormatException.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <com/sun/star/presentation/TextAnimationType.hpp> +#include <comphelper/processfactory.hxx> +#include <oox/helper/addtosequence.hxx> +#include <oox/ppt/pptfilterhelpers.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/math.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <osl/diagnose.h> +#include <o3tl/string_view.hxx> + +#include <svx/svdotext.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editobj.hxx> +#include <animations.hxx> +#include "pptanimations.hxx" +#include "pptinanimations.hxx" +#include "pptatom.hxx" +#include "pptin.hxx" +#include <randomnode.hxx> + +#include <algorithm> +#include <memory> + +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +namespace ppt +{ + +static SvStream& operator>>(SvStream& rIn, AnimationNode& rNode ) +{ + rIn.ReadInt32( rNode.mnU1 ); + rIn.ReadInt32( rNode.mnRestart ); + rIn.ReadInt32( rNode.mnGroupType ); + rIn.ReadInt32( rNode.mnFill ); + rIn.ReadInt32( rNode.mnU3 ); + rIn.ReadInt32( rNode.mnU4 ); + rIn.ReadInt32( rNode.mnDuration ); + rIn.ReadInt32( rNode.mnNodeType ); + + return rIn; +} + +bool PropertySet::hasProperty( sal_Int32 nProperty ) const +{ + return maProperties.find( nProperty ) != maProperties.end(); +} + +Any PropertySet::getProperty( sal_Int32 nProperty ) const +{ + PropertySetMap_t::const_iterator aIter( maProperties.find( nProperty ) ); + if( aIter != maProperties.end() ) + return (*aIter).second; + else + return Any(); +} + +AnimationImporter::AnimationImporter( ImplSdPPTImport* pPPTImport, SvStream& rStCtrl ) +: mpPPTImport( pPPTImport ), mrStCtrl( rStCtrl ) +{ +} + +int AnimationImporter::import( const Reference< XDrawPage >& xPage, const DffRecordHeader& rProgTagContentHd ) +{ + int nNodes = 0; + +#ifdef DBG_ANIM_LOG + static int ppt_anim_debug_stream_number = 1; + OUString ppt_anim_debug_filename("ppt-animation-import-debug-output-"); + ppt_anim_debug_filename += OUString::number(ppt_anim_debug_stream_number++); + ppt_anim_debug_filename += ".xml"; + mpFile = fopen( OUStringToOString( ppt_anim_debug_filename, RTL_TEXTENCODING_UTF8).getStr() , "w+" ); +#endif + dump("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + + Reference< XAnimationNodeSupplier > xNodeSupplier( xPage, UNO_QUERY ); + if( xNodeSupplier.is() ) + { + mxRootNode = xNodeSupplier->getAnimationNode(); + if( mxRootNode.is() ) + { + Reference< XAnimationNode > xParent; + + std::unique_ptr<Atom> pAtom(Atom::import( rProgTagContentHd, mrStCtrl )); + if( pAtom ) + { + nNodes = importAnimationContainer( pAtom.get(), xParent ); + } + + std::for_each( maAfterEffectNodes.begin(), maAfterEffectNodes.end(), + sd::stl_process_after_effect_node_func ); + } + } + +#ifdef DBG_ANIM_LOG + fclose( mpFile ); +#endif + + return nNodes; +} + +Reference< XAnimationNode > AnimationImporter::createNode( const Atom* pAtom, const AnimationNode& rNode ) +{ + const char* pServiceName = nullptr; + + switch( rNode.mnGroupType ) + { + case mso_Anim_GroupType_PAR: + if( pAtom->hasChildAtom( DFF_msofbtAnimIteration ) ) + pServiceName = "com.sun.star.animations.IterateContainer"; + else + pServiceName = "com.sun.star.animations.ParallelTimeContainer"; + break; + case mso_Anim_GroupType_SEQ: + pServiceName = "com.sun.star.animations.SequenceTimeContainer"; + break; + case mso_Anim_GroupType_NODE: + { + switch( rNode.mnNodeType ) + { + case mso_Anim_Behaviour_FILTER: + case mso_Anim_Behaviour_ANIMATION: + if( pAtom->hasChildAtom( DFF_msofbtAnimateSet ) ) + pServiceName = "com.sun.star.animations.AnimateSet"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateColor ) ) + pServiceName = "com.sun.star.animations.AnimateColor"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateScale ) ) + pServiceName = "com.sun.star.animations.AnimateTransform"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateRotation ) ) + pServiceName = "com.sun.star.animations.AnimateTransform"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateMotion ) ) + pServiceName = "com.sun.star.animations.AnimateMotion"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateFilter ) ) + pServiceName = "com.sun.star.animations.TransitionFilter"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimCommand ) ) + pServiceName = "com.sun.star.animations.Command"; + else + pServiceName = "com.sun.star.animations.Animate"; + break; + } + break; + } + case mso_Anim_GroupType_MEDIA: + pServiceName = "com.sun.star.animations.Audio"; + break; + + default: + pServiceName = "com.sun.star.animations.Animate"; + break; + } + + Reference< XAnimationNode > xNode; + if( pServiceName ) + { + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + const OUString aServiceName( OUString::createFromAscii(pServiceName) ); + Reference< XInterface > xFac( xContext->getServiceManager()->createInstanceWithContext(aServiceName, xContext) ); + xNode.set(xFac , UNO_QUERY ); + } + + DBG_ASSERT( xNode.is(), "sd::AnimationImporter::createNode(), node creation failed!" ); + return xNode; +} + +static bool is_random( const AnimationNode& rNode, const PropertySet& rSet, sal_Int32& rPresetClass ) +{ + if( rNode.mnGroupType != mso_Anim_GroupType_PAR ) + return false; + + if( !rSet.hasProperty( DFF_ANIM_PRESET_ID ) || !rSet.hasProperty( DFF_ANIM_PRESET_CLASS ) ) + return false; + + sal_Int32 nPresetId = 0; + if( !(rSet.getProperty( DFF_ANIM_PRESET_ID ) >>= nPresetId) || (nPresetId != 24) ) + return false; + + sal_Int32 nPresetClass = 0; + if( !(rSet.getProperty( DFF_ANIM_PRESET_CLASS ) >>= nPresetClass) ) + return false; + + switch( nPresetClass ) + { + case DFF_ANIM_PRESS_CLASS_ENTRANCE: rPresetClass = EffectPresetClass::ENTRANCE; return true; + case DFF_ANIM_PRESS_CLASS_EXIT: rPresetClass = EffectPresetClass::EXIT; return true; + } + return false; +} + +int AnimationImporter::importAnimationContainer( const Atom* pAtom, const Reference< XAnimationNode >& xParent ) +{ + int nNodes = 0; + if( pAtom->seekToContent() ) + { + AnimationNode aNode; + const Atom* pAnimationNodeAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimNode ); + if( pAnimationNodeAtom && pAnimationNodeAtom->seekToContent() ) + mrStCtrl >> aNode; + + PropertySet aSet; + const Atom* pAnimationPropertySetAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimPropertySet ); + if( pAnimationPropertySetAtom ) + importPropertySetContainer( pAnimationPropertySetAtom, aSet ); + + Reference< XAnimationNode > xNode; + + if( xParent.is() ) + { + sal_Int32 nPresetClass; + if( is_random( aNode, aSet, nPresetClass ) ) + { + // create a random animation node with the given preset class + xNode.set( sd::RandomAnimationNode_createInstance( static_cast<sal_Int16>(nPresetClass) ), UNO_QUERY ); + } + + if( !xNode.is() ) + { + // create a node for the given atom + xNode = createNode( pAtom, aNode ); + } + } + else + { + // if we have no parent we fill the root node + xNode = mxRootNode; + } + + // import if we have a node and it's not random + if( xNode.is() ) + { + fillNode( xNode, aNode, aSet ); + + switch( aNode.mnGroupType ) + { + case mso_Anim_GroupType_PAR: + { + dump( "<par" ); + dump( aNode ); + dump( aSet ); + nNodes += importTimeContainer( pAtom, xNode ); + dump( "</par>\n" ); + + // for iteration containers, map target from children to iteration + Reference< XIterateContainer > xIter( xNode, UNO_QUERY ); + if( xIter.is() ) + { + double fDuration = 0.0; + Any aTarget, aEmpty; + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xChildNode.is() ) + { + double fChildBegin = 0.0; + double fChildDuration = 0.0; + xChildNode->getBegin() >>= fChildBegin; + xChildNode->getDuration() >>= fChildDuration; + + fChildDuration += fChildBegin; + if( fChildDuration > fDuration ) + fDuration = fChildDuration; + + if( !aTarget.hasValue() ) + aTarget = xChildNode->getTarget(); + + xChildNode->setTarget( aEmpty ); + } + } + } + } + + xIter->setTarget( aTarget ); + + double fIterateInterval = xIter->getIterateInterval() * fDuration / 100; + xIter->setIterateInterval( fIterateInterval ); + } + } + break; + + case mso_Anim_GroupType_SEQ: + { + dump( "<seq" ); + dump( aNode ); + dump( aSet ); + nNodes += importTimeContainer( pAtom, xNode ); + dump( "</seq>\n" ); + + if( aSet.hasProperty( DFF_ANIM_NODE_TYPE ) ) + { + sal_Int32 nPPTNodeType = 0; + if( aSet.getProperty( DFF_ANIM_NODE_TYPE ) >>= nPPTNodeType ) + { + switch(nPPTNodeType) + { + case DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE: + oox::ppt::fixMainSequenceTiming( xNode ); + break; + case DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ: + oox::ppt::fixInteractiveSequenceTiming( xNode ); + break; + } + } + } + } + break; + + case mso_Anim_GroupType_NODE: + { +#ifdef DBG_ANIM_LOG + if( pAtom->hasChildAtom( DFF_msofbtAnimateSet ) ) + { + dump( "<set" ); + } + else if( pAtom->hasChildAtom( DFF_msofbtAnimateColor ) ) + { + dump( "<animateColor" ); + } + else if( pAtom->hasChildAtom( DFF_msofbtAnimateScale ) ) + { + dump( "<animateScale" ); + } + else if( pAtom->hasChildAtom( DFF_msofbtAnimateRotation ) ) + { + dump( "<animateRotation" ); + } + else if( pAtom->hasChildAtom( DFF_msofbtAnimateMotion ) ) + { + dump( "<animateMotion" ); + } + else if( pAtom->hasChildAtom( DFF_msofbtAnimate ) ) + { + dump( "<animate" ); + } + else if( pAtom->hasChildAtom( DFF_msofbtAnimateFilter ) ) + { + dump( "<animateFilter" ); + } + else if( pAtom->hasChildAtom( DFF_msofbtAnimCommand ) ) + { + dump( "<command" ); + } + else + { + OSL_FAIL( "unknown node atom!" ); + dump_atom_header( pAtom, true, false ); + dump_atom( pAtom ); + dump_atom_header( pAtom, false, false ); + break; + } + dump( aNode ); + dump( aSet ); +#endif + int nANCNodes = importAnimationNodeContainer( pAtom, xNode ); + if( !convertAnimationNode( xNode, xParent ) ) + xNode = nullptr; + else + nNodes += nANCNodes; + dump( "/>\n"); + + } + break; + + case mso_Anim_GroupType_MEDIA: + { + dump( "<audio" ); + dump( aNode ); + dump( aSet ); + nNodes += importAudioContainer( pAtom, xNode ); + dump( "</audio>\n" ); + } + break; + + default: + OSL_FAIL( "unknown group atom!" ); + + dump_atom_header( pAtom, true, false ); + dump_atom( pAtom ); + dump_atom_header( pAtom, false, false ); + break; + + } + } + + if( xParent.is() && xNode.is() ) + { + Reference< XTimeContainer > xParentContainer( xParent, UNO_QUERY ); + DBG_ASSERT( xParentContainer.is(), "parent is no container, then why do I have a child here?" ); + if( xParentContainer.is() ) + { + xParentContainer->appendChild( xNode ); + } + } + } + + return nNodes; +} + +bool AnimationImporter::convertAnimationNode( const Reference< XAnimationNode >& xNode, const Reference< XAnimationNode >& xParent ) +{ + Reference< XAnimate > xAnimate( xNode, UNO_QUERY ); + if( !xAnimate.is() ) + return true; + + if( !xAnimate->getTarget().hasValue() ) + return false; + + const sal_Int16 nNodeType = xNode->getType(); + + if( nNodeType == AnimationNodeType::TRANSITIONFILTER ) + return true; + + OUString aAttributeName( xAnimate->getAttributeName() ); + + if( (nNodeType == AnimationNodeType::SET) && aAttributeName == "fill.on" ) + return false; + + const oox::ppt::ImplAttributeNameConversion* p = oox::ppt::getAttributeConversionList(); + + oox::ppt::AnimationAttributeEnum eAttribute = oox::ppt::AnimationAttributeEnum::UNKNOWN; + + if( (nNodeType == AnimationNodeType::ANIMATEMOTION) || + (nNodeType == AnimationNodeType::ANIMATETRANSFORM) ) + { + aAttributeName.clear(); + } + else + { + while( p->mpMSName ) + { + if( aAttributeName.equalsAscii( p->mpMSName ) ) + break; + + p++; + } + + DBG_ASSERT( p->mpMSName || aAttributeName.isEmpty(), "sd::AnimationImporter::convertAnimationNode(), unknown attribute!" ); +#ifdef DBG_ANIM_LOG + if( p->mpMSName == 0 ) dump( "<error text=\"sd::AnimationImporter::convertAnimationNode(), unknown attribute!\"/>\n" ); +#endif + + eAttribute = p->meAttribute; + + if( p->mpAPIName ) + aAttributeName = OUString::createFromAscii( p->mpAPIName ); + } + + xAnimate->setAttributeName( aAttributeName ); + + if(eAttribute != oox::ppt::AnimationAttributeEnum::UNKNOWN) + { + Any aAny( xAnimate->getFrom() ); + if( aAny.hasValue() ) + { + if(oox::ppt::convertAnimationValue(eAttribute, aAny)) + xAnimate->setFrom( aAny ); + } + + aAny = xAnimate->getBy(); + if( aAny.hasValue() ) + { + if(oox::ppt::convertAnimationValue(eAttribute, aAny)) + xAnimate->setBy( aAny ); + } + + aAny = xAnimate->getTo(); + if( aAny.hasValue() ) + { + if(oox::ppt::convertAnimationValue(eAttribute, aAny)) + xAnimate->setTo( aAny ); + } + + Sequence< Any > aValues( xAnimate->getValues() ); + if( aValues.hasElements() ) + { + for( Any& rValue : asNonConstRange(aValues) ) + oox::ppt::convertAnimationValue(eAttribute, rValue); + + xAnimate->setValues( aValues ); + } + + OUString aFormula( xAnimate->getFormula() ); + if( !aFormula.isEmpty() ) + { + if(oox::ppt::convertMeasure(aFormula)) + xAnimate->setFormula( aFormula ); + } + } + + // check for after-effect + Sequence< NamedValue > aUserData( xNode->getUserData() ); + NamedValue* pLastValue = aUserData.getArray(); + sal_Int32 nRemoved = 0; + + bool bAfterEffect = false; + sal_Int32 nMasterRel = 0; + for( const NamedValue& rValue : std::as_const(aUserData) ) + { + if ( rValue.Name == "after-effect" ) + { + rValue.Value >>= bAfterEffect; + nRemoved++; + } + else if ( rValue.Name == "master-rel" ) + { + rValue.Value >>= nMasterRel; + nRemoved++; + } + else + { + if( nRemoved ) + *pLastValue = rValue; + pLastValue++; + } + } + + if( nRemoved ) + { + aUserData.realloc( aUserData.getLength() - nRemoved ); + xNode->setUserData( aUserData ); + } + + // if it's an after effect node, add it to the list for + // later processing + // after effect nodes are not inserted at their import + // position, so return false in this case + if( bAfterEffect ) + { + if( nMasterRel != 2 ) + { + Event aEvent; + + aEvent.Source <<= xParent; + aEvent.Trigger = EventTrigger::END_EVENT; + aEvent.Repeat = 0; + + xNode->setBegin( Any( aEvent ) ); + } + + // add to after effect nodes for later processing + sd::AfterEffectNode aNode( xNode, xParent, nMasterRel == 2 ); + maAfterEffectNodes.push_back( aNode ); + return false; + } + + return true; +} + +void AnimationImporter::fillNode( Reference< XAnimationNode > const & xNode, const AnimationNode& rNode, const PropertySet& rSet ) +{ + bool bAfterEffect = false; + + // attribute Restart + if( rNode.mnRestart ) + { + sal_Int16 nRestart = AnimationRestart::DEFAULT; + switch( rNode.mnRestart ) + { + case 1: nRestart = AnimationRestart::ALWAYS; break; + case 2: nRestart = AnimationRestart::WHEN_NOT_ACTIVE; break; + case 3: nRestart = AnimationRestart::NEVER; break; + } + xNode->setRestart( nRestart ); + } + + // attribute Fill + if( rNode.mnFill ) + { + sal_Int16 nFill = AnimationFill::DEFAULT; + switch( rNode.mnFill ) + { + case 1: nFill = AnimationFill::REMOVE; break; + case 2: nFill = AnimationFill::FREEZE; break; + case 3: nFill = AnimationFill::HOLD; break; + case 4: nFill = AnimationFill::TRANSITION; break; + } + xNode->setFill( nFill ); + } + + // attribute Duration + if( rNode.mnDuration ) + { + Any aDuration; + if( rNode.mnDuration > 0 ) + { + aDuration <<= rNode.mnDuration / 1000.0; + } + else if( rNode.mnDuration < 0 ) + { + aDuration <<= Timing_INDEFINITE; + } + xNode->setDuration( aDuration ); + } + + // TODO: DFF_ANIM_PATH_EDIT_MODE + + // set user data + Sequence< NamedValue > aUserData; + + // attribute Type + if( rSet.hasProperty( DFF_ANIM_NODE_TYPE ) ) + { + sal_Int32 nPPTNodeType = 0; + if( rSet.getProperty( DFF_ANIM_NODE_TYPE ) >>= nPPTNodeType ) + { + sal_Int16 nNodeType = css::presentation::EffectNodeType::DEFAULT; + switch( nPPTNodeType ) + { + case DFF_ANIM_NODE_TYPE_CLICK_PARALLEL: [[fallthrough]]; + case DFF_ANIM_NODE_TYPE_ON_CLICK: nNodeType = css::presentation::EffectNodeType::ON_CLICK; break; + case DFF_ANIM_NODE_TYPE_WITH_GROUP: [[fallthrough]]; + case DFF_ANIM_NODE_TYPE_WITH_PREVIOUS: nNodeType = css::presentation::EffectNodeType::WITH_PREVIOUS; break; + case DFF_ANIM_NODE_TYPE_AFTER_GROUP: [[fallthrough]]; + case DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS: nNodeType = css::presentation::EffectNodeType::AFTER_PREVIOUS; break; + case DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE: nNodeType = css::presentation::EffectNodeType::MAIN_SEQUENCE; break; + case DFF_ANIM_NODE_TYPE_TIMING_ROOT: nNodeType = css::presentation::EffectNodeType::TIMING_ROOT; break; + case DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ:nNodeType = css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE; break; + } + + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "node-type"; + pUserData[nSize].Value <<= nNodeType; + } + } + + if( rSet.hasProperty( DFF_ANIM_GROUP_ID ) ) + { + sal_Int32 nGroupId; + if( rSet.getProperty( DFF_ANIM_GROUP_ID ) >>= nGroupId ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "group-id"; + pUserData[nSize].Value <<= nGroupId; + } + } + + sal_Int16 nEffectPresetClass = EffectPresetClass::CUSTOM; + sal_Int32 nPresetId = 0; + + if( rSet.hasProperty( DFF_ANIM_PRESET_CLASS ) ) + { + sal_Int32 nPresetClass = 0; + if ( rSet.getProperty( DFF_ANIM_PRESET_CLASS ) >>= nPresetClass ) + { + switch( nPresetClass ) + { + case DFF_ANIM_PRESS_CLASS_ENTRANCE: nEffectPresetClass = EffectPresetClass::ENTRANCE; break; + case DFF_ANIM_PRESS_CLASS_EXIT: nEffectPresetClass = EffectPresetClass::EXIT; break; + case DFF_ANIM_PRESS_CLASS_EMPHASIS: nEffectPresetClass = EffectPresetClass::EMPHASIS; break; + case DFF_ANIM_PRESS_CLASS_MOTIONPATH: nEffectPresetClass = EffectPresetClass::MOTIONPATH; break; + case DFF_ANIM_PRESS_CLASS_OLE_ACTION: nEffectPresetClass = EffectPresetClass::OLEACTION; break; + case DFF_ANIM_PRESS_CLASS_MEDIACALL: nEffectPresetClass = EffectPresetClass::MEDIACALL; break; + } + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "preset-class"; + pUserData[nSize].Value <<= nEffectPresetClass; + } + } + + if( rSet.hasProperty( DFF_ANIM_PRESET_ID ) ) + { + if( rSet.getProperty( DFF_ANIM_PRESET_ID ) >>= nPresetId ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "preset-id"; + + const oox::ppt::preset_mapping* p = oox::ppt::preset_mapping::getList(); + while( p->mpStrPresetId && ((p->mnPresetClass != nEffectPresetClass) || (p->mnPresetId != nPresetId )) ) + p++; + + if( p->mpStrPresetId ) + { + pUserData[nSize].Value <<= OUString::createFromAscii( p->mpStrPresetId ); + } + else + { + OUStringBuffer sBuffer; + sBuffer.append( "ppt_" ); + switch( nEffectPresetClass ) + { + case EffectPresetClass::ENTRANCE: sBuffer.append( "entrance_" ); break; + case EffectPresetClass::EXIT: sBuffer.append( "exit_" ); break; + case EffectPresetClass::EMPHASIS: sBuffer.append( "emphasis_" ); break; + case EffectPresetClass::MOTIONPATH: sBuffer.append( "motionpath_" ); break; + case EffectPresetClass::OLEACTION: sBuffer.append( "oleaction_" ); break; + case EffectPresetClass::MEDIACALL: sBuffer.append( "mediacall_" ); break; + } + sBuffer.append( nPresetId ); + + pUserData[nSize].Value <<= sBuffer.makeStringAndClear(); + } + } + } + + if( rSet.hasProperty( DFF_ANIM_PRESET_SUB_TYPE ) ) + { + sal_Int32 nPresetSubType = 0; + if( rSet.getProperty( DFF_ANIM_PRESET_SUB_TYPE ) >>= nPresetSubType ) + { + if( nPresetSubType ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "preset-sub-type"; + pUserData[nSize].Value <<= oox::ppt::getConvertedSubType( nEffectPresetClass, nPresetId, nPresetSubType ); + } + } + } + + if( rSet.hasProperty( DFF_ANIM_AFTEREFFECT ) ) + { + if( rSet.getProperty( DFF_ANIM_AFTEREFFECT ) >>= bAfterEffect ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "after-effect"; + pUserData[nSize].Value <<= bAfterEffect; + } + } + + if( bAfterEffect && rSet.hasProperty( DFF_ANIM_MASTERREL ) ) + { + sal_Int32 nMasterRel = 2; + if( rSet.getProperty( DFF_ANIM_MASTERREL ) >>= nMasterRel ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "master-rel"; + pUserData[nSize].Value <<= nMasterRel; + } + } + + xNode->setUserData( aUserData ); + + // TODO: DFF_ANIM_ID + if( rSet.hasProperty( DFF_ANIM_ID ) ) + { + OUString aString; + rSet.getProperty( DFF_ANIM_ID ) >>= aString; + //if( !aString.isEmpty() ) + //{ + //} + } + + // TODO: DFF_ANIM_EVENT_FILTER + if( rSet.hasProperty( DFF_ANIM_EVENT_FILTER ) ) + { + OUString aString; + rSet.getProperty( DFF_ANIM_EVENT_FILTER ) >>= aString; + //if( !aString.isEmpty() ) + //{ + //} + } + + // DFF_ANIM_TIMEFILTER + if( rSet.hasProperty( DFF_ANIM_TIMEFILTER ) ) + { + Reference< XAnimate > xAnim( xNode, UNO_QUERY ); + if( xAnim.is() ) + { + OUString aString; + rSet.getProperty( DFF_ANIM_TIMEFILTER ) >>= aString; + if( !aString.isEmpty() ) + { + sal_Int32 nElements = 1; // a non empty string has at least one value + + sal_Int32 fromIndex = 0; + while(true) + { + fromIndex = aString.indexOf( ';', fromIndex ); + if( fromIndex == -1 ) + break; + + fromIndex++; + nElements++; + } + + Sequence< TimeFilterPair > aTimeFilter( nElements ); + + TimeFilterPair* pValues = aTimeFilter.getArray(); + sal_Int32 nIndex = 0; + while( (nElements--) && (nIndex >= 0) ) + { + const std::u16string_view aToken( o3tl::getToken(aString, 0, ';', nIndex ) ); + + size_t nPos = aToken.find( ',' ); + if( nPos != std::u16string_view::npos ) + { + pValues->Time = o3tl::toDouble(aToken.substr( 0, nPos )); + pValues->Progress = o3tl::toDouble(aToken.substr( nPos+1 )); + } + pValues++; + } + + xAnim->setTimeFilter( aTimeFilter ); + } + } + } + +// TODO: DFF_ANIM_ENDAFTERSLIDE / DFF_ANIM_VOLUME handling. git history has sample code + Reference< XAnimateColor > xColor( xNode, UNO_QUERY ); + if( !xColor.is() ) + return; + + if( rSet.hasProperty( DFF_ANIM_DIRECTION ) ) + { + bool bDirection = false; + if( rSet.getProperty( DFF_ANIM_DIRECTION ) >>= bDirection ) + xColor->setDirection( !bDirection ); + } + + if( rSet.hasProperty( DFF_ANIM_COLORSPACE ) ) + { + sal_Int32 nColorSpace = 0; + rSet.getProperty( DFF_ANIM_COLORSPACE ) >>= nColorSpace; + xColor->setColorInterpolation( (nColorSpace == 0) ? AnimationColorSpace::RGB : AnimationColorSpace::HSL ); + } +} + +int AnimationImporter::importTimeContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + int nNodes = 0; + + DBG_ASSERT( pAtom && xNode.is(), "invalid call to ppt::AnimationImporter::importTimeContainer()!"); + if( pAtom && xNode.is() ) + { + importAnimationEvents( pAtom, xNode ); + importAnimationValues( pAtom, xNode ); + importAnimationActions( pAtom, xNode ); + + dump(">\n"); + + // import sub containers + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimNode: + case DFF_msofbtAnimEvent: + case DFF_msofbtAnimValue: + case DFF_msofbtAnimAction: + case DFF_msofbtAnimPropertySet: + break; + + case DFF_msofbtAnimSubGoup : + { + if( pChildAtom->hasChildAtom( DFF_msofbtAnimCommand ) ) + { + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference< XAnimationNode > xChildNode( Command::create(xContext), UNO_QUERY_THROW ); + nNodes += importAnimationNodeContainer( pChildAtom, xChildNode ); + Reference< XTimeContainer > xParentContainer( xNode, UNO_QUERY ); + if( xParentContainer.is() && xChildNode.is() ) + xParentContainer->appendChild( xChildNode ); + } + else + { + nNodes += importAnimationContainer( pChildAtom, xNode ); + } + } + break; + case DFF_msofbtAnimGroup : + { + nNodes += importAnimationContainer( pChildAtom, xNode ); + } + break; + case DFF_msofbtAnimIteration: + { + if( pChildAtom->seekToContent() ) + { + float fInterval(0.0); + sal_Int32 nTextUnitEffect(0), nU1(0), nU2(0), nU3(0); + + mrStCtrl.ReadFloat( fInterval ).ReadInt32( nTextUnitEffect ).ReadInt32( nU1 ).ReadInt32( nU2 ).ReadInt32( nU3 ); + + Reference< XIterateContainer > xIter( xNode, UNO_QUERY ); + if( xIter.is() ) + { + sal_Int16 nIterateType = TextAnimationType::BY_PARAGRAPH; + switch( nTextUnitEffect ) + { + case 1: nIterateType = TextAnimationType::BY_WORD; break; + case 2: nIterateType = TextAnimationType::BY_LETTER; break; + } + xIter->setIterateType( nIterateType ); + xIter->setIterateInterval( static_cast<double>(fInterval) ); + } + + nNodes++; + + dump( "<iterate" ); + dump( " iterateType=\"%s\"", (nTextUnitEffect == 0) ? "byElement" : (nTextUnitEffect == 1) ? "byWord" : "byLetter" ); + dump( " iterateInterval=\"%g\"", fInterval ); + dump( " u1=\"%ld\"", nU1 ); + dump( " u2=\"%ld\"", nU2 ); + dump( " u3=\"%ld\"/>\n", nU3 ); + } + } + break; + + case 0xf136: + { +#ifdef DBG_ANIM_LOG + sal_uInt32 nU1, nU2; + mrStCtrl.ReadUInt32(nU1).ReadUInt32(nU2); + + fprintf( mpFile, "<unknown_0xf136 nU1=\"%" SAL_PRIdINT32 "\" nU2=\"%" SAL_PRIdINT32 "\"/>\n", nU1, nU2 ); +#endif + } + break; + + default: + { + dump_atom_header( pChildAtom, true, false ); + dump_atom( pChildAtom ); + dump_atom_header( pChildAtom, false, false ); + } + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + } + + return nNodes; +} + +int AnimationImporter::importAnimationNodeContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + int nNodes = 0; + + DBG_ASSERT( pAtom && xNode.is(), "invalid call to ppt::AnimationImporter::importAnimationNodeContainer()!"); + if( pAtom && xNode.is() ) + { + importAnimationEvents( pAtom, xNode ); + importAnimationValues( pAtom, xNode ); + importAnimationActions( pAtom, xNode ); + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + nNodes ++; + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimNode: + case DFF_msofbtAnimEvent: + case DFF_msofbtAnimValue: + case DFF_msofbtAnimAction: + case DFF_msofbtAnimPropertySet: + break; + + case DFF_msofbtAnimateFilter: + importAnimateFilterContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateSet: + importAnimateSetContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimate: + importAnimateContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateScale: + importAnimateScaleContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateColor: + importAnimateColorContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateRotation: + importAnimateRotationContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateMotion: + importAnimateMotionContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimCommand: + importCommandContainer( pChildAtom, xNode ); + break; + + default: + { + nNodes --; + dump_atom_header( pChildAtom, true, false ); + dump_atom( pChildAtom ); + dump_atom_header( pChildAtom, false, false ); + } + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + } + + return nNodes; +} + +void AnimationImporter::importAnimateFilterContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XTransitionFilter > xFilter( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateFilter && xFilter.is(), "invalid call to ppt::AnimationImporter::importAnimateFilterContainer()!"); + if( !(pAtom && xFilter.is()) ) + return; + + sal_uInt32 nBits = 0; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateFilterData: + { + sal_uInt32 transition(0); + mrStCtrl.ReadUInt32( nBits ); + mrStCtrl.ReadUInt32( transition ); + + if( nBits & 1 ) + xFilter->setMode( transition == 0 ); + + dump( " transition=\"%s\"", (transition == 0) ? "in" : "out" ); + } + break; + + case DFF_msofbtAnimAttributeValue: + { + if( (nBits & 2 ) && ( pChildAtom->getInstance() == 1 ) ) + { + Any aAny; + if ( importAttributeValue( pChildAtom, aAny ) ) + { + OUString filter; + aAny >>= filter; + + dump( " filter=\"%s\"", filter ); + + const oox::ppt::transition* pTransition = oox::ppt::transition::find( filter ); + if( pTransition ) + { + xFilter->setTransition( pTransition->mnType ); + xFilter->setSubtype( pTransition->mnSubType ); + xFilter->setDirection( pTransition->mbDirection ); + } + else + { + OSL_FAIL( "unknown transition!" ); + } + } + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateAttributeTargetContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateTarget, "invalid call to ppt::AnimationImporter::importAnimateAttributeTargetContainer()!"); + + Any aTarget; + + Reference< XAnimate > xAnimate( xNode, UNO_QUERY ); + + bool bWrongContext = false; + + if( pAtom ) + { + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimPropertySet: + { + PropertySet aSet; + importPropertySetContainer( pChildAtom, aSet ); + if( aSet.hasProperty( DFF_ANIM_RUNTIMECONTEXT ) ) + { + OUString aContext; + if( aSet.getProperty( DFF_ANIM_RUNTIMECONTEXT ) >>= aContext ) + { + if( aContext != "PPT" ) + bWrongContext = true; + } + } + + dump( aSet ); + } + break; + + case DFF_msofbtAnimateTargetSettings: + { + if( xAnimate.is() ) + { + sal_uInt32 nBits(0); + sal_uInt32 nAdditive(0); + sal_uInt32 nAccumulate(0); + sal_uInt32 nTransformType(0); + + mrStCtrl.ReadUInt32( nBits ).ReadUInt32( nAdditive ).ReadUInt32( nAccumulate ).ReadUInt32( nTransformType ); + + // nBits %0001: additive, %0010: accumulate, %0100: attributeName, %1000: transformtype + // nAdditive 0 = base, 1 = sum, 2 = replace, 3 = multiply, 4 = none + // nAccumulate 0 = none, 1 = always + // nTransformType 0: "property" else "image" + + if( nBits & 3 && xAnimate.is() ) + { + if( nBits & 1 ) + { + sal_Int16 nTemp = AnimationAdditiveMode::BASE; + switch( nAdditive ) + { + case 1: nTemp = AnimationAdditiveMode::SUM; break; + case 2: nTemp = AnimationAdditiveMode::REPLACE; break; + case 3: nTemp = AnimationAdditiveMode::MULTIPLY; break; + case 4: nTemp = AnimationAdditiveMode::NONE; break; + } + xAnimate->setAdditive( nTemp ); + } + + if( nBits & 2 ) + { + xAnimate->setAccumulate( nAccumulate == 0 ); + } + } +#ifdef DBG_ANIM_LOG + if( nBits & 1 ) + fprintf( mpFile, " additive=\"%s\"", (nAdditive == 0) ? "base" : (nAdditive == 2) ? "replace" : (nAdditive == 1) ? "sum" : (nAdditive == 3 ) ? "multiply" : (nAdditive == 4) ? "none" : "unknown" ); + + if( nBits & 2 ) + fprintf( mpFile, " accumulate=\"%s\"", (nAccumulate == 0) ? "none" : "always" ); + + if( nBits & 8 ) + fprintf( mpFile, " transformType=\"%s\"", (nTransformType == 0) ? "property" : "image" ); +#endif + } + } + break; + + case DFF_msofbtAnimateAttributeNames: + { + if( xAnimate.is() ) + { + OUString aAttributeName; + importAttributeNamesContainer( pChildAtom, aAttributeName ); + if( xAnimate.is() ) + xAnimate->setAttributeName( aAttributeName ); + dump( " attributeName=\"%s\"", aAttributeName ); + } + } + break; + + case DFF_msofbtAnimateTargetElement: + { + sal_Int16 nSubType; + importTargetElementContainer( pChildAtom, aTarget, nSubType ); + if( xAnimate.is() ) + xAnimate->setSubItem( nSubType ); + + dump( " target=\"" ); + dump_target( aTarget ); + dump( "\"" ); + } + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + } + + if( bWrongContext ) + aTarget.clear(); + + if( xAnimate.is() ) + xAnimate->setTarget( aTarget ); + else + { + Reference< XCommand > xCommand( xNode, UNO_QUERY ); + if( xCommand.is() ) + xCommand->setTarget( aTarget ); + } +} + +sal_Int16 AnimationImporter::implGetColorSpace( sal_Int32 nMode, sal_Int32 /*nA*/, sal_Int32 /*nB*/, sal_Int32 /*nC*/ ) +{ + switch( nMode ) + { + case 2: // index + default: + case 0: // rgb + return AnimationColorSpace::RGB; + + case 1: // hsl + return AnimationColorSpace::HSL; + } +} + +Any AnimationImporter::implGetColorAny( sal_Int32 nMode, sal_Int32 nA, sal_Int32 nB, sal_Int32 nC ) +{ + switch( nMode ) + { + case 0: // rgb + { + dump( "rgb(%ld", nA ); + dump( ",%ld", nB ); + dump( ",%ld)", nC ); + Color aColor( static_cast<sal_uInt8>(nA), static_cast<sal_uInt8>(nB), static_cast<sal_uInt8>(nC) ); + return Any( static_cast<sal_Int32>(aColor.GetRGBColor()) ); + } + case 1: // hsl + { + dump( "hsl(%ld", nA ); + dump( ",%ld", nB ); + dump( ",%ld)", nC ); + Sequence< double > aHSL{ nA * 360.0/255.0, + nB / 255.0, + nC / 255.0 }; + return Any( aHSL ); + } + + case 2: // index + { + Color aColor; + mpPPTImport->GetColorFromPalette(static_cast<sal_uInt16>(nA), aColor ); + dump( "index(%ld", nA ); + dump( " [%ld", static_cast<sal_Int32>(aColor.GetRed()) ); + dump( ",%ld", static_cast<sal_Int32>(aColor.GetGreen()) ); + dump( ",%ld])", static_cast<sal_Int32>(aColor.GetBlue()) ); + return Any( static_cast<sal_Int32>(aColor.GetRGBColor()) ); + } + + default: + { + dump( "unknown_%ld(", nMode ); + dump( "%ld", nA ); + dump( ",%ld", nB ); + dump( ",%ld)", nC ); + OSL_FAIL( "ppt::implGetColorAny(), unhandled color type" ); + + Any aAny; + return aAny; + } + } +} + +void AnimationImporter::importAnimateColorContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateColor > xColor( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateColor && xColor.is(), "invalid call to ppt::AnimationImporter::importAnimateColorContainer()!"); + if( !(pAtom && xColor.is()) ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateColorData: + { + sal_uInt32 nBits; + sal_Int32 nByMode, nByA, nByB, nByC; + sal_Int32 nFromMode, nFromA, nFromB, nFromC; + sal_Int32 nToMode, nToA, nToB, nToC; + mrStCtrl.ReadUInt32( nBits ); + mrStCtrl.ReadInt32( nByMode ).ReadInt32( nByA ).ReadInt32( nByB ).ReadInt32( nByC ); + mrStCtrl.ReadInt32( nFromMode ).ReadInt32( nFromA ).ReadInt32( nFromB ).ReadInt32( nFromC ); + mrStCtrl.ReadInt32( nToMode ).ReadInt32( nToA ).ReadInt32( nToB ).ReadInt32( nToC ); + + if (!mrStCtrl.good()) + { + SAL_WARN("filter.ms", "DFF_msofbtAnimateColorData: short read"); + break; + } + + if( nBits & 1 ) + { + dump( " by=\"" ); + xColor->setBy( implGetColorAny( nByMode, nByA, nByB, nByC ) ); + xColor->setColorInterpolation( implGetColorSpace( nByMode, nByA, nByB, nByC ) ); + dump( "\""); + } + + if( nBits & 2 ) + { + dump( " from=\"" ); + xColor->setFrom( implGetColorAny( nFromMode, nFromA, nFromB, nFromC ) ); + xColor->setColorInterpolation( implGetColorSpace( nFromMode, nFromA, nFromB, nFromC ) ); + dump( "\""); + } + + if( nBits & 4 ) + { + dump( " to=\"" ); + xColor->setTo( implGetColorAny( nToMode, nToA, nToB, nToC ) ); + xColor->setColorInterpolation( implGetColorSpace( nToMode, nToA, nToB, nToC ) ); + dump( "\""); + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateSetContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateSet > xSet( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateSet && xSet.is(), "invalid call to ppt::AnimationImporter::importAnimateSetContainer()!"); + if( !(pAtom && xSet.is()) ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateSetData: + { + sal_Int32 nU1, nU2; + mrStCtrl.ReadInt32( nU1 ).ReadInt32( nU2 ); + + dump( " set_1=\"%ld\"", nU1 ); + dump( " set_2=\"%ld\"", nU2 ); + } + break; + + case DFF_msofbtAnimAttributeValue: + { + Any aTo; + if ( importAttributeValue( pChildAtom, aTo ) ) + { + xSet->setTo( aTo ); + + dump( " value=\"" ); + dump( aTo ); + dump( "\"" ); + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimate > xAnim( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimate && xAnim.is(), "invalid call to ppt::AnimationImporter::importAnimateContainer()!"); + if( !(pAtom && xAnim.is()) ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateData: + { + sal_uInt32 nCalcmode(0), nBits(0), nValueType(0); + mrStCtrl.ReadUInt32( nCalcmode ).ReadUInt32( nBits ).ReadUInt32( nValueType ); + + if( nBits & 0x08 ) + { + sal_Int16 n = (nCalcmode == 1) ? AnimationCalcMode::LINEAR : /* (nCalcmode == 2) ? AnimationCalcMode::FORMULA : */ AnimationCalcMode::DISCRETE; + xAnim->setCalcMode( n ); + dump( " calcmode=\"%s\"", (nCalcmode == 0) ? "discrete" : (nCalcmode == 1) ? "linear" : (nCalcmode == 2) ? "formula" : "unknown" ); + } + + if( nBits & 0x30 ) + { + sal_Int16 n = (nValueType == 1) ? AnimationValueType::NUMBER : (nValueType == 2 ) ? AnimationValueType::COLOR : AnimationValueType::STRING; + xAnim->setValueType( n ); + dump( " valueType=\"%s\"", (nValueType == 0) ? "string" : (nValueType == 1) ? "number" : (nValueType == 2) ? "color" : "unknown" ); + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimKeyPoints: + importAnimateKeyPoints( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimAttributeValue: + { + Any a; + if ( importAttributeValue( pChildAtom, a ) ) + { + switch( pChildAtom->getInstance() ) + { + case 1: xAnim->setBy( a ); dump( " by=\"" ); break; + case 2: xAnim->setFrom( a ); dump( " from=\"" ); break; + case 3: xAnim->setTo( a ); dump( " to=\"" ); break; + default: + dump( " unknown_value=\"" ); + } + + dump( a ); + dump( "\"" ); + } + } + break; + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateMotionContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateMotion > xMotion( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateMotion && xMotion.is(), "invalid call to ppt::AnimationImporter::importAnimateMotionContainer()!"); + if( !(pAtom && xMotion.is()) ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateMotionData: + { + sal_uInt32 nBits, nOrigin; + float fByX, fByY, fFromX, fFromY, fToX, fToY; + + mrStCtrl.ReadUInt32( nBits ).ReadFloat( fByX ).ReadFloat( fByY ).ReadFloat( fFromX ).ReadFloat( fFromY ).ReadFloat( fToX ).ReadFloat( fToY ).ReadUInt32( nOrigin ); + +#ifdef DBG_ANIM_LOG + if( nBits & 1 ) + fprintf( mpFile, " by=\"%g,%g\"", (double)fByX, (double)fByY ); + + if( nBits & 2 ) + fprintf( mpFile, " from=\"%g,%g\"", (double)fFromX, (double)fFromY ); + + if( nBits & 4 ) + fprintf( mpFile, " to=\"%g,%g\"", (double)fToX, (double)fToY ); + + if( nBits & 8 ) + fprintf( mpFile, " origin=\"%s\"", (nOrigin == 1) ? "parent" : (nOrigin == 2) ? "layout" : "unknown" ); + +#endif + } + break; + + case DFF_msofbtAnimAttributeValue: + { + Any aPath; + if ( importAttributeValue( pChildAtom, aPath ) ) + { + OUString aStr; + if ( aPath >>= aStr ) + { + // E can appear inside a number, so we only check for its presence at the end + aStr = aStr.trim(); + if (aStr.endsWith("E")) + aStr = aStr.copy(0, aStr.getLength() - 1); + aStr = aStr.trim(); + aPath <<= aStr; + xMotion->setPath( aPath ); + dump( " path=\"" ); + dump( aPath ); + dump( "\"" ); + } + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importCommandContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XCommand > xCommand( xNode, UNO_QUERY ); + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimCommand && xCommand.is(), "invalid call to ppt::AnimationImporter::importCommandContainer()!"); + if( !(pAtom && xCommand.is()) ) + return; + + sal_Int32 nBits = 0; + Any aValue; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtCommandData: + { + sal_Int32 nCommandType; + // looks like U1 is a bitset, bit 1 enables the type and bit 2 enables + // a propertyvalue that follows + mrStCtrl.ReadInt32( nBits ); + mrStCtrl.ReadInt32( nCommandType ); + + if( nBits & 1 ) + { + dump( " type=\"%s\"", (nCommandType == 0) ? "event" : ( nCommandType == 1) ? "call" : "verb" ); + } + } + break; + + case DFF_msofbtAnimAttributeValue: + { + if ( importAttributeValue( pChildAtom, aValue ) ) + { + if( nBits & 2 ) + { + dump( " cmd=\"" ); + dump( aValue ); + dump( "\"" ); + } + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + + if( !(nBits & 3) ) + return; + + OUString aParam; + aValue >>= aParam; + + sal_Int16 nCommand = EffectCommands::CUSTOM; + + NamedValue aParamValue; + + if ( aParam == "onstopaudio" ) + { + nCommand = EffectCommands::STOPAUDIO; + } + else if ( aParam == "play" ) + { + nCommand = EffectCommands::PLAY; + } + else if( aParam.startsWith( "playFrom" ) ) + { + const std::u16string_view aMediaTime( aParam.subView( 9, aParam.getLength() - 10 ) ); + rtl_math_ConversionStatus eStatus; + double fMediaTime = ::rtl::math::stringToDouble( aMediaTime, u'.', u',', &eStatus ); + if( eStatus == rtl_math_ConversionStatus_Ok ) + { + aParamValue.Name = "MediaTime"; + aParamValue.Value <<= fMediaTime; + } + nCommand = EffectCommands::PLAY; + } + else if ( aParam == "togglePause" ) + { + nCommand = EffectCommands::TOGGLEPAUSE; + } + else if ( aParam == "stop" ) + { + nCommand = EffectCommands::STOP; + } + + xCommand->setCommand( nCommand ); + if( nCommand == EffectCommands::CUSTOM ) + { + OSL_FAIL("sd::AnimationImporter::importCommandContainer(), unknown command!"); + aParamValue.Name = "UserDefined"; + aParamValue.Value <<= aParam; + } + + if( aParamValue.Value.hasValue() ) + { + Sequence< NamedValue > aParamSeq( &aParamValue, 1 ); + xCommand->setParameter( Any( aParamSeq ) ); + } +} + +int AnimationImporter::importAudioContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + int nNodes = 0; + + Reference< XAudio > xAudio( xNode, UNO_QUERY ); + DBG_ASSERT( pAtom && xAudio.is() && + ( (pAtom->getType() == DFF_msofbtAnimGroup) || + (pAtom->getType() == DFF_msofbtAnimSubGoup) ), "invalid call to ppt::AnimationImporter::importAudioContainer()!"); + if( pAtom && xAudio.is() ) + { + importAnimationEvents( pAtom, xNode ); + importAnimationValues( pAtom, xNode ); + importAnimationActions( pAtom, xNode ); + + dump(">\n"); + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimNode: + case DFF_msofbtAnimEvent: + case DFF_msofbtAnimValue: + case DFF_msofbtAnimAction: + case DFF_msofbtAnimPropertySet: + break; + + case DFF_msofbtAnimAttributeValue: + { + Any aValue; + if ( importAttributeValue( pChildAtom, aValue ) ) + { + nNodes ++; + dump( " value=\"" ); + dump( aValue ); + dump( "\"" ); + } + } + break; + + case DFF_msofbtAnimateTargetElement: + { + sal_Int16 nSubType; + Any aSource; + importTargetElementContainer( pChildAtom, aSource, nSubType ); + if( xAudio.is() ) { + xAudio->setSource( aSource ); + nNodes ++; + } + } + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + + // TODO: What to do with them? + Any aEmpty; + xAudio->setBegin( aEmpty ); + xAudio->setEnd( aEmpty ); + } + + return nNodes; +} + +void AnimationImporter::importAnimateScaleContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateTransform > xTransform( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateScale && xTransform.is(), "invalid call to ppt::AnimationImporter::importAnimateScaleContainer()!"); + if( !(pAtom && xTransform.is()) ) + return; + + xTransform->setTransformType( AnimationTransformType::SCALE ); + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateScaleData: + { + sal_uInt32 nBits(0), nZoomContents(0); + float fByX(0.0), fByY(0.0), fFromX(0.0), fFromY(0.0), fToX(0.0), fToY(0.0); + + // nBits %001: by, %010: from, %100: to, %1000: zoomContents(bool) + mrStCtrl.ReadUInt32( nBits ).ReadFloat( fByX ).ReadFloat( fByY ).ReadFloat( fFromX ).ReadFloat( fFromY ).ReadFloat( fToX ).ReadFloat( fToY ).ReadUInt32( nZoomContents ); + + ValuePair aPair; + // 'from' value + if( nBits & 2 ) + { + aPair.First <<= static_cast<double>(fFromX) / 100.0; + aPair.Second <<= static_cast<double>(fFromY) / 100.0; + xTransform->setFrom( Any( aPair ) ); + } + + // 'to' value + if( nBits & 4 ) + { + aPair.First <<= static_cast<double>(fToX) / 100.0; + aPair.Second <<= static_cast<double>(fToY) / 100.0; + xTransform->setTo( Any( aPair ) ); + } + + // 'by' value + if( nBits & 1 ) + { + aPair.First <<= static_cast<double>(fByX) / 100.0; + aPair.Second <<= static_cast<double>(fByY) / 100.0; + + if( nBits & 2 ) + { + // 'from' value given, import normally + xTransform->setBy( Any( aPair ) ); + } + else + { + // mapping 'by' to 'to', if no 'from' is + // given. This is due to a non-conformity in + // PPT, which exports animateScale effects + // with a sole 'by' value, but with the + // semantics of a sole 'to' animation + xTransform->setTo( Any( aPair ) ); + } + } + +#ifdef DBG_ANIM_LOG + if( nBits & 1 ) + fprintf( mpFile, " by=\"%g,%g\"", (double)fByX, (double)fByY ); + + if( nBits & 2 ) + fprintf( mpFile, " from=\"%g,%g\"", (double)fFromX, (double)fFromY ); + + if( nBits & 4 ) + fprintf( mpFile, " to=\"%g,%g\"", (double)fToX, (double)fToY ); + + if( nBits & 8 ) + fprintf( mpFile, " zoomContents=\"%s\"", nZoomContents ? "true" : "false" ); +#endif + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateRotationContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateTransform > xTransform( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateRotation && xTransform.is(), "invalid call to ppt::AnimationImporter::importAnimateRotationContainer()!"); + if( !(pAtom && xTransform.is()) ) + return; + + xTransform->setTransformType( AnimationTransformType::ROTATE ); + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateRotationData: + { + sal_uInt32 nBits(0), nU1(0); + float fBy(0.0), fFrom(0.0), fTo(0.0); + + // nBits %001: by, %010: from, %100: to, %1000: zoomContents(bool) + mrStCtrl.ReadUInt32( nBits ).ReadFloat( fBy ).ReadFloat( fFrom ).ReadFloat( fTo ).ReadUInt32( nU1 ); + + if( nBits & 1 ) + xTransform->setBy( Any( static_cast<double>(fBy) ) ); + + if( nBits & 2 ) + xTransform->setFrom( Any( static_cast<double>(fFrom) ) ); + + if( nBits & 4 ) + xTransform->setTo( Any( static_cast<double>(fTo) ) ); + +#ifdef DBG_ANIM_LOG + if( nBits & 1 ) + fprintf( mpFile, " by=\"%g\"", (double)fBy ); + + if( nBits & 2 ) + fprintf( mpFile, " from=\"%g\"", (double)fFrom ); + + if( nBits & 4 ) + fprintf( mpFile, " to=\"%g\"", (double)fTo ); + + if( nU1 ) + fprintf( mpFile, " rotation_1=\"%" SAL_PRIdINT32 "\"", nU1 ); +#endif + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAttributeNamesContainer( const Atom* pAtom, OUString& rAttributeNames ) +{ + OUStringBuffer aNames; + + DBG_ASSERT( pAtom && (pAtom->getType() == DFF_msofbtAnimateAttributeNames), "invalid call to ppt::AnimationImporter::importAttributeName()!" ); + if( pAtom ) + { + const Atom* pAttributeValueAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimAttributeValue ); + + while( pAttributeValueAtom ) + { + Any aAny; + if ( importAttributeValue( pAttributeValueAtom, aAny ) ) + { + OUString aName; + if( aAny >>= aName ) + { + if( !aNames.isEmpty() ) + aNames.append( ';' ); + + aNames.append( aName ); + } + } + else + { + OSL_FAIL( "error during ppt::AnimationImporter::importAttributeName()!" ); + } + + pAttributeValueAtom = pAtom->findNextChildAtom( DFF_msofbtAnimAttributeValue, pAttributeValueAtom ); + } + } + + rAttributeNames = aNames.makeStringAndClear(); +} + +void AnimationImporter::importAnimationValues( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( pAtom, "invalid call to ppt::AnimationImporter::importAnimationValues()!" ); + + if( !pAtom ) + return; + + const Atom* pValueAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimValue ); + + while( pValueAtom && pValueAtom->seekToContent() ) + { + sal_uInt32 nType(0); + mrStCtrl.ReadUInt32( nType ); + switch( nType ) + { + case 0: + { + float fRepeat(0.0); + mrStCtrl.ReadFloat( fRepeat ); + xNode->setRepeatCount( (fRepeat < (float(3.40282346638528860e+38))) ? Any( static_cast<double>(fRepeat) ) : Any( Timing_INDEFINITE ) ); + +#ifdef DBG_ANIM_LOG + if( (fRepeat < ((float)3.40282346638528860e+38)) ) + { + dump( " repeat=\"%g\"", (double)fRepeat ); + } + else + { + dump( " repeat=\"indefinite\"" ); + } +#endif + } + break; + + case 3: + { + float faccelerate(0.0); + mrStCtrl.ReadFloat( faccelerate ); + xNode->setAcceleration( faccelerate ); + dump( " accelerate=\"%g\"", static_cast<double>(faccelerate) ); + } + break; + + case 4: + { + float fdecelerate(0.0); + mrStCtrl.ReadFloat( fdecelerate ); + xNode->setDecelerate( fdecelerate ); + dump( " decelerate=\"%g\"", static_cast<double>(fdecelerate) ); + } + break; + + case 5: + { + sal_Int32 nAutoreverse(0); + mrStCtrl.ReadInt32( nAutoreverse ); + xNode->setAutoReverse( nAutoreverse != 0 ); + dump( " autoreverse=\"%#lx\"", nAutoreverse ); + } + break; + + default: + { + sal_uInt32 nUnknown; + mrStCtrl.ReadUInt32( nUnknown ); +#ifdef DBG_ANIM_LOG + fprintf(mpFile, " attribute_%d=\"%#lx\"", nType, nUnknown ); +#endif + } + break; + } + + pValueAtom = pAtom->findNextChildAtom( DFF_msofbtAnimValue, pValueAtom ); + } +} + +void AnimationImporter::importAnimateKeyPoints( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimate > xAnim( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimKeyPoints && xAnim.is(), "invalid call to ppt::AnimationImporter::importAnimateKeyPoints()!" ); + + if( !(pAtom && xAnim.is()) ) + return; + + // first count keytimes + const Atom* pIter = nullptr; + int nKeyTimes = 0; + + while( (pIter = pAtom->findNextChildAtom( DFF_msofbtAnimKeyTime, pIter )) != nullptr ) + nKeyTimes++; + + Sequence< double > aKeyTimes( nKeyTimes ); + auto aKeyTimesRange = asNonConstRange(aKeyTimes); + Sequence< Any > aValues( nKeyTimes ); + auto aValuesRange = asNonConstRange(aValues); + OUString aFormula; + + pIter = pAtom->findFirstChildAtom(DFF_msofbtAnimKeyTime); + bool bToNormalize = false; + for( int nKeyTime = 0; (nKeyTime < nKeyTimes) && pIter; nKeyTime++ ) + { + if( pIter->seekToContent() ) + { + sal_Int32 nTemp(0); + mrStCtrl.ReadInt32(nTemp); + double fTemp = static_cast<double>(nTemp) / 1000.0; + aKeyTimesRange[nKeyTime] = fTemp; + if( fTemp == -1 ) + bToNormalize = true; + + const Atom* pValue = Atom::findNextChildAtom(pIter); + if( pValue && pValue->getType() == DFF_msofbtAnimAttributeValue ) + { + Any aValue1, aValue2; + if( importAttributeValue( pValue, aValue1 ) ) + { + pValue = Atom::findNextChildAtom(pValue); + if( pValue && pValue->getType() == DFF_msofbtAnimAttributeValue ) + { + // Any occurrence of the formula becomes the formula of the whole list. + if (importAttributeValue(pValue, aValue2) && aFormula.isEmpty()) + aValue2 >>= aFormula; + } + aValuesRange[nKeyTime] = aValue1; + } + } + } + pIter = pAtom->findNextChildAtom(DFF_msofbtAnimKeyTime, pIter); + } + +#ifdef DBG_ANIM_LOG + dump( " keyTimes=\"" ); + for( int i=0; i<nKeyTimes; ++i ) + dump( "%f;", aKeyTimes[i] ); + + if( !aFormula.isEmpty() ) + { + dump( "formula=\"%s", aFormula ); + } + + dump( "\" values=\"" ); + double nVal; + OUString aStr; + for( int i=0; i<nKeyTimes; ++i ) + { + if( i != 0 ) + dump( ";" ); + + if( aValues[i] >>= aStr ) + dump( "%s", + OUStringToOString( aStr, + RTL_TEXTENCODING_ASCII_US ).getStr() ); + else if( aValues[i] >>= nVal ) + dump( "%f", nVal ); + else + { + ValuePair aValuePair; + + if( aValues[i] >>= aValuePair ) + { + if( aValuePair.First >>= aStr ) + dump( "%s", + OUStringToOString( aStr, + RTL_TEXTENCODING_ASCII_US ).getStr() ); + else if( aValuePair.First >>= nVal ) + dump( "%f", nVal ); + else + dump( "%X", (sal_Int64)&aValuePair.First ); + + if( aValuePair.Second >>= aStr ) + dump( ",%s", + OUStringToOString( aStr, + RTL_TEXTENCODING_ASCII_US ).getStr() ); + else if( aValuePair.Second >>= nVal ) + dump( ",%f", nVal ); + else + dump( ",%X", (sal_Int64)&aValuePair.Second ); + } + } + } + dump( "\"" ); +#endif + if( bToNormalize && nKeyTimes >= 2 ) + { + // if TimeAnimationValueList contains time -1000, key points must be evenly distributed between 0 and 1 ([MS-PPT] 2.8.31) + for( int nKeyTime = 0; nKeyTime < nKeyTimes; ++nKeyTime ) + { + aKeyTimesRange[nKeyTime] = static_cast<double>(nKeyTime) / static_cast<double>(nKeyTimes - 1); + } + } + + if (aValues.getLength() != aKeyTimes.getLength()) + throw css::io::WrongFormatException(); + + xAnim->setKeyTimes( aKeyTimes ); + xAnim->setValues( aValues ); + xAnim->setFormula( aFormula ); +} + +bool AnimationImporter::importAttributeValue( const Atom* pAtom, Any& rAny ) +{ + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimAttributeValue, "invalid call to ppt::AnimationImporter::importAttributeValue()!" ); + + bool bOk = false; + + if( pAtom && pAtom->seekToContent() ) + { + sal_uInt32 nRecLen = pAtom->getLength(); + if ( nRecLen >= 1 ) + { + sal_Int8 nType(0); + mrStCtrl.ReadSChar( nType ); + switch( nType ) + { + case DFF_ANIM_PROP_TYPE_BYTE : + { + if ( nRecLen == 2 ) + { + sal_uInt8 nByte(0); + mrStCtrl.ReadUChar( nByte ); + rAny <<= nByte; + + bOk = true; + } + } + break; + + case DFF_ANIM_PROP_TYPE_INT32 : + { + if ( nRecLen == 5 ) + { + sal_uInt32 nInt32(0); + mrStCtrl.ReadUInt32( nInt32 ); + rAny <<= nInt32; + + bOk = true; + } + } + break; + + case DFF_ANIM_PROP_TYPE_FLOAT: + { + if( nRecLen == 5 ) + { + float fFloat(0.0); + mrStCtrl.ReadFloat( fFloat ); + rAny <<= static_cast<double>(fFloat); + + bOk = true; + } + } + break; + + case DFF_ANIM_PROP_TYPE_UNISTRING : + { + if ( ( nRecLen & 1 ) && ( nRecLen > 1 ) ) + { + OUString aOUString = SvxMSDffManager::MSDFFReadZString( mrStCtrl, nRecLen - 1, true ); + rAny <<= aOUString; + + bOk = true; + } + } + break; + } + } + } + + DBG_ASSERT( bOk, "invalid value inside ppt::AnimationImporter::importAttributeValue()!" ); + return bOk; +} + +void AnimationImporter::importAnimationEvents( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( xNode.is() && pAtom, "invalid call to ppt::AnimationImporter::importAnimationEvents()!" ); + + Any aBegin, aEnd, aNext, aPrev; + + const Atom* pEventAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimEvent ); + while( pEventAtom ) + { + Any* pEvents = nullptr; + + switch( pEventAtom->getInstance() ) + { + case 1: pEvents = &aBegin; break; + case 2: pEvents = &aEnd; break; + case 3: pEvents = &aNext; break; + case 4: pEvents = &aPrev; break; + } + + if( pEvents ) + { + Event aEvent; + aEvent.Trigger = EventTrigger::NONE; + aEvent.Repeat = 0; + + const Atom* pChildAtom = pEventAtom->findFirstChildAtom(); + + while( pChildAtom && pChildAtom->seekToContent() ) + { + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimTrigger: + { + sal_Int32 nU1(0), nTrigger(0), nU3(0), nBegin(0); + mrStCtrl.ReadInt32( nU1 ); + mrStCtrl.ReadInt32( nTrigger ); + mrStCtrl.ReadInt32( nU3 ); + mrStCtrl.ReadInt32( nBegin ); + + switch( nTrigger ) + { + case 0: aEvent.Trigger = EventTrigger::NONE; break; + case 1: aEvent.Trigger = EventTrigger::ON_BEGIN; break; + case 2: aEvent.Trigger = EventTrigger::ON_END; break; + case 3: aEvent.Trigger = EventTrigger::BEGIN_EVENT; break; + case 4: aEvent.Trigger = EventTrigger::END_EVENT; break; + case 5: aEvent.Trigger = EventTrigger::ON_CLICK; break; + case 6: aEvent.Trigger = EventTrigger::ON_DBL_CLICK; break; + case 7: aEvent.Trigger = EventTrigger::ON_MOUSE_ENTER; break; + case 8: aEvent.Trigger = EventTrigger::ON_MOUSE_LEAVE; break; + case 9: aEvent.Trigger = EventTrigger::ON_NEXT; break; + case 10: aEvent.Trigger = EventTrigger::ON_PREV; break; + case 11: aEvent.Trigger = EventTrigger::ON_STOP_AUDIO; break; + } + + if( (nBegin != 0) || (aEvent.Trigger == EventTrigger::NONE) ) + aEvent.Offset = (nBegin == -1) ? Any( Timing_INDEFINITE ) : Any( nBegin / 1000.0 ); + } + break; + case DFF_msofbtAnimateTargetElement: + { + sal_Int16 nSubType; + importTargetElementContainer( pChildAtom, aEvent.Source, nSubType ); + } + break; + default: + { + OSL_FAIL("unknown atom inside ppt::AnimationImporter::importAnimationEvents()!"); + } + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + + *pEvents = oox::addToSequence( *pEvents, (aEvent.Trigger == EventTrigger::NONE) ? aEvent.Offset : Any( aEvent ) ); + } + + pEventAtom = pAtom->findNextChildAtom( DFF_msofbtAnimEvent, pEventAtom ); + } + + xNode->setBegin( aBegin ); + xNode->setEnd( aEnd ); + // TODO: xNode->setNext( aNext ); + // TODO: xNode->setPrev( aNext ); + +#ifdef DBG_ANIM_LOG + if( aBegin.hasValue() ) + { + dump( " begin=\"" ); + dump( aBegin ); + dump( "\"" ); + } + + if( aEnd.hasValue() ) + { + dump( " end=\"" ); + dump( aEnd ); + dump( "\"" ); + } + + if( aNext.hasValue() ) + { + dump( " next=\"" ); + dump( aNext ); + dump( "\"" ); + } + + if( aPrev.hasValue() ) + { + dump( " prev=\"" ); + dump( aPrev ); + dump( "\"" ); + } +#endif +} + +void AnimationImporter::importAnimationActions( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( pAtom && xNode.is(), "invalid call to ppt::AnimationImporter::importAnimationActions()!"); + + if( !pAtom ) + return; + + const Atom* pActionAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimAction ); + + if( !(pActionAtom && pActionAtom->seekToContent()) ) + return; + + sal_Int32 nConcurrent(0), nNextAction(0), nEndSync(0), nU4(0), nU5(0); + mrStCtrl.ReadInt32( nConcurrent ); + mrStCtrl.ReadInt32( nNextAction ); + mrStCtrl.ReadInt32( nEndSync ); + mrStCtrl.ReadInt32( nU4 ); + mrStCtrl.ReadInt32( nU5 ); + + if( nEndSync == 1 ) + xNode->setEndSync( Any( AnimationEndSync::ALL ) ); + +#ifdef DBG_ANIM_LOG + dump( " concurrent=\"%s\"", nConcurrent == 0 ? "disabled" : (nConcurrent == 1 ? "enabled" : "unknown") ); + + dump( " nextAction=\"%s\"", nNextAction == 0 ? "none" : (nNextAction == 1 ? "seek" : "unknown") ); + + if( nEndSync != 0 ) + { + dump( " endSync=\"%s\"", nEndSync == 1 ? "all" : "unknown" ); + } + + dump( " action_4=\"%#lx\"", nU4 ); + dump( " action_5=\"%#lx\"", nU5 ); +#endif +} + +void AnimationImporter::importTargetElementContainer( const Atom* pAtom, Any& rTarget, sal_Int16& rSubType ) +{ + rSubType = ShapeAnimationSubType::AS_WHOLE; + sal_Int32 nRefMode = -1; + + DBG_ASSERT( pAtom && (pAtom->getType() == DFF_msofbtAnimateTargetElement), "invalid call to ppt::AnimationImporter::importTargetElementContainer()!" ); + if( !pAtom ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + while( pChildAtom && pChildAtom->seekToContent() ) + { + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimReference: + { + sal_Int32 nRefType(0), nRefId(0); + sal_Int32 begin(0), end(0); + mrStCtrl.ReadInt32( nRefMode ); + mrStCtrl.ReadInt32( nRefType ); + mrStCtrl.ReadInt32( nRefId ); + mrStCtrl.ReadInt32( begin ); + mrStCtrl.ReadInt32( end ); + + switch( nRefType ) + { + case 1: // shape + { + SdrObject* pSdrObject = mpPPTImport->getShapeForId( nRefId ); + if( pSdrObject == nullptr ) + break; + + rTarget <<= pSdrObject->getUnoShape(); + + switch( nRefMode ) + { + case 6: rSubType = ShapeAnimationSubType::ONLY_BACKGROUND; break; + case 8: rSubType = ShapeAnimationSubType::ONLY_TEXT; break; + case 2: // one paragraph + { + if((begin == -1) && (end == -1)) + break; + + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pSdrObject ); + if(!pTextObj) + break; + + const OutlinerParaObject* pOPO = pTextObj->GetOutlinerParaObject(); + if( pOPO == nullptr ) + break; + + const EditTextObject& rEditTextObject = pOPO->GetTextObject(); + + const sal_Int32 nParaCount = rEditTextObject.GetParagraphCount(); + + sal_Int32 nPara = 0; + + while( (nPara < nParaCount) && (begin > 0) ) + { + sal_Int32 nParaLength = rEditTextObject.GetText( nPara ).getLength() + 1; + begin -= nParaLength; + end -= nParaLength; + nPara++; + } + + if( nPara < nParaCount ) + { + ParagraphTarget aParaTarget; + rTarget >>= aParaTarget.Shape; + /* FIXME: Paragraph should be sal_Int32 as well */ + aParaTarget.Paragraph = static_cast<sal_Int16>(nPara); + rTarget <<= aParaTarget; + + rSubType = ShapeAnimationSubType::ONLY_TEXT; + dump( " paragraph %d,", nPara); + dump( " %d characters", end ); + } + } + } + } + break; + + case 2: // sound + { + OUString aSoundURL( mpPPTImport->ReadSound( nRefId ) ); + rTarget <<= aSoundURL; + dump( " srcRef=\"%s\"", aSoundURL ); + } + break; + case 3: // audio object + case 4: // video object + { + SdrObject* pSdrObject = mpPPTImport->getShapeForId( nRefId ); + if( pSdrObject == nullptr ) + break; + + rTarget <<= pSdrObject->getUnoShape(); + } + break; + default: + OSL_FAIL("unknown reference type"); + } + + } + break; + case 0x2b01: + { + sal_Int32 nU1; + mrStCtrl.ReadInt32( nU1 ); + } + break; + default: + OSL_FAIL("unknown atom inside ppt::AnimationImporter::importTargetElementContainer()!"); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + + } +} + +void AnimationImporter::importPropertySetContainer( const Atom* pAtom, PropertySet& rSet ) +{ + DBG_ASSERT( pAtom && (pAtom->getType() == DFF_msofbtAnimPropertySet), "invalid call to ppt::AnimationImporter::importPropertySetContainer()!" ); + + if( !pAtom ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + while( pChildAtom ) + { + if( pChildAtom->getType() == DFF_msofbtAnimAttributeValue ) + { + Any aAny; + (void)importAttributeValue( pChildAtom, aAny ); + rSet.maProperties[ pChildAtom->getInstance() ] = aAny; + } + else + { + OSL_FAIL("unknown atom inside ppt::AnimationImporter::importPropertySetContainer()!"); + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +#ifdef DBG_ANIM_LOG +void AnimationImporter::dump_atom_header( const Atom* pAtom, bool bOpen, bool bAppend ) +{ + if( pAtom ) + { + const char* pTitle; + + switch( pAtom->getType() ) + { + case DFF_msofbtAnimEvent: pTitle = "AnimEvent"; break; + case DFF_msofbtAnimTrigger: pTitle = "AnimTrigger"; break; + case DFF_msofbtAnimateMotion: pTitle = "AnimateMotion"; break; + case DFF_msofbtAnimPropertySet: pTitle = "AnimPropertySet"; break; + case DFF_msofbtAnimateAttributeNames: pTitle = "AnimAttributeName"; break; + case DFF_msofbtAnimAttributeValue: pTitle = "AnimAttributeValue"; break; + case DFF_msofbtAnimGroup: pTitle = "AnimGroup"; break; + case DFF_msofbtAnimNode: pTitle = "AnimNode"; break; + case DFF_msofbtAnimValue: pTitle = "AnimValue"; break; + case DFF_msofbtAnimateFilter: pTitle = "animateFilter"; break; + case DFF_msofbtAnimate: pTitle = "animate"; break; + case DFF_msofbtAnimateSet: pTitle = "set"; break; + case DFF_msofbtAnimKeyTime: pTitle = "AnimKeyTime"; break; + case DFF_msofbtAnimKeyPoints: pTitle = "AnimKeyPoints"; break; + case DFF_msofbtAnimReference: pTitle = "AnimReference"; break; + case DFF_msofbtAnimateTargetElement: pTitle = "AnimTargetElementContainer"; break; + case DFF_msofbtAnimAction: pTitle = "AnimAction"; break; + case DFF_msofbtAnimCommand: pTitle = "AnimCommand"; break; + case DFF_msofbtAnimateTarget: pTitle = "TransformationTarget"; break; + case DFF_msofbtAnimateTargetSettings: pTitle = "TransformationTargetSettings"; break; + case DFF_msofbtAnimIteration: pTitle = "iterate"; break; + case DFF_msofbtAnimateColorData: pTitle = "colorData"; break; + case DFF_msofbtAnimateScaleData: pTitle = "scaleData"; break; + case DFF_msofbtAnimateSetData: pTitle = "setData"; break; + + default: + { + static char buffer[128]; + sprintf( buffer, "unknown_%#x", pAtom->getType() ); + pTitle = buffer; + } + } + + if( bOpen ) + { + fprintf(mpFile, "<%s", pTitle ); + + fprintf(mpFile, " instance=\"%hu\"%s", + pAtom->getInstance(), + bAppend ? "" : ">\n"); + } + else + { + if( bAppend ) + fprintf(mpFile,"/>\n"); + else + fprintf(mpFile, "</%s>\n", pTitle ); + } + } +} + +void AnimationImporter::dump( sal_uInt32 nLen, bool bNewLine ) +{ + char * faul = "0123456789abcdef"; + + sal_uInt32 i = 0; + int b = 0; + char nData; + + for( i = 0; i < nLen; i++ ) + { + mrStCtrl.ReadChar(nData); + + fprintf( mpFile, "%c%c ", faul[ (nData >> 4) & 0x0f ], faul[ nData & 0x0f ] ); + + b++; + if( bNewLine && (b == 32) ) + { + fprintf(mpFile,"\n"); + b = 0; + } + } + if( (b != 0) && bNewLine ) + fprintf(mpFile,"\n"); +} + +void AnimationImporter::dump_atom( const Atom* pAtom, bool bNewLine ) +{ + if( pAtom ) + { + if( pAtom->isContainer() ) + { + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + while( pChildAtom ) + { + if( pChildAtom->getType() == DFF_msofbtAnimAttributeValue ) + { + fprintf(mpFile, "<attributeValue instance=\"%hu\"", pChildAtom->getInstance() ); + + Any aValue; + if( importAttributeValue( pChildAtom, aValue ) ) + { + sal_Int32 nInt; + OUString aString; + double fDouble; + + if( aValue >>= nInt ) + { + fprintf(mpFile, " value=\"%" SAL_PRIdINT32 "\"", nInt ); + } + else if( aValue >>= aString ) + { + fprintf(mpFile, " value=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr()); + } + else if( aValue >>= fDouble ) + { + fprintf(mpFile, " value=\"%g\"", fDouble ); + } + } + else + { + if( pChildAtom->seekToContent() ) + { + fprintf(mpFile, " value=\"" ); + dump_atom( pChildAtom, false ); + fprintf(mpFile, "\""); + } + } + + fprintf(mpFile, "/>\n" ); + } + else + { + dump_atom_header( pChildAtom, true, pChildAtom->getType() == DFF_msofbtAnimAttributeValue ); + dump_atom( pChildAtom ); + dump_atom_header( pChildAtom, false, pChildAtom->getType() == DFF_msofbtAnimAttributeValue ); + } + + pChildAtom = Atom::findNextChildAtom(pChildAtom); + } + } + else if( pAtom->seekToContent() ) + { + dump( pAtom->getLength(), bNewLine ); + } + } +} + +void AnimationImporter::dump_anim_group( const Atom* pAtom, const AnimationNode& rNode, const PropertySet& rSet, bool bOpen ) +{ + fprintf( mpFile, bOpen ? "<" : "</" ); + + switch( rNode.mnGroupType ) + { + case mso_Anim_GroupType_PAR: + fprintf( mpFile, "par" ); + break; + case mso_Anim_GroupType_SEQ: + fprintf( mpFile, "seq" ); + break; + case mso_Anim_GroupType_NODE: + switch( rNode.mnNodeType ) + { + case mso_Anim_Behaviour_FILTER: + fprintf( mpFile, "animateFilter" ); + break; + case mso_Anim_Behaviour_ANIMATION: + if( pAtom->hasChildAtom( DFF_msofbtAnimateSet ) ) + fprintf( mpFile, "set" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimateColor ) ) + fprintf( mpFile, "animateColor" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimateScale ) ) + fprintf( mpFile, "animateScale" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimateRotation ) ) + fprintf( mpFile, "animateRotation" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimateMotion ) ) + fprintf( mpFile, "animateMotion" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimCommand ) ) + fprintf( mpFile, "command" ); + else + fprintf( mpFile, "animation" ); + break; + default: + { + fprintf( mpFile, "unknown_node_%#lx", rNode.mnNodeType ); + } + break; + } + break; + case mso_Anim_GroupType_MEDIA: + fprintf( mpFile, "media" ); + break; + default: + fprintf( mpFile, "unknown_group_%#lx", rNode.mnGroupType ); + break; + } + + if( bOpen ) + { + dump( rNode ); + dump( rSet ); + } + + fprintf(mpFile,">\n"); +} + +void AnimationImporter::dump( const AnimationNode& rNode ) +{ + // dump animation node + if( rNode.mnRestart != 0 ) + { + fprintf(mpFile," restart=\"%s\"", + rNode.mnRestart == 1 ? "always" : (rNode.mnRestart == 2 ? "whenOff" : (rNode.mnRestart == 3 ? "never" : "unknown")) ); + } + + if( rNode.mnFill ) + { + fprintf(mpFile," fill=\"%s\"", + rNode.mnFill == 1 ? "remove" : (rNode.mnFill == 3 ? "hold" : (rNode.mnFill == 2 ? "freeze" : "unknown")) ); + } + + if( rNode.mnDuration > 0 ) + { + double fSeconds = rNode.mnDuration; + fSeconds /= 1000.0; + fprintf(mpFile, " dur=\"%g\"", fSeconds); + } + else if( rNode.mnDuration < 0 ) + { + fprintf(mpFile, " dur=\"indefinite\"" ); + } + + if( rNode.mnU1 ) fprintf(mpFile," u1=\"%#lx\"", rNode.mnU1); + if( rNode.mnU3 ) fprintf(mpFile," u3=\"%#lx\"", rNode.mnU3); + if( rNode.mnU4 ) fprintf(mpFile," u4=\"%#lx\"", rNode.mnU4); +} + +void AnimationImporter::dump( Any& rAny ) +{ + Sequence< Any > aSeq; + sal_Int32 nInt; + double fDouble; + OUString aString; + sal_Bool bBool; + Event aEvent; + Timing aTiming; + + if( rAny >>= aSeq ) + { + const sal_Int32 nSize = aSeq.getLength(); + sal_Int32 nIndex = 0; + while( nIndex < nSize ) + { + dump( aSeq[nIndex++] ); + if(nIndex < nSize) + fprintf( mpFile, "," ); + } + } + else if( rAny >>= aString ) + { + fprintf( mpFile, "%s", OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + } + else if( rAny >>= nInt ) + { + fprintf( mpFile, "%" SAL_PRIdINT32, nInt ); + } + else if( rAny >>= bBool ) + { + fprintf( mpFile, "%s", bBool ? "true" : "false" ); + } + else if( rAny >>= fDouble ) + { + fprintf( mpFile, "%g", fDouble ); + } + else if( rAny >>= aTiming ) + { + fprintf( mpFile, "%s", aTiming == (Timing_INDEFINITE) ? "indefinite" : "media" ); + } + else if( rAny >>= aEvent ) + { + if( aEvent.Trigger != EventTrigger::NONE ) + { + static const char* triggers[] = + { + "none","onbegin","onend","begin", + "end","onclick","ondoubleclick","onmouseenter", + "onmouseleave","onpptnext","onpptprev","onstopaudio" + }; + + if( aEvent.Source.hasValue() ) + { + dump_target( aEvent.Source ); + dump( "." ); + } + + dump( triggers[ aEvent.Trigger ] ); + } + + if( aEvent.Offset.hasValue() ) + { + double fOffset; + if( aEvent.Offset >>= fOffset ) + fprintf( mpFile, "%g", fOffset ); + else + dump( "indefinite" ); + } + } +} + +void AnimationImporter::dump( const PropertySet& rSet ) +{ + // dump property set + + for( const auto& rProp : rSet.maProperties ) + { + bool bKnown = false; + + const sal_Int32 nInstance = rProp.first; + Any aAny( rProp.second ); + + switch ( nInstance ) + { + case DFF_ANIM_COLORSPACE: + { + sal_Int32 nColorSpace; + if( aAny >>= nColorSpace ) + { + fprintf( mpFile, " colorSpace=\"%s\"", (nColorSpace == 0) ? "rgb" : (nColorSpace == 1) ? "hsl" : "unknown" ); + bKnown = true; + } + } + break; + + case DFF_ANIM_DIRECTION: + { + sal_Bool bDirection; + if( aAny >>= bDirection ) + { + fprintf( mpFile, " direction=\"%s\"", bDirection ? "cclockwise" : "clockwise" ); + bKnown = true; + } + else + { + sal_Int32 nMasterRel; + if( aAny >>= nMasterRel ) + { + fprintf( mpFile, " direction=\"%s\"", nMasterRel == 0 ? "sameClick" : ( nMasterRel == 2 ? "nextClick" : "lastClick" ) ); + bKnown = true; + } + } + } + break; + + case DFF_ANIM_OVERRIDE: // TODO + { + sal_Int32 nOverride; + if( aAny >>= nOverride ) + { + fprintf( mpFile, " override=\"%s\"", (nOverride == 1) ? "childStyle" : (nOverride == 0) ? "normal" : "unknown" ); + bKnown = true; + } + } + break; + + case DFF_ANIM_PATH_EDIT_MODE: + { + sal_Bool bPathEditMode; + if( aAny >>= bPathEditMode ) + { + fprintf( mpFile, " pptPathEditMode=\"%s\"", bPathEditMode ? "relative" : "fixed" ); + bKnown = true; + } + } + break; + + case DFF_ANIM_PRESET_ID : + { + sal_Int32 nPresetId ; + if( aAny >>= nPresetId ) + { + fprintf(mpFile, " presetid=\"%" SAL_PRIdINT32 "\"", nPresetId ); + bKnown = true; + } + } + break; + + case DFF_ANIM_PRESET_SUB_TYPE : + { + sal_Int32 nPointsType ; + if( aAny >>= nPointsType ) + { + fprintf(mpFile, " presetSubType=\"%" SAL_PRIdINT32 "\"", nPointsType ); + bKnown = true; + } + } + break; + + case DFF_ANIM_PRESET_CLASS : + { + sal_Int32 nPresetClass; + if ( aAny >>= nPresetClass ) + { + const char* pMode; + switch( nPresetClass ) + { + case DFF_ANIM_PRESS_CLASS_USER_DEFINED: pMode = "userdefined"; break; + case DFF_ANIM_PRESS_CLASS_ENTRANCE: pMode = "entrance"; break; + case DFF_ANIM_PRESS_CLASS_EXIT: pMode = "exit"; break; + case DFF_ANIM_PRESS_CLASS_EMPHASIS: pMode = "emphasis"; break; + case DFF_ANIM_PRESS_CLASS_MOTIONPATH: pMode = "motionpath"; break; + case DFF_ANIM_PRESS_CLASS_OLE_ACTION: pMode = "oleaction"; break; + case DFF_ANIM_PRESS_CLASS_MEDIACALL: pMode = "mediacall"; break; + default: + pMode = nullptr; + break; + } + + if (pMode) + fprintf(mpFile, " class=\"%s\"", pMode); + else + fprintf(mpFile, " class =\"%" SAL_PRIdINT32 "\"", nPresetClass); + bKnown = true; + } + } + break; + + case DFF_ANIM_NODE_TYPE : + { + sal_Int32 nNodeType; + if ( aAny >>= nNodeType ) + { + const char* pNode; + switch( nNodeType ) + { + case DFF_ANIM_NODE_TYPE_ON_CLICK: pNode = "onclick"; break; + case DFF_ANIM_NODE_TYPE_WITH_PREVIOUS: pNode = "withprevious"; break; + case DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS: pNode = "afterprevious"; break; + case DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE: pNode = "mainsequence"; break; + case DFF_ANIM_NODE_TYPE_TIMING_ROOT: pNode = "timingroot"; break; + case DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ:pNode = "interactivesequence"; break; + default : + { + static char buffer[128]; + sprintf( buffer, "%" SAL_PRIdINT32, nNodeType ); + pNode = buffer; + } + break; + } + + fprintf(mpFile, " nodeType=\"%s\"", pNode); + bKnown = true; + } + } + break; + + case DFF_ANIM_GROUP_ID: + { + sal_Int32 nGroupId; + if ( aAny >>= nGroupId ) + { + fprintf( mpFile, " groupId=\"%" SAL_PRIdINT32 "\"", nGroupId ); + bKnown = true; + } + } + break; + + case DFF_ANIM_ID: + { + OUString aString; + if( aAny >>= aString ) + { + fprintf( mpFile, " id=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + bKnown = true; + } + } + break; + + case DFF_ANIM_EVENT_FILTER: + { + OUString aString; + if( aAny >>= aString ) + { + fprintf( mpFile, " eventFilter=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + bKnown = true; + } + } + break; + + case DFF_ANIM_ENDAFTERSLIDE: + { + sal_Int32 nEndAfterSlide; + if( aAny >>= nEndAfterSlide ) + { + fprintf(mpFile, " endAfterSlide=\"%" SAL_PRIdINT32 "\"", nEndAfterSlide ); + bKnown = true; + } + } + + case DFF_ANIM_TIMEFILTER: + { + OUString aString; + if( aAny >>= aString ) + { + fprintf( mpFile, " timeFilter=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + bKnown = true; + } + } + break; + + case DFF_ANIM_RUNTIMECONTEXT: + { + OUString aString; + if( aAny >>= aString ) + { + fprintf( mpFile, " runtimeContext=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + bKnown = true; + } + } + break; + + case DFF_ANIM_VOLUME: + { + double fVolume(0.0); + if( aAny >>= fVolume ) + { + fprintf( mpFile, " volume=\"%g%%\"", (double)(fVolume * 100.0) ); + bKnown = true; + } + } + break; + + case DFF_ANIM_AFTEREFFECT: + { + sal_Bool bAfterEffect; + if( aAny >>= bAfterEffect ) + { + fprintf( mpFile, "afterEffect=\"%s\"", bAfterEffect ? "true" : "false" ); + bKnown = true; + } + } + break; + + } + + if( !bKnown ) + { + fprintf( mpFile, " unknown_%" SAL_PRIdINT32 "=\"", nInstance ); + dump( aAny ); + fprintf( mpFile, "\"" ); + } + } +} + +void AnimationImporter::dump_target( Any& rAny ) +{ + Any aSource, aSourceData; + Sequence< Any > aSeq; + if( rAny >>= aSeq ) + { + if( aSeq.getLength() >= 1 ) aSource = aSeq[0]; + if( aSeq.getLength() >= 2 ) aSourceData = aSeq[1]; + } + else + { + aSource = rAny; + } + + Reference< XShape > xShape; + aSource >>= xShape; + if( xShape.is() ) + { + OUString aStr( xShape->getShapeType() ); + dump( aStr ); + + if( aSourceData.hasValue() ) + { + dump( "(" ); + dump( aSourceData ); + dump( ")" ); + } + } +} + +void AnimationImporter::dump( const char * pText ) +{ + fprintf( mpFile, "%s", pText ); +} + +void AnimationImporter::dump( const OUString& rString ) +{ + fprintf( mpFile, OUStringToOString(rString, + RTL_TEXTENCODING_UTF8).getStr() ); +} + +void AnimationImporter::dump( const char * pText, sal_Int64 nInt ) +{ + fprintf( mpFile, pText, nInt ); +} + +void AnimationImporter::dump( const char * pText, sal_Int32 nInt ) +{ + fprintf( mpFile, pText, nInt ); +} + +void AnimationImporter::dump( const char * pText, double fDouble ) +{ + fprintf( mpFile, pText, fDouble ); +} + +void AnimationImporter::dump( const char * pText, const char * pText2 ) +{ + fprintf( mpFile, pText, pText2 ); +} + +void AnimationImporter::dump( const char * pText, const OUString& rString ) +{ + fprintf( mpFile, pText, OUStringToOString(rString, + RTL_TEXTENCODING_UTF8).getStr() ); +} + +#else + +void AnimationImporter::dump_atom_header( const Atom* , bool , bool ) +{ +} + +void AnimationImporter::dump_atom( const Atom* , bool ) +{ +} + +void AnimationImporter::dump_target( css::uno::Any& ) +{ +} + +void AnimationImporter::dump( css::uno::Any& ) +{ +} + +void AnimationImporter::dump( const PropertySet& ) +{ +} + +void AnimationImporter::dump( const AnimationNode& ) +{ +} + +void AnimationImporter::dump( const char * ) +{ +} + +void AnimationImporter::dump( const char * , sal_Int32 ) +{ +} + +void AnimationImporter::dump( const char * , double ) +{ +} + +void AnimationImporter::dump( const char * , const char * ) +{ +} + +void AnimationImporter::dump( const char * , std::u16string_view ) +{ +} + +#endif + +} // namespace ppt; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptinanimations.hxx b/sd/source/filter/ppt/pptinanimations.hxx new file mode 100644 index 000000000..ed79144b9 --- /dev/null +++ b/sd/source/filter/ppt/pptinanimations.hxx @@ -0,0 +1,115 @@ +/* -*- 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 <com/sun/star/uno/Reference.hxx> + +#include <vector> +#include <animations.hxx> + +#ifdef DBG_ANIM_LOG +#include <stdio.h> +#endif + +namespace com::sun::star::animations { class XAnimationNode; } +namespace com::sun::star::drawing { class XDrawPage; } +namespace ppt { struct AnimationNode; } + +class DffRecordHeader; +class SvStream; +class ImplSdPPTImport; + +namespace ppt +{ +class PropertySet; +class Atom; + +class AnimationImporter +{ +public: + AnimationImporter( ImplSdPPTImport* pPPTImport, SvStream& rStCtrl ); + + int import( const css::uno::Reference< css::drawing::XDrawPage >& xPage, const DffRecordHeader& rProgTagContentHd ); + +private: + int importAnimationContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xParent ); + int importTimeContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + int importAnimationNodeContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + void importAnimateSetContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateFilterContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateScaleContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateColorContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateRotationContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateMotionContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importCommandContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + int importAudioContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + void importAnimationEvents( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimationValues( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimationActions( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateAttributeTargetContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + void importAnimateKeyPoints( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importPropertySetContainer( const Atom* pAtom,PropertySet& rSet ); + bool importAttributeValue( const Atom* pAtom, css::uno::Any& rAny ); + void importAttributeNamesContainer( const Atom* pAtom, OUString& rAttributeNames ); + void importTargetElementContainer( const Atom* pAtom, css::uno::Any& rTarget, sal_Int16& nSubType ); + + static void fillNode( css::uno::Reference< css::animations::XAnimationNode > const & xTiming, const AnimationNode& rNode, const PropertySet& rSet ); + static css::uno::Reference< css::animations::XAnimationNode > createNode( const Atom* pAtom, const AnimationNode& rNode ); + + bool convertAnimationNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode, const css::uno::Reference< css::animations::XAnimationNode >& xParent ); + css::uno::Any implGetColorAny( sal_Int32 nMode, sal_Int32 nA, sal_Int32 nB, sal_Int32 nC ); + static sal_Int16 implGetColorSpace( sal_Int32 nMode, sal_Int32 nA, sal_Int32 nB, sal_Int32 nC ); + +private: + css::uno::Reference< css::animations::XAnimationNode > mxRootNode; + + ImplSdPPTImport* mpPPTImport; + SvStream& mrStCtrl; + + std::vector< sd::AfterEffectNode > maAfterEffectNodes; + +#ifdef DBG_ANIM_LOG + FILE * mpFile; + void dump_anim_group( const Atom* pAtom, const AnimationNode& rNode, const PropertySet& rSet, bool bOpen ); + void dump( const OUString& rString ); + void dump( sal_uInt32 nLen, bool bNewLine = true ); +#endif + + static void dump_atom_header( const Atom* pAtom, bool bOpen, bool bAppend ); + static void dump_atom( const Atom* pAtom, bool bNewLine = true ); + static void dump_target( css::uno::Any& rAny ); + static void dump( css::uno::Any& rAny ); + static void dump( const PropertySet& rSet ); + static void dump( const AnimationNode& rNode ); + static void dump( const char * pText ); + static void dump( const char * pText, sal_Int32 nInt ); + void dump( const char * pText, sal_Int64 nInt ); + static void dump( const char * pText, double fDouble ); + static void dump( const char * pText, const char * pText2 ); + static void dump( const char * pText, std::u16string_view rString ); +}; + +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/propread.cxx b/sd/source/filter/ppt/propread.cxx new file mode 100644 index 000000000..c82c0d791 --- /dev/null +++ b/sd/source/filter/ppt/propread.cxx @@ -0,0 +1,615 @@ +/* -*- 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 "propread.hxx" +#include <rtl/tencinfo.h> +#include <rtl/textenc.h> +#include <sal/log.hxx> +#include <o3tl/sorted_vector.hxx> +#include <osl/diagnose.h> +#include <memory> + +PropEntry::PropEntry( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize ) : + mnId ( nId ), + mnSize ( nBufSize ), + mpBuf ( new sal_uInt8[ nBufSize ] ) +{ + memcpy( mpBuf.get(), pBuf, nBufSize ); +}; + +PropEntry::PropEntry( const PropEntry& rProp ) : + mnId ( rProp.mnId ), + mnSize ( rProp.mnSize ), + mpBuf ( new sal_uInt8[ mnSize ] ) +{ + memcpy( mpBuf.get(), rProp.mpBuf.get(), mnSize ); +}; + +PropEntry& PropEntry::operator=(const PropEntry& rPropEntry) +{ + if ( this != &rPropEntry ) + { + mnId = rPropEntry.mnId; + mnSize = rPropEntry.mnSize; + mpBuf.reset( new sal_uInt8[ mnSize ] ); + memcpy( mpBuf.get(), rPropEntry.mpBuf.get(), mnSize ); + } + return *this; +} + +void PropItem::Clear() +{ + Seek( STREAM_SEEK_TO_BEGIN ); + delete[] static_cast<sal_uInt8*>(SwitchBuffer()); +} + +static sal_Int32 lcl_getMaxSafeStrLen(sal_uInt32 nSize) +{ + nSize -= 1; //Drop NULL terminator + + //If it won't fit in a string, clip it to the max size that does + if (nSize > SAL_MAX_INT32) + nSize = SAL_MAX_INT32; + + return static_cast< sal_Int32 >( nSize ); +} + +bool PropItem::Read( OUString& rString, sal_uInt32 nStringType, bool bAlign ) +{ + sal_uInt32 nType, nItemPos; + bool bRetValue = false; + + nItemPos = Tell(); + + if ( nStringType == VT_EMPTY ) + { + nType = VT_NULL; // Initialize in case stream fails. + ReadUInt32( nType ); + } + else + nType = nStringType & VT_TYPEMASK; + + sal_uInt32 nItemSize(0); // Initialize in case stream fails. + ReadUInt32(nItemSize); + + switch( nType ) + { + case VT_LPSTR : + { + if (nItemSize) + { + auto nMaxSizePossible = remainingSize(); + if (nItemSize > nMaxSizePossible) + { + SAL_WARN("sd.filter", "String of Len " << nItemSize << " claimed, only " << nMaxSizePossible << " possible"); + nItemSize = nMaxSizePossible; + } + } + + if (nItemSize) + { + try + { + std::unique_ptr<char[]> pString( new char[ nItemSize ] ); + if ( mnTextEnc == RTL_TEXTENCODING_UCS2 ) + { + nItemSize >>= 1; + if ( nItemSize > 1 ) + { + sal_Unicode* pWString = reinterpret_cast<sal_Unicode*>(pString.get()); + for (sal_uInt32 i = 0; i < nItemSize; ++i) + ReadUtf16( pWString[ i ] ); + rString = OUString(pWString, lcl_getMaxSafeStrLen(nItemSize)); + } + else + rString.clear(); + bRetValue = true; + } + else + { + SvMemoryStream::ReadBytes(pString.get(), nItemSize); + if ( pString[ nItemSize - 1 ] == 0 ) + { + if ( nItemSize > 1 ) + rString = OUString(pString.get(), rtl_str_getLength(pString.get()), mnTextEnc); + else + rString.clear(); + bRetValue = true; + } + } + } + catch( const std::bad_alloc& ) + { + OSL_FAIL( "sd PropItem::Read bad alloc" ); + } + } + if ( bAlign ) + SeekRel( ( 4 - ( nItemSize & 3 ) ) & 3 ); // dword align + } + break; + + case VT_LPWSTR : + { + if (nItemSize) + { + auto nMaxSizePossible = remainingSize() / sizeof(sal_Unicode); + if (nItemSize > nMaxSizePossible) + { + SAL_WARN("sd.filter", "String of Len " << nItemSize << " claimed, only " << nMaxSizePossible << " possible"); + nItemSize = nMaxSizePossible; + } + } + + if (nItemSize) + { + try + { + std::unique_ptr<sal_Unicode[]> pString( new sal_Unicode[ nItemSize ] ); + for (sal_uInt32 i = 0; i < nItemSize; ++i) + ReadUtf16( pString[ i ] ); + if ( pString[ nItemSize - 1 ] == 0 ) + { + if ( static_cast<sal_uInt16>(nItemSize) > 1 ) + rString = OUString(pString.get(), lcl_getMaxSafeStrLen(nItemSize)); + else + rString.clear(); + bRetValue = true; + } + } + catch( const std::bad_alloc& ) + { + OSL_FAIL( "sd PropItem::Read bad alloc" ); + } + } + if ( bAlign && ( nItemSize & 1 ) ) + SeekRel( 2 ); // dword align + } + break; + } + if ( !bRetValue ) + Seek( nItemPos ); + return bRetValue; +} + +PropItem& PropItem::operator=( PropItem& rPropItem ) +{ + if ( this != &rPropItem ) + { + Seek( STREAM_SEEK_TO_BEGIN ); + delete[] static_cast<sal_uInt8*>(SwitchBuffer()); + + mnTextEnc = rPropItem.mnTextEnc; + SvMemoryStream::WriteBytes(rPropItem.GetData(), rPropItem.TellEnd()); + } + return *this; +} + +Section::Section( const Section& rSection ) + : mnTextEnc(rSection.mnTextEnc) +{ + for ( int i = 0; i < 16; i++ ) + aFMTID[ i ] = rSection.aFMTID[ i ]; + for(const std::unique_ptr<PropEntry>& rEntry : rSection.maEntries) + maEntries.push_back(std::make_unique<PropEntry>(*rEntry)); +} + +Section::Section( const sal_uInt8* pFMTID ) : mnTextEnc(RTL_TEXTENCODING_MS_1252) +{ + for ( int i = 0; i < 16; i++ ) + aFMTID[ i ] = pFMTID[ i ]; +} + +bool Section::GetProperty( sal_uInt32 nId, PropItem& rPropItem ) +{ + if ( nId ) + { + auto iter = std::find_if(maEntries.begin(), maEntries.end(), + [nId](const std::unique_ptr<PropEntry>& rxEntry) { return rxEntry->mnId == nId; }); + + if (iter != maEntries.end()) + { + rPropItem.Clear(); + rPropItem.SetTextEncoding( mnTextEnc ); + rPropItem.WriteBytes( (*iter)->mpBuf.get(), (*iter)->mnSize ); + rPropItem.Seek( STREAM_SEEK_TO_BEGIN ); + return true; + } + } + return false; +} + +void Section::AddProperty( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize ) +{ + // just a simple id check + + if ( !nId ) + return; + if ( nId == 0xffffffff ) + nId = 0; + + // do not allow same PropId's, sort + auto iter = std::find_if(maEntries.begin(), maEntries.end(), + [nId](const std::unique_ptr<PropEntry>& rxEntry) { return rxEntry->mnId >= nId; }); + if (iter != maEntries.end()) + { + if ( (*iter)->mnId == nId ) + (*iter).reset(new PropEntry( nId, pBuf, nBufSize )); + else + maEntries.insert( iter, std::make_unique<PropEntry>( nId, pBuf, nBufSize )); + } + else + { + maEntries.push_back( std::make_unique<PropEntry>( nId, pBuf, nBufSize ) ); + } +} + +void Section::GetDictionary(PropDictionary& rDict) +{ + auto iter = std::find_if(maEntries.begin(), maEntries.end(), + [](const std::unique_ptr<PropEntry>& rxEntry) { return rxEntry->mnId == 0; }); + + if (iter == maEntries.end()) + return; + + SvMemoryStream aStream( (*iter)->mpBuf.get(), (*iter)->mnSize, StreamMode::READ ); + aStream.Seek( STREAM_SEEK_TO_BEGIN ); + sal_uInt32 nDictCount(0); + aStream.ReadUInt32( nDictCount ); + for (sal_uInt32 i = 0; i < nDictCount; ++i) + { + sal_uInt32 nId(0), nSize(0); + aStream.ReadUInt32(nId).ReadUInt32(nSize); + if (!aStream.good() || nSize > aStream.remainingSize()) + break; + if (mnTextEnc == RTL_TEXTENCODING_UCS2) + nSize >>= 1; + if (!nSize) + continue; + OUString aString; + try + { + if ( mnTextEnc == RTL_TEXTENCODING_UCS2 ) + { + std::unique_ptr<sal_Unicode[]> pWString( new sal_Unicode[nSize] ); + for (sal_uInt32 j = 0; j < nSize; ++j) + aStream.ReadUtf16(pWString[j]); + aString = OUString(pWString.get(), lcl_getMaxSafeStrLen(nSize)); + } + else + { + std::unique_ptr<char[]> pString( new char[nSize] ); + aStream.ReadBytes(pString.get(), nSize); + aString = OUString(pString.get(), lcl_getMaxSafeStrLen(nSize), mnTextEnc); + } + } + catch( const std::bad_alloc& ) + { + OSL_FAIL( "sd Section::GetDictionary bad alloc" ); + } + if (aString.isEmpty()) + break; + rDict.insert( std::make_pair(aString,nId) ); + } +} + +void Section::Read( SotStorageStream *pStrm ) +{ + sal_uInt32 nSecOfs = pStrm->Tell(); + sal_uInt32 nStrmSize = pStrm->remainingSize(); + + mnTextEnc = RTL_TEXTENCODING_MS_1252; + sal_uInt32 nSecSize(0), nPropCount(0); + pStrm->ReadUInt32(nSecSize).ReadUInt32(nPropCount); + if (nSecSize > nStrmSize) + { + SAL_WARN("sd.filter", "Section Len " << nSecSize << " claimed, only " << nStrmSize << " possible"); + nSecSize = nStrmSize; + } + + while (nPropCount--) + { + sal_uInt32 nPropId(0), nPropOfs(0); + pStrm->ReadUInt32(nPropId).ReadUInt32(nPropOfs); + if (!pStrm->good()) + break; + auto nCurrent = pStrm->Tell(); + sal_uInt64 nOffset = nPropOfs + nSecOfs; + if (!checkSeek(*pStrm, nOffset)) + break; + if ( nPropId ) // do not read dictionary + { + sal_uInt32 nPropType(0), nVectorCount(0); + pStrm->ReadUInt32(nPropType); + + sal_uInt32 nPropSize = 4; + if ( nPropType & VT_VECTOR ) + { + pStrm->ReadUInt32( nVectorCount ); + nPropType &=~VT_VECTOR; + nPropSize += 4; + } + else + nVectorCount = 1; + + bool bVariant = ( nPropType == VT_VARIANT ); + + o3tl::sorted_vector<sal_uInt64> aVisitedOffsets; + + for (sal_uInt32 i = 0; nPropSize && i < nVectorCount && pStrm->good(); ++i) + { + if ( bVariant ) + { + pStrm->ReadUInt32( nPropType ); + nPropSize += 4; + } + sal_uInt32 nTemp(0); + switch( nPropType ) + { + case VT_UI1 : + nPropSize++; + break; + + case VT_I2 : + case VT_UI2 : + case VT_BOOL : + nPropSize += 2; + break; + + case VT_I4 : + case VT_R4 : + case VT_UI4 : + case VT_ERROR : + nPropSize += 4; + break; + + case VT_I8 : + case VT_R8 : + case VT_CY : + case VT_UI8 : + case VT_DATE : + case VT_FILETIME : + nPropSize += 8; + break; + + case VT_BSTR : + pStrm->ReadUInt32( nTemp ); + nPropSize += ( nTemp + 4 ); + break; + + case VT_LPSTR : + pStrm->ReadUInt32( nTemp ); + nPropSize += ( nTemp + 4 ); + break; + + case VT_LPWSTR : + { + pStrm->ReadUInt32( nTemp ); + // looks like these are aligned to 4 bytes + sal_uInt32 nLength = nPropOfs + nSecOfs + nPropSize + ( nTemp << 1 ) + 4; + nPropSize += ( nTemp << 1 ) + 4 + (nLength % 4); + } + break; + + case VT_BLOB_OBJECT : + case VT_BLOB : + case VT_CF : + pStrm->ReadUInt32( nTemp ); + nPropSize += ( nTemp + 4 ); + break; + + case VT_CLSID : + case VT_STREAM : + case VT_STORAGE : + case VT_STREAMED_OBJECT : + case VT_STORED_OBJECT : + case VT_VARIANT : + case VT_VECTOR : + default : + nPropSize = 0; + } + if ( nPropSize ) + { + if ( ( nVectorCount - i ) > 1 ) + { + nOffset = nPropOfs + nSecOfs + nPropSize; + if (!checkSeek(*pStrm, nOffset)) + break; + // inserts returns false if an equivalent element already existed + if (!aVisitedOffsets.insert(nOffset).second) + { + SAL_WARN("sd.filter", "loop in Section::Read property list"); + break; + } + } + } + else + break; + } + if ( nPropSize ) + { + if ( nPropSize > nStrmSize ) + { + break; + } + pStrm->Seek( nPropOfs + nSecOfs ); + // make sure we don't overflow the section size + if( nPropSize > nSecSize - nSecOfs ) + nPropSize = nSecSize - nSecOfs; + std::unique_ptr<sal_uInt8[]> pBuf( new sal_uInt8[ nPropSize ] ); + nPropSize = pStrm->ReadBytes(pBuf.get(), nPropSize); + AddProperty( nPropId, pBuf.get(), nPropSize ); + } + if ( nPropId == 1 ) + { + PropItem aPropItem; + if ( GetProperty( 1, aPropItem ) ) + { + aPropItem.ReadUInt32( nPropType ); + if ( nPropType == VT_I2 ) + { + sal_uInt16 nCodePage(0); + aPropItem.ReadUInt16(nCodePage); + + if ( nCodePage == 1200 ) + { + mnTextEnc = RTL_TEXTENCODING_UCS2; + } + else + { + mnTextEnc = rtl_getTextEncodingFromWindowsCodePage( nCodePage ); + if ( mnTextEnc == RTL_TEXTENCODING_DONTKNOW ) + mnTextEnc = RTL_TEXTENCODING_MS_1252; + } + } + else + { + mnTextEnc = RTL_TEXTENCODING_MS_1252; + } + } + } + } + else + { + sal_uInt32 nDictCount(0); + pStrm->ReadUInt32(nDictCount); + auto nMaxRecordsPossible = pStrm->remainingSize() / (sizeof(sal_uInt32)*2); + if (nDictCount > nMaxRecordsPossible) + { + SAL_WARN("sd.filter", "Dictionary count of " << nDictCount << " claimed, only " << nMaxRecordsPossible << " possible"); + nDictCount = nMaxRecordsPossible; + } + for (sal_uInt32 i = 0; i < nDictCount; ++i) + { + sal_uInt32 nSize(0); + pStrm->ReadUInt32( nSize ).ReadUInt32( nSize ); + if (!pStrm->good()) + break; + sal_uInt64 nPos = pStrm->Tell() + nSize; + if (!checkSeek(*pStrm, nPos)) + break; + } + sal_uInt32 nSize = pStrm->Tell(); + pStrm->Seek( nPropOfs + nSecOfs ); + nSize -= pStrm->Tell(); + if ( nSize > nStrmSize ) + { + break; + } + std::unique_ptr<sal_uInt8[]> pBuf( new sal_uInt8[ nSize ] ); + nSize = pStrm->ReadBytes(pBuf.get(), nSize); + AddProperty( 0xffffffff, pBuf.get(), nSize ); + } + pStrm->Seek(nCurrent); + } + pStrm->Seek(nSecOfs + nSecSize); +} + +Section& Section::operator=( const Section& rSection ) +{ + if ( this != &rSection ) + { + memcpy( static_cast<void*>(aFMTID), static_cast<void const *>(rSection.aFMTID), 16 ); + + for(const std::unique_ptr<PropEntry>& rEntry : rSection.maEntries) + maEntries.push_back(std::make_unique<PropEntry>(*rEntry)); + } + return *this; +} + +PropRead::PropRead( SotStorage& rStorage, const OUString& rName ) : + mbStatus ( false ), + mnByteOrder ( 0xfffe ) +{ + if ( rStorage.IsStream( rName ) ) + { + mpSvStream = rStorage.OpenSotStream( rName, StreamMode::STD_READ ); + if ( mpSvStream.is() ) + { + mpSvStream->SetEndian( SvStreamEndian::LITTLE ); + memset( mApplicationCLSID, 0, 16 ); + mbStatus = true; + } + } +} + +const Section* PropRead::GetSection( const sal_uInt8* pFMTID ) +{ + auto it = std::find_if(maSections.begin(), maSections.end(), + [&pFMTID](const std::unique_ptr<Section>& rxSection) { return memcmp( rxSection->GetFMTID(), pFMTID, 16 ) == 0; }); + if (it != maSections.end()) + return it->get(); + return nullptr; +} + +void PropRead::Read() +{ + maSections.clear(); + + if ( !mbStatus ) + return; + + sal_uInt16 mnVersionLo; + sal_uInt16 mnVersionHi; + sal_uInt16 mnFormat; + mpSvStream->ReadUInt16( mnByteOrder ).ReadUInt16( mnFormat ).ReadUInt16( mnVersionLo ).ReadUInt16( mnVersionHi ); + if ( mnByteOrder != 0xfffe ) + return; + + std::vector<sal_uInt8> aSectCLSID(16); + mpSvStream->ReadBytes(mApplicationCLSID, 16); + sal_uInt32 nSections(0); + mpSvStream->ReadUInt32(nSections); + if ( nSections > 2 ) // sj: PowerPoint documents are containing max 2 sections + { + mbStatus = false; + } + else + for ( sal_uInt32 i = 0; i < nSections; i++ ) + { + mpSvStream->ReadBytes(aSectCLSID.data(), aSectCLSID.size()); + sal_uInt32 nSectionOfs(0); + mpSvStream->ReadUInt32( nSectionOfs ); + sal_uInt32 nCurrent = mpSvStream->Tell(); + if (checkSeek(*mpSvStream, nSectionOfs)) + { + Section aSection(aSectCLSID.data()); + aSection.Read(mpSvStream.get()); + maSections.push_back(std::make_unique<Section>(aSection)); + } + mpSvStream->Seek( nCurrent ); + } +} + +PropRead& PropRead::operator=( const PropRead& rPropRead ) +{ + if ( this != &rPropRead ) + { + mbStatus = rPropRead.mbStatus; + mpSvStream = rPropRead.mpSvStream; + + mnByteOrder = rPropRead.mnByteOrder; + memcpy( mApplicationCLSID, rPropRead.mApplicationCLSID, 16 ); + + for(const std::unique_ptr<Section>& rSection : rPropRead.maSections) + maSections.push_back(std::make_unique<Section>(*rSection)); + } + return *this; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/propread.hxx b/sd/source/filter/ppt/propread.hxx new file mode 100644 index 000000000..402a04624 --- /dev/null +++ b/sd/source/filter/ppt/propread.hxx @@ -0,0 +1,151 @@ +/* -*- 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 <map> +#include <vector> +#include <memory> + +#include <sal/types.h> +#include <sot/storage.hxx> +#include <tools/stream.hxx> + +// SummaryInformation +#define PID_TITLE 0x02 +#define PID_SUBJECT 0x03 +#define PID_AUTHOR 0x04 +#define PID_KEYWORDS 0x05 +#define PID_COMMENTS 0x06 +#define PID_TEMPLATE 0x07 +#define PID_LASTAUTHOR 0x08 +#define PID_REVNUMBER 0x09 +#define PID_CREATE_DTM 0x0c + +// DocumentSummaryInformation +#define PID_SLIDECOUNT 0x07 +#define PID_HEADINGPAIR 0x0c +#define PID_DOCPARTS 0x0d + +#define VT_EMPTY 0 +#define VT_NULL 1 +#define VT_I2 2 +#define VT_I4 3 +#define VT_R4 4 +#define VT_R8 5 +#define VT_CY 6 +#define VT_DATE 7 +#define VT_BSTR 8 +#define VT_UI4 9 +#define VT_ERROR 10 +#define VT_BOOL 11 +#define VT_VARIANT 12 +#define VT_DECIMAL 14 +#define VT_I1 16 +#define VT_UI1 17 +#define VT_UI2 18 +#define VT_I8 20 +#define VT_UI8 21 +#define VT_INT 22 +#define VT_UINT 23 +#define VT_LPSTR 30 +#define VT_LPWSTR 31 +#define VT_FILETIME 64 +#define VT_BLOB 65 +#define VT_STREAM 66 +#define VT_STORAGE 67 +#define VT_STREAMED_OBJECT 68 +#define VT_STORED_OBJECT 69 +#define VT_BLOB_OBJECT 70 +#define VT_CF 71 +#define VT_CLSID 72 +#define VT_VECTOR 0x1000 +#define VT_ARRAY 0x2000 +#define VT_BYREF 0x4000 +#define VT_TYPEMASK 0xFFF + +typedef std::map<OUString,sal_uInt32> PropDictionary; + +struct PropEntry +{ + sal_uInt32 mnId; + sal_uInt32 mnSize; + std::unique_ptr<sal_uInt8[]> mpBuf; + + PropEntry( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize ); + PropEntry( const PropEntry& rProp ); + + PropEntry& operator=(const PropEntry& rPropEntry); +}; + +class PropItem : public SvMemoryStream +{ + sal_uInt16 mnTextEnc; + +public: + PropItem() + : mnTextEnc(RTL_TEXTENCODING_DONTKNOW) + { + } + void Clear(); + + void SetTextEncoding( sal_uInt16 nTextEnc ){ mnTextEnc = nTextEnc; }; + bool Read( OUString& rString, sal_uInt32 nType = VT_EMPTY, bool bDwordAlign = true ); + PropItem& operator=( PropItem& rPropItem ); +}; + +class Section final +{ + sal_uInt16 mnTextEnc; + std::vector<std::unique_ptr<PropEntry> > maEntries; + + sal_uInt8 aFMTID[ 16 ]; + + void AddProperty( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize ); + + public: + explicit Section( const sal_uInt8* pFMTID ); + Section( const Section& rSection ); + + Section& operator=( const Section& rSection ); + bool GetProperty( sal_uInt32 nId, PropItem& rPropItem ); + void GetDictionary( PropDictionary& rDict ); + const sal_uInt8* GetFMTID() const { return aFMTID; }; + void Read( SotStorageStream* pStrm ); +}; + +class PropRead +{ + bool mbStatus; + tools::SvRef<SotStorageStream> mpSvStream; + + sal_uInt16 mnByteOrder; + sal_uInt8 mApplicationCLSID[ 16 ]; + std::vector<std::unique_ptr<Section> > maSections; + + public: + PropRead( SotStorage& rSvStorage, const OUString& rName ); + + PropRead& operator=( const PropRead& rPropRead ); + const Section* GetSection( const sal_uInt8* pFMTID ); + bool IsValid() const { return mbStatus; }; + void Read(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/sdfilter.cxx b/sd/source/filter/sdfilter.cxx new file mode 100644 index 000000000..11ad11d76 --- /dev/null +++ b/sd/source/filter/sdfilter.cxx @@ -0,0 +1,108 @@ +/* -*- 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 <memory> +#include <map> + +#include <osl/module.hxx> +#include <tools/svlibrary.h> +#include <sfx2/docfile.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/itemset.hxx> + +#include <DrawDocShell.hxx> + +#include <pres.hxx> +#include <sdfilter.hxx> + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::task; + + +SdFilter::SdFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell ) +: mxModel( rDocShell.GetModel() ) +, mrMedium( rMedium ) +, mrDocShell( rDocShell ) +, mrDocument( *rDocShell.GetDoc() ) +, mbIsDraw( rDocShell.GetDocumentType() == DocumentType::Draw ) +{ +} + +SdFilter::~SdFilter() +{ +} + +OUString SdFilter::ImplGetFullLibraryName( std::u16string_view rLibraryName ) +{ + return OUString(SVLIBRARY("?")).replaceFirst( "?", rLibraryName ); +} + +#ifndef DISABLE_DYNLOADING + +static std::map<OUString, std::unique_ptr<osl::Module>> g_SdModuleMap; + +extern "C" { static void thisModule() {} } + +oslGenericFunction SdFilter::GetLibrarySymbol( const OUString& rLibraryName, const OUString &rFnSymbol ) +{ + osl::Module *pMod = nullptr; + auto it = g_SdModuleMap.find(rLibraryName); + if (it != g_SdModuleMap.end()) + pMod = it->second.get(); + + if (!pMod) + { + pMod = new osl::Module; + if (pMod->loadRelative(&thisModule, ImplGetFullLibraryName(rLibraryName), + SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_LAZY)) + g_SdModuleMap[rLibraryName] = std::unique_ptr<osl::Module>(pMod); + else + { + delete pMod; + pMod = nullptr; + } + } + if (!pMod) + return nullptr; + else + return pMod->getFunctionSymbol(rFnSymbol); +} + +void SdFilter::Preload() +{ + (void)GetLibrarySymbol("sdfilt", "ImportPPT"); + (void)GetLibrarySymbol("icg", "ImportCGM"); +} + +#endif + +void SdFilter::CreateStatusIndicator() +{ + // The status indicator must be retrieved from the provided medium arguments + const SfxUnoAnyItem* pStatusBarItem = + mrMedium.GetItemSet()->GetItem(SID_PROGRESS_STATUSBAR_CONTROL); + + if ( pStatusBarItem ) + pStatusBarItem->GetValue() >>= mxStatusIndicator; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/sdpptwrp.cxx b/sd/source/filter/sdpptwrp.cxx new file mode 100644 index 000000000..59829f854 --- /dev/null +++ b/sd/source/filter/sdpptwrp.cxx @@ -0,0 +1,377 @@ +/* -*- 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/docfilt.hxx> +#include <sfx2/sfxsids.hrc> +#include <filter/msfilter/msoleexp.hxx> +#include <svx/svxerr.hxx> +#include <unotools/fltrcfg.hxx> +#include <unotools/streamwrap.hxx> +#include <sot/storage.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/packages/XPackageEncryption.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <sdpptwrp.hxx> +#include <DrawDocShell.hxx> +#include <sfx2/frame.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::frame; + +typedef sal_Bool ( *ExportPPTPointer )( const std::vector< css::beans::PropertyValue >&, tools::SvRef<SotStorage> const&, + Reference< XModel > const &, + Reference< XStatusIndicator > const &, + SvMemoryStream*, sal_uInt32 nCnvrtFlags ); + +typedef sal_Bool ( *ImportPPTPointer )( SdDrawDocument*, SvStream&, SotStorage&, SfxMedium& ); + +typedef sal_Bool ( *SaveVBAPointer )( SfxObjectShell&, SvMemoryStream*& ); + +#ifdef DISABLE_DYNLOADING + +extern "C" sal_Bool ExportPPT( const std::vector< css::beans::PropertyValue >&, tools::SvRef<SotStorage> const&, + Reference< XModel > const &, + Reference< XStatusIndicator > const &, + SvMemoryStream*, sal_uInt32 nCnvrtFlags ); + +extern "C" sal_Bool ImportPPT( SdDrawDocument*, SvStream&, SotStorage&, SfxMedium& ); + +extern "C" sal_Bool SaveVBA( SfxObjectShell&, SvMemoryStream*& ); + +#endif + + +SdPPTFilter::SdPPTFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell ) : + SdFilter( rMedium, rDocShell ), + pBas ( nullptr ) +{ +} + +SdPPTFilter::~SdPPTFilter() +{ + delete pBas; // deleting the compressed basic storage +} + +static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, const OUString& sPrefix) +{ + SvStorageInfoList aElements; + pStorage->FillInfoList(&aElements); + for (const auto & aElement : aElements) + { + OUString sStreamFullName = sPrefix.getLength() ? sPrefix + "/" + aElement.GetName() : aElement.GetName(); + if (aElement.IsStorage()) + { + tools::SvRef<SotStorage> xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL); + lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName); + } + else + { + // Read stream + tools::SvRef<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL); + if (rStream.is()) + { + sal_Int32 nStreamSize = rStream->GetSize(); + Sequence< sal_Int8 > oData; + oData.realloc(nStreamSize); + sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize); + if (nStreamSize == nReadBytes) + aStreamsData[sStreamFullName] <<= oData; + } + } + } +} + +static tools::SvRef<SotStorage> lcl_DRMDecrypt(const SfxMedium& rMedium, const tools::SvRef<SotStorage>& rStorage, std::shared_ptr<SvStream>& rNewStorageStrm) +{ + tools::SvRef<SotStorage> aNewStorage; + + // We have DRM encrypted storage. We should try to decrypt it first, if we can + Sequence< Any > aArguments; + Reference<XComponentContext> xComponentContext(comphelper::getProcessComponentContext()); + Reference< css::packages::XPackageEncryption > xPackageEncryption( + xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.oox.crypto.DRMDataSpace", aArguments, xComponentContext), UNO_QUERY); + + if (!xPackageEncryption.is()) + { + // We do not know how to decrypt this + return aNewStorage; + } + + comphelper::SequenceAsHashMap aStreamsData; + lcl_getListOfStreams(rStorage.get(), aStreamsData, ""); + + try { + Sequence<NamedValue> aStreams = aStreamsData.getAsConstNamedValueList(); + if (!xPackageEncryption->readEncryptionInfo(aStreams)) + { + // We failed with decryption + return aNewStorage; + } + + tools::SvRef<SotStorageStream> rContentStream = rStorage->OpenSotStream("\011DRMContent", StreamMode::READ | StreamMode::SHARE_DENYALL); + if (!rContentStream.is()) + { + return aNewStorage; + } + + rNewStorageStrm = std::make_shared<SvMemoryStream>(); + + Reference<css::io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false)); + Reference<css::io::XOutputStream > xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*rNewStorageStrm)); + + if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream)) + { + // We failed with decryption + return aNewStorage; + } + + rNewStorageStrm->Seek(0); + + // Further reading is done from new document + aNewStorage = new SotStorage(*rNewStorageStrm); + + // Set the media descriptor data + Sequence<NamedValue> aEncryptionData = xPackageEncryption->createEncryptionData(""); + rMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, Any(aEncryptionData))); + } + catch (const std::exception&) + { + return aNewStorage; + } + + return aNewStorage; +} + +bool SdPPTFilter::Import() +{ + bool bRet = false; + std::shared_ptr<SvStream> aDecryptedStorageStrm; + tools::SvRef<SotStorage> pStorage = new SotStorage( mrMedium.GetInStream(), false ); + if( !pStorage->GetError() ) + { + /* check if there is a dualstorage, then the + document is probably a PPT95 containing PPT97 */ + tools::SvRef<SotStorage> xDualStorage; + OUString sDualStorage( "PP97_DUALSTORAGE" ); + if ( pStorage->IsContained( sDualStorage ) ) + { + xDualStorage = pStorage->OpenSotStorage( sDualStorage, StreamMode::STD_READ ); + pStorage = xDualStorage; + } + if (pStorage->IsContained("\011DRMContent")) + { + // Document is DRM encrypted + pStorage = lcl_DRMDecrypt(mrMedium, pStorage, aDecryptedStorageStrm); + } + tools::SvRef<SotStorageStream> pDocStream(pStorage->OpenSotStream( "PowerPoint Document" , StreamMode::STD_READ )); + if( pDocStream ) + { + pDocStream->SetVersion( pStorage->GetVersion() ); + pDocStream->SetCryptMaskKey(pStorage->GetKey()); + + if ( pStorage->IsStream( "EncryptedSummary" ) ) + mrMedium.SetError(ERRCODE_SVX_READ_FILTER_PPOINT); + else + { +#ifdef DISABLE_DYNLOADING + ImportPPTPointer pPPTImport = ImportPPT; +#else + ImportPPTPointer pPPTImport = reinterpret_cast< ImportPPTPointer >( + SdFilter::GetLibrarySymbol(mrMedium.GetFilter()->GetUserData(), "ImportPPT")); +#endif + + if ( pPPTImport ) + bRet = pPPTImport( &mrDocument, *pDocStream, *pStorage, mrMedium ); + + if ( !bRet ) + mrMedium.SetError(SVSTREAM_WRONGVERSION); + } + } + } + + return bRet; +} + +bool SdPPTFilter::Export() +{ + bool bRet = false; + + if( mxModel.is() ) + { +#ifdef DISABLE_DYNLOADING + ExportPPTPointer PPTExport = ExportPPT; +#else + ExportPPTPointer PPTExport = reinterpret_cast< ExportPPTPointer >( + SdFilter::GetLibrarySymbol(mrMedium.GetFilter()->GetUserData(), "ExportPPT")); +#endif + + if( PPTExport) + { + sal_uInt32 nCnvrtFlags = 0; + const SvtFilterOptions& rFilterOptions = SvtFilterOptions::Get(); + if ( rFilterOptions.IsMath2MathType() ) + nCnvrtFlags |= OLE_STARMATH_2_MATHTYPE; + if ( rFilterOptions.IsWriter2WinWord() ) + nCnvrtFlags |= OLE_STARWRITER_2_WINWORD; + if ( rFilterOptions.IsCalc2Excel() ) + nCnvrtFlags |= OLE_STARCALC_2_EXCEL; + if ( rFilterOptions.IsImpress2PowerPoint() ) + nCnvrtFlags |= OLE_STARIMPRESS_2_POWERPOINT; + if ( rFilterOptions.IsEnablePPTPreview() ) + nCnvrtFlags |= 0x8000; + + CreateStatusIndicator(); + + //OUString sBaseURI( "BaseURI"); + std::vector< PropertyValue > aProperties; + PropertyValue aProperty; + aProperty.Name = "BaseURI"; + aProperty.Value <<= mrMedium.GetBaseURL( true ); + aProperties.push_back( aProperty ); + + SvStream * pOutputStrm = mrMedium.GetOutStream(); + + Sequence< NamedValue > aEncryptionData; + Reference< css::packages::XPackageEncryption > xPackageEncryption; + const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(mrMedium.GetItemSet(), SID_ENCRYPTIONDATA, false); + std::shared_ptr<SvStream> pMediaStrm; + if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData)) + { + ::comphelper::SequenceAsHashMap aHashData(aEncryptionData); + OUString sCryptoType = aHashData.getUnpackedValueOrDefault("CryptoType", OUString()); + + if (sCryptoType.getLength()) + { + Reference<XComponentContext> xComponentContext(comphelper::getProcessComponentContext()); + Sequence<Any> aArguments{ + Any(NamedValue("Binary", Any(true))) }; + xPackageEncryption.set( + xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), UNO_QUERY); + + if (xPackageEncryption.is()) + { + // We have an encryptor. Export document into memory stream and encrypt it later + pMediaStrm = std::make_shared<SvMemoryStream>(); + pOutputStrm = pMediaStrm.get(); + + // Temp removal of EncryptionData to avoid password protection triggering + mrMedium.GetItemSet()->ClearItem(SID_ENCRYPTIONDATA); + } + } + } + + tools::SvRef<SotStorage> xStorRef = new SotStorage(pOutputStrm, false); + + if (xStorRef.is()) + { + bRet = PPTExport(aProperties, xStorRef, mxModel, mxStatusIndicator, pBas, nCnvrtFlags); + xStorRef->Commit(); + + if (xPackageEncryption.is()) + { + // Perform DRM encryption + pOutputStrm->Seek(0); + + xPackageEncryption->setupEncryption(aEncryptionData); + + Reference<css::io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(pOutputStrm, false)); + Sequence<NamedValue> aStreams = xPackageEncryption->encrypt(xInputStream); + + tools::SvRef<SotStorage> xEncryptedRootStrg = new SotStorage(mrMedium.GetOutStream(), false); + for (const NamedValue & aStreamData : std::as_const(aStreams)) + { + // To avoid long paths split and open substorages recursively + // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09 + tools::SvRef<SotStorage> pStorage = xEncryptedRootStrg.get(); + OUString sFileName; + sal_Int32 idx = 0; + do + { + OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx); + if (!sPathElem.isEmpty()) + { + if (idx < 0) + { + sFileName = sPathElem; + } + else + { + pStorage = pStorage->OpenSotStorage(sPathElem); + } + } + } while (pStorage && idx >= 0); + + if (!pStorage) + { + bRet = false; + break; + } + + tools::SvRef<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName); + if (!pStream) + { + bRet = false; + break; + } + Sequence<sal_Int8> aStreamContent; + aStreamData.Value >>= aStreamContent; + size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getConstArray(), aStreamContent.getLength()); + if (nBytesWritten != static_cast<size_t>(aStreamContent.getLength())) + { + bRet = false; + break; + } + } + xEncryptedRootStrg->Commit(); + + // Restore encryption data + mrMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, Any(aEncryptionData))); + } + } + } + } + + return bRet; +} + +void SdPPTFilter::PreSaveBasic() +{ + const SvtFilterOptions& rFilterOptions = SvtFilterOptions::Get(); + if( rFilterOptions.IsLoadPPointBasicStorage() ) + { +#ifdef DISABLE_DYNLOADING + SaveVBAPointer pSaveVBA= SaveVBA; +#else + SaveVBAPointer pSaveVBA = reinterpret_cast< SaveVBAPointer >( + SdFilter::GetLibrarySymbol(mrMedium.GetFilter()->GetUserData(), "SaveVBA")); +#endif + if( pSaveVBA ) + pSaveVBA( static_cast<SfxObjectShell&>(mrDocShell), pBas ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/xml/sdtransform.cxx b/sd/source/filter/xml/sdtransform.cxx new file mode 100644 index 000000000..4e296eaf6 --- /dev/null +++ b/sd/source/filter/xml/sdtransform.cxx @@ -0,0 +1,368 @@ +/* -*- 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 <svl/style.hxx> +#include <svl/itemset.hxx> +#include <svl/whiter.hxx> + +#include <svx/svdoutl.hxx> +#include <editeng/xmlcnitm.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdogrp.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/outlobj.hxx> + +#include <drawdoc.hxx> +#include "sdtransform.hxx" + +using namespace ::com::sun::star::style; + +namespace { + +class SdTransformOOo2xDocument +{ +public: + explicit SdTransformOOo2xDocument( SdDrawDocument& rDocument ); + + void transform(); + + void transformMasterPages(); + void transformDrawPages(); + + void transformStyles(); + void transformStyles( SfxStyleFamily eFam ); + void transformStyle( SfxStyleSheetBase& rSheet ); + + void transformShapes( SdrObjList const & rShapes ); + void transformShape( SdrObject& rObj ); + + void transformTextShape( SdrTextObj& rTextShape ); + + bool getBulletState( const SfxItemSet& rSet, SfxStyleSheetBase* pSheet, bool& rState ); + static bool getBulletState( const SfxItemSet& rSet, sal_uInt16 nWhich, bool& rState ); + + static bool transformItemSet( SfxItemSet& rSet, bool bNumbering ); + + static bool removeAlienAttributes( SfxItemSet& rSet ); + static bool removeAlienAttributes( SfxItemSet& rSet, sal_uInt16 nWhich ); + + SdDrawDocument& mrDocument; + SdrOutliner& mrOutliner; +}; + +} + +/** transforms the given model from OOo 2.x to OOo 3.x. This maps + the deprecated EE_PARA_BULLETSTATE and clears the EE_PARA_LRSPACE + if used together with a EE_PARA_NUMBULLET */ +void TransformOOo2xDocument( SdDrawDocument* pDocument ) +{ + if( pDocument ) + { + SdTransformOOo2xDocument aTransformer( *pDocument ); + aTransformer.transform(); + } +} + +constexpr OUStringLiteral gsEnableNumbering( u"enable-numbering" ); +constexpr OUStringLiteral gsTextNamespace( u"urn:oasis:names:tc:opendocument:xmlns:text:1.0" ); +constexpr OUStringLiteral gsTrue( u"true" ); + +SdTransformOOo2xDocument::SdTransformOOo2xDocument( SdDrawDocument& rDocument ) +: mrDocument( rDocument ) +, mrOutliner( rDocument.GetDrawOutliner() ) +{ +} + +void SdTransformOOo2xDocument::transform() +{ + transformMasterPages(); + transformDrawPages(); + transformStyles(); +} + +void SdTransformOOo2xDocument::transformMasterPages() +{ + sal_uInt16 nMasterPageCount = mrDocument.GetMasterPageCount(); + for( sal_uInt16 nMasterPage = 0; nMasterPage < nMasterPageCount; nMasterPage++ ) + { + SdrObjList* pPage = mrDocument.GetMasterPage( nMasterPage ); + if( pPage ) + transformShapes( *pPage ); + } +} + +void SdTransformOOo2xDocument::transformDrawPages() +{ + sal_uInt16 nPageCount = mrDocument.GetPageCount(); + for( sal_uInt16 nPage = 0; nPage < nPageCount; nPage++ ) + { + SdrObjList* pPage = mrDocument.GetPage( nPage ); + if( pPage ) + transformShapes( *pPage ); + } +} + +void SdTransformOOo2xDocument::transformStyles() +{ + transformStyles( SfxStyleFamily::Para ); + transformStyles( SfxStyleFamily::Page ); +} + +void SdTransformOOo2xDocument::transformStyles( SfxStyleFamily eFam ) +{ + + rtl::Reference< SfxStyleSheetBasePool > xStyleSheetPool( mrDocument.GetStyleSheetPool() ); + + SfxStyleSheetIterator aIter( xStyleSheetPool.get(), eFam ); + + SfxStyleSheetBase* pSheet = aIter.First(); + while( pSheet ) + { + transformStyle( *pSheet ); + pSheet = aIter.Next(); + } +} + +void SdTransformOOo2xDocument::transformStyle( SfxStyleSheetBase& rSheet ) +{ + SfxItemSet& rSet = rSheet.GetItemSet(); + + bool bState = false; + getBulletState( rSheet.GetItemSet(), rSheet.GetPool()->Find( rSheet.GetParent(), rSheet.GetFamily() ), bState ); + + transformItemSet( rSet, bState ); + removeAlienAttributes( rSet ); +} + +void SdTransformOOo2xDocument::transformShapes( SdrObjList const & rShapes ) +{ + const size_t nShapeCount = rShapes.GetObjCount(); + for( size_t nShape = 0; nShape < nShapeCount; ++nShape ) + { + SdrObject* pObj = rShapes.GetObj( nShape ); + if( pObj ) + transformShape( *pObj ); + } +} + +void SdTransformOOo2xDocument::transformShape( SdrObject& rObj ) +{ + SdrTextObj* pTextShape = dynamic_cast< SdrTextObj* >( &rObj ); + if( pTextShape ) + { + transformTextShape( *pTextShape ); + return; + } + + SdrObjGroup* pGroupShape = dynamic_cast< SdrObjGroup* >( &rObj ); + if( pGroupShape ) + { + SdrObjList* pObjList = pGroupShape->GetSubList(); + if( pObjList ) + transformShapes( *pObjList ); + return; + } +} + +void SdTransformOOo2xDocument::transformTextShape( SdrTextObj& rTextShape ) +{ + + if(rTextShape.IsEmptyPresObj()) + return; + + OutlinerParaObject* pOPO = rTextShape.GetOutlinerParaObject(); + if (!pOPO) + return; + + mrOutliner.SetText( *pOPO ); + + sal_Int32 nCount = mrOutliner.GetParagraphCount(); + + bool bChange = false; + + for(sal_Int32 nPara = 0; nPara < nCount; nPara++) + { + SfxItemSet aParaSet( mrOutliner.GetParaAttribs( nPara ) ); + + bool bItemChange = false; + + bool bState = false; + const sal_Int16 nDepth = mrOutliner.GetDepth( nPara ); + if( (nDepth != -1) && (!getBulletState( aParaSet, mrOutliner.GetStyleSheet( nPara ), bState ) || !bState) ) + { + // disable bullet if text::enable-bullet="false" is found + if( (nDepth > 0 ) && (rTextShape.GetObjInventor() == SdrInventor::Default) && (rTextShape.GetObjIdentifier() == SdrObjKind::OutlineText) ) + { + // for outline object and level > 0 burn in the style sheet because it will be changed to "outline 1" + SfxStyleSheet* pStyleSheet = mrOutliner.GetStyleSheet( nPara ); + + if( pStyleSheet ) + { + // optimize me: only put items hard into paragraph that are not equal to "outline 1" style! + SfxItemSet& rStyleSet = pStyleSheet->GetItemSet(); + + SfxWhichIter aIter(aParaSet); + sal_uInt16 nWhich(aIter.FirstWhich()); + + // now set all none hard attributes from the style + while(nWhich) + { + if(SfxItemState::SET != aIter.GetItemState()) + { + aParaSet.Put(rStyleSet.Get(nWhich)); + bItemChange = true; + } + + nWhich = aIter.NextWhich(); + } + } + } + + mrOutliner.SetDepth( mrOutliner.GetParagraph( nPara ), -1 ); + + bChange = true; + } + + bItemChange |= transformItemSet( aParaSet, bState ); + + bItemChange |= removeAlienAttributes( aParaSet ); + + if( bItemChange ) + { + mrOutliner.SetParaAttribs( nPara, aParaSet ); + bChange = true; + } + } + + if( bChange ) + rTextShape.SetOutlinerParaObject(mrOutliner.CreateParaObject()); + + mrOutliner.Clear(); +} + +bool SdTransformOOo2xDocument::getBulletState( const SfxItemSet& rSet, SfxStyleSheetBase* pSheet, bool& rState ) +{ + if( getBulletState( rSet, EE_PARA_XMLATTRIBS, rState ) ) + return true; + + if( getBulletState( rSet, SDRATTR_XMLATTRIBUTES, rState ) ) + return true; + + if( pSheet && getBulletState( pSheet->GetItemSet(), pSheet->GetPool()->Find( pSheet->GetParent(), pSheet->GetFamily() ), rState ) ) + return true; + + return false; +} + +bool SdTransformOOo2xDocument::getBulletState( const SfxItemSet& rSet, sal_uInt16 nWhich, bool& rState ) +{ + if( rSet.GetItemState( nWhich ) == SfxItemState::SET ) + { + const SvXMLAttrContainerItem& rAttr = *rSet.GetItem<SvXMLAttrContainerItem>( nWhich ); + + const sal_uInt16 nCount = rAttr.GetAttrCount(); + for( sal_uInt16 nItem = 0; nItem < nCount; nItem++ ) + { + if( ( rAttr.GetAttrLName( nItem ) == gsEnableNumbering ) && ( rAttr.GetAttrNamespace( nItem ) == gsTextNamespace ) ) + { + const OUString& sValue( rAttr.GetAttrValue( nItem ) ); + rState = sValue == gsTrue; + return true; + } + } + } + + return false; +} + +bool SdTransformOOo2xDocument::transformItemSet( SfxItemSet& rSet, bool bNumbering ) +{ + bool bRet = false; + const SvxLRSpaceItem* pItem = bNumbering ? rSet.GetItem<SvxLRSpaceItem>(EE_PARA_LRSPACE) : nullptr; + if (pItem) + { + SvxLRSpaceItem aItem(*pItem); + if( (aItem.GetLeft() != 0) || (aItem.GetTextFirstLineOffset() != 0) ) + { + aItem.SetLeftValue( 0 ); + aItem.SetTextFirstLineOffset( 0 ); + rSet.Put( aItem ); + bRet = true; + } + } + + return bRet; +} + +bool SdTransformOOo2xDocument::removeAlienAttributes( SfxItemSet& rSet ) +{ + bool b = removeAlienAttributes( rSet, EE_PARA_XMLATTRIBS ); + b |= removeAlienAttributes( rSet, SDRATTR_XMLATTRIBUTES ); + return b; +} + +bool SdTransformOOo2xDocument::removeAlienAttributes( SfxItemSet& rSet, sal_uInt16 nWhich ) +{ + if( rSet.GetItemState( nWhich ) == SfxItemState::SET ) + { + const SvXMLAttrContainerItem& rAttr = *rSet.GetItem<SvXMLAttrContainerItem>( nWhich ); + + const sal_uInt16 nCount = rAttr.GetAttrCount(); + for( sal_uInt16 nItem = 0; nItem < nCount; nItem++ ) + { + if( ( rAttr.GetAttrLName( nItem ) == gsEnableNumbering ) && ( rAttr.GetAttrNamespace( nItem ) == gsTextNamespace ) ) + { + if( nCount == 1 ) + { + rSet.ClearItem( nWhich ); + } + else + { + SvXMLAttrContainerItem aNewItem( nWhich ); + + const sal_uInt16 nFound = nItem; + for( nItem = 0; nItem < nCount; nItem++ ) + { + if( nItem != nFound ) + { + OUString const& rNamespace(rAttr.GetAttrNamespace(nItem)); + OUString const& rPrefix(rAttr.GetAttrPrefix(nItem)); + if (rPrefix.isEmpty()) + { + aNewItem.AddAttr(rAttr.GetAttrLName(nItem), rAttr.GetAttrValue(nItem)); + } + else + { + aNewItem.AddAttr(rPrefix, rNamespace, rAttr.GetAttrLName(nItem), rAttr.GetAttrValue(nItem)); + } + } + } + + rSet.Put( aNewItem ); + } + return true; + } + } + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/xml/sdtransform.hxx b/sd/source/filter/xml/sdtransform.hxx new file mode 100644 index 000000000..64bb1c0a1 --- /dev/null +++ b/sd/source/filter/xml/sdtransform.hxx @@ -0,0 +1,28 @@ +/* -*- 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 <sal/config.h> + +#include <drawdoc.hxx> + +void TransformOOo2xDocument(SdDrawDocument* pDocument); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/xml/sdxmlwrp.cxx b/sd/source/filter/xml/sdxmlwrp.cxx new file mode 100644 index 000000000..90ef68e35 --- /dev/null +++ b/sd/source/filter/xml/sdxmlwrp.cxx @@ -0,0 +1,1056 @@ +/* -*- 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 <officecfg/Office/Common.hxx> +#include <vcl/errinf.hxx> +#include <sal/log.hxx> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <o3tl/string_view.hxx> +#include <editeng/outlobj.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/sfxsids.hrc> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <Outliner.hxx> +#include <unotools/streamwrap.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <svx/xmlgrhlp.hxx> + +#include <DrawDocShell.hxx> + +#include <sdxmlwrp.hxx> +#include <svx/xmleohlp.hxx> +#include <com/sun/star/xml/sax/Parser.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <com/sun/star/xml/sax/XFastParser.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/document/XImporter.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/packages/zip/ZipIOException.hpp> + +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <editeng/eeitem.hxx> + +// include necessary for XML progress bar at load time +#include <svl/itemset.hxx> +#include <svl/stritem.hxx> +#include <svtools/sfxecode.hxx> + +#include <sddll.hxx> +#include <sderror.hxx> +#include <sdresid.hxx> +#include "sdtransform.hxx" +#include <strings.hrc> + +#include <sfx2/frame.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::document; +using namespace comphelper; + +#define SD_XML_READERROR ErrCode(1234) + +char const sXML_export_impress_meta_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisMetaExporter"; +char const sXML_export_impress_styles_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisStylesExporter"; +char const sXML_export_impress_content_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisContentExporter"; +char const sXML_export_impress_settings_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisSettingsExporter"; + +char const sXML_export_draw_meta_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisMetaExporter"; +char const sXML_export_draw_styles_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisStylesExporter"; +char const sXML_export_draw_content_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisContentExporter"; +char const sXML_export_draw_settings_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisSettingsExporter"; + +char const sXML_import_impress_meta_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisMetaImporter"; +char const sXML_import_impress_styles_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisStylesImporter"; +char const sXML_import_impress_content_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisContentImporter"; +char const sXML_import_impress_settings_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisSettingsImporter"; + +char const sXML_import_draw_meta_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisMetaImporter"; +char const sXML_import_draw_styles_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisStylesImporter"; +char const sXML_import_draw_content_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisContentImporter"; +char const sXML_import_draw_settings_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisSettingsImporter"; + +// OOo +char const sXML_import_impress_meta_ooo_service[] = "com.sun.star.comp.Impress.XMLMetaImporter"; +char const sXML_import_impress_styles_ooo_service[] = "com.sun.star.comp.Impress.XMLStylesImporter"; +char const sXML_import_impress_content_ooo_service[] = "com.sun.star.comp.Impress.XMLContentImporter"; +char const sXML_import_impress_settings_ooo_service[] = "com.sun.star.comp.Impress.XMLSettingsImporter"; + +char const sXML_import_draw_meta_ooo_service[] = "com.sun.star.comp.Draw.XMLMetaImporter"; +char const sXML_import_draw_styles_ooo_service[] = "com.sun.star.comp.Draw.XMLStylesImporter"; +char const sXML_import_draw_content_ooo_service[] = "com.sun.star.comp.Draw.XMLContentImporter"; +char const sXML_import_draw_settings_ooo_service[] = "com.sun.star.comp.Draw.XMLSettingsImporter"; + +namespace { + +struct XML_SERVICEMAP +{ + const char* mpService; + const char* mpStream; +}; + +struct XML_SERVICES +{ + const char* mpMeta; + const char* mpStyles; + const char* mpContent; + const char* mpSettings; +}; + +} + +static XML_SERVICES const * getServices( bool bImport, bool bDraw, sal_uLong nStoreVer ) +{ + // Expect that export always sets nStoreVer to SOFFICE_FILEFORMAT_8. + assert(bImport || nStoreVer != SOFFICE_FILEFORMAT_60); + + static XML_SERVICES const gServices[] = + { + { sXML_import_impress_meta_oasis_service, sXML_import_impress_styles_oasis_service, sXML_import_impress_content_oasis_service, sXML_import_impress_settings_oasis_service }, + { sXML_import_draw_meta_oasis_service, sXML_import_draw_styles_oasis_service, sXML_import_draw_content_oasis_service, sXML_import_draw_settings_oasis_service }, + { sXML_export_impress_meta_oasis_service, sXML_export_impress_styles_oasis_service, sXML_export_impress_content_oasis_service, sXML_export_impress_settings_oasis_service }, + { sXML_export_draw_meta_oasis_service, sXML_export_draw_styles_oasis_service, sXML_export_draw_content_oasis_service, sXML_export_draw_settings_oasis_service }, + + { sXML_import_impress_meta_ooo_service, sXML_import_impress_styles_ooo_service, sXML_import_impress_content_ooo_service, sXML_import_impress_settings_ooo_service }, + { sXML_import_draw_meta_ooo_service, sXML_import_draw_styles_ooo_service, sXML_import_draw_content_ooo_service, sXML_import_draw_settings_ooo_service }, + }; + + return &gServices[ (bImport ? 0 : 2) + ((nStoreVer == SOFFICE_FILEFORMAT_60) ? 4 : 0) + (bDraw ? 1 : 0 ) ]; +} + + +SdXMLFilter::SdXMLFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell, SdXMLFilterMode eFilterMode, sal_uLong nStoreVer ) : + SdFilter( rMedium, rDocShell ), meFilterMode( eFilterMode ), mnStoreVer( nStoreVer ) +{ +} + +SdXMLFilter::~SdXMLFilter() +{ +} + +namespace +{ + +ErrCode ReadThroughComponent( + const Reference<io::XInputStream>& xInputStream, + const Reference<XComponent>& xModelComponent, + const OUString& rStreamName, + Reference<uno::XComponentContext> const & rxContext, + const char* pFilterName, + const Sequence<Any>& rFilterArguments, + const OUString& rName, + bool bMustBeSuccessful, + bool bEncrypted ) +{ + DBG_ASSERT(xInputStream.is(), "input stream missing"); + DBG_ASSERT(xModelComponent.is(), "document missing"); + DBG_ASSERT(rxContext.is(), "factory missing"); + DBG_ASSERT(nullptr != pFilterName,"I need a service name for the component!"); + + SAL_INFO( "sd.filter", "ReadThroughComponent" ); + + // prepare ParserInputSource + xml::sax::InputSource aParserInput; + aParserInput.sSystemId = rName; + aParserInput.aInputStream = xInputStream; + + // get filter + OUString aFilterName(OUString::createFromAscii(pFilterName)); + // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler + Reference< XInterface > xFilter( + rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(aFilterName, rFilterArguments, rxContext), + UNO_QUERY ); + SAL_WARN_IF(!xFilter.is(), "sd.filter", "Can't instantiate filter component: " << aFilterName); + if( !xFilter.is() ) + return SD_XML_READERROR; + Reference< xml::sax::XFastParser > xFastParser(xFilter, UNO_QUERY); + Reference< xml::sax::XDocumentHandler > xDocumentHandler; + if (!xFastParser) + xDocumentHandler.set(xFilter, UNO_QUERY); + if (!xFastParser && !xDocumentHandler) + { + SAL_WARN("sd", "service does not implement XFastParser or XDocumentHandler"); + assert(false); + return SD_XML_READERROR; + } + SAL_INFO( "sd.filter", "" << pFilterName << " created" ); + + // connect model and filter + Reference < XImporter > xImporter( xFilter, UNO_QUERY ); + xImporter->setTargetDocument( xModelComponent ); + + // finally, parser the stream + SAL_INFO( "sd.filter", "parsing stream" ); + try + { + if (xFastParser) + xFastParser->parseStream( aParserInput ); + else + { + Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(rxContext); + // connect parser and filter + xParser->setDocumentHandler( xDocumentHandler ); + xParser->parseStream( aParserInput ); + } + } + catch (const xml::sax::SAXParseException& r) + { + css::uno::Any ex( cppu::getCaughtException() ); + // sax parser sends wrapped exceptions, + // try to find the original one + xml::sax::SAXException aSaxEx = *static_cast<xml::sax::SAXException const *>(&r); + bool bTryChild = true; + + while( bTryChild ) + { + xml::sax::SAXException aTmp; + if ( aSaxEx.WrappedException >>= aTmp ) + aSaxEx = aTmp; + else + bTryChild = false; + } + + packages::zip::ZipIOException aBrokenPackage; + if ( aSaxEx.WrappedException >>= aBrokenPackage ) + return ERRCODE_IO_BROKENPACKAGE; + + if( bEncrypted ) + return ERRCODE_SFX_WRONGPASSWORD; + + SAL_WARN( "sd.filter", "SAX parse exception caught while importing: " << exceptionToString(ex)); + + OUString sErr = OUString::number( r.LineNumber ) + + "," + OUString::number( r.ColumnNumber ); + + if (!rStreamName.isEmpty()) + { + return *new TwoStringErrorInfo( + (bMustBeSuccessful ? ERR_FORMAT_FILE_ROWCOL + : WARN_FORMAT_FILE_ROWCOL), + rStreamName, sErr, + DialogMask::ButtonsOk | DialogMask::MessageError ); + } + else + { + DBG_ASSERT( bMustBeSuccessful, "Warnings are not supported" ); + return *new StringErrorInfo( ERR_FORMAT_ROWCOL, sErr, + DialogMask::ButtonsOk | DialogMask::MessageError ); + } + } + catch (const xml::sax::SAXException& r) + { + css::uno::Any ex( cppu::getCaughtException() ); + packages::zip::ZipIOException aBrokenPackage; + if ( r.WrappedException >>= aBrokenPackage ) + return ERRCODE_IO_BROKENPACKAGE; + + if( bEncrypted ) + return ERRCODE_SFX_WRONGPASSWORD; + + SAL_WARN( "sd.filter", "SAX exception caught while importing: " << exceptionToString(ex)); + return SD_XML_READERROR; + } + catch (const packages::zip::ZipIOException&) + { + TOOLS_WARN_EXCEPTION( "sd.filter", "Zip exception caught while importing"); + return ERRCODE_IO_BROKENPACKAGE; + } + catch (const io::IOException&) + { + TOOLS_WARN_EXCEPTION( "sd.filter", "IO exception caught while importing"); + return SD_XML_READERROR; + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd.filter", "uno exception caught while importing"); + return SD_XML_READERROR; + } + + // success! + return ERRCODE_NONE; +} + +ErrCode ReadThroughComponent( + const uno::Reference < embed::XStorage >& xStorage, + const Reference<XComponent>& xModelComponent, + const char* pStreamName, + Reference<uno::XComponentContext> const & rxContext, + const char* pFilterName, + const Sequence<Any>& rFilterArguments, + const OUString& rName, + bool bMustBeSuccessful ) +{ + DBG_ASSERT(xStorage.is(), "Need storage!"); + DBG_ASSERT(nullptr != pStreamName, "Please, please, give me a name!"); + + // open stream (and set parser input) + OUString sStreamName = OUString::createFromAscii(pStreamName); + bool bContainsStream = false; + try + { + bContainsStream = xStorage->isStreamElement(sStreamName); + } + catch (const container::NoSuchElementException&) + { + } + + if (!bContainsStream ) + { + // stream name not found! return immediately with OK signal + return ERRCODE_NONE; + } + + // set Base URL + uno::Reference< beans::XPropertySet > xInfoSet; + if( rFilterArguments.hasElements() ) + rFilterArguments.getConstArray()[0] >>= xInfoSet; + DBG_ASSERT( xInfoSet.is(), "missing property set" ); + if( xInfoSet.is() ) + { + xInfoSet->setPropertyValue( "StreamName", Any( sStreamName ) ); + } + + try + { + // get input stream + Reference <io::XStream> xStream = + xStorage->openStreamElement( sStreamName, embed::ElementModes::READ ); + Reference <beans::XPropertySet > xProps( xStream, uno::UNO_QUERY ); + if ( !xStream.is() || ! xProps.is() ) + return SD_XML_READERROR; + + Any aAny = xProps->getPropertyValue( "Encrypted" ); + + bool bEncrypted = false; + aAny >>= bEncrypted; + + Reference <io::XInputStream> xInputStream = xStream->getInputStream(); + + // read from the stream + return ReadThroughComponent( + xInputStream, xModelComponent, sStreamName, rxContext, + pFilterName, rFilterArguments, + rName, bMustBeSuccessful, bEncrypted ); + } + catch (const packages::WrongPasswordException&) + { + return ERRCODE_SFX_WRONGPASSWORD; + } + catch (const packages::zip::ZipIOException&) + { + return ERRCODE_IO_BROKENPACKAGE; + } + catch (const uno::Exception&) + {} + + return SD_XML_READERROR; +} + +} + +//PresObjKind::Outlines in master pages are the preview of the outline styles +//numbering format. Since fdo#78151 toggling bullets on and off changes +//the style they are a preview of, previously toggling bullets on and off +//would only affect the preview paragraph itself without an effect on the +//style. i.e. previews of numbering which don't match the real numbering +//they are supposed to be a preview of. +// +//But there exist documents which were saved previous to that modification +//so here we detect such cases and fix them up to ensure the previews +//numbering level matches that of the outline level it previews +static void fixupOutlinePlaceholderNumberingDepths(SdDrawDocument* pDoc) +{ + for (sal_uInt16 i = 0; i < pDoc->GetMasterSdPageCount(PageKind::Standard); ++i) + { + SdPage *pMasterPage = pDoc->GetMasterSdPage(i, PageKind::Standard); + SdrObject* pMasterOutline = pMasterPage->GetPresObj(PresObjKind::Outline); + if (!pMasterOutline) + continue; + OutlinerParaObject* pOutlParaObj = pMasterOutline->GetOutlinerParaObject(); + if (!pOutlParaObj) + continue; + SdOutliner* pOutliner = pDoc->GetInternalOutliner(); + pOutliner->Clear(); + pOutliner->SetText(*pOutlParaObj); + bool bInconsistent = false; + const sal_Int32 nParaCount = pOutliner->GetParagraphCount(); + for (sal_Int32 j = 0; j < nParaCount; ++j) + { + //Make sure the depth of the paragraph matches that of the outline style it previews + const sal_Int16 nExpectedDepth = j; + if (nExpectedDepth != pOutliner->GetDepth(j)) + { + Paragraph* p = pOutliner->GetParagraph(j); + pOutliner->SetDepth(p, nExpectedDepth); + bInconsistent = true; + } + + //If the preview has hard-coded bullets/numbering then they must + //be stripped to reveal the true underlying styles attributes + SfxItemSet aAttrs(pOutliner->GetParaAttribs(j)); + if (aAttrs.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET) + { + aAttrs.ClearItem(EE_PARA_NUMBULLET); + pOutliner->SetParaAttribs(j, aAttrs); + bInconsistent = true; + } + + } + if (bInconsistent) + { + SAL_WARN("sd.filter", "Fixing inconsistent outline numbering placeholder preview"); + pMasterOutline->SetOutlinerParaObject(pOutliner->CreateParaObject(0, nParaCount)); + } + pOutliner->Clear(); + } +} + +bool SdXMLFilter::Import( ErrCode& nError ) +{ + ErrCode nRet = ERRCODE_NONE; + + // Get service factory + Reference< uno::XComponentContext > rxContext = + comphelper::getProcessComponentContext(); + + SdDrawDocument* pDoc = mrDocShell.GetDoc(); + bool const bWasUndo(pDoc->IsUndoEnabled()); + pDoc->EnableUndo(false); + pDoc->NewOrLoadCompleted( DocCreationMode::New ); + pDoc->CreateFirstPages(); + pDoc->StopWorkStartupDelay(); + + mxModel->lockControllers(); + + /** property map for import info set */ + static PropertyMapEntry const aImportInfoMap[] = + { + // necessary properties for XML progress bar at load time + { OUString("ProgressRange"), 0, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressMax"), 0, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressCurrent"), 0, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("Preview"), 0, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("PageLayouts"), 0, cppu::UnoType<container::XNameAccess>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("PrivateData"), 0, cppu::UnoType<XInterface>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("BaseURI"), 0, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamRelPath"), 0, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("BuildId"), 0, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("OrganizerMode"), 0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("SourceStorage"), 0, cppu::UnoType<embed::XStorage>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + }; + + uno::Reference< beans::XPropertySet > xInfoSet( GenericPropertySet_CreateInstance( new PropertySetInfo( aImportInfoMap ) ) ); + xInfoSet->setPropertyValue( "Preview" , uno::Any( mrDocShell.GetDoc()->IsStarDrawPreviewMode() ) ); + + // ---- get BuildId from parent container if available + + uno::Reference< container::XChild > xChild( mxModel, uno::UNO_QUERY ); + if( xChild.is() ) + { + uno::Reference< beans::XPropertySet > xParentSet( xChild->getParent(), uno::UNO_QUERY ); + if( xParentSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xParentSet->getPropertySetInfo() ); + OUString sPropName( "BuildId" ); + if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(sPropName) ) + { + xInfoSet->setPropertyValue( sPropName, xParentSet->getPropertyValue(sPropName) ); + } + } + } + + uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler; + rtl::Reference<SvXMLGraphicHelper> xGraphicHelper; + Reference< document::XEmbeddedObjectResolver > xObjectResolver; + rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper; + + Reference< lang::XComponent > xModelComp = mxModel; + + // try to get an XStatusIndicator from the Medium + { + SfxItemSet* pSet = mrMedium.GetItemSet(); + if(pSet) + { + const SfxUnoAnyItem* pItem = pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL); + if (pItem) + { + pItem->GetValue() >>= mxStatusIndicator; + } + } + + if(mxStatusIndicator.is()) + { + sal_Int32 nProgressRange(1000000); + OUString aMsg(SvxResId(RID_SVXSTR_DOC_LOAD)); + mxStatusIndicator->start(aMsg, nProgressRange); + + // set ProgressRange + uno::Any aProgRange; + aProgRange <<= nProgressRange; + xInfoSet->setPropertyValue( "ProgressRange" , aProgRange); + + // set ProgressCurrent + uno::Any aProgCurrent; + aProgCurrent <<= sal_Int32(0); + xInfoSet->setPropertyValue( "ProgressCurrent" , aProgCurrent); + } + } + + // get the input stream (storage or stream) + + uno::Reference < embed::XStorage > xStorage = mrMedium.GetStorage(); + + xInfoSet->setPropertyValue( "SourceStorage", Any( xStorage ) ); + + if( !xStorage.is() ) + nRet = SD_XML_READERROR; + + if( ERRCODE_NONE == nRet ) + { + xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, + SvXMLGraphicHelperMode::Read ); + xGraphicStorageHandler = xGraphicHelper.get(); + xObjectHelper = SvXMLEmbeddedObjectHelper::Create( + xStorage, *pDoc->GetPersist(), + SvXMLEmbeddedObjectHelperMode::Read ); + xObjectResolver = xObjectHelper.get(); + } + + // Set base URI + OUString const baseURI(mrMedium.GetBaseURL()); + // needed for relative URLs, but in clipboard copy/paste there may be none + SAL_INFO_IF(baseURI.isEmpty(), "sd.filter", "SdXMLFilter: no base URL"); + xInfoSet->setPropertyValue("BaseURI", Any(baseURI)); + + if( ERRCODE_NONE == nRet && SfxObjectCreateMode::EMBEDDED == mrDocShell.GetCreateMode() ) + { + OUString aName; + if ( mrMedium.GetItemSet() ) + { + const SfxStringItem* pDocHierarchItem = + mrMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME); + if ( pDocHierarchItem ) + aName = pDocHierarchItem->GetValue(); + } + else + aName = "dummyObjectName" ; + + if( !aName.isEmpty() ) + xInfoSet->setPropertyValue( "StreamRelPath", Any( aName ) ); + } + + if (SdXMLFilterMode::Organizer == meFilterMode) + xInfoSet->setPropertyValue("OrganizerMode", uno::Any(true)); + + if( ERRCODE_NONE == nRet ) + { + + // prepare filter arguments + Sequence<Any> aFilterArgs( 4 ); + Any *pArgs = aFilterArgs.getArray(); + *pArgs++ <<= xInfoSet; + *pArgs++ <<= xGraphicStorageHandler; + *pArgs++ <<= xObjectResolver; + *pArgs++ <<= mxStatusIndicator; + + Sequence<Any> aEmptyArgs( 2 ); + pArgs = aEmptyArgs.getArray(); + *pArgs++ <<= xInfoSet; + *pArgs++ <<= mxStatusIndicator; + + const OUString aName( mrMedium.GetName() ); + + XML_SERVICES const * pServices = getServices( true, IsDraw(), mnStoreVer ); + + ErrCode nWarn = ERRCODE_NONE; + ErrCode nWarn2 = ERRCODE_NONE; + // read storage streams + // #i103539#: always read meta.xml for generator + nWarn = ReadThroughComponent( + xStorage, xModelComp, "meta.xml", rxContext, + pServices->mpMeta, + aEmptyArgs, aName, false ); + + if( meFilterMode != SdXMLFilterMode::Organizer ) + { + nWarn2 = ReadThroughComponent( + xStorage, xModelComp, "settings.xml", rxContext, + pServices->mpSettings, + aFilterArgs, aName, false ); + } + + nRet = ReadThroughComponent( + xStorage, xModelComp, "styles.xml", rxContext, + pServices->mpStyles, + aFilterArgs, aName, true ); + + if( !nRet && (meFilterMode != SdXMLFilterMode::Organizer) ) + nRet = ReadThroughComponent( + xStorage, xModelComp, "content.xml", rxContext, + pServices->mpContent, + aFilterArgs, aName, true ); + + if( !nRet ) + { + if( nWarn ) + nRet = nWarn; + else if( nWarn2 ) + nRet = nWarn2; + } + } + + if( xGraphicHelper ) + xGraphicHelper->dispose(); + xGraphicHelper.clear(); + xGraphicStorageHandler = nullptr; + if( xObjectHelper.is() ) + xObjectHelper->dispose(); + xObjectHelper.clear(); + xObjectResolver = nullptr; + + if( mxStatusIndicator.is() ) + mxStatusIndicator->end(); + + if( mxModel.is() ) + mxModel->unlockControllers(); + + if( nRet == ERRCODE_NONE ) + pDoc->UpdateAllLinks(); + + if( nRet.anyOf( ERRCODE_NONE, SD_XML_READERROR ) ) + ; + else if( nRet == ERRCODE_IO_BROKENPACKAGE && xStorage.is() ) + nError = ERRCODE_IO_BROKENPACKAGE; + else + { + // TODO/LATER: this is completely wrong! Filter code should never call ErrorHandler directly! + ErrorHandler::HandleError( nRet ); + if( nRet.IsWarning() ) + nRet = ERRCODE_NONE; + } + + // clear unused named items from item pool + + ::svx::DropUnusedNamedItems(mxModel); + + // set BuildId on XModel for later OLE object loading + if( xInfoSet.is() ) + { + uno::Reference< beans::XPropertySet > xModelSet( mxModel, uno::UNO_QUERY ); + if( xModelSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xModelSetInfo( xModelSet->getPropertySetInfo() ); + static const OUStringLiteral sPropName( u"BuildId" ); + + OUString sBuildId; + xInfoSet->getPropertyValue(sPropName) >>= sBuildId; + + if( xModelSetInfo.is() && xModelSetInfo->hasPropertyByName(sPropName) ) + { + xModelSet->setPropertyValue( sPropName, Any( sBuildId ) ); + } + + bool bTransform = false; + + if( nRet == ERRCODE_NONE ) + { + if( !sBuildId.isEmpty() ) + { + sal_Int32 nIndex = sBuildId.indexOf('$'); + if( nIndex != -1 ) + { + sal_Int32 nUPD = o3tl::toInt32(sBuildId.subView( 0, nIndex )); + + if( nUPD == 300 ) + { + sal_Int32 nBuildId = o3tl::toInt32(sBuildId.subView( nIndex+1 )); + if( (nBuildId > 0) && (nBuildId < 9316) ) + bTransform = true; // treat OOo 3.0 beta1 as OOo 2.x + } + else if( (nUPD == 680) || ( nUPD >= 640 && nUPD <= 645 ) ) + bTransform = true; + } + } + else + { + // check for binary formats + std::shared_ptr<const SfxFilter> pFilter = mrMedium.GetFilter(); + if( pFilter ) + { + OUString typeName(pFilter->GetRealTypeName()); + if( typeName.startsWith( "impress_StarImpress" ) || + typeName.startsWith( "draw_StarDraw" ) ) + { + bTransform = true; + } + } + } + } + + if( bTransform ) + TransformOOo2xDocument( pDoc ); + } + } + + fixupOutlinePlaceholderNumberingDepths(pDoc); + + pDoc->EnableUndo(bWasUndo); + mrDocShell.ClearUndoBuffer(); + return nRet == ERRCODE_NONE; +} + +bool SdXMLFilter::Export() +{ + rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper; + rtl::Reference<SvXMLGraphicHelper> xGraphicHelper; + bool bDocRet = false; + + if( !mxModel.is() ) + { + SAL_WARN( "sd.filter","Got NO Model in XMLExport"); + return false; + } + + bool bLocked = mxModel->hasControllersLocked(); + + try + { + mxModel->lockControllers(); + + uno::Reference< lang::XServiceInfo > xServiceInfo( mxModel, uno::UNO_QUERY ); + + if( !xServiceInfo.is() || !xServiceInfo->supportsService( "com.sun.star.drawing.GenericDrawingDocument" ) ) + { + SAL_WARN( "sd.filter", "Model is no DrawingDocument in XMLExport" ); + return false; + } + + uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + + uno::Reference< xml::sax::XWriter > xWriter = xml::sax::Writer::create( xContext ); + + /** property map for export info set */ + static PropertyMapEntry const aExportInfoMap[] = + { + { OUString("ProgressRange"), 0, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressMax"), 0, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressCurrent"), 0, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("UsePrettyPrinting"),0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("PageLayoutNames"), 0, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("BaseURI"), 0, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamRelPath"), 0, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleNames"), 0, cppu::UnoType<Sequence<OUString>>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleFamilies"), 0, cppu::UnoType<Sequence<sal_Int32>>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("TargetStorage"), 0, cppu::UnoType<embed::XStorage>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + }; + + uno::Reference< beans::XPropertySet > xInfoSet( GenericPropertySet_CreateInstance( new PropertySetInfo( aExportInfoMap ) ) ); + + bool bUsePrettyPrinting = officecfg::Office::Common::Save::Document::PrettyPrinting::get(); + xInfoSet->setPropertyValue( "UsePrettyPrinting", Any( bUsePrettyPrinting ) ); + + const uno::Reference < embed::XStorage >& xStorage = mrMedium.GetOutputStorage(); + + // Set base URI + OUString sPropName( "BaseURI" ); + xInfoSet->setPropertyValue( sPropName, Any( mrMedium.GetBaseURL( true ) ) ); + + xInfoSet->setPropertyValue( "TargetStorage", Any( xStorage ) ); + + if( SfxObjectCreateMode::EMBEDDED == mrDocShell.GetCreateMode() ) + { + OUString aName; + if ( mrMedium.GetItemSet() ) + { + const SfxStringItem* pDocHierarchItem = + mrMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME); + if ( pDocHierarchItem ) + aName = pDocHierarchItem->GetValue(); + } + + if( !aName.isEmpty() ) + { + sPropName = "StreamRelPath"; + xInfoSet->setPropertyValue( sPropName, Any( aName ) ); + } + } + + // initialize descriptor + uno::Sequence< beans::PropertyValue > aDescriptor( 1 ); + beans::PropertyValue* pProps = aDescriptor.getArray(); + + pProps[0].Name = "FileName"; + pProps[0].Value <<= mrMedium.GetName(); + + { + uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver; + uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler; + + // create helper for graphic and ole export if we have a storage + if( xStorage.is() ) + { + xObjectHelper = SvXMLEmbeddedObjectHelper::Create( xStorage, *mrDocShell.GetDoc()->GetPersist(), SvXMLEmbeddedObjectHelperMode::Write ); + xObjectResolver = xObjectHelper.get(); + + xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, SvXMLGraphicHelperMode::Write ); + xGraphicStorageHandler = xGraphicHelper.get(); + } + + CreateStatusIndicator(); + if(mxStatusIndicator.is()) + { + sal_Int32 nProgressRange(1000000); + OUString aMsg(SdResId(STR_SAVE_DOC)); + mxStatusIndicator->start(aMsg, nProgressRange); + + // set ProgressRange + uno::Any aProgRange; + aProgRange <<= nProgressRange; + xInfoSet->setPropertyValue( "ProgressRange" , aProgRange); + + // set ProgressCurrent + uno::Any aProgCurrent; + aProgCurrent <<= sal_Int32(0); + xInfoSet->setPropertyValue( "ProgressCurrent" , aProgCurrent); + } + + XML_SERVICES const * pServiceNames = getServices( false, IsDraw(), mnStoreVer ); + + XML_SERVICEMAP aServices[5]; sal_uInt16 i = 0; + aServices[i ].mpService = pServiceNames->mpStyles; + aServices[i++].mpStream = "styles.xml"; + + aServices[i ].mpService = pServiceNames->mpContent; + aServices[i++].mpStream = "content.xml"; + + aServices[i ].mpService = pServiceNames->mpSettings; + aServices[i++].mpStream = "settings.xml"; + + if( mrDocShell.GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) + { + aServices[i ].mpService = pServiceNames->mpMeta; + aServices[i++].mpStream = "meta.xml"; + }; + + aServices[i].mpService = nullptr; + aServices[i].mpStream = nullptr; + + XML_SERVICEMAP* pServices = aServices; + + // doc export + do + { + SAL_INFO( "sd.filter", "exporting substream " << pServices->mpStream ); + + uno::Reference<io::XOutputStream> xDocOut; + if( xStorage.is() ) + { + const OUString sDocName( OUString::createFromAscii( pServices->mpStream ) ); + uno::Reference<io::XStream> xStream = + xStorage->openStreamElement( sDocName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + + DBG_ASSERT(xStream.is(), "Can't create output stream in package!"); + if( !xStream.is() ) + return false; + + xDocOut = xStream->getOutputStream(); + Reference <beans::XPropertySet > xProps( xStream, uno::UNO_QUERY ); + if( !xDocOut.is() || !xProps.is() ) + return false; + + xProps->setPropertyValue( "MediaType", Any(OUString( "text/xml"))); + + // encrypt all streams + xProps->setPropertyValue( "UseCommonStoragePasswordEncryption", + uno::Any( true ) ); + + xInfoSet->setPropertyValue( "StreamName", Any( sDocName ) ); + } + + xWriter->setOutputStream( xDocOut ); + + uno::Sequence< uno::Any > aArgs( 2 + ( mxStatusIndicator.is() ? 1 : 0 ) + ( xGraphicStorageHandler.is() ? 1 : 0 ) + ( xObjectResolver.is() ? 1 : 0 ) ); + uno::Any* pArgs = aArgs.getArray(); + *pArgs++ <<= xInfoSet; + if (xGraphicStorageHandler.is()) + *pArgs++ <<= xGraphicStorageHandler; + if (xObjectResolver.is()) + *pArgs++ <<= xObjectResolver; + if (mxStatusIndicator.is()) + *pArgs++ <<= mxStatusIndicator; + + *pArgs <<= xWriter; + + uno::Reference< document::XFilter > xFilter( xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii( pServices->mpService ), aArgs, xContext ), uno::UNO_QUERY ); + if( xFilter.is() ) + { + uno::Reference< document::XExporter > xExporter( xFilter, uno::UNO_QUERY ); + if( xExporter.is() ) + { + xExporter->setSourceDocument( mxModel ); + // outputstream will be closed by SAX parser + bDocRet = xFilter->filter( aDescriptor ); + } + } + + pServices++; + } + while( bDocRet && pServices->mpService ); + + if(mxStatusIndicator.is()) + mxStatusIndicator->end(); + } + } + catch (const uno::Exception &) + { + TOOLS_WARN_EXCEPTION( "sd.filter", "uno Exception caught while exporting"); + bDocRet = false; + } + if ( !bLocked ) + mxModel->unlockControllers(); + + if( xGraphicHelper ) + xGraphicHelper->dispose(); + xGraphicHelper.clear(); + + if( xObjectHelper ) + xObjectHelper->dispose(); + xObjectHelper.clear(); + + return bDocRet; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportFODP(SvStream &rStream) +{ + SdDLL::Init(); + + sd::DrawDocShellRef xDocSh(new sd::DrawDocShell(SfxObjectCreateMode::EMBEDDED, false, DocumentType::Impress)); + xDocSh->DoInitNew(); + uno::Reference<frame::XModel> xModel(xDocSh->GetModel()); + + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference<io::XInputStream> xStream(new ::utl::OSeekableInputStreamWrapper(rStream)); + uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.XmlFilterAdaptor"), uno::UNO_SET_THROW); + + css::uno::Sequence<OUString> aUserData + { + "com.sun.star.comp.filter.OdfFlatXml", + "", + "com.sun.star.comp.Impress.XMLOasisImporter", + "com.sun.star.comp.Impress.XMLOasisExporter", + "", + "", + "true" + }; + uno::Sequence<beans::PropertyValue> aAdaptorArgs(comphelper::InitPropertySequence( + { + { "UserData", uno::Any(aUserData) }, + })); + css::uno::Sequence<uno::Any> aOuterArgs{ uno::Any(aAdaptorArgs) }; + + uno::Reference<lang::XInitialization> xInit(xInterface, uno::UNO_QUERY_THROW); + xInit->initialize(aOuterArgs); + + uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence( + { + { "InputStream", uno::Any(xStream) }, + { "URL", uno::Any(OUString("private:stream")) }, + })); + xImporter->setTargetDocument(xModel); + + uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW); + //SetLoading hack because the document properties will be re-initted + //by the xml filter and during the init, while it's considered uninitialized, + //setting a property will inform the document it's modified, which attempts + //to update the properties, which throws cause the properties are uninitialized + xDocSh->SetLoading(SfxLoadedFlags::NONE); + bool ret = xFilter->filter(aArgs); + xDocSh->SetLoading(SfxLoadedFlags::ALL); + + xDocSh->DoClose(); + + return ret; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportPPTX(SvStream &rStream) +{ + SdDLL::Init(); + + sd::DrawDocShellRef xDocSh(new sd::DrawDocShell(SfxObjectCreateMode::EMBEDDED, false, DocumentType::Impress)); + xDocSh->DoInitNew(); + uno::Reference<frame::XModel> xModel(xDocSh->GetModel()); + + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream)); + + uno::Reference<document::XFilter> xFilter(xMultiServiceFactory->createInstance("com.sun.star.comp.oox.ppt.PowerPointImport"), uno::UNO_QUERY_THROW); + + uno::Reference<document::XImporter> xImporter(xFilter, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence( + { + { "InputStream", uno::Any(xStream) }, + { "InputMode", uno::Any(true) }, + })); + xImporter->setTargetDocument(xModel); + + //SetLoading hack because the document properties will be re-initted + //by the xml filter and during the init, while it's considered uninitialized, + //setting a property will inform the document it's modified, which attempts + //to update the properties, which throws cause the properties are uninitialized + xDocSh->SetLoading(SfxLoadedFlags::NONE); + bool ret = false; + try + { + ret = xFilter->filter(aArgs); + } + catch (...) + { + } + xDocSh->SetLoading(SfxLoadedFlags::ALL); + + xDocSh->DoClose(); + + return ret; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |