/* -*- 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 . */ // must be first #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drawshapesubsetting.hxx" #include "drawshape.hxx" #include "backgroundshape.hxx" #include "mediashape.hxx" #include "appletshape.hxx" #include "shapeimporter.hxx" #include "slideshowexceptions.hxx" #include "gdimtftools.hxx" #include "tools.hxx" #include "slideshowcontext.hxx" #include #include using namespace com::sun::star; namespace slideshow { namespace internal { namespace { bool importShapeGraphic( GraphicObject & o_rGraphic, uno::Reference const& xPropSet ) { OUString aURL; if( !getPropertyValue( aURL, xPropSet, "GraphicURL") || aURL.isEmpty() ) { // no or empty property - cannot import shape graphic return false; } OUString const aVndUrl( "vnd.sun.star.GraphicObject:" ); sal_Int32 nIndex( aURL.indexOf( aVndUrl ) ); if(nIndex != -1) { // skip past the end of the "vnd..." prefix nIndex += aVndUrl.getLength(); if(nIndex >= aURL.getLength()) { OSL_FAIL( "ShapeImporter::importShape(): " "embedded graphic has no graphic ID" ); return false; } // unique ID string found in URL, extract // to separate string OUString const aUniqueId( aURL.copy( nIndex, aURL.getLength() - nIndex ) ); // TODO(T2): Creating a GraphicObject is not // thread safe (internally calls VCL, and has // unguarded internal singleton mpGlobalMgr) // fetch already loaded graphic from graphic manager. OString const aOldString(OUStringToOString(aUniqueId, RTL_TEXTENCODING_UTF8)); o_rGraphic = GraphicObject( aOldString ); if( GRAPHIC_DEFAULT == o_rGraphic.GetType() || GRAPHIC_NONE == o_rGraphic.GetType() ) { // even the GrfMgr does not seem to know this graphic return false; } } else { // no special string found, graphic must be // external. Load via GraphicIm porter INetURLObject aTmp( aURL ); boost::scoped_ptr pGraphicStream( utl::UcbStreamHelper::CreateStream( aTmp.GetMainURL( INetURLObject::NO_DECODE ), STREAM_READ ) ); if( !pGraphicStream ) { OSL_FAIL( "ShapeImporter::importShape(): " "cannot create input stream for graphic" ); return false; } Graphic aTmpGraphic; if( GraphicConverter::Import( *pGraphicStream, aTmpGraphic ) != ERRCODE_NONE ) { OSL_FAIL( "ShapeImporter::importShape(): " "Failed to import shape graphic from given URL" ); return false; } o_rGraphic = GraphicObject( aTmpGraphic ); } return true; } /** This shape implementation just acts as a dummy for the layermanager. Its sole role is for hit test detection of group shapes. */ class ShapeOfGroup : public Shape { public: ShapeOfGroup( ShapeSharedPtr const& pGroupShape, uno::Reference const& xShape, uno::Reference const& xPropSet, double nPrio ); // Shape: virtual uno::Reference getXShape() const SAL_OVERRIDE; virtual void addViewLayer( ViewLayerSharedPtr const& pNewLayer, bool bRedrawLayer ) SAL_OVERRIDE; virtual bool removeViewLayer( ViewLayerSharedPtr const& pNewLayer ) SAL_OVERRIDE; virtual bool clearAllViewLayers() SAL_OVERRIDE; virtual bool update() const SAL_OVERRIDE; virtual bool render() const SAL_OVERRIDE; virtual bool isContentChanged() const SAL_OVERRIDE; virtual basegfx::B2DRectangle getBounds() const SAL_OVERRIDE; virtual basegfx::B2DRectangle getDomBounds() const SAL_OVERRIDE; virtual basegfx::B2DRectangle getUpdateArea() const SAL_OVERRIDE; virtual bool isVisible() const SAL_OVERRIDE; virtual double getPriority() const SAL_OVERRIDE; virtual bool isBackgroundDetached() const SAL_OVERRIDE; private: ShapeSharedPtr const mpGroupShape; uno::Reference const mxShape; double const mnPrio; basegfx::B2DPoint maPosOffset; double mnWidth; double mnHeight; }; ShapeOfGroup::ShapeOfGroup( ShapeSharedPtr const& pGroupShape, uno::Reference const& xShape, uno::Reference const& xPropSet, double nPrio ) : mpGroupShape(pGroupShape), mxShape(xShape), mnPrio(nPrio) { // read bound rect uno::Any const aTmpRect_( xPropSet->getPropertyValue( "BoundRect" )); awt::Rectangle const aTmpRect( aTmpRect_.get() ); basegfx::B2DRectangle const groupPosSize( pGroupShape->getBounds() ); maPosOffset = basegfx::B2DPoint( aTmpRect.X - groupPosSize.getMinX(), aTmpRect.Y - groupPosSize.getMinY() ); mnWidth = aTmpRect.Width; mnHeight = aTmpRect.Height; } uno::Reference ShapeOfGroup::getXShape() const { return mxShape; } void ShapeOfGroup::addViewLayer( ViewLayerSharedPtr const& /*pNewLayer*/, bool /*bRedrawLayer*/ ) { } bool ShapeOfGroup::removeViewLayer( ViewLayerSharedPtr const& /*pNewLayer*/ ) { return true; } bool ShapeOfGroup::clearAllViewLayers() { return true; } bool ShapeOfGroup::update() const { return true; } bool ShapeOfGroup::render() const { return true; } bool ShapeOfGroup::isContentChanged() const { return false; } basegfx::B2DRectangle ShapeOfGroup::getBounds() const { basegfx::B2DRectangle const groupPosSize( mpGroupShape->getBounds() ); double const posX = (groupPosSize.getMinX() + maPosOffset.getX()); double const posY = (groupPosSize.getMinY() + maPosOffset.getY()); return basegfx::B2DRectangle( posX, posY, posX + mnWidth, posY + mnHeight ); } basegfx::B2DRectangle ShapeOfGroup::getDomBounds() const { return getBounds(); } basegfx::B2DRectangle ShapeOfGroup::getUpdateArea() const { return getBounds(); } bool ShapeOfGroup::isVisible() const { return mpGroupShape->isVisible(); } double ShapeOfGroup::getPriority() const { return mnPrio; } bool ShapeOfGroup::isBackgroundDetached() const { return false; } } // anon namespace ShapeSharedPtr ShapeImporter::createShape( uno::Reference const& xCurrShape, uno::Reference const& xPropSet, OUString const& shapeType ) const { if( shapeType == "com.sun.star.drawing.MediaShape" || shapeType == "com.sun.star.presentation.MediaShape" ) { // Media shape (video etc.). This is a special object return createMediaShape(xCurrShape, mnAscendingPrio, mrContext); } else if( shapeType == "com.sun.star.drawing.PluginShape" ) { // PropertyValues to copy from XShape to plugin static const char* aPropertyValues[] = { "PluginURL", "PluginMimeType", "PluginCommands" }; // (Netscape)Plugin shape. This is a special object return createAppletShape( xCurrShape, mnAscendingPrio, OUString( "com.sun.star.comp.sfx2.PluginObject" ), aPropertyValues, SAL_N_ELEMENTS(aPropertyValues), mrContext ); } else if( shapeType == "com.sun.star.drawing.AppletShape" ) { // PropertyValues to copy from XShape to applet static const char* aPropertyValues[] = { "AppletCodeBase", "AppletName", "AppletCode", "AppletCommands", "AppletIsScript" }; // (Java)Applet shape. This is a special object return createAppletShape( xCurrShape, mnAscendingPrio, OUString( "com.sun.star.comp.sfx2.AppletObject" ), aPropertyValues, SAL_N_ELEMENTS(aPropertyValues), mrContext ); } else if( shapeType == "com.sun.star.drawing.OLE2Shape" || shapeType == "com.sun.star.presentation.OLE2Shape" ) { // #i46224# Mark OLE shapes as foreign content - scan them for // unsupported actions, and fallback to bitmap, if necessary return DrawShape::create( xCurrShape, mxPage, mnAscendingPrio, true, mrContext ); } else if( shapeType == "com.sun.star.drawing.GraphicObjectShape" || shapeType == "com.sun.star.presentation.GraphicObjectShape" ) { GraphicObject aGraphicObject; // to get hold of GIF animations, inspect Graphic // objects more thoroughly (the plain-jane shape // metafile of course would only contain the first // animation frame) if( !importShapeGraphic( aGraphicObject, xPropSet ) ) return ShapeSharedPtr(); // error loading graphic - // no placeholders in // slideshow if( !aGraphicObject.IsAnimated() ) { // no animation - simply utilize plain draw shape import // import shape as bitmap - either it's a bitmap // anyway, or it's a metafile, which currently the // metafile renderer might not display correctly. return DrawShape::create( xCurrShape, mxPage, mnAscendingPrio, true, mrContext ); } // now extract relevant shape attributes via API drawing::ColorMode eColorMode( drawing::ColorMode_STANDARD ); sal_Int16 nLuminance(0); sal_Int16 nContrast(0); sal_Int16 nRed(0); sal_Int16 nGreen(0); sal_Int16 nBlue(0); double nGamma(1.0); sal_Int16 nTransparency(0); sal_Int32 nRotation(0); getPropertyValue( eColorMode, xPropSet, "GraphicColorMode" ); getPropertyValue( nLuminance, xPropSet, "AdjustLuminance" ); getPropertyValue( nContrast, xPropSet, "AdjustContrast" ); getPropertyValue( nRed, xPropSet, "AdjustRed" ); getPropertyValue( nGreen, xPropSet, "AdjustGreen" ); getPropertyValue( nBlue, xPropSet, "AdjustBlue" ); getPropertyValue( nGamma, xPropSet, "Gamma" ); getPropertyValue( nTransparency, xPropSet, "Transparency" ); getPropertyValue( nRotation, xPropSet, "RotateAngle" ); GraphicAttr aGraphAttrs; aGraphAttrs.SetDrawMode( (GraphicDrawMode)eColorMode ); aGraphAttrs.SetLuminance( nLuminance ); aGraphAttrs.SetContrast( nContrast ); aGraphAttrs.SetChannelR( nRed ); aGraphAttrs.SetChannelG( nGreen ); aGraphAttrs.SetChannelB( nBlue ); aGraphAttrs.SetGamma( nGamma ); aGraphAttrs.SetTransparency( static_cast(nTransparency) ); aGraphAttrs.SetRotation( static_cast(nRotation*10) ); text::GraphicCrop aGraphCrop; if( getPropertyValue( aGraphCrop, xPropSet, "GraphicCrop" )) { aGraphAttrs.SetCrop( aGraphCrop.Left, aGraphCrop.Top, aGraphCrop.Right, aGraphCrop.Bottom ); } // fetch readily transformed and color-modified // graphic Graphic aGraphic( aGraphicObject.GetTransformedGraphic( aGraphicObject.GetPrefSize(), aGraphicObject.GetPrefMapMode(), aGraphAttrs ) ); return DrawShape::create( xCurrShape, mxPage, mnAscendingPrio, aGraphic, mrContext ); } else { return DrawShape::create( xCurrShape, mxPage, mnAscendingPrio, false, mrContext ); } } bool ShapeImporter::isSkip( uno::Reference const& xPropSet, OUString const& shapeType, uno::Reference< drawing::XLayer> const& xLayer ) { // skip empty presentation objects: bool bEmpty = false; if( getPropertyValue( bEmpty, xPropSet, "IsEmptyPresentationObject") && bEmpty ) { return true; } //skip shapes which corresponds to annotations if(xLayer.is()) { OUString layerName; uno::Reference xPropLayerSet( xLayer, uno::UNO_QUERY ); const uno::Any& a(xPropLayerSet->getPropertyValue("Name") ); bool const bRet = (a >>= layerName); if(bRet) { if( layerName == "DrawnInSlideshow" ) { //Transform shapes into PolyPolygons importPolygons(xPropSet); return true; } } } // don't export presentation placeholders on masterpage // they can be non empty when user edits the default texts if(mbConvertingMasterPage) { if( shapeType == "com.sun.star.presentation.TitleTextShape" || shapeType == "com.sun.star.presentation.OutlinerShape" ) { return true; } } return false; } void ShapeImporter::importPolygons(uno::Reference const& xPropSet) { drawing::PointSequenceSequence aRetval; sal_Int32 nLineColor=0; double fLineWidth; getPropertyValue( aRetval, xPropSet, "PolyPolygon" ); getPropertyValue( nLineColor, xPropSet, "LineColor" ); getPropertyValue( fLineWidth, xPropSet, "LineWidth" ); drawing::PointSequence* pOuterSequence = aRetval.getArray(); awt::Point* pInnerSequence = pOuterSequence->getArray(); ::basegfx::B2DPolygon aPoly; basegfx::B2DPoint aPoint; for( sal_Int32 nCurrPoly=0; nCurrPolygetLength(); ++nCurrPoly, ++pInnerSequence ) { aPoint.setX((*pInnerSequence).X); aPoint.setY((*pInnerSequence).Y); aPoly.append( aPoint ); } UnoViewVector::const_iterator aIter=(mrContext.mrViewContainer).begin(); UnoViewVector::const_iterator aEnd=(mrContext.mrViewContainer).end(); while(aIter != aEnd) { ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( (*aIter)->getCanvas(), aPoly ) ); if( pPolyPoly ) { pPolyPoly->setRGBALineColor( unoColor2RGBColor( nLineColor ).getIntegerColor() ); pPolyPoly->setStrokeWidth(fLineWidth); pPolyPoly->draw(); maPolygons.push_back(pPolyPoly); } ++aIter; } } ShapeSharedPtr ShapeImporter::importBackgroundShape() // throw (ShapeLoadFailedException) { if( maShapesStack.empty() ) throw ShapeLoadFailedException(); XShapesEntry& rTop = maShapesStack.top(); ShapeSharedPtr pBgShape( createBackgroundShape(mxPage, uno::Reference( rTop.mxShapes, uno::UNO_QUERY_THROW), mrContext) ); mnAscendingPrio += 1.0; return pBgShape; } ShapeSharedPtr ShapeImporter::importShape() // throw (ShapeLoadFailedException) { ShapeSharedPtr pRet; bool bIsGroupShape = false; while( !maShapesStack.empty() && !pRet ) { XShapesEntry& rTop = maShapesStack.top(); if( rTop.mnPos < rTop.mnCount ) { uno::Reference const xCurrShape( rTop.mxShapes->getByIndex( rTop.mnPos ), uno::UNO_QUERY ); ++rTop.mnPos; uno::Reference xPropSet( xCurrShape, uno::UNO_QUERY ); if( !xPropSet.is() ) { // we definitely need the properties of // the shape here. This will also fail, // if getByIndex did not return a valid // shape throw ShapeLoadFailedException(); } //Retrieve the layer for the current shape uno::Reference< drawing::XLayer > xDrawnInSlideshow; uno::Reference< drawing::XLayerSupplier > xLayerSupplier(mxPagesSupplier, uno::UNO_QUERY); if(xLayerSupplier.is()) { uno::Reference< container::XNameAccess > xNameAccess = xLayerSupplier->getLayerManager(); uno::Reference< drawing::XLayerManager > xLayerManager(xNameAccess, uno::UNO_QUERY); xDrawnInSlideshow = xLayerManager->getLayerForShape(xCurrShape); } OUString const shapeType( xCurrShape->getShapeType()); // is this shape presentation-invisible? if( !isSkip(xPropSet, shapeType, xDrawnInSlideshow) ) { bIsGroupShape = shapeType == "com.sun.star.drawing.GroupShape"; if( rTop.mpGroupShape ) // in group particle mode? { pRet.reset( new ShapeOfGroup( rTop.mpGroupShape /* container shape */, xCurrShape, xPropSet, mnAscendingPrio ) ); } else { pRet = createShape( xCurrShape, xPropSet, shapeType ); } mnAscendingPrio += 1.0; } } if( rTop.mnPos >= rTop.mnCount ) { // group or top-level shapes finished: maShapesStack.pop(); } if( bIsGroupShape && pRet ) { // push new group on the stack: group traversal maShapesStack.push( XShapesEntry( pRet ) ); } } return pRet; } bool ShapeImporter::isImportDone() const { return maShapesStack.empty(); } PolyPolygonVector ShapeImporter::getPolygons() { return maPolygons; } ShapeImporter::ShapeImporter( uno::Reference const& xPage, uno::Reference const& xActualPage, uno::Reference const& xPagesSupplier, const SlideShowContext& rContext, sal_Int32 nOrdNumStart, bool bConvertingMasterPage ) : mxPage( xActualPage ), mxPagesSupplier( xPagesSupplier ), mrContext( rContext ), maPolygons(), maShapesStack(), mnAscendingPrio( nOrdNumStart ), mbConvertingMasterPage( bConvertingMasterPage ) { uno::Reference const xShapes( xPage, uno::UNO_QUERY_THROW ); maShapesStack.push( XShapesEntry(xShapes) ); } } // namespace internal } // namespace presentation /* vim:set shiftwidth=4 softtabstop=4 expandtab: */