diff options
Diffstat (limited to 'writerfilter/source/dmapper/GraphicImport.cxx')
-rw-r--r-- | writerfilter/source/dmapper/GraphicImport.cxx | 2029 |
1 files changed, 2029 insertions, 0 deletions
diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx new file mode 100644 index 000000000..88a6be9ac --- /dev/null +++ b/writerfilter/source/dmapper/GraphicImport.cxx @@ -0,0 +1,2029 @@ +/* -*- 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 <string.h> + +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/drawing/ColorMode.hpp> +#include <com/sun/star/drawing/PointSequenceSequence.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/io/BufferSizeExceededException.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/table/BorderLine2.hpp> +#include <com/sun/star/text/GraphicCrop.hpp> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/RelOrientation.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/text/WrapTextMode.hpp> +#include <com/sun/star/text/XTextContent.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/table/ShadowFormat.hpp> + +#include <svx/svditer.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdtrans.hxx> +#include <svx/unoapi.hxx> +#include <cppuhelper/implbase.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <rtl/math.hxx> +#include <tools/diagnose_ex.h> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/string.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/sequence.hxx> + +#include <oox/drawingml/drawingmltypes.hxx> + +#include "DomainMapper.hxx" +#include <dmapper/GraphicZOrderHelper.hxx> +#include <ooxml/resourceids.hxx> + +#include "ConversionHelper.hxx" +#include "GraphicHelpers.hxx" +#include "GraphicImport.hxx" +#include "PropertyMap.hxx" +#include "TagLogger.hxx" +#include "WrapPolygonHandler.hxx" +#include "util.hxx" + +#include <comphelper/propertysequence.hxx> +#include <algorithm> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <o3tl/unit_conversion.hxx> +#include <oox/export/drawingml.hxx> + +using namespace css; + +namespace +{ +bool isTopGroupObj(const uno::Reference<drawing::XShape>& xShape) +{ + SdrObject* pObject = SdrObject::getSdrObjectFromXShape(xShape); + if (!pObject) + return false; + + if (pObject->getParentSdrObjectFromSdrObject()) + return false; + + return pObject->IsGroupObject(); +} +} + +namespace writerfilter::dmapper +{ + +namespace { + +class XInputStreamHelper : public cppu::WeakImplHelper<io::XInputStream> +{ + const sal_uInt8* m_pBuffer; + const sal_Int32 m_nLength; + sal_Int32 m_nPosition; +public: + XInputStreamHelper(const sal_uInt8* buf, size_t len); + + virtual ::sal_Int32 SAL_CALL readBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead ) override; + virtual ::sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead ) override; + virtual void SAL_CALL skipBytes( ::sal_Int32 nBytesToSkip ) override; + virtual ::sal_Int32 SAL_CALL available( ) override; + virtual void SAL_CALL closeInput( ) override; +}; + +} + +XInputStreamHelper::XInputStreamHelper(const sal_uInt8* buf, size_t len) : + m_pBuffer( buf ), + m_nLength( len ), + m_nPosition( 0 ) +{ +} + +sal_Int32 XInputStreamHelper::readBytes( uno::Sequence<sal_Int8>& aData, sal_Int32 nBytesToRead ) +{ + return readSomeBytes( aData, nBytesToRead ); +} + +sal_Int32 XInputStreamHelper::readSomeBytes( uno::Sequence<sal_Int8>& aData, sal_Int32 nMaxBytesToRead ) +{ + sal_Int32 nRet = 0; + if( nMaxBytesToRead > 0 ) + { + if( nMaxBytesToRead > m_nLength - m_nPosition ) + nRet = m_nLength - m_nPosition; + else + nRet = nMaxBytesToRead; + aData.realloc( nRet ); + sal_Int8* pData = aData.getArray(); + if( nRet ) + { + memcpy( pData, m_pBuffer + m_nPosition, nRet ); + m_nPosition += nRet; + } + } + return nRet; +} + + +void XInputStreamHelper::skipBytes( sal_Int32 nBytesToSkip ) +{ + if( nBytesToSkip < 0 || m_nPosition + nBytesToSkip > m_nLength) + throw io::BufferSizeExceededException(); + m_nPosition += nBytesToSkip; +} + + +sal_Int32 XInputStreamHelper::available( ) +{ + return m_nLength - m_nPosition; +} + + +void XInputStreamHelper::closeInput( ) +{ +} + +namespace { + +struct GraphicBorderLine +{ + sal_Int32 nLineWidth; + bool bHasShadow; + + GraphicBorderLine() : + nLineWidth(0) + ,bHasShadow(false) + {} + + bool isEmpty() const + { + return nLineWidth == 0 && !bHasShadow; + } + +}; + +} + +class GraphicImport_Impl +{ +private: + sal_Int32 nXSize; + bool bXSizeValid; + sal_Int32 nYSize; + bool bYSizeValid; + +public: + GraphicImportType eGraphicImportType; + DomainMapper& rDomainMapper; + + sal_Int32 nLeftPosition; + sal_Int32 nTopPosition; + + bool bUseSimplePos; + sal_Int32 zOrder; + + sal_Int16 nHoriOrient; + sal_Int16 nHoriRelation; + bool bPageToggle = false; + sal_Int16 nVertOrient; + sal_Int16 nVertRelation; + text::WrapTextMode nWrap; + bool bLayoutInCell; + bool bCompatForcedLayoutInCell; + bool bAllowOverlap = true; + bool bOpaque; + bool bBehindDoc; + bool bContour; + bool bContourOutside; + WrapPolygon::Pointer_t mpWrapPolygon; + + sal_Int32 nLeftMargin; + sal_Int32 nLeftMarginOrig = 0; + sal_Int32 nRightMargin; + sal_Int32 nTopMargin; + sal_Int32 nBottomMargin; + + bool bShadow; + sal_Int32 nShadowXDistance; + sal_Int32 nShadowYDistance; + sal_Int32 nShadowColor; + sal_Int32 nShadowTransparence; + + sal_Int32 nContrast; + sal_Int32 nBrightness; + + static constexpr sal_Int32 nFillColor = 0xffffffff; + + drawing::ColorMode eColorMode; + + GraphicBorderLine aBorders[4]; + + bool bIsGraphic; + + bool bSizeProtected; + bool bPositionProtected; + bool bHidden; + + sal_Int32 nShapeOptionType; + + OUString sName; + OUString sAlternativeText; + OUString title; + OUString sHyperlinkURL; + std::pair<OUString, OUString>& m_rPositionOffsets; + std::pair<OUString, OUString>& m_rAligns; + std::queue<OUString>& m_rPositivePercentages; + OUString sAnchorId; + comphelper::SequenceAsHashMap m_aInteropGrabBag; + std::optional<sal_Int32> m_oEffectExtentLeft; + std::optional<sal_Int32> m_oEffectExtentTop; + std::optional<sal_Int32> m_oEffectExtentRight; + std::optional<sal_Int32> m_oEffectExtentBottom; + + GraphicImport_Impl(GraphicImportType eImportType, DomainMapper& rDMapper, std::pair<OUString, OUString>& rPositionOffsets, std::pair<OUString, OUString>& rAligns, std::queue<OUString>& rPositivePercentages) : + nXSize(0) + ,bXSizeValid(false) + ,nYSize(0) + ,bYSizeValid(false) + ,eGraphicImportType( eImportType ) + ,rDomainMapper( rDMapper ) + ,nLeftPosition(0) + ,nTopPosition(0) + ,bUseSimplePos(false) + ,zOrder(-1) + ,nHoriOrient( text::HoriOrientation::NONE ) + ,nHoriRelation( text::RelOrientation::FRAME ) + ,nVertOrient( text::VertOrientation::NONE ) + ,nVertRelation( text::RelOrientation::FRAME ) + ,nWrap(text::WrapTextMode_NONE) + ,bLayoutInCell(true) + ,bCompatForcedLayoutInCell(false) + ,bOpaque( !rDMapper.IsInHeaderFooter() ) + ,bBehindDoc(false) + ,bContour(false) + ,bContourOutside(true) + ,nLeftMargin(319) + ,nRightMargin(319) + ,nTopMargin(0) + ,nBottomMargin(0) + ,bShadow(false) + ,nShadowXDistance(0) + ,nShadowYDistance(0) + ,nShadowColor(0) + ,nShadowTransparence(0) + ,nContrast(0) + ,nBrightness(0) + ,eColorMode( drawing::ColorMode_STANDARD ) + ,bIsGraphic(false) + ,bSizeProtected(false) + ,bPositionProtected(false) + ,bHidden(false) + ,nShapeOptionType(0) + ,m_rPositionOffsets(rPositionOffsets) + ,m_rAligns(rAligns) + ,m_rPositivePercentages(rPositivePercentages) + { + if (eGraphicImportType == GraphicImportType::IMPORT_AS_DETECTED_INLINE + && !rDMapper.IsInShape()) + { + zOrder = 0; + } + } + + void setXSize(sal_Int32 _nXSize) + { + nXSize = _nXSize; + bXSizeValid = true; + } + + sal_uInt32 getXSize() const + { + return nXSize; + } + + bool isXSizeValid() const + { + return bXSizeValid; + } + + void setYSize(sal_Int32 _nYSize) + { + nYSize = _nYSize; + bYSizeValid = true; + } + + sal_uInt32 getYSize() const + { + return nYSize; + } + + bool isYSizeValid() const + { + return bYSizeValid; + } + + void applyMargins(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties) const + { + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ), uno::Any(nLeftMargin)); + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ), uno::Any(nRightMargin)); + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ), uno::Any(nTopMargin)); + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ), uno::Any(nBottomMargin)); + } + + void applyPosition(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties) const + { + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT ), + uno::Any(nHoriOrient)); + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT ), + uno::Any(nVertOrient)); + } + + void applyRelativePosition(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties, bool bRelativeOnly = false) const + { + if (!bRelativeOnly) + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT_POSITION), + uno::Any(nLeftPosition)); + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT_RELATION ), + uno::Any(nHoriRelation)); + xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_PAGE_TOGGLE), + uno::Any(bPageToggle)); + if (!bRelativeOnly) + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT_POSITION), + uno::Any(nTopPosition)); + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT_RELATION ), + uno::Any(nVertRelation)); + } + + void applyZOrder(uno::Reference<beans::XPropertySet> const & xGraphicObjectProperties) const + { + if (zOrder >= 0) + { + // tdf#120760 Send objects with behinddoc=true to the back. + sal_Int32 nZOrder = zOrder; + if (bBehindDoc && rDomainMapper.IsInHeaderFooter()) + nZOrder -= SAL_MAX_INT32; + GraphicZOrderHelper* pZOrderHelper = rDomainMapper.graphicZOrderHelper(); + bool bOldStyle = eGraphicImportType == GraphicImportType::IMPORT_AS_DETECTED_INLINE; + xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_Z_ORDER), + uno::Any(pZOrderHelper->findZOrder(nZOrder, bOldStyle))); + pZOrderHelper->addItem(xGraphicObjectProperties, nZOrder); + } + } + + void applyName(uno::Reference<beans::XPropertySet> const & xGraphicObjectProperties) const + { + try + { + if (!sName.isEmpty()) + { + uno::Reference<container::XNamed> const xNamed(xGraphicObjectProperties, uno::UNO_QUERY_THROW); + xNamed->setName(sName); + } + // else: name is automatically generated by SwDoc::MakeFlySection_() + + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_DESCRIPTION ), + uno::Any( sAlternativeText )); + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_TITLE ), + uno::Any( title )); + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("writerfilter", "failed"); + } + } + + void applyHyperlink(uno::Reference<beans::XPropertySet> const & xShapeProps, bool bIsShape) + { + // Graphic objects have a different hyperlink prop than shapes + auto aHyperlinkProp = bIsShape ? PROP_HYPERLINK : PROP_HYPER_LINK_U_R_L; + if (!sHyperlinkURL.isEmpty()) + { + xShapeProps->setPropertyValue( + getPropertyName(aHyperlinkProp), uno::Any(sHyperlinkURL)); + } + } + + /// Getter for m_aInteropGrabBag, but also merges in the values from other members if they are set. + comphelper::SequenceAsHashMap const & getInteropGrabBag() + { + comphelper::SequenceAsHashMap aEffectExtent; + if (m_oEffectExtentLeft) + aEffectExtent["l"] <<= *m_oEffectExtentLeft; + if (m_oEffectExtentTop) + aEffectExtent["t"] <<= *m_oEffectExtentTop; + if (m_oEffectExtentRight) + aEffectExtent["r"] <<= *m_oEffectExtentRight; + if (m_oEffectExtentBottom) + aEffectExtent["b"] <<= *m_oEffectExtentBottom; + if (!aEffectExtent.empty()) + m_aInteropGrabBag["CT_EffectExtent"] <<= aEffectExtent.getAsConstPropertyValueList(); + return m_aInteropGrabBag; + } +}; + +GraphicImport::GraphicImport(uno::Reference<uno::XComponentContext> const& xComponentContext, + uno::Reference<lang::XMultiServiceFactory> const& xTextFactory, + DomainMapper& rDMapper, + GraphicImportType eImportType, + std::pair<OUString, OUString>& rPositionOffsets, + std::pair<OUString, OUString>& rAligns, + std::queue<OUString>& rPositivePercentages) +: LoggedProperties("GraphicImport") +, LoggedTable("GraphicImport") +, LoggedStream("GraphicImport") +, m_pImpl(new GraphicImport_Impl(eImportType, rDMapper, rPositionOffsets, rAligns, rPositivePercentages)) +, m_xComponentContext(xComponentContext) +, m_xTextFactory(xTextFactory) +{ +} + +GraphicImport::~GraphicImport() +{ +} + +com::sun::star::awt::Point GraphicImport::GetGraphicObjectPosition() const +{ + return (com::sun::star::awt::Point(m_pImpl->nLeftPosition, m_pImpl->nTopPosition)); +} + +bool GraphicImport::GetLayoutInCell() const +{ + return m_pImpl->bLayoutInCell; +} + +void GraphicImport::handleWrapTextValue(sal_uInt32 nVal) +{ + switch (nVal) + { + case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_bothSides: // 90920; + m_pImpl->nWrap = text::WrapTextMode_PARALLEL; + break; + case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_left: // 90921; + m_pImpl->nWrap = text::WrapTextMode_LEFT; + break; + case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_right: // 90922; + m_pImpl->nWrap = text::WrapTextMode_RIGHT; + break; + case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_largest: // 90923; + m_pImpl->nWrap = text::WrapTextMode_DYNAMIC; + break; + default:; + } +} + +void GraphicImport::putPropertyToFrameGrabBag( const OUString& sPropertyName, const uno::Any& aPropertyValue ) +{ + beans::PropertyValue aProperty; + aProperty.Name = sPropertyName; + aProperty.Value = aPropertyValue; + + if (!m_xShape.is()) + return; + + uno::Reference< beans::XPropertySet > xSet(m_xShape, uno::UNO_QUERY_THROW); + + uno::Reference< beans::XPropertySetInfo > xSetInfo(xSet->getPropertySetInfo()); + if (!xSetInfo.is()) + return; + + OUString aGrabBagPropName; + uno::Reference<lang::XServiceInfo> xServiceInfo(m_xShape, uno::UNO_QUERY_THROW); + if (xServiceInfo->supportsService("com.sun.star.text.TextFrame")) + aGrabBagPropName = "FrameInteropGrabBag"; + else + aGrabBagPropName = "InteropGrabBag"; + + if (xSetInfo->hasPropertyByName(aGrabBagPropName)) + { + //Add pProperty to the end of the Sequence for aGrabBagPropName + uno::Sequence<beans::PropertyValue> aTmp; + xSet->getPropertyValue(aGrabBagPropName) >>= aTmp; + std::vector<beans::PropertyValue> aGrabBag(comphelper::sequenceToContainer<std::vector<beans::PropertyValue> >(aTmp)); + aGrabBag.push_back(aProperty); + + xSet->setPropertyValue(aGrabBagPropName, uno::Any(comphelper::containerToSequence(aGrabBag))); + } +} + +static bool lcl_bHasGroupSlantedChild(const SdrObject* pObj) +{ + // Returns true, if a child object differs more than 0.02deg from horizontal or vertical. + // Because lines sometimes are imported as customshapes, a horizontal or vertical line + // might not have exactly 0, 90, 180, or 270 degree as rotate angle. + if (!pObj) + return false; + if (!pObj->IsGroupObject()) + return false; + SdrObjList* pSubList = pObj->GetSubList(); + if (!pSubList) + return false; + SdrObjListIter aIterator(pSubList, SdrIterMode::DeepNoGroups); + while (aIterator.IsMore()) + { + const SdrObject* pSubObj = aIterator.Next(); + const Degree100 nRotateAngle = NormAngle36000(pSubObj->GetRotateAngle()); + const sal_uInt16 nRot = nRotateAngle.get(); + if ((3 < nRot && nRot < 8997) || (9003 < nRot && nRot < 17997) + || (18003 < nRot && nRot < 26997) || (27003 < nRot && nRot < 35997)) + return true; + } + return false; +} + +void GraphicImport::lcl_correctWord2007EffectExtent(const sal_Int32 nMSOAngle) +{ + // Word versions older than 14 do not swap width and height (see lcl_doMSOWidthHeightSwap) + // and therefore generate different effectExtent. We correct them here. + sal_Int16 nAngleDeg = (nMSOAngle / 60000) % 180; + if (nAngleDeg < 45 || nAngleDeg >= 135) + return; + + sal_Int32 nDiff = o3tl::convert( + (double(m_pImpl->getXSize()) - double(m_pImpl->getYSize())) / 2.0, + o3tl::Length::mm100, o3tl::Length::emu); + if (m_pImpl->m_oEffectExtentLeft) + *m_pImpl->m_oEffectExtentLeft += nDiff; + if (m_pImpl->m_oEffectExtentRight) + *m_pImpl->m_oEffectExtentRight += nDiff; + if (m_pImpl->m_oEffectExtentTop) + *m_pImpl->m_oEffectExtentTop -= nDiff; + if (m_pImpl->m_oEffectExtentBottom) + *m_pImpl->m_oEffectExtentBottom -= nDiff; +} + +static void lcl_doMSOWidthHeightSwap(awt::Point& rLeftTop, awt::Size& rSize, + const sal_Int32 nMSOAngle) +{ + if (nMSOAngle == 0) + return; + // convert nMSOAngle to degree in [0°,180°[ + sal_Int16 nAngleDeg = (nMSOAngle / 60000) % 180; + if (nAngleDeg >= 45 && nAngleDeg < 135) + { + // keep center of rectangle given in rLeftTop and rSize + sal_Int32 aTemp = rSize.Width - rSize.Height; + rLeftTop.X += aTemp / 2; + rLeftTop.Y -= aTemp / 2; + std::swap(rSize.Width, rSize.Height); + } + return; +} + +void GraphicImport::lcl_expandRectangleByEffectExtent(awt::Point& rLeftTop, awt::Size& rSize) +{ + sal_Int32 nEffectExtent = (m_pImpl->m_oEffectExtentLeft) + ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentLeft) + : 0; + rLeftTop.X -= nEffectExtent; + rSize.Width += nEffectExtent; + nEffectExtent = (m_pImpl->m_oEffectExtentRight) + ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentRight) + : 0; + rSize.Width += nEffectExtent; + nEffectExtent = (m_pImpl->m_oEffectExtentTop) + ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentTop) + : 0; + rLeftTop.Y -= nEffectExtent; + rSize.Height += nEffectExtent; + nEffectExtent = (m_pImpl->m_oEffectExtentBottom) + ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentBottom) + : 0; + rSize.Height += nEffectExtent; +} + +void GraphicImport::lcl_attribute(Id nName, Value& rValue) +{ + sal_Int32 nIntValue = rValue.getInt(); + switch( nName ) + { + case NS_ooxml::LN_CT_Hyperlink_URL://90682; + m_pImpl->sHyperlinkURL = rValue.getString(); + break; + case NS_ooxml::LN_blip: //the binary graphic data in a shape + { + writerfilter::Reference<Properties>::Pointer_t pProperties = rValue.getProperties(); + if( pProperties ) + { + pProperties->resolve(*this); + } + } + break; + case NS_ooxml::LN_payload : + { + writerfilter::Reference<BinaryObj>::Pointer_t pPictureData = rValue.getBinary(); + if( pPictureData ) + pPictureData->resolve(*this); + } + break; + + //border properties + case NS_ooxml::LN_CT_Border_sz: + m_pImpl->aBorders[BORDER_TOP].nLineWidth = nIntValue; + break; + case NS_ooxml::LN_CT_Border_val: + //graphic borders don't support different line types + break; + case NS_ooxml::LN_CT_Border_space: + break; + case NS_ooxml::LN_CT_Border_shadow: + m_pImpl->aBorders[BORDER_TOP].bHasShadow = nIntValue != 0; + break; + case NS_ooxml::LN_CT_Border_frame: + break; + case NS_ooxml::LN_CT_PositiveSize2D_cx: + case NS_ooxml::LN_CT_PositiveSize2D_cy: + { + sal_Int32 nDim = oox::drawingml::convertEmuToHmm(nIntValue); + // drawingML equivalent of oox::vml::ShapeType::getAbsRectangle(): + // make sure a shape isn't hidden implicitly just because it has + // zero height or width. + if (nDim == 0) + nDim = 1; + + if( nName == NS_ooxml::LN_CT_PositiveSize2D_cx ) + m_pImpl->setXSize(nDim); + else + m_pImpl->setYSize(nDim); + } + break; + case NS_ooxml::LN_CT_EffectExtent_l: + m_pImpl->m_oEffectExtentLeft = nIntValue; + break; + case NS_ooxml::LN_CT_EffectExtent_t: + m_pImpl->m_oEffectExtentTop = nIntValue; + break; + case NS_ooxml::LN_CT_EffectExtent_r: + m_pImpl->m_oEffectExtentRight = nIntValue; + break; + case NS_ooxml::LN_CT_EffectExtent_b: + m_pImpl->m_oEffectExtentBottom = nIntValue; + break; + case NS_ooxml::LN_CT_NonVisualDrawingProps_id:// 90650; + //id of the object - ignored + break; + case NS_ooxml::LN_CT_NonVisualDrawingProps_name:// 90651; + //name of the object + m_pImpl->sName = rValue.getString(); + break; + case NS_ooxml::LN_CT_NonVisualDrawingProps_descr:// 90652; + //alternative text + m_pImpl->sAlternativeText = rValue.getString(); + break; + case NS_ooxml::LN_CT_NonVisualDrawingProps_title: + //alternative text + m_pImpl->title = rValue.getString(); + break; + case NS_ooxml::LN_CT_NonVisualDrawingProps_hidden: + m_pImpl->bHidden = (nIntValue == 1); + break; + case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noChangeAspect://90644; + //disallow aspect ratio change - ignored + break; + case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noMove:// 90645; + m_pImpl->bPositionProtected = true; + break; + case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noResize: // 90646; + m_pImpl->bSizeProtected = true; + break; + case NS_ooxml::LN_CT_Anchor_distT: // 90983; + case NS_ooxml::LN_CT_Anchor_distB: // 90984; + case NS_ooxml::LN_CT_Anchor_distL: // 90985; + case NS_ooxml::LN_CT_Anchor_distR: // 90986; + { + m_pImpl->nShapeOptionType = nName; + ProcessShapeOptions(rValue); + } + break; + case NS_ooxml::LN_CT_Anchor_simplePos_attr: // 90987; + m_pImpl->bUseSimplePos = nIntValue > 0; + break; + case NS_ooxml::LN_CT_Anchor_relativeHeight: // 90988; + m_pImpl->zOrder = nIntValue; + break; + case NS_ooxml::LN_CT_Anchor_behindDoc: // 90989; - in background + if (nIntValue > 0) + { + m_pImpl->bOpaque = false; + m_pImpl->bBehindDoc = true; + } + break; + case NS_ooxml::LN_CT_Anchor_locked: // 90990; - ignored + break; + case NS_ooxml::LN_CT_Anchor_layoutInCell: // 90991; - ignored + // Starting in MSO 2013, anchors are ALWAYS considered to be laid out in table cell. + m_pImpl->bCompatForcedLayoutInCell = !nIntValue + && m_pImpl->rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14 + && m_pImpl->rDomainMapper.IsInTable(); + m_pImpl->bLayoutInCell = m_pImpl->bCompatForcedLayoutInCell || nIntValue; + break; + case NS_ooxml::LN_CT_Anchor_hidden: // 90992; - ignored + break; + case NS_ooxml::LN_CT_Anchor_allowOverlap: + m_pImpl->bAllowOverlap = nIntValue != 0; + break; + case NS_ooxml::LN_CT_Anchor_wp14_anchorId: + case NS_ooxml::LN_CT_Inline_wp14_anchorId: + { + OUStringBuffer aBuffer = OUString::number(nIntValue, 16); + OUStringBuffer aString; + comphelper::string::padToLength(aString, 8 - aBuffer.getLength(), '0'); + aString.append(aBuffer.getStr()); + m_pImpl->sAnchorId = aString.makeStringAndClear().toAsciiUpperCase(); + } + break; + case NS_ooxml::LN_CT_Point2D_x: // 90405; + m_pImpl->nLeftPosition = ConversionHelper::convertTwipToMM100(nIntValue); + m_pImpl->nHoriRelation = text::RelOrientation::PAGE_FRAME; + m_pImpl->nHoriOrient = text::HoriOrientation::NONE; + break; + case NS_ooxml::LN_CT_Point2D_y: // 90406; + m_pImpl->nTopPosition = ConversionHelper::convertTwipToMM100(nIntValue); + m_pImpl->nVertRelation = text::RelOrientation::PAGE_FRAME; + m_pImpl->nVertOrient = text::VertOrientation::NONE; + break; + case NS_ooxml::LN_CT_WrapTight_wrapText: // 90934; + m_pImpl->bContour = true; + m_pImpl->bContourOutside = true; + + handleWrapTextValue(rValue.getInt()); + + break; + case NS_ooxml::LN_CT_WrapThrough_wrapText: + m_pImpl->bContour = true; + m_pImpl->bContourOutside = false; + + handleWrapTextValue(rValue.getInt()); + + break; + case NS_ooxml::LN_CT_WrapSquare_wrapText: //90928; + handleWrapTextValue(rValue.getInt()); + break; + case NS_ooxml::LN_shape: + { + uno::Reference< drawing::XShape> xShape; + rValue.getAny( ) >>= xShape; + if ( xShape.is( ) ) + { + // Is it a graphic image + bool bUseShape = true; + try + { + uno::Reference< beans::XPropertySet > xShapeProps + ( xShape, uno::UNO_QUERY_THROW ); + + uno::Reference<graphic::XGraphic> xGraphic; + xShapeProps->getPropertyValue("Graphic") >>= xGraphic; + + sal_Int32 nRotation = 0; + xShapeProps->getPropertyValue("RotateAngle") >>= nRotation; + + css::beans::PropertyValues aGrabBag; + xShapeProps->getPropertyValue("InteropGrabBag") >>= aGrabBag; + // if the shape contains effects in the grab bag, we should not transform it + // in a XTextContent so those effects can be preserved + bool bContainsEffects = std::any_of(std::cbegin(aGrabBag), std::cend(aGrabBag), [](const auto& rProp) { + return rProp.Name == "EffectProperties" + || rProp.Name == "3DEffectProperties" + || rProp.Name == "ArtisticEffectProperties"; + }); + + xShapeProps->getPropertyValue("Shadow") >>= m_pImpl->bShadow; + if (m_pImpl->bShadow) + { + xShapeProps->getPropertyValue("ShadowXDistance") >>= m_pImpl->nShadowXDistance; + xShapeProps->getPropertyValue("ShadowYDistance") >>= m_pImpl->nShadowYDistance; + xShapeProps->getPropertyValue("ShadowColor") >>= m_pImpl->nShadowColor; + xShapeProps->getPropertyValue("ShadowTransparence") >>= m_pImpl->nShadowTransparence; + } + + xShapeProps->getPropertyValue("GraphicColorMode") >>= m_pImpl->eColorMode; + xShapeProps->getPropertyValue("AdjustLuminance") >>= m_pImpl->nBrightness; + xShapeProps->getPropertyValue("AdjustContrast") >>= m_pImpl->nContrast; + + // fdo#70457: transform XShape into a SwXTextGraphicObject only if there's no rotation + if ( nRotation == 0 && !bContainsEffects ) + m_xGraphicObject = createGraphicObject( xGraphic, xShapeProps ); + + bUseShape = !m_xGraphicObject.is( ); + + if ( !bUseShape ) + { + // Define the object size + uno::Reference< beans::XPropertySet > xGraphProps( m_xGraphicObject, + uno::UNO_QUERY ); + awt::Size aSize = xShape->getSize( ); + xGraphProps->setPropertyValue("Height", + uno::Any( aSize.Height ) ); + xGraphProps->setPropertyValue("Width", + uno::Any( aSize.Width ) ); + + text::GraphicCrop aGraphicCrop( 0, 0, 0, 0 ); + uno::Reference< beans::XPropertySet > xSourceGraphProps( xShape, uno::UNO_QUERY ); + uno::Any aAny = xSourceGraphProps->getPropertyValue("GraphicCrop"); + if(aAny >>= aGraphicCrop) { + xGraphProps->setPropertyValue("GraphicCrop", + uno::Any( aGraphicCrop ) ); + } + + // We need to drop the shape here somehow + uno::Reference< lang::XComponent > xShapeComponent( xShape, uno::UNO_QUERY ); + xShapeComponent->dispose( ); + } + } + catch( const beans::UnknownPropertyException & ) + { + // It isn't a graphic image + } + + if ( bUseShape ) + m_xShape = xShape; + + if ( m_xShape.is( ) ) + { + uno::Reference< beans::XPropertySet > xShapeProps + (m_xShape, uno::UNO_QUERY_THROW); + + + xShapeProps->setPropertyValue + (getPropertyName(PROP_ANCHOR_TYPE), + uno::Any + (text::TextContentAnchorType_AS_CHARACTER)); + + // In Word, if a shape is anchored inline, that + // excludes being in the background. + xShapeProps->setPropertyValue("Opaque", uno::Any(true)); + + uno::Reference<lang::XServiceInfo> xServiceInfo(m_xShape, uno::UNO_QUERY_THROW); + + // TextFrames can't be rotated. But for anything else, + // make sure that setting size doesn't affect rotation, + // that would not match Word's definition of rotation. + bool bKeepRotation = false; + if (!xServiceInfo->supportsService("com.sun.star.text.TextFrame")) + { + bKeepRotation = true; + xShapeProps->setPropertyValue + (getPropertyName(PROP_TEXT_RANGE), + uno::Any + (m_pImpl->rDomainMapper.GetCurrentTextRange())); + } + + awt::Size aSize(m_xShape->getSize()); + + // One purpose of the next part is, to set the logic rectangle of the SdrObject + // to nXSize and nYSize from import. That doesn't work for groups or lines, + // because they do not have a logic rectangle and m_xShape->getSize and + // m_xShape->setSize would work on the snap rectangle. In case a shape is + // rotated, non-uniform scaling the snap rectangle will introduce shearing on + // the shape. In case group or line is rotated, nXSize and nYSize contain the + // unrotated size from oox. The rotation is already incorporated into group + // children and line points. We must not scale them to unrotated size. Exclude + // those shapes here. + + // Get MSO rotation angle. GetRotateAngle from SdrObject is not suitable + // here, because it returns the rotate angle of the first child for groups + // and slope angle for lines, even if line or group had not been rotated. + // Import in oox has put the rotation from oox file into InteropGrabBag. + comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue("InteropGrabBag")); + sal_Int32 nOOXAngle(0); + aInteropGrabBag.getValue("mso-rotation-angle") >>= nOOXAngle; // 1/60000 deg + // tdf#143455: A diagram is imported as group, but has no valid object list + // and contour wrap is different to Word. As workaround diagrams are excluded + // here in various places. + const SdrObject* pDiagramCandidate(SdrObject::getSdrObjectFromXShape(m_xShape)); + const bool bIsDiagram(nullptr != pDiagramCandidate && pDiagramCandidate->isDiagram()); + // tdf#143476: A lockedCanvas (Word2007) is imported as group, but has not + // got size and position. Values from m_Impl has to be used. + bool bIsLockedCanvas(false); + aInteropGrabBag.getValue("LockedCanvas") >>= bIsLockedCanvas; + const bool bIsGroupOrLine = (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape") + && !bIsDiagram && !bIsLockedCanvas) + || xServiceInfo->supportsService("com.sun.star.drawing.LineShape"); + SdrObject* pShape = SdrObject::getSdrObjectFromXShape(m_xShape); + if ((bIsGroupOrLine && !lcl_bHasGroupSlantedChild(pShape) && nOOXAngle == 0) + || !bIsGroupOrLine) + { + if (m_pImpl->isXSizeValid()) + aSize.Width = m_pImpl->getXSize(); + if (m_pImpl->isYSizeValid()) + aSize.Height = m_pImpl->getYSize(); + } + + Degree100 nRotation; + if (bKeepRotation) + { + // Use internal API, getPropertyValue("RotateAngle") + // would use GetObjectRotation(), which is not what + // we want. + if (pShape) + nRotation = pShape->GetRotateAngle(); + } + m_xShape->setSize(aSize); + if (bKeepRotation) + { + xShapeProps->setPropertyValue("RotateAngle", uno::Any(nRotation.get())); + } + + m_pImpl->bIsGraphic = true; + + if (!m_pImpl->sAnchorId.isEmpty()) + { + putPropertyToFrameGrabBag("AnchorId", uno::Any(m_pImpl->sAnchorId)); + } + + // Calculate mso unrotated rectangle and its center, needed below + awt::Size aImportSize(m_xShape->getSize()); // here only fallback + if (m_pImpl->isXSizeValid()) + aImportSize.Width = m_pImpl->getXSize(); // Hmm + if (m_pImpl->isYSizeValid()) + aImportSize.Height = m_pImpl->getYSize(); // Hmm + const awt::Point aImportPosition(GetGraphicObjectPosition()); // Hmm + double fCentrumX = aImportPosition.X + aImportSize.Width / 2.0; + double fCentrumY = aImportPosition.Y + aImportSize.Height / 2.0; + + // In case of group and lines, transformations are incorporated in the child + // shapes or points respectively in LO. MSO has rotation as separate property. + // The position refers to the unrotated rectangle of MSO. We need to adapt it + // to the left-top of the transformed shape. + awt::Size aLOSize(m_xShape->getSize()); // LO snap rectangle size in Hmm + if (bIsGroupOrLine && !(m_pImpl->mpWrapPolygon)) + { + // Set LO position. MSO rotation is done on shape center. + if(pShape && pShape->IsGroupObject()) + { + tools::Rectangle aSnapRect = pShape->GetSnapRect(); // Twips + m_pImpl->nLeftPosition = ConversionHelper::convertTwipToMM100(aSnapRect.Left()); + m_pImpl->nTopPosition = ConversionHelper::convertTwipToMM100(aSnapRect.Top()); + aLOSize.Width = ConversionHelper::convertTwipToMM100(aSnapRect.getWidth()); + aLOSize.Height = ConversionHelper::convertTwipToMM100(aSnapRect.getHeight()); + } + else + { + m_pImpl->nLeftPosition = fCentrumX - aLOSize.Width / 2.0; + m_pImpl->nTopPosition = fCentrumY - aLOSize.Height / 2.0; + } + m_xShape->setPosition(GetGraphicObjectPosition()); + } + // ToDo: Rotated shapes with position type "Alignment" (UI of Word) have + // wrong position. Word aligns the unrotated logic rectangle, LO the rotated + // snap rectangle. + + // Margin correction + + // tdf#143475: Word 2007 (vers 12) calculates effectExtent for rotated images + // based on the unrotated image without width-height-swap. We correct this to + // those values, which would be calculated if width-height-swap was used. + if (m_pImpl->rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() < 14 + && xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape") + && nOOXAngle != 0) + { + lcl_correctWord2007EffectExtent(nOOXAngle); + } + + if (m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_INLINE) + { + if (nOOXAngle == 0) + { + // EffectExtent contains all needed additional space, including fat + // stroke and shadow. Simple add it to the margins. + sal_Int32 nEffectExtent = (m_pImpl->m_oEffectExtentLeft) + ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentLeft) + : 0; + m_pImpl->nLeftMargin += nEffectExtent; + nEffectExtent = (m_pImpl->m_oEffectExtentRight) + ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentRight) : 0; + m_pImpl->nRightMargin += nEffectExtent; + nEffectExtent = (m_pImpl->m_oEffectExtentTop) + ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentTop) : 0; + m_pImpl->nTopMargin += nEffectExtent; + nEffectExtent = (m_pImpl->m_oEffectExtentBottom) + ? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentBottom) : 0; + m_pImpl->nBottomMargin += nEffectExtent; + } + else + { + // As of June 2021 LibreOffice uses an area, which is large enough to + // contain the rotated snap rectangle. MSO uses a smaller area, so + // that the rotated snap rectangle covers text. + awt::Point aMSOBaseLeftTop = aImportPosition; + awt::Size aMSOBaseSize = aImportSize; + lcl_doMSOWidthHeightSwap(aMSOBaseLeftTop, aMSOBaseSize, nOOXAngle); + lcl_expandRectangleByEffectExtent(aMSOBaseLeftTop, aMSOBaseSize); + + // Get LO SnapRect from SdrObject if possible + awt::Rectangle aLOSnapRect; + // For case we have no SdrObject, initialize with values from m_pImpl + aLOSnapRect.X = m_pImpl->nLeftPosition; + aLOSnapRect.Y = m_pImpl->nTopPosition; + aLOSnapRect.Width = aLOSize.Width; + aLOSnapRect.Height = aLOSize.Height; + if (pShape) + { + tools::Rectangle aSnapRect = pShape->GetSnapRect(); // Twip + aLOSnapRect.X = ConversionHelper::convertTwipToMM100(aSnapRect.Left()); + aLOSnapRect.Y = ConversionHelper::convertTwipToMM100(aSnapRect.Top()); + aLOSnapRect.Width = ConversionHelper::convertTwipToMM100(aSnapRect.getWidth()); + aLOSnapRect.Height = ConversionHelper::convertTwipToMM100(aSnapRect.getHeight()); + } + + m_pImpl->nLeftMargin += aLOSnapRect.X - aMSOBaseLeftTop.X; + m_pImpl->nRightMargin += aMSOBaseLeftTop.X + aMSOBaseSize.Width + - (aLOSnapRect.X + aLOSnapRect.Width); + m_pImpl->nTopMargin += aLOSnapRect.Y - aMSOBaseLeftTop.Y; + m_pImpl->nBottomMargin += aMSOBaseLeftTop.Y + aMSOBaseSize.Height + - (aLOSnapRect.Y + aLOSnapRect.Height); + // tdf#141880 LibreOffice cannot handle negative vertical margins. + // Those cases are caught below at common place. + } + } // end IMPORT_AS_DETECTED_INLINE + else if ((m_pImpl->nWrap == text::WrapTextMode_PARALLEL + || m_pImpl->nWrap == text::WrapTextMode_DYNAMIC + || m_pImpl->nWrap == text::WrapTextMode_LEFT + || m_pImpl->nWrap == text::WrapTextMode_RIGHT + || m_pImpl->nWrap == text::WrapTextMode_NONE) + && !(m_pImpl->mpWrapPolygon) && !bIsDiagram) + { + // For wrap "Square" an area is defined around which the text wraps. MSO + // describes the area by a base rectangle and effectExtent. LO uses the + // shape bounding box and margins. We adapt the margins to get the same + // area as MSO. + awt::Point aMSOBaseLeftTop = aImportPosition; + awt::Size aMSOBaseSize = aImportSize; + lcl_doMSOWidthHeightSwap(aMSOBaseLeftTop, aMSOBaseSize, nOOXAngle); + lcl_expandRectangleByEffectExtent(aMSOBaseLeftTop, aMSOBaseSize); + + // Get LO bound rectangle from SdrObject if possible + awt::Rectangle aLOBoundRect; + // For case we have no SdrObject, initialize with values from m_pImpl + aLOBoundRect.X = m_pImpl->nLeftPosition; + aLOBoundRect.Y = m_pImpl->nTopPosition; + aLOBoundRect.Width = aLOSize.Width; + aLOBoundRect.Height = aLOSize.Height; + if (pShape) + { + tools::Rectangle aBoundRect = pShape->GetCurrentBoundRect(); // Twip + aLOBoundRect.X = ConversionHelper::convertTwipToMM100(aBoundRect.Left()); + aLOBoundRect.Y = ConversionHelper::convertTwipToMM100(aBoundRect.Top()); + aLOBoundRect.Width = ConversionHelper::convertTwipToMM100(aBoundRect.getWidth()); + aLOBoundRect.Height = ConversionHelper::convertTwipToMM100(aBoundRect.getHeight()); + } + + m_pImpl->nLeftMargin += aLOBoundRect.X - aMSOBaseLeftTop.X; + m_pImpl->nRightMargin += aMSOBaseLeftTop.X + aMSOBaseSize.Width + - (aLOBoundRect.X + aLOBoundRect.Width); + m_pImpl->nTopMargin += aLOBoundRect.Y - aMSOBaseLeftTop.Y; + m_pImpl->nBottomMargin += aMSOBaseLeftTop.Y + aMSOBaseSize.Height + - (aLOBoundRect.Y + aLOBoundRect.Height); + } + else if (m_pImpl->mpWrapPolygon && !bIsDiagram) + { + // Word uses a wrap polygon, LibreOffice has no explicit wrap polygon + // but creates the wrap contour based on the shape geometry, without + // stroke width and shadow, but with rotation and flip. The concepts + // are not compatible. We approximate Word's rendering by setting + // wrap margins. + + // Build a range from the wrap polygon from Word. + const drawing::PointSequenceSequence aWrapPolygon + = m_pImpl->mpWrapPolygon->getPointSequenceSequence(); + basegfx::B2DPolyPolygon aB2DWrapPolyPolygon + = basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon( + aWrapPolygon); + // Wrap polygon values are relative to 0..21600|0..21600. + // Scale to shape size (in Hmm). + basegfx::B2DHomMatrix aMatrix = basegfx::utils::createScaleB2DHomMatrix( + aImportSize.Width / 21600.0, aImportSize.Height / 21600.0); + aB2DWrapPolyPolygon.transform(aMatrix); + + // Shape geometry will be rotated, rotate wrap polygon too. + if (nOOXAngle != 0) + { + aMatrix = basegfx::utils::createRotateAroundPoint( + aImportSize.Width / 2.0, aImportSize.Height / 2.0, + basegfx::deg2rad<60000>(nOOXAngle)); + aB2DWrapPolyPolygon.transform(aMatrix); + } + basegfx::B2DRange aB2DWrapRange = aB2DWrapPolyPolygon.getB2DRange(); + + // Build a range from shape geometry + basegfx::B2DRange aShapeRange; + if (pShape) + { + basegfx::B2DPolyPolygon aShapePolygon = pShape->TakeXorPoly(); // Twips + aMatrix = basegfx::utils::createScaleB2DHomMatrix( + o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100), + o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100)); + aShapePolygon.transform(aMatrix); + // Wrap polygon treats left/top of shape as origin, shift shape polygon accordingly + aMatrix = basegfx::utils::createTranslateB2DHomMatrix( + -aImportPosition.X, -aImportPosition.Y); + aShapePolygon.transform(aMatrix); + aShapeRange = aShapePolygon.getB2DRange(); + } + else // can this happen? + { + aShapeRange + = basegfx::B2DRange(0, 0, aImportSize.Width, aImportSize.Height); + if (nOOXAngle != 0) + { + aMatrix = basegfx::utils::createRotateB2DHomMatrix( + basegfx::deg2rad<60000>(nOOXAngle)); + aShapeRange.transform(aMatrix); + } + } + + // Add difference between shape and wrap range to margin and remember + // difference in Twips for export. + comphelper::SequenceAsHashMap aAnchorDistDiff; + + const double fTopDiff = aShapeRange.getMinY() - aB2DWrapRange.getMinY(); + m_pImpl->nTopMargin += basegfx::fround(fTopDiff); + aAnchorDistDiff["distTDiff"] <<= basegfx::fround( + o3tl::convert(fTopDiff, o3tl::Length::mm100, o3tl::Length::twip)); + + const double fBottomDiff = aB2DWrapRange.getMaxY() - aShapeRange.getMaxY(); + m_pImpl->nBottomMargin += basegfx::fround(fBottomDiff); + aAnchorDistDiff["distBDiff"] <<= basegfx::fround( + o3tl::convert(fBottomDiff, o3tl::Length::mm100, o3tl::Length::twip)); + + const double fLeftDiff = aShapeRange.getMinX() - aB2DWrapRange.getMinX(); + m_pImpl->nLeftMargin += basegfx::fround(fLeftDiff); + aAnchorDistDiff["distLDiff"] <<= basegfx::fround( + o3tl::convert(fLeftDiff, o3tl::Length::mm100, o3tl::Length::twip)); + + const double fRightDiff = aB2DWrapRange.getMaxX() - aShapeRange.getMaxX(); + m_pImpl->nRightMargin += basegfx::fround(fRightDiff); + aAnchorDistDiff["distRDiff"] <<= basegfx::fround( + o3tl::convert(fRightDiff, o3tl::Length::mm100, o3tl::Length::twip)); + + m_pImpl->m_aInteropGrabBag["AnchorDistDiff"] + <<= aAnchorDistDiff.getAsConstPropertyValueList(); + + // FixMe: tdf#141880. LibreOffice cannot handle negative horizontal margin in contour wrap + if (m_pImpl->nLeftMargin < 0) + m_pImpl->nLeftMargin = 0; + if (m_pImpl->nRightMargin < 0) + m_pImpl->nRightMargin = 0; + } + else if (!bIsDiagram) // text::WrapTextMode_THROUGH + { + // Word writes and evaluates the effectExtent in case of position + // type 'Alignment' (UI). We move these values to margin to approximate + // Word's rendering. + if (m_pImpl->m_oEffectExtentLeft) + { + m_pImpl->nLeftMargin + += oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentLeft); + } + if (m_pImpl->m_oEffectExtentTop) + { + m_pImpl->nTopMargin + += oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentTop); + } + if (m_pImpl->m_oEffectExtentRight) + { + m_pImpl->nRightMargin + += oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentRight); + } + if (m_pImpl->m_oEffectExtentBottom) + { + m_pImpl->nBottomMargin + += oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentBottom); + } + } + + // FixMe: tdf#141880 LibreOffice cannot handle negative vertical margins + // although they are allowed in ODF. + if (m_pImpl->nTopMargin < 0) + m_pImpl->nTopMargin = 0; + if (m_pImpl->nBottomMargin < 0) + m_pImpl->nBottomMargin = 0; + } + + if (bUseShape && m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR) + { + // If we are here, this is a drawingML shape. For those, only dmapper (and not oox) knows the anchoring infos (just like for Writer pictures). + // But they aren't Writer pictures, either (which are already handled above). + uno::Reference< beans::XPropertySet > xShapeProps(m_xShape, uno::UNO_QUERY_THROW); + + if (m_pImpl->nWrap == text::WrapTextMode_THROUGH && m_pImpl->nHoriRelation == text::RelOrientation::FRAME) + { + // text::RelOrientation::FRAME is OOXML's "column", which behaves as if + // layout-in-cell would be always off. + m_pImpl->bLayoutInCell = false; + } + + // Anchored: Word only supports at-char in that case. + text::TextContentAnchorType eAnchorType = text::TextContentAnchorType_AT_CHARACTER; + + if (m_pImpl->bHidden) + { + xShapeProps->setPropertyValue("Visible", uno::Any(false)); + xShapeProps->setPropertyValue("Printable", uno::Any(false)); + } + + // Avoid setting AnchorType for TextBoxes till SwTextBoxHelper::syncProperty() doesn't handle transition. + bool bTextBox = false; + xShapeProps->getPropertyValue("TextBox") >>= bTextBox; + + // The positioning change caused by LayoutInCell doesn't sync well + // in the text / frame duo. So the compatibility fix only correctly + // positions the frame and not the text currently. + // tdf#135943: Instead of half-fixing and making a complete mess, + // just avoid until layout's repositioning is sync'd to the text frame. + if (m_pImpl->bLayoutInCell && bTextBox) + m_pImpl->bLayoutInCell = !m_pImpl->bCompatForcedLayoutInCell; + + xShapeProps->setPropertyValue("AnchorType", uno::Any(eAnchorType)); + + if (m_pImpl->nVertRelation == text::RelOrientation::TEXT_LINE) + { + // Word's "line" is "below the bottom of the line", our TEXT_LINE is + // "towards top, from the bottom of the line", so invert the vertical + // position. + awt::Point aPoint = xShape->getPosition(); + aPoint.Y *= -1; + xShape->setPosition(aPoint); + } + + if (m_pImpl->bLayoutInCell && bTextBox && m_pImpl->rDomainMapper.IsInTable() + && m_pImpl->nHoriRelation == text::RelOrientation::PAGE_FRAME) + m_pImpl->nHoriRelation = text::RelOrientation::FRAME; + if(m_pImpl->rDomainMapper.IsInTable()) + xShapeProps->setPropertyValue(getPropertyName(PROP_FOLLOW_TEXT_FLOW), + uno::Any(m_pImpl->bLayoutInCell)); + //only the position orientation is handled in applyPosition() + m_pImpl->applyPosition(xShapeProps); + + uno::Reference<lang::XServiceInfo> xServiceInfo(m_xShape, uno::UNO_QUERY_THROW); + if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape") || + xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape")) + { + // You would expect that position and rotation are + // independent, but they are not. Till we are not + // there yet to handle all scaling, translation and + // rotation with a single transformation matrix, + // make sure there is no graphic rotation set when we set + // the position. + sal_Int32 nRotation = 0; + if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape")) + { + xShapeProps->getPropertyValue("RotateAngle") >>= nRotation; + } + if (nRotation) + xShapeProps->setPropertyValue("RotateAngle", uno::Any(sal_Int32(0))); + + // Position of the groupshape should be set after children have been added. + // Long-term we should get rid of positioning group + // shapes, though. Do it for top-level ones with + // absolute page position as a start. + // fdo#80555: also set position for graphic shapes here + if (!isTopGroupObj(m_xShape) + || m_pImpl->nHoriRelation != text::RelOrientation::PAGE_FRAME + || m_pImpl->nVertRelation != text::RelOrientation::PAGE_FRAME) + m_xShape->setPosition( + awt::Point(m_pImpl->nLeftPosition, m_pImpl->nTopPosition)); + + if (nRotation) + xShapeProps->setPropertyValue("RotateAngle", uno::Any(nRotation)); + } + + + m_pImpl->applyRelativePosition(xShapeProps, /*bRelativeOnly=*/true); + + xShapeProps->setPropertyValue("SurroundContour", uno::Any(m_pImpl->bContour)); + xShapeProps->setPropertyValue("ContourOutside", uno::Any(m_pImpl->bContourOutside)); + m_pImpl->applyMargins(xShapeProps); + xShapeProps->setPropertyValue("Opaque", uno::Any(m_pImpl->bOpaque)); + xShapeProps->setPropertyValue("Surround", uno::Any(static_cast<sal_Int32>(m_pImpl->nWrap))); + m_pImpl->applyZOrder(xShapeProps); + m_pImpl->applyName(xShapeProps); + m_pImpl->applyHyperlink(xShapeProps, bUseShape); + xShapeProps->setPropertyValue("AllowOverlap", + uno::Any(m_pImpl->bAllowOverlap)); + + // Get the grab-bag set by oox, merge with our one and then put it back. + comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue("InteropGrabBag")); + aInteropGrabBag.update(m_pImpl->getInteropGrabBag()); + xShapeProps->setPropertyValue("InteropGrabBag", uno::Any(aInteropGrabBag.getAsConstPropertyValueList())); + } + else if (bUseShape && m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_INLINE) + { + uno::Reference< beans::XPropertySet > xShapeProps(m_xShape, uno::UNO_QUERY_THROW); + m_pImpl->applyMargins(xShapeProps); + m_pImpl->applyZOrder(xShapeProps); + comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue("InteropGrabBag")); + aInteropGrabBag.update(m_pImpl->getInteropGrabBag()); + xShapeProps->setPropertyValue("InteropGrabBag", uno::Any(aInteropGrabBag.getAsConstPropertyValueList())); + } + } + } + break; + case NS_ooxml::LN_CT_Inline_distT: + m_pImpl->nTopMargin = 0; + break; + case NS_ooxml::LN_CT_Inline_distB: + m_pImpl->nBottomMargin = 0; + break; + case NS_ooxml::LN_CT_Inline_distL: + m_pImpl->nLeftMargin = 0; + break; + case NS_ooxml::LN_CT_Inline_distR: + m_pImpl->nRightMargin = 0; + break; + case NS_ooxml::LN_CT_GraphicalObjectData_uri: + rValue.getString(); + //TODO: does it need to be handled? + break; + case NS_ooxml::LN_CT_SizeRelH_relativeFrom: + { + switch (nIntValue) + { + case NS_ooxml::LN_ST_SizeRelFromH_margin: + if (m_xShape.is()) + { + uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY); + xPropertySet->setPropertyValue("RelativeWidthRelation", uno::Any(text::RelOrientation::FRAME)); + } + break; + case NS_ooxml::LN_ST_SizeRelFromH_leftMargin: + case NS_ooxml::LN_ST_SizeRelFromH_outsideMargin: + if (m_xShape.is()) + { + // Here we handle the relative size of the width of some shape. + // The size of the shape's width is going to be relative to the size of the left margin. + // E.g.: (left margin = 8 && relative size = 150%) -> width of some shape = 12. + uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY); + xPropertySet->setPropertyValue("RelativeWidthRelation", uno::Any(text::RelOrientation::PAGE_LEFT)); + } + break; + case NS_ooxml::LN_ST_SizeRelFromH_rightMargin: + case NS_ooxml::LN_ST_SizeRelFromH_insideMargin: + if (m_xShape.is()) + { + // Same as the left margin above. + uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY); + xPropertySet->setPropertyValue("RelativeWidthRelation", uno::Any(text::RelOrientation::PAGE_RIGHT)); + } + break; + case NS_ooxml::LN_ST_SizeRelFromH_page: + if (m_xShape.is()) + { + uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY); + xPropertySet->setPropertyValue("RelativeWidthRelation", uno::Any(text::RelOrientation::PAGE_FRAME)); + } + break; + default: + SAL_WARN("writerfilter", "GraphicImport::lcl_attribute: unhandled NS_ooxml::LN_CT_SizeRelH_relativeFrom value: " << nIntValue); + break; + } + } + break; + case NS_ooxml::LN_CT_SizeRelV_relativeFrom: + { + switch (nIntValue) + { + case NS_ooxml::LN_ST_SizeRelFromV_margin: + if (m_xShape.is()) + { + uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY); + xPropertySet->setPropertyValue("RelativeHeightRelation", uno::Any(text::RelOrientation::FRAME)); + } + break; + case NS_ooxml::LN_ST_SizeRelFromV_page: + if (m_xShape.is()) + { + uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY); + xPropertySet->setPropertyValue("RelativeHeightRelation", uno::Any(text::RelOrientation::PAGE_FRAME)); + } + break; + case NS_ooxml::LN_ST_SizeRelFromV_topMargin: + if (m_xShape.is()) + { + uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY); + xPropertySet->setPropertyValue("RelativeHeightRelation", uno::Any(text::RelOrientation::PAGE_PRINT_AREA)); + } + break; + case NS_ooxml::LN_ST_SizeRelFromV_bottomMargin: + if (m_xShape.is()) + { + uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY); + xPropertySet->setPropertyValue("RelativeHeightRelation", uno::Any(text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)); + } + break; + default: + SAL_WARN("writerfilter", "GraphicImport::lcl_attribute: unhandled NS_ooxml::LN_CT_SizeRelV_relativeFrom value: " << nIntValue); + break; + } + } + break; + default: +#ifdef DBG_UTIL + TagLogger::getInstance().element("unhandled"); +#endif + break; + } +} + +uno::Reference<text::XTextContent> GraphicImport::GetGraphicObject() +{ + uno::Reference<text::XTextContent> xResult; + + if (m_xGraphicObject.is()) + xResult = m_xGraphicObject; + else if (m_xShape.is()) + { + xResult.set(m_xShape, uno::UNO_QUERY_THROW); + } + + return xResult; +} + + +void GraphicImport::ProcessShapeOptions(Value const & rValue) +{ + sal_Int32 nIntValue = rValue.getInt(); + switch( m_pImpl->nShapeOptionType ) + { + case NS_ooxml::LN_CT_Anchor_distL: + m_pImpl->nLeftMargin = nIntValue / 360; + m_pImpl->nLeftMarginOrig = m_pImpl->nLeftMargin; + break; + case NS_ooxml::LN_CT_Anchor_distT: + //todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustULWrapForWordMargins() + m_pImpl->nTopMargin = nIntValue / 360; + break; + case NS_ooxml::LN_CT_Anchor_distR: + //todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustLRWrapForWordMargins() + m_pImpl->nRightMargin = nIntValue / 360; + break; + case NS_ooxml::LN_CT_Anchor_distB: + //todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustULWrapForWordMargins() + m_pImpl->nBottomMargin = nIntValue / 360; + break; + default: + OSL_FAIL( "shape option unsupported?"); + } +} + + +void GraphicImport::lcl_sprm(Sprm& rSprm) +{ + sal_uInt32 nSprmId = rSprm.getId(); + + switch(nSprmId) + { + case NS_ooxml::LN_CT_Inline_extent: // 90911; + case NS_ooxml::LN_CT_Inline_effectExtent: // 90912; + case NS_ooxml::LN_CT_Inline_docPr: // 90913; + case NS_ooxml::LN_CT_Inline_cNvGraphicFramePr: // 90914; + case NS_ooxml::LN_CT_NonVisualGraphicFrameProperties_graphicFrameLocks:// 90657 + case NS_ooxml::LN_CT_Inline_a_graphic:// 90915 + case NS_ooxml::LN_CT_Anchor_simplePos_elem: // 90975; + case NS_ooxml::LN_CT_Anchor_extent: // 90978; + case NS_ooxml::LN_CT_Anchor_effectExtent: // 90979; + case NS_ooxml::LN_EG_WrapType_wrapSquare: // 90945; + case NS_ooxml::LN_EG_WrapType_wrapTight: // 90946; + case NS_ooxml::LN_EG_WrapType_wrapThrough: + case NS_ooxml::LN_CT_Anchor_docPr: // 90980; + case NS_ooxml::LN_CT_Anchor_cNvGraphicFramePr: // 90981; + case NS_ooxml::LN_CT_Anchor_a_graphic: // 90982; + case NS_ooxml::LN_CT_WrapPath_start: // 90924; + case NS_ooxml::LN_CT_WrapPath_lineTo: // 90925; + case NS_ooxml::LN_graphic_graphic: + case NS_ooxml::LN_pic_pic: + case NS_ooxml::LN_dgm_relIds: + case NS_ooxml::LN_lc_lockedCanvas: + case NS_ooxml::LN_c_chart: + case NS_ooxml::LN_wps_wsp: + case NS_ooxml::LN_wpg_wgp: + case NS_ooxml::LN_sizeRelH_sizeRelH: + case NS_ooxml::LN_sizeRelV_sizeRelV: + case NS_ooxml::LN_hlinkClick_hlinkClick: + { + writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); + if( pProperties ) + { + pProperties->resolve(*this); + } + + // We'll map these to PARALLEL, save the original wrap type. + if (nSprmId == NS_ooxml::LN_EG_WrapType_wrapTight) + m_pImpl->m_aInteropGrabBag["EG_WrapType"] <<= OUString("wrapTight"); + else if (nSprmId == NS_ooxml::LN_EG_WrapType_wrapThrough) + m_pImpl->m_aInteropGrabBag["EG_WrapType"] <<= OUString("wrapThrough"); + + switch (nSprmId) + { + case NS_ooxml::LN_EG_WrapType_wrapSquare: + case NS_ooxml::LN_EG_WrapType_wrapThrough: + case NS_ooxml::LN_EG_WrapType_wrapTight: + { + // tdf#137850: Word >= 2013 seems to ignore bBehindDoc except for wrapNone, but older versions honour it. + if (m_pImpl->bBehindDoc && m_pImpl->rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14) + m_pImpl->bOpaque = true; + } + break; + } + + } + break; + case NS_ooxml::LN_CT_WrapTight_wrapPolygon: + case NS_ooxml::LN_CT_WrapThrough_wrapPolygon: + { + WrapPolygonHandler aHandler; + + resolveSprmProps(aHandler, rSprm); + + m_pImpl->mpWrapPolygon = aHandler.getPolygon(); + + // Save the wrap path in case we can't handle it natively: drawinglayer shapes, TextFrames. + m_pImpl->m_aInteropGrabBag["CT_WrapPath"] <<= m_pImpl->mpWrapPolygon->getPointSequenceSequence(); + } + break; + case NS_ooxml::LN_CT_Anchor_positionH: // 90976; + { + // Use a special handler for the positioning + auto pHandler = std::make_shared<PositionHandler>( m_pImpl->m_rPositionOffsets, m_pImpl->m_rAligns ); + writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); + if( pProperties ) + { + pProperties->resolve( *pHandler ); + if( !m_pImpl->bUseSimplePos ) + { + m_pImpl->nHoriRelation = pHandler->relation(); + m_pImpl->bPageToggle = pHandler->GetPageToggle(); + m_pImpl->nHoriOrient = pHandler->orientation(); + m_pImpl->nLeftPosition = pHandler->position(); + + // Left adjustments: if horizontally aligned to left of margin, then remove the + // left wrapping. + if (m_pImpl->nHoriOrient == text::HoriOrientation::LEFT) + { + if (m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA) + { + m_pImpl->nLeftMargin = 0; + } + } + } + } + } + break; + case NS_ooxml::LN_CT_Anchor_positionV: // 90977; + { + // Use a special handler for the positioning + auto pHandler = std::make_shared<PositionHandler>( m_pImpl->m_rPositionOffsets, m_pImpl->m_rAligns); + writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); + if( pProperties ) + { + pProperties->resolve( *pHandler ); + if( !m_pImpl->bUseSimplePos ) + { + m_pImpl->nVertRelation = pHandler->relation(); + m_pImpl->nVertOrient = pHandler->orientation(); + m_pImpl->nTopPosition = pHandler->position(); + } + } + } + break; + case NS_ooxml::LN_CT_SizeRelH_pctWidth: + case NS_ooxml::LN_CT_SizeRelV_pctHeight: + if (m_pImpl->m_rPositivePercentages.empty()) + break; + + if (m_xShape.is()) + { + sal_Int16 nPositivePercentage = rtl::math::round(m_pImpl->m_rPositivePercentages.front().toDouble() / oox::drawingml::PER_PERCENT); + + if (nPositivePercentage) + { + uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY); + OUString aProperty = nSprmId == NS_ooxml::LN_CT_SizeRelH_pctWidth ? OUString("RelativeWidth") : OUString("RelativeHeight"); + + sal_Int32 nTextPreRotateAngle = 0; + uno::Any aAny; + if (xPropertySet->getPropertySetInfo()->hasPropertyByName( + "CustomShapeGeometry")) + { + aAny = xPropertySet->getPropertyValue("CustomShapeGeometry"); + } + comphelper::SequenceAsHashMap aCustomShapeGeometry(aAny); + auto it = aCustomShapeGeometry.find("TextPreRotateAngle"); + if (it != aCustomShapeGeometry.end()) + { + nTextPreRotateAngle = it->second.get<sal_Int32>(); + } + if (nTextPreRotateAngle == 0) + { + xPropertySet->setPropertyValue(aProperty, + uno::Any(nPositivePercentage)); + } + } + } + + // Make sure the token is consumed even if xShape is an empty + // reference. + m_pImpl->m_rPositivePercentages.pop(); + break; + case NS_ooxml::LN_EG_WrapType_wrapNone: // 90944; - doesn't contain attributes + //depending on the behindDoc attribute text wraps through behind or in front of the object + m_pImpl->nWrap = text::WrapTextMode_THROUGH; + + // Wrap though means the margins defined earlier should not be + // respected. + m_pImpl->nLeftMargin = 0; + m_pImpl->nTopMargin = 0; + m_pImpl->nRightMargin = 0; + m_pImpl->nBottomMargin = 0; + break; + case NS_ooxml::LN_EG_WrapType_wrapTopAndBottom: // 90948; + // tdf#137850: Word >= 2013 seems to ignore bBehindDoc except for wrapNone, but older versions honour it. + if (m_pImpl->bBehindDoc && m_pImpl->rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14) + m_pImpl->bOpaque = true; + m_pImpl->nWrap = text::WrapTextMode_NONE; + break; + case NS_ooxml::LN_CT_GraphicalObject_graphicData:// 90660; + { + m_pImpl->bIsGraphic = true; + + writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); + if( pProperties ) + pProperties->resolve(*this); + } + break; + case NS_ooxml::LN_CT_NonVisualDrawingProps_a_hlinkClick: // 90689; + { + writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); + if( pProperties ) + pProperties->resolve( *this ); + } + break; + default: + SAL_WARN("writerfilter", "GraphicImport::lcl_sprm: unhandled token: " << nSprmId); + break; + } +} + +void GraphicImport::lcl_entry(writerfilter::Reference<Properties>::Pointer_t /*ref*/) +{ +} + +uno::Reference<text::XTextContent> GraphicImport::createGraphicObject(uno::Reference<graphic::XGraphic> const & rxGraphic, + uno::Reference<beans::XPropertySet> const & xShapeProps) +{ + uno::Reference<text::XTextContent> xGraphicObject; + try + { + if (rxGraphic.is()) + { + uno::Reference< beans::XPropertySet > xGraphicObjectProperties( + m_xTextFactory->createInstance("com.sun.star.text.TextGraphicObject"), + uno::UNO_QUERY_THROW); + xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_GRAPHIC), uno::Any(rxGraphic)); + xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE), + uno::Any( m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR ? + text::TextContentAnchorType_AT_CHARACTER : + text::TextContentAnchorType_AS_CHARACTER )); + xGraphicObject.set( xGraphicObjectProperties, uno::UNO_QUERY_THROW ); + + //shapes have only one border + table::BorderLine2 aBorderLine; + GraphicBorderLine& rBorderLine = m_pImpl->aBorders[0]; + if (rBorderLine.isEmpty() && xShapeProps.is() && xShapeProps->getPropertyValue("LineStyle").get<drawing::LineStyle>() != drawing::LineStyle_NONE) + { + // In case we got no border tokens and we have the + // original shape, then use its line properties as the + // border. + aBorderLine.Color = xShapeProps->getPropertyValue("LineColor").get<sal_Int32>(); + aBorderLine.LineWidth = xShapeProps->getPropertyValue("LineWidth").get<sal_Int32>(); + } + else + { + aBorderLine.Color = 0; + aBorderLine.InnerLineWidth = 0; + aBorderLine.OuterLineWidth = static_cast<sal_Int16>(rBorderLine.nLineWidth); + aBorderLine.LineDistance = 0; + } + PropertyIds const aBorderProps[] = + { + PROP_LEFT_BORDER, + PROP_RIGHT_BORDER, + PROP_TOP_BORDER, + PROP_BOTTOM_BORDER + }; + + for(PropertyIds const & rBorderProp : aBorderProps) + xGraphicObjectProperties->setPropertyValue(getPropertyName(rBorderProp), uno::Any(aBorderLine)); + + // setting graphic object shadow properties + if (m_pImpl->bShadow) + { + // Shadow width is approximated by average of X and Y + table::ShadowFormat aShadow; + sal_uInt32 nShadowColor = m_pImpl->nShadowColor & 0x00FFFFFF; // The shadow color we get is RGB only. + sal_Int32 nShadowWidth = (abs(m_pImpl->nShadowXDistance) + + abs(m_pImpl->nShadowYDistance)) / 2; + + aShadow.ShadowWidth = nShadowWidth; + sal_uInt8 nShadowTransparence = float(m_pImpl->nShadowTransparence) * 2.55; + nShadowColor |= (nShadowTransparence << 24); // Add transparence to the color. + aShadow.Color = nShadowColor; + // Distances -ve for top and right, +ve for bottom and left + if (m_pImpl->nShadowXDistance > 0) + { + if (m_pImpl->nShadowYDistance > 0) + aShadow.Location = table::ShadowLocation_BOTTOM_RIGHT; + else + aShadow.Location = table::ShadowLocation_TOP_RIGHT; + } + else + { + if (m_pImpl->nShadowYDistance > 0) + aShadow.Location = table::ShadowLocation_BOTTOM_LEFT; + else + aShadow.Location = table::ShadowLocation_TOP_LEFT; + } + + xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_SHADOW_FORMAT), uno::Any(aShadow)); + } + + // setting properties for all types + if( m_pImpl->bPositionProtected ) + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_POSITION_PROTECTED ), + uno::Any(true)); + if( m_pImpl->bSizeProtected ) + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_SIZE_PROTECTED ), + uno::Any(true)); + + sal_Int32 nWidth = - m_pImpl->nLeftPosition; + if (m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR) + { + //adjust margins + if( (m_pImpl->nHoriOrient == text::HoriOrientation::LEFT && + (m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA || + m_pImpl->nHoriRelation == text::RelOrientation::FRAME) ) || + (m_pImpl->nHoriOrient == text::HoriOrientation::INSIDE && + m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA )) + m_pImpl->nLeftMargin = 0; + if((m_pImpl->nHoriOrient == text::HoriOrientation::RIGHT && + (m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA || + m_pImpl->nHoriRelation == text::RelOrientation::FRAME) ) || + (m_pImpl->nHoriOrient == text::HoriOrientation::INSIDE && + m_pImpl->nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA )) + m_pImpl->nRightMargin = 0; + // adjust top/bottom margins + if( m_pImpl->nVertOrient == text::VertOrientation::TOP && + ( m_pImpl->nVertRelation == text::RelOrientation::PAGE_PRINT_AREA || + m_pImpl->nVertRelation == text::RelOrientation::PAGE_FRAME)) + m_pImpl->nTopMargin = 0; + if( m_pImpl->nVertOrient == text::VertOrientation::BOTTOM && + ( m_pImpl->nVertRelation == text::RelOrientation::PAGE_PRINT_AREA || + m_pImpl->nVertRelation == text::RelOrientation::PAGE_FRAME)) + m_pImpl->nBottomMargin = 0; + if( m_pImpl->nVertOrient == text::VertOrientation::BOTTOM && + m_pImpl->nVertRelation == text::RelOrientation::PAGE_PRINT_AREA ) + m_pImpl->nBottomMargin = 0; + //adjust alignment + if( m_pImpl->nHoriOrient == text::HoriOrientation::INSIDE && + m_pImpl->nHoriRelation == text::RelOrientation::PAGE_FRAME ) + { + // convert 'left to page' to 'from left -<width> to page text area' + m_pImpl->nHoriOrient = text::HoriOrientation::NONE; + m_pImpl->nHoriRelation = text::RelOrientation::PAGE_PRINT_AREA; + m_pImpl->nLeftPosition = - nWidth; + } + else if( m_pImpl->nHoriOrient == text::HoriOrientation::OUTSIDE && + m_pImpl->nHoriRelation == text::RelOrientation::PAGE_FRAME ) + { + // convert 'right to page' to 'from left 0 to right page border' + m_pImpl->nHoriOrient = text::HoriOrientation::NONE; + m_pImpl->nHoriRelation = text::RelOrientation::PAGE_RIGHT; + m_pImpl->nLeftPosition = 0; + } + + m_pImpl->applyPosition(xGraphicObjectProperties); + m_pImpl->applyRelativePosition(xGraphicObjectProperties); + if( !m_pImpl->bOpaque ) + { + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::Any(m_pImpl->bOpaque)); + } + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_SURROUND ), + uno::Any(static_cast<sal_Int32>(m_pImpl->nWrap))); + if( m_pImpl->rDomainMapper.IsInTable()) + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_FOLLOW_TEXT_FLOW ), + uno::Any(m_pImpl->bLayoutInCell)); + + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_SURROUND_CONTOUR ), + uno::Any(m_pImpl->bContour)); + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_CONTOUR_OUTSIDE ), + uno::Any(m_pImpl->bContourOutside)); + m_pImpl->applyMargins(xGraphicObjectProperties); + } + + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_ADJUST_CONTRAST ), + uno::Any(static_cast<sal_Int16>(m_pImpl->nContrast))); + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_ADJUST_LUMINANCE ), + uno::Any(static_cast<sal_Int16>(m_pImpl->nBrightness))); + if(m_pImpl->eColorMode != drawing::ColorMode_STANDARD) + { + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_GRAPHIC_COLOR_MODE ), + uno::Any(m_pImpl->eColorMode)); + } + + xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_BACK_COLOR ), + uno::Any( GraphicImport_Impl::nFillColor )); + m_pImpl->applyZOrder(xGraphicObjectProperties); + + //there seems to be no way to detect the original size via _real_ API + uno::Reference< beans::XPropertySet > xGraphicProperties(rxGraphic, uno::UNO_QUERY_THROW); + + if (m_pImpl->mpWrapPolygon) + { + uno::Any aContourPolyPolygon; + awt::Size aGraphicSize; + WrapPolygon::Pointer_t pCorrected; + xGraphicProperties->getPropertyValue(getPropertyName(PROP_SIZE100th_M_M)) >>= aGraphicSize; + if (aGraphicSize.Width && aGraphicSize.Height) + { + pCorrected = m_pImpl->mpWrapPolygon->correctWordWrapPolygon(aGraphicSize); + } + else + { + xGraphicProperties->getPropertyValue(getPropertyName(PROP_SIZE_PIXEL)) >>= aGraphicSize; + if (aGraphicSize.Width && aGraphicSize.Height) + { + pCorrected = m_pImpl->mpWrapPolygon->correctWordWrapPolygonPixel(aGraphicSize); + } + } + + text::GraphicCrop aGraphicCrop; + xShapeProps->getPropertyValue("GraphicCrop") >>= aGraphicCrop; + if (aGraphicCrop.Top != 0 || aGraphicCrop.Bottom != 0 || aGraphicCrop.Left != 0 + || aGraphicCrop.Right != 0) + { + // Word's wrap polygon deals with a canvas which has the size of the already + // cropped graphic, correct our polygon to have the same render result. + pCorrected = pCorrected->correctCrop(aGraphicSize, aGraphicCrop); + } + + if (pCorrected) + { + aContourPolyPolygon <<= pCorrected->getPointSequenceSequence(); + xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_CONTOUR_POLY_POLYGON), + aContourPolyPolygon); + // We should bring it to front, even if wp:anchor's behindDoc="1", + // because otherwise paragraph background (if set) overlaps the graphic + // TODO: if paragraph's background becomes bottommost, then remove this hack + xGraphicObjectProperties->setPropertyValue("Opaque", uno::Any(true)); + } + } + + + if(m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_INLINE || m_pImpl->eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR) + { + if( m_pImpl->getXSize() && m_pImpl->getYSize() ) + xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_SIZE), + uno::Any( awt::Size( m_pImpl->getXSize(), m_pImpl->getYSize() ))); + m_pImpl->applyMargins(xGraphicObjectProperties); + m_pImpl->applyName(xGraphicObjectProperties); + m_pImpl->applyHyperlink(xGraphicObjectProperties, false); + } + + // Handle horizontal flip. + bool bMirrored = false; + xShapeProps->getPropertyValue("IsMirrored") >>= bMirrored; + if (bMirrored) + { + xGraphicObjectProperties->setPropertyValue("HoriMirroredOnEvenPages", + uno::Any(true)); + xGraphicObjectProperties->setPropertyValue("HoriMirroredOnOddPages", + uno::Any(true)); + } + } + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("writerfilter", ""); + } + return xGraphicObject; +} + + +void GraphicImport::data(const sal_uInt8* buf, size_t len) +{ + uno::Reference< io::XInputStream > xIStream = new XInputStreamHelper( buf, len ); + beans::PropertyValues aMediaProperties{ comphelper::makePropertyValue( + getPropertyName(PROP_INPUT_STREAM), xIStream) }; + + uno::Reference<beans::XPropertySet> xPropertySet; + uno::Reference<graphic::XGraphicProvider> xGraphicProvider(graphic::GraphicProvider::create(m_xComponentContext)); + uno::Reference<graphic::XGraphic> xGraphic = xGraphicProvider->queryGraphic(aMediaProperties); + m_xGraphicObject = createGraphicObject(xGraphic, xPropertySet); +} + + +void GraphicImport::lcl_startSectionGroup() +{ +} + + +void GraphicImport::lcl_endSectionGroup() +{ +} + + +void GraphicImport::lcl_startParagraphGroup() +{ +} + + +void GraphicImport::lcl_endParagraphGroup() +{ +} + + +void GraphicImport::lcl_startCharacterGroup() +{ +} + + +void GraphicImport::lcl_endCharacterGroup() +{ +} + + +void GraphicImport::lcl_text(const sal_uInt8 * /*_data*/, size_t /*len*/) +{ +} + + +void GraphicImport::lcl_utext(const sal_uInt8 * /*_data*/, size_t /*len*/) +{ +} + + +void GraphicImport::lcl_props(writerfilter::Reference<Properties>::Pointer_t /*ref*/) +{ +} + + +void GraphicImport::lcl_table(Id /*name*/, writerfilter::Reference<Table>::Pointer_t /*ref*/) +{ +} + + +void GraphicImport::lcl_substream(Id /*name*/, ::writerfilter::Reference<Stream>::Pointer_t /*ref*/) +{ +} + +void GraphicImport::lcl_startShape(uno::Reference<drawing::XShape> const&) +{ +} + +void GraphicImport::lcl_endShape( ) +{ +} + +bool GraphicImport::IsGraphic() const +{ + return m_pImpl->bIsGraphic; +} + +sal_Int32 GraphicImport::GetLeftMarginOrig() const +{ + return m_pImpl->nLeftMarginOrig; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |