/* -*- 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 <comphelper/diagnose_ex.hxx>
#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>
#include <utility>
#include <unoframe.hxx>
#include <unotxdoc.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 m_nXSize;
bool m_bXSizeValid;
sal_Int32 m_nYSize;
bool m_bYSizeValid;
public:
GraphicImportType & m_rGraphicImportType;
DomainMapper& m_rDomainMapper;
sal_Int32 m_nLeftPosition;
sal_Int32 m_nTopPosition;
bool m_bUseSimplePos;
std::optional<sal_Int64> m_oZOrder;
sal_Int16 m_nHoriOrient;
sal_Int16 m_nHoriRelation;
bool m_bPageToggle = false;
sal_Int16 m_nVertOrient;
sal_Int16 m_nVertRelation;
text::WrapTextMode m_nWrap;
bool m_bLayoutInCell;
bool m_bCompatForcedLayoutInCell;
bool m_bAllowOverlap = true;
// Opaque means not in the background (but instead, the graphic will be over top of the text)
// This flag holds where LO will ACTUALLY put the graphic
bool m_bOpaque;
// BehindDoc means in the background. This flag says the graphic REQUESTED to be behind the text
bool m_bBehindDoc;
bool m_bContour;
bool m_bContourOutside;
WrapPolygon::Pointer_t mpWrapPolygon;
sal_Int32 m_nLeftMargin;
sal_Int32 m_nLeftMarginOrig = 0;
sal_Int32 m_nRightMargin;
sal_Int32 m_nTopMargin;
sal_Int32 m_nBottomMargin;
bool m_bShadow;
sal_Int32 m_nShadowXDistance;
sal_Int32 m_nShadowYDistance;
sal_Int32 m_nShadowColor;
sal_Int32 m_nShadowTransparence;
sal_Int32 m_nContrast;
sal_Int32 m_nBrightness;
static constexpr sal_Int32 nFillColor = 0xffffffff;
drawing::ColorMode m_eColorMode;
GraphicBorderLine m_aBorders[4];
bool m_bIsGraphic;
bool m_bSizeProtected;
bool m_bPositionProtected;
bool m_bHidden;
bool m_bDecorative = false;
sal_Int32 m_nShapeOptionType;
OUString m_sName;
OUString m_sAlternativeText;
OUString m_title;
OUString m_sHyperlinkURL;
std::pair<OUString, OUString>& m_rPositionOffsets;
std::pair<OUString, OUString>& m_rAligns;
std::queue<OUString>& m_rPositivePercentages;
OUString m_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;
std::optional<text::GraphicCrop> m_oCrop;
GraphicImport_Impl(GraphicImportType & rImportType, DomainMapper& rDMapper,
std::pair<OUString, OUString>& rPositionOffsets,
std::pair<OUString, OUString>& rAligns,
std::queue<OUString>& rPositivePercentages)
: m_nXSize(0)
,m_bXSizeValid(false)
,m_nYSize(0)
,m_bYSizeValid(false)
,m_rGraphicImportType(rImportType)
,m_rDomainMapper( rDMapper )
,m_nLeftPosition(0)
,m_nTopPosition(0)
,m_bUseSimplePos(false)
,m_nHoriOrient( text::HoriOrientation::NONE )
,m_nHoriRelation( text::RelOrientation::FRAME )
,m_nVertOrient( text::VertOrientation::NONE )
,m_nVertRelation( text::RelOrientation::FRAME )
,m_nWrap(text::WrapTextMode_NONE)
,m_bLayoutInCell(true)
,m_bCompatForcedLayoutInCell(false)
,m_bOpaque( !rDMapper.IsInHeaderFooter() )
,m_bBehindDoc(false)
,m_bContour(false)
,m_bContourOutside(true)
,m_nLeftMargin(319)
,m_nRightMargin(319)
,m_nTopMargin(0)
,m_nBottomMargin(0)
,m_bShadow(false)
,m_nShadowXDistance(0)
,m_nShadowYDistance(0)
,m_nShadowColor(0)
,m_nShadowTransparence(0)
,m_nContrast(0)
,m_nBrightness(0)
,m_eColorMode( drawing::ColorMode_STANDARD )
,m_bIsGraphic(false)
,m_bSizeProtected(false)
,m_bPositionProtected(false)
,m_bHidden(false)
,m_nShapeOptionType(0)
,m_rPositionOffsets(rPositionOffsets)
,m_rAligns(rAligns)
,m_rPositivePercentages(rPositivePercentages)
{
}
void setXSize(sal_Int32 _nXSize)
{
m_nXSize = _nXSize;
m_bXSizeValid = true;
}
sal_uInt32 getXSize() const
{
return m_nXSize;
}
bool isXSizeValid() const
{
return m_bXSizeValid;
}
void setYSize(sal_Int32 _nYSize)
{
m_nYSize = _nYSize;
m_bYSizeValid = true;
}
sal_uInt32 getYSize() const
{
return m_nYSize;
}
bool isYSizeValid() const
{
return m_bYSizeValid;
}
void applyMargins(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties) const
{
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ), uno::Any(m_nLeftMargin));
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ), uno::Any(m_nRightMargin));
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ), uno::Any(m_nTopMargin));
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ), uno::Any(m_nBottomMargin));
}
void applyPosition(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties) const
{
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT ),
uno::Any(m_nHoriOrient));
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT ),
uno::Any(m_nVertOrient));
}
void applyRelativePosition(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties, bool bRelativeOnly = false) const
{
if (!bRelativeOnly)
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT_POSITION),
uno::Any(m_nLeftPosition));
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT_RELATION ),
uno::Any(m_nHoriRelation));
xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_PAGE_TOGGLE),
uno::Any(m_bPageToggle));
if (!bRelativeOnly)
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT_POSITION),
uno::Any(m_nTopPosition));
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT_RELATION ),
uno::Any(m_nVertRelation));
}
void applyZOrder(uno::Reference<beans::XPropertySet> const & xGraphicObjectProperties) const
{
std::optional<sal_Int64> oZOrder = m_oZOrder;
if (m_rGraphicImportType == GraphicImportType::IMPORT_AS_DETECTED_INLINE
&& !m_rDomainMapper.IsInShape())
{
oZOrder = SAL_MIN_INT64;
}
else if (!oZOrder)
return;
else
{
const bool bBehindText = m_bBehindDoc && !m_bOpaque;
GraphicZOrderHelper::adjustRelativeHeight(*oZOrder, /*IsZIndex=*/false, bBehindText,
m_rDomainMapper.IsInHeaderFooter());
}
// TODO: it is possible that RTF has been wrong all along as well. Always true here?
const bool bLastDuplicateWins(!m_rDomainMapper.IsRTFImport()
|| m_rGraphicImportType == GraphicImportType::IMPORT_AS_DETECTED_INLINE);
GraphicZOrderHelper& rZOrderHelper = m_rDomainMapper.graphicZOrderHelper();
xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_Z_ORDER),
uno::Any(rZOrderHelper.findZOrder(*oZOrder, bLastDuplicateWins)));
rZOrderHelper.addItem(xGraphicObjectProperties, *oZOrder);
}
void applyName(uno::Reference<beans::XPropertySet> const & xGraphicObjectProperties) const
{
try
{
if (!m_sName.isEmpty())
{
uno::Reference<container::XNamed> const xNamed(xGraphicObjectProperties, uno::UNO_QUERY_THROW);
xNamed->setName(m_sName);
}
// else: name is automatically generated by SwDoc::MakeFlySection_()
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_DESCRIPTION ),
uno::Any( m_sAlternativeText ));
xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_TITLE ),
uno::Any( m_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 (!m_sHyperlinkURL.isEmpty())
{
xShapeProps->setPropertyValue(
getPropertyName(aHyperlinkProp), uno::Any(m_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[u"l"_ustr] <<= *m_oEffectExtentLeft;
if (m_oEffectExtentTop)
aEffectExtent[u"t"_ustr] <<= *m_oEffectExtentTop;
if (m_oEffectExtentRight)
aEffectExtent[u"r"_ustr] <<= *m_oEffectExtentRight;
if (m_oEffectExtentBottom)
aEffectExtent[u"b"_ustr] <<= *m_oEffectExtentBottom;
if (!aEffectExtent.empty())
m_aInteropGrabBag[u"CT_EffectExtent"_ustr] <<= aEffectExtent.getAsConstPropertyValueList();
return m_aInteropGrabBag;
}
};
GraphicImport::GraphicImport(uno::Reference<uno::XComponentContext> xComponentContext,
rtl::Reference<SwXTextDocument> xTextDoc,
DomainMapper& rDMapper,
GraphicImportType & rImportType,
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(rImportType, rDMapper, rPositionOffsets, rAligns, rPositivePercentages))
, m_xComponentContext(std::move(xComponentContext))
, m_xTextDoc(std::move(xTextDoc))
{
}
GraphicImport::~GraphicImport()
{
}
css::awt::Point GraphicImport::GetGraphicObjectPosition() const
{
return (css::awt::Point(m_pImpl->m_nLeftPosition, m_pImpl->m_nTopPosition));
}
bool GraphicImport::GetLayoutInCell() const
{
return m_pImpl->m_bLayoutInCell;
}
void GraphicImport::handleWrapTextValue(sal_uInt32 nVal)
{
switch (nVal)
{
case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_bothSides:
m_pImpl->m_nWrap = text::WrapTextMode_PARALLEL;
break;
case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_left:
m_pImpl->m_nWrap = text::WrapTextMode_LEFT;
break;
case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_right:
m_pImpl->m_nWrap = text::WrapTextMode_RIGHT;
break;
case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_largest:
m_pImpl->m_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(u"com.sun.star.text.TextFrame"_ustr))
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, const Value& rValue)
{
sal_Int32 nIntValue = rValue.getInt();
switch( nName )
{
case NS_ooxml::LN_OfficeArtExtension_Decorative_val:
m_pImpl->m_bDecorative = true;
break;
case NS_ooxml::LN_CT_Hyperlink_URL:
m_pImpl->m_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->m_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->m_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:
//id of the object - ignored
break;
case NS_ooxml::LN_CT_NonVisualDrawingProps_name:
//name of the object
m_pImpl->m_sName = rValue.getString();
break;
case NS_ooxml::LN_CT_NonVisualDrawingProps_descr:
//alternative text
m_pImpl->m_sAlternativeText = rValue.getString();
break;
case NS_ooxml::LN_CT_NonVisualDrawingProps_title:
//alternative text
m_pImpl->m_title = rValue.getString();
break;
case NS_ooxml::LN_CT_NonVisualDrawingProps_hidden:
m_pImpl->m_bHidden = (nIntValue == 1);
break;
case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noChangeAspect:
//disallow aspect ratio change - ignored
break;
case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noMove:
m_pImpl->m_bPositionProtected = true;
break;
case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noResize:
m_pImpl->m_bSizeProtected = true;
break;
case NS_ooxml::LN_CT_Anchor_distT:
case NS_ooxml::LN_CT_Anchor_distB:
case NS_ooxml::LN_CT_Anchor_distL:
case NS_ooxml::LN_CT_Anchor_distR:
{
m_pImpl->m_nShapeOptionType = nName;
ProcessShapeOptions(rValue);
}
break;
case NS_ooxml::LN_CT_Anchor_simplePos_attr:
m_pImpl->m_bUseSimplePos = nIntValue > 0;
break;
case NS_ooxml::LN_CT_Anchor_relativeHeight: // unsigned content
{
// undocumented - based on testing: both 0 and 1 are equivalent to the maximum 503316479
const sal_Int32 nMaxAllowed = 0x1DFFFFFF;
if (nIntValue < 2 || nIntValue > nMaxAllowed)
m_pImpl->m_oZOrder = nMaxAllowed;
else
m_pImpl->m_oZOrder = nIntValue;
// all relativeHeight objects (i.e. DOCX graphics that use GraphicImport),
// no matter how high their value, are below the lowest z-index shape (in same layer)
// so emulate that by pretending that they are below text (in the hell-layer).
// Please be assured that this does not actually place it in the hell-layer.
m_pImpl->m_oZOrder = *m_pImpl->m_oZOrder - (nMaxAllowed + 1);
}
break;
case NS_ooxml::LN_CT_Anchor_behindDoc:
if (nIntValue > 0)
{
m_pImpl->m_bOpaque = false;
m_pImpl->m_bBehindDoc = true;
}
break;
case NS_ooxml::LN_CT_Anchor_locked:
break;
case NS_ooxml::LN_CT_Anchor_layoutInCell:
// Starting in MSO 2013, anchors are ALWAYS considered to be laid out in table cell.
m_pImpl->m_bCompatForcedLayoutInCell = !nIntValue
&& m_pImpl->m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14
&& m_pImpl->m_rDomainMapper.IsInTable();
m_pImpl->m_bLayoutInCell = m_pImpl->m_bCompatForcedLayoutInCell || nIntValue;
break;
case NS_ooxml::LN_CT_Anchor_hidden:
break;
case NS_ooxml::LN_CT_Anchor_allowOverlap:
m_pImpl->m_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->m_sAnchorId = aString.makeStringAndClear().toAsciiUpperCase();
}
break;
case NS_ooxml::LN_CT_Point2D_x:
m_pImpl->m_nLeftPosition = ConversionHelper::convertTwipToMm100_Limited(nIntValue);
m_pImpl->m_nHoriRelation = text::RelOrientation::PAGE_FRAME;
m_pImpl->m_nHoriOrient = text::HoriOrientation::NONE;
break;
case NS_ooxml::LN_CT_Point2D_y:
m_pImpl->m_nTopPosition = ConversionHelper::convertTwipToMm100_Limited(nIntValue);
m_pImpl->m_nVertRelation = text::RelOrientation::PAGE_FRAME;
m_pImpl->m_nVertOrient = text::VertOrientation::NONE;
break;
case NS_ooxml::LN_CT_WrapTight_wrapText:
m_pImpl->m_bContour = true;
m_pImpl->m_bContourOutside = true;
handleWrapTextValue(rValue.getInt());
break;
case NS_ooxml::LN_CT_WrapThrough_wrapText:
m_pImpl->m_bContour = true;
m_pImpl->m_bContourOutside = false;
handleWrapTextValue(rValue.getInt());
break;
case NS_ooxml::LN_CT_WrapSquare_wrapText:
handleWrapTextValue(rValue.getInt());
break;
case NS_ooxml::LN_CT_BlipFillProperties_srcRect:
m_pImpl->m_oCrop.emplace(rValue.getAny().get<text::GraphicCrop>());
break;
case NS_ooxml::LN_shape:
{
uno::Reference< drawing::XShape> xShape;
rValue.getAny( ) >>= xShape;
if ( xShape.is( ) )
{
if (!m_pImpl->m_bLayoutInCell && m_pImpl->m_rDomainMapper.IsInTable()
&& m_pImpl->m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
{
// Microsoft apparently forces layoutInCell behaviour
// if either horizontal orientation is based on character
// or vertical orientation is based on line
// so make it explicit instead of trying to hack in tons of adjustments.
if (m_pImpl->m_nVertRelation == text::RelOrientation::TEXT_LINE
|| m_pImpl->m_nHoriRelation == text::RelOrientation::CHAR)
{
m_pImpl->m_bLayoutInCell = true;
m_pImpl->m_bCompatForcedLayoutInCell = true;
}
}
if (m_pImpl->m_bLayoutInCell && m_pImpl->m_rDomainMapper.IsInTable())
{
// Microsoft is buggy and inconsistent in how they handle layoutInCell.
// Map wrongly-implemented settings to the closest implemented setting
// "page" is implemented as if it was "margin" - to cell spacing, not edge
if (m_pImpl->m_nVertRelation == text::RelOrientation::PAGE_FRAME)
m_pImpl->m_nVertRelation = text::RelOrientation::PAGE_PRINT_AREA;
// only "from top" and "top" are appropriate. Others are implemented as Top
if (m_pImpl->m_nVertOrient != text::VertOrientation::NONE)
m_pImpl->m_nVertOrient = text::VertOrientation::TOP;
}
else if (!m_pImpl->m_bLayoutInCell && m_pImpl->m_rDomainMapper.IsInTable())
{
// if the object is horizontally aligned to the paragraph,
// Microsoft strangely doesn't orient the object
// to the cell paragraph it is anchored to
// but to the paragraph that contains the entire table
// (which is equivalent to page margins).
// Amazingly, a VERT orientation to "line" can pin the horizontal alignment
// back to the cell paragraph.
if (m_pImpl->m_nHoriRelation == text::RelOrientation::FRAME
&& m_pImpl->m_nVertRelation != text::RelOrientation::TEXT_LINE)
{
m_pImpl->m_nHoriRelation = text::RelOrientation::PAGE_PRINT_AREA;
}
}
// 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(u"Graphic"_ustr) >>= xGraphic;
sal_Int32 nRotation = 0;
xShapeProps->getPropertyValue(u"RotateAngle"_ustr) >>= nRotation;
css::beans::PropertyValues aGrabBag;
xShapeProps->getPropertyValue(u"InteropGrabBag"_ustr) >>= 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(u"Shadow"_ustr) >>= m_pImpl->m_bShadow;
if (m_pImpl->m_bShadow)
{
xShapeProps->getPropertyValue(u"ShadowXDistance"_ustr) >>= m_pImpl->m_nShadowXDistance;
xShapeProps->getPropertyValue(u"ShadowYDistance"_ustr) >>= m_pImpl->m_nShadowYDistance;
xShapeProps->getPropertyValue(u"ShadowColor"_ustr) >>= m_pImpl->m_nShadowColor;
xShapeProps->getPropertyValue(u"ShadowTransparence"_ustr) >>= m_pImpl->m_nShadowTransparence;
}
xShapeProps->getPropertyValue(u"GraphicColorMode"_ustr) >>= m_pImpl->m_eColorMode;
xShapeProps->getPropertyValue(u"AdjustLuminance"_ustr) >>= m_pImpl->m_nBrightness;
xShapeProps->getPropertyValue(u"AdjustContrast"_ustr) >>= m_pImpl->m_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
awt::Size aSize = xShape->getSize( );
m_xGraphicObject->setPropertyValue(u"Height"_ustr,
uno::Any( aSize.Height ) );
m_xGraphicObject->setPropertyValue(u"Width"_ustr,
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(u"GraphicCrop"_ustr);
if (m_pImpl->m_oCrop)
{ // RTF: RTFValue from resolvePict()
m_xGraphicObject->setPropertyValue(u"GraphicCrop"_ustr,
uno::Any(*m_pImpl->m_oCrop));
}
else if (aAny >>= aGraphicCrop)
{ // DOCX: imported in oox BlipFillContext
m_xGraphicObject->setPropertyValue(u"GraphicCrop"_ustr,
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(u"Opaque"_ustr, 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(u"com.sun.star.text.TextFrame"_ustr))
{
bKeepRotation = true;
xShapeProps->setPropertyValue
(getPropertyName(PROP_TEXT_RANGE),
uno::Any
(m_pImpl->m_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(u"InteropGrabBag"_ustr));
sal_Int32 nOOXAngle(0);
aInteropGrabBag.getValue(u"mso-rotation-angle"_ustr) >>= 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(u"LockedCanvas"_ustr) >>= bIsLockedCanvas;
bool bIsWordprocessingCanvas(false);
aInteropGrabBag.getValue(u"WordprocessingCanvas"_ustr) >>= bIsWordprocessingCanvas;
const bool bIsGroupOrLine = (xServiceInfo->supportsService(u"com.sun.star.drawing.GroupShape"_ustr)
&& !bIsDiagram && !bIsLockedCanvas && !bIsWordprocessingCanvas)
|| xServiceInfo->supportsService(u"com.sun.star.drawing.LineShape"_ustr);
SdrObject* pShape = SdrObject::getSdrObjectFromXShape(m_xShape);
if (!bIsGroupOrLine || (!nOOXAngle && !lcl_bHasGroupSlantedChild(pShape)))
{
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();
}
// tdf#157960: SdrEdgeObj::NbcResize would reset the adjustment values of
// connectors to default zero. Thus we do not resize in case of a group that
// represents a Word drawing canvas.
if (!bIsWordprocessingCanvas)
m_xShape->setSize(aSize);
if (bKeepRotation)
{
xShapeProps->setPropertyValue(u"RotateAngle"_ustr, uno::Any(nRotation.get()));
}
m_pImpl->m_bIsGraphic = true;
if (!m_pImpl->m_sAnchorId.isEmpty())
{
putPropertyToFrameGrabBag(u"AnchorId"_ustr, uno::Any(m_pImpl->m_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->m_nLeftPosition = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.Left());
m_pImpl->m_nTopPosition = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.Top());
aLOSize.Width = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.getOpenWidth());
aLOSize.Height = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.getOpenHeight());
}
else
{
m_pImpl->m_nLeftPosition = fCentrumX - aLOSize.Width / 2.0;
m_pImpl->m_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->m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() < 14
&& xServiceInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr)
&& nOOXAngle != 0)
{
lcl_correctWord2007EffectExtent(nOOXAngle);
}
if (m_pImpl->m_rGraphicImportType == 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->m_nLeftMargin += nEffectExtent;
nEffectExtent = (m_pImpl->m_oEffectExtentRight)
? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentRight) : 0;
m_pImpl->m_nRightMargin += nEffectExtent;
nEffectExtent = (m_pImpl->m_oEffectExtentTop)
? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentTop) : 0;
m_pImpl->m_nTopMargin += nEffectExtent;
nEffectExtent = (m_pImpl->m_oEffectExtentBottom)
? oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentBottom) : 0;
m_pImpl->m_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->m_nLeftPosition;
aLOSnapRect.Y = m_pImpl->m_nTopPosition;
aLOSnapRect.Width = aLOSize.Width;
aLOSnapRect.Height = aLOSize.Height;
if (pShape)
{
tools::Rectangle aSnapRect = pShape->GetSnapRect(); // Twip
aLOSnapRect.X = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.Left());
aLOSnapRect.Y = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.Top());
aLOSnapRect.Width = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.getOpenWidth());
aLOSnapRect.Height = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.getOpenHeight());
}
m_pImpl->m_nLeftMargin += aLOSnapRect.X - aMSOBaseLeftTop.X;
m_pImpl->m_nRightMargin += aMSOBaseLeftTop.X + aMSOBaseSize.Width
- (aLOSnapRect.X + aLOSnapRect.Width);
m_pImpl->m_nTopMargin += aLOSnapRect.Y - aMSOBaseLeftTop.Y;
m_pImpl->m_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->m_nWrap == text::WrapTextMode_PARALLEL
|| m_pImpl->m_nWrap == text::WrapTextMode_DYNAMIC
|| m_pImpl->m_nWrap == text::WrapTextMode_LEFT
|| m_pImpl->m_nWrap == text::WrapTextMode_RIGHT
|| m_pImpl->m_nWrap == text::WrapTextMode_NONE)
&& !(m_pImpl->mpWrapPolygon) && !bIsDiagram && !bIsWordprocessingCanvas)
{
// 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->m_nLeftPosition;
aLOBoundRect.Y = m_pImpl->m_nTopPosition;
aLOBoundRect.Width = aLOSize.Width;
aLOBoundRect.Height = aLOSize.Height;
if (pShape)
{
tools::Rectangle aBoundRect = pShape->GetCurrentBoundRect(); // Twip
aLOBoundRect.X = ConversionHelper::convertTwipToMm100_Limited(aBoundRect.Left());
aLOBoundRect.Y = ConversionHelper::convertTwipToMm100_Limited(aBoundRect.Top());
aLOBoundRect.Width = ConversionHelper::convertTwipToMm100_Limited(aBoundRect.getOpenWidth());
aLOBoundRect.Height = ConversionHelper::convertTwipToMm100_Limited(aBoundRect.getOpenHeight());
}
m_pImpl->m_nLeftMargin += aLOBoundRect.X - aMSOBaseLeftTop.X;
m_pImpl->m_nRightMargin += aMSOBaseLeftTop.X + aMSOBaseSize.Width
- (aLOBoundRect.X + aLOBoundRect.Width);
m_pImpl->m_nTopMargin += aLOBoundRect.Y - aMSOBaseLeftTop.Y;
m_pImpl->m_nBottomMargin += aMSOBaseLeftTop.Y + aMSOBaseSize.Height
- (aLOBoundRect.Y + aLOBoundRect.Height);
}
else if (m_pImpl->mpWrapPolygon && !bIsDiagram && !bIsWordprocessingCanvas)
{
// 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->m_nTopMargin += basegfx::fround(fTopDiff);
aAnchorDistDiff[u"distTDiff"_ustr] <<= basegfx::fround(
o3tl::convert(fTopDiff, o3tl::Length::mm100, o3tl::Length::twip));
const double fBottomDiff = aB2DWrapRange.getMaxY() - aShapeRange.getMaxY();
m_pImpl->m_nBottomMargin += basegfx::fround(fBottomDiff);
aAnchorDistDiff[u"distBDiff"_ustr] <<= basegfx::fround(
o3tl::convert(fBottomDiff, o3tl::Length::mm100, o3tl::Length::twip));
const double fLeftDiff = aShapeRange.getMinX() - aB2DWrapRange.getMinX();
m_pImpl->m_nLeftMargin += basegfx::fround(fLeftDiff);
aAnchorDistDiff[u"distLDiff"_ustr] <<= basegfx::fround(
o3tl::convert(fLeftDiff, o3tl::Length::mm100, o3tl::Length::twip));
const double fRightDiff = aB2DWrapRange.getMaxX() - aShapeRange.getMaxX();
m_pImpl->m_nRightMargin += basegfx::fround(fRightDiff);
aAnchorDistDiff[u"distRDiff"_ustr] <<= basegfx::fround(
o3tl::convert(fRightDiff, o3tl::Length::mm100, o3tl::Length::twip));
m_pImpl->m_aInteropGrabBag[u"AnchorDistDiff"_ustr]
<<= aAnchorDistDiff.getAsConstPropertyValueList();
// FixMe: tdf#141880. LibreOffice cannot handle negative horizontal margin in contour wrap
if (m_pImpl->m_nLeftMargin < 0)
m_pImpl->m_nLeftMargin = 0;
if (m_pImpl->m_nRightMargin < 0)
m_pImpl->m_nRightMargin = 0;
}
else if (!bIsDiagram && !bIsWordprocessingCanvas) // 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->m_nLeftMargin
+= oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentLeft);
}
if (m_pImpl->m_oEffectExtentTop)
{
m_pImpl->m_nTopMargin
+= oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentTop);
}
if (m_pImpl->m_oEffectExtentRight)
{
m_pImpl->m_nRightMargin
+= oox::drawingml::convertEmuToHmm(*m_pImpl->m_oEffectExtentRight);
}
if (m_pImpl->m_oEffectExtentBottom)
{
m_pImpl->m_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->m_nTopMargin < 0)
m_pImpl->m_nTopMargin = 0;
if (m_pImpl->m_nBottomMargin < 0)
m_pImpl->m_nBottomMargin = 0;
}
if (bUseShape && m_pImpl->m_rGraphicImportType == 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->m_nHoriRelation == text::RelOrientation::FRAME
&& (m_pImpl->m_nHoriOrient == text::HoriOrientation::LEFT
|| m_pImpl->m_nHoriOrient == text::HoriOrientation::RIGHT
|| m_pImpl->m_nHoriOrient == text::HoriOrientation::INSIDE
|| m_pImpl->m_nHoriOrient == text::HoriOrientation::OUTSIDE))
{
// before compat15, relative left/right/inside/outside honored margins.
if (m_pImpl->m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() < 15)
m_pImpl->m_nHoriRelation = text::RelOrientation::PRINT_AREA;
}
// Anchored: Word only supports at-char in that case.
text::TextContentAnchorType eAnchorType = text::TextContentAnchorType_AT_CHARACTER;
if (m_pImpl->m_bHidden)
{
xShapeProps->setPropertyValue(u"Visible"_ustr, uno::Any(false));
xShapeProps->setPropertyValue(u"Printable"_ustr, uno::Any(false));
}
// Avoid setting AnchorType for TextBoxes till SwTextBoxHelper::syncProperty() doesn't handle transition.
bool bTextBox = false;
xShapeProps->getPropertyValue(u"TextBox"_ustr) >>= 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->m_bLayoutInCell && bTextBox)
m_pImpl->m_bLayoutInCell = !m_pImpl->m_bCompatForcedLayoutInCell;
xShapeProps->setPropertyValue(u"AnchorType"_ustr, uno::Any(eAnchorType));
if (m_pImpl->m_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->m_bLayoutInCell && bTextBox && m_pImpl->m_rDomainMapper.IsInTable()
&& m_pImpl->m_nHoriRelation == text::RelOrientation::PAGE_FRAME)
{
// This claim is a bit of a stretch, but should be OK for horizontal.
// For layoutInCell, MSO does apply PAGE_FRAME and PAGE_PRINT_AREA
// horizontal adjustments onto the cell's frame and print area.
// Note that FRAME cannot be substituted for vertical (only first para).
m_pImpl->m_nHoriRelation = text::RelOrientation::FRAME;
}
if(m_pImpl->m_rDomainMapper.IsInTable())
xShapeProps->setPropertyValue(getPropertyName(PROP_FOLLOW_TEXT_FLOW),
uno::Any(m_pImpl->m_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(u"com.sun.star.drawing.GroupShape"_ustr) ||
xServiceInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr))
{
// 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(u"com.sun.star.drawing.GraphicObjectShape"_ustr))
{
xShapeProps->getPropertyValue(u"RotateAngle"_ustr) >>= nRotation;
}
if (nRotation)
xShapeProps->setPropertyValue(u"RotateAngle"_ustr, 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->m_nHoriRelation != text::RelOrientation::PAGE_FRAME
|| m_pImpl->m_nVertRelation != text::RelOrientation::PAGE_FRAME)
m_xShape->setPosition(
awt::Point(m_pImpl->m_nLeftPosition, m_pImpl->m_nTopPosition));
if (nRotation)
xShapeProps->setPropertyValue(u"RotateAngle"_ustr, uno::Any(nRotation));
}
m_pImpl->applyRelativePosition(xShapeProps, /*bRelativeOnly=*/true);
xShapeProps->setPropertyValue(u"SurroundContour"_ustr, uno::Any(m_pImpl->m_bContour));
xShapeProps->setPropertyValue(u"ContourOutside"_ustr, uno::Any(m_pImpl->m_bContourOutside));
m_pImpl->applyMargins(xShapeProps);
xShapeProps->setPropertyValue(u"Opaque"_ustr, uno::Any(m_pImpl->m_bOpaque));
xShapeProps->setPropertyValue(u"Surround"_ustr, uno::Any(static_cast<sal_Int32>(m_pImpl->m_nWrap)));
m_pImpl->applyZOrder(xShapeProps);
m_pImpl->applyName(xShapeProps);
m_pImpl->applyHyperlink(xShapeProps, bUseShape);
xShapeProps->setPropertyValue(u"AllowOverlap"_ustr,
uno::Any(m_pImpl->m_bAllowOverlap));
// Get the grab-bag set by oox, merge with our one and then put it back.
comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue(u"InteropGrabBag"_ustr));
aInteropGrabBag.update(m_pImpl->getInteropGrabBag());
xShapeProps->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aInteropGrabBag.getAsConstPropertyValueList()));
}
else if (bUseShape && m_pImpl->m_rGraphicImportType == IMPORT_AS_DETECTED_INLINE)
{
uno::Reference< beans::XPropertySet > xShapeProps(m_xShape, uno::UNO_QUERY_THROW);
m_pImpl->applyMargins(xShapeProps);
m_pImpl->applyZOrder(xShapeProps);
m_pImpl->applyName(xShapeProps);
comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue(u"InteropGrabBag"_ustr));
aInteropGrabBag.update(m_pImpl->getInteropGrabBag());
xShapeProps->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aInteropGrabBag.getAsConstPropertyValueList()));
}
}
}
break;
case NS_ooxml::LN_CT_Inline_distT:
m_pImpl->m_nTopMargin = 0;
break;
case NS_ooxml::LN_CT_Inline_distB:
m_pImpl->m_nBottomMargin = 0;
break;
case NS_ooxml::LN_CT_Inline_distL:
m_pImpl->m_nLeftMargin = 0;
break;
case NS_ooxml::LN_CT_Inline_distR:
m_pImpl->m_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(u"RelativeWidthRelation"_ustr, 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(u"RelativeWidthRelation"_ustr, 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(u"RelativeWidthRelation"_ustr, 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(u"RelativeWidthRelation"_ustr, 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(u"RelativeHeightRelation"_ustr, 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(u"RelativeHeightRelation"_ustr, 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(u"RelativeHeightRelation"_ustr, 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(u"RelativeHeightRelation"_ustr, 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->m_nShapeOptionType )
{
case NS_ooxml::LN_CT_Anchor_distL:
m_pImpl->m_nLeftMargin = nIntValue / 360;
m_pImpl->m_nLeftMarginOrig = m_pImpl->m_nLeftMargin;
break;
case NS_ooxml::LN_CT_Anchor_distT:
//todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustULWrapForWordMargins()
m_pImpl->m_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->m_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->m_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:
case NS_ooxml::LN_CT_Inline_effectExtent:
case NS_ooxml::LN_CT_Inline_docPr:
case NS_ooxml::LN_CT_Inline_cNvGraphicFramePr:
case NS_ooxml::LN_CT_NonVisualGraphicFrameProperties_graphicFrameLocks:
case NS_ooxml::LN_CT_Inline_a_graphic:
case NS_ooxml::LN_CT_Anchor_simplePos_elem:
case NS_ooxml::LN_CT_Anchor_extent:
case NS_ooxml::LN_CT_Anchor_effectExtent:
case NS_ooxml::LN_EG_WrapType_wrapSquare:
case NS_ooxml::LN_EG_WrapType_wrapTight:
case NS_ooxml::LN_EG_WrapType_wrapThrough:
case NS_ooxml::LN_CT_Anchor_docPr:
case NS_ooxml::LN_CT_NonVisualDrawingProps_extLst:
case NS_ooxml::LN_CT_Anchor_cNvGraphicFramePr:
case NS_ooxml::LN_CT_Anchor_a_graphic:
case NS_ooxml::LN_CT_WrapPath_start:
case NS_ooxml::LN_CT_WrapPath_lineTo:
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:
case NS_ooxml::LN_wpc_wpc:
{
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[u"EG_WrapType"_ustr] <<= u"wrapTight"_ustr;
else if (nSprmId == NS_ooxml::LN_EG_WrapType_wrapThrough)
m_pImpl->m_aInteropGrabBag[u"EG_WrapType"_ustr] <<= u"wrapThrough"_ustr;
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->m_bBehindDoc && m_pImpl->m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14)
m_pImpl->m_bOpaque = !m_pImpl->m_rDomainMapper.IsInHeaderFooter();
}
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[u"CT_WrapPath"_ustr] <<= m_pImpl->mpWrapPolygon->getPointSequenceSequence();
}
break;
case NS_ooxml::LN_CT_Anchor_positionH:
{
// 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->m_bUseSimplePos )
{
m_pImpl->m_nHoriRelation = pHandler->relation();
m_pImpl->m_bPageToggle = pHandler->GetPageToggle();
m_pImpl->m_nHoriOrient = pHandler->orientation();
m_pImpl->m_nLeftPosition = pHandler->position();
// Left adjustments: if horizontally aligned to left of margin, then remove the
// left wrapping.
if (m_pImpl->m_nHoriOrient == text::HoriOrientation::LEFT)
{
if (m_pImpl->m_nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA)
{
m_pImpl->m_nLeftMargin = 0;
}
}
}
}
}
break;
case NS_ooxml::LN_CT_Anchor_positionV:
{
// 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->m_bUseSimplePos )
{
m_pImpl->m_nVertRelation = pHandler->relation();
m_pImpl->m_nVertOrient = pHandler->orientation();
m_pImpl->m_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 ? u"RelativeWidth"_ustr : u"RelativeHeight"_ustr;
sal_Int32 nTextPreRotateAngle = 0;
uno::Any aAny;
if (xPropertySet->getPropertySetInfo()->hasPropertyByName(
u"CustomShapeGeometry"_ustr))
{
aAny = xPropertySet->getPropertyValue(u"CustomShapeGeometry"_ustr);
}
comphelper::SequenceAsHashMap aCustomShapeGeometry(aAny);
auto it = aCustomShapeGeometry.find(u"TextPreRotateAngle"_ustr);
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:
//depending on the behindDoc attribute text wraps through behind or in front of the object
m_pImpl->m_nWrap = text::WrapTextMode_THROUGH;
// Wrap though means the margins defined earlier should not be
// respected.
m_pImpl->m_nLeftMargin = 0;
m_pImpl->m_nTopMargin = 0;
m_pImpl->m_nRightMargin = 0;
m_pImpl->m_nBottomMargin = 0;
break;
case NS_ooxml::LN_EG_WrapType_wrapTopAndBottom:
// tdf#137850: Word >= 2013 seems to ignore bBehindDoc except for wrapNone, but older versions honour it.
if (m_pImpl->m_bBehindDoc && m_pImpl->m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14)
m_pImpl->m_bOpaque = !m_pImpl->m_rDomainMapper.IsInHeaderFooter();
m_pImpl->m_nWrap = text::WrapTextMode_NONE;
break;
case NS_ooxml::LN_CT_GraphicalObject_graphicData:
{
m_pImpl->m_bIsGraphic = true;
writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
if( pProperties )
pProperties->resolve(*this);
}
break;
case NS_ooxml::LN_CT_NonVisualDrawingProps_a_hlinkClick:
{
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(const writerfilter::Reference<Properties>::Pointer_t& /*ref*/)
{
}
rtl::Reference<SwXTextGraphicObject> GraphicImport::createGraphicObject(uno::Reference<graphic::XGraphic> const & rxGraphic,
uno::Reference<beans::XPropertySet> const & xShapeProps)
{
rtl::Reference<SwXTextGraphicObject> xGraphicObject;
try
{
if (rxGraphic.is())
{
xGraphicObject = m_xTextDoc->createTextGraphicObject();
xGraphicObject->setPropertyValue(getPropertyName(PROP_GRAPHIC), uno::Any(rxGraphic));
xGraphicObject->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE),
uno::Any( m_pImpl->m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR ?
text::TextContentAnchorType_AT_CHARACTER :
text::TextContentAnchorType_AS_CHARACTER ));
//shapes have only one border
table::BorderLine2 aBorderLine;
GraphicBorderLine& rBorderLine = m_pImpl->m_aBorders[0];
if (rBorderLine.isEmpty() && xShapeProps.is() && xShapeProps->getPropertyValue(u"LineStyle"_ustr).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(u"LineColor"_ustr).get<sal_Int32>();
aBorderLine.LineWidth = xShapeProps->getPropertyValue(u"LineWidth"_ustr).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)
xGraphicObject->setPropertyValue(getPropertyName(rBorderProp), uno::Any(aBorderLine));
// setting graphic object shadow properties
if (m_pImpl->m_bShadow)
{
// Shadow width is approximated by average of X and Y
table::ShadowFormat aShadow;
sal_uInt32 nShadowColor = m_pImpl->m_nShadowColor & 0x00FFFFFF; // The shadow color we get is RGB only.
sal_Int32 nShadowWidth = (abs(m_pImpl->m_nShadowXDistance)
+ abs(m_pImpl->m_nShadowYDistance)) / 2;
aShadow.ShadowWidth = nShadowWidth;
sal_uInt8 nShadowTransparence = float(m_pImpl->m_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->m_nShadowXDistance > 0)
{
if (m_pImpl->m_nShadowYDistance > 0)
aShadow.Location = table::ShadowLocation_BOTTOM_RIGHT;
else
aShadow.Location = table::ShadowLocation_TOP_RIGHT;
}
else
{
if (m_pImpl->m_nShadowYDistance > 0)
aShadow.Location = table::ShadowLocation_BOTTOM_LEFT;
else
aShadow.Location = table::ShadowLocation_TOP_LEFT;
}
xGraphicObject->setPropertyValue(getPropertyName(PROP_SHADOW_FORMAT), uno::Any(aShadow));
}
// setting properties for all types
if( m_pImpl->m_bPositionProtected )
xGraphicObject->setPropertyValue(getPropertyName( PROP_POSITION_PROTECTED ),
uno::Any(true));
if( m_pImpl->m_bSizeProtected )
xGraphicObject->setPropertyValue(getPropertyName( PROP_SIZE_PROTECTED ),
uno::Any(true));
xGraphicObject->setPropertyValue(getPropertyName(PROP_DECORATIVE), uno::Any(m_pImpl->m_bDecorative));
sal_Int32 nWidth = - m_pImpl->m_nLeftPosition;
if (m_pImpl->m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
{
if (m_pImpl->m_nHoriRelation == text::RelOrientation::FRAME
&& (m_pImpl->m_nHoriOrient == text::HoriOrientation::LEFT
|| m_pImpl->m_nHoriOrient == text::HoriOrientation::RIGHT
|| m_pImpl->m_nHoriOrient == text::HoriOrientation::INSIDE
|| m_pImpl->m_nHoriOrient == text::HoriOrientation::OUTSIDE))
{
// before compat15, relative left/right/inside/outside honored margins.
if (m_pImpl->m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() < 15)
m_pImpl->m_nHoriRelation = text::RelOrientation::PRINT_AREA;
}
//adjust margins
if( (m_pImpl->m_nHoriOrient == text::HoriOrientation::LEFT &&
(m_pImpl->m_nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA ||
m_pImpl->m_nHoriRelation == text::RelOrientation::FRAME) ) ||
(m_pImpl->m_nHoriOrient == text::HoriOrientation::INSIDE &&
m_pImpl->m_nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA ))
m_pImpl->m_nLeftMargin = 0;
if((m_pImpl->m_nHoriOrient == text::HoriOrientation::RIGHT &&
(m_pImpl->m_nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA ||
m_pImpl->m_nHoriRelation == text::RelOrientation::FRAME) ) ||
(m_pImpl->m_nHoriOrient == text::HoriOrientation::INSIDE &&
m_pImpl->m_nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA ))
m_pImpl->m_nRightMargin = 0;
// adjust top/bottom margins
if( m_pImpl->m_nVertOrient == text::VertOrientation::TOP &&
( m_pImpl->m_nVertRelation == text::RelOrientation::PAGE_PRINT_AREA ||
m_pImpl->m_nVertRelation == text::RelOrientation::PAGE_FRAME))
m_pImpl->m_nTopMargin = 0;
if( m_pImpl->m_nVertOrient == text::VertOrientation::BOTTOM &&
( m_pImpl->m_nVertRelation == text::RelOrientation::PAGE_PRINT_AREA ||
m_pImpl->m_nVertRelation == text::RelOrientation::PAGE_FRAME))
m_pImpl->m_nBottomMargin = 0;
if( m_pImpl->m_nVertOrient == text::VertOrientation::BOTTOM &&
m_pImpl->m_nVertRelation == text::RelOrientation::PAGE_PRINT_AREA )
m_pImpl->m_nBottomMargin = 0;
//adjust alignment
if( m_pImpl->m_nHoriOrient == text::HoriOrientation::INSIDE &&
m_pImpl->m_nHoriRelation == text::RelOrientation::PAGE_FRAME )
{
// convert 'left to page' to 'from left -<width> to page text area'
m_pImpl->m_nHoriOrient = text::HoriOrientation::NONE;
m_pImpl->m_nHoriRelation = text::RelOrientation::PAGE_PRINT_AREA;
m_pImpl->m_nLeftPosition = - nWidth;
}
else if( m_pImpl->m_nHoriOrient == text::HoriOrientation::OUTSIDE &&
m_pImpl->m_nHoriRelation == text::RelOrientation::PAGE_FRAME )
{
// convert 'right to page' to 'from left 0 to right page border'
m_pImpl->m_nHoriOrient = text::HoriOrientation::NONE;
m_pImpl->m_nHoriRelation = text::RelOrientation::PAGE_RIGHT;
m_pImpl->m_nLeftPosition = 0;
}
if (m_pImpl->m_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.
m_pImpl->m_nTopPosition *= -1;
}
m_pImpl->applyPosition(xGraphicObject);
m_pImpl->applyRelativePosition(xGraphicObject);
if( !m_pImpl->m_bOpaque )
{
xGraphicObject->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::Any(m_pImpl->m_bOpaque));
}
xGraphicObject->setPropertyValue(getPropertyName( PROP_SURROUND ),
uno::Any(static_cast<sal_Int32>(m_pImpl->m_nWrap)));
if( m_pImpl->m_rDomainMapper.IsInTable())
xGraphicObject->setPropertyValue(getPropertyName( PROP_FOLLOW_TEXT_FLOW ),
uno::Any(m_pImpl->m_bLayoutInCell));
xGraphicObject->setPropertyValue(getPropertyName(PROP_ALLOW_OVERLAP),
uno::Any(m_pImpl->m_bAllowOverlap));
xGraphicObject->setPropertyValue(getPropertyName( PROP_SURROUND_CONTOUR ),
uno::Any(m_pImpl->m_bContour));
xGraphicObject->setPropertyValue(getPropertyName( PROP_CONTOUR_OUTSIDE ),
uno::Any(m_pImpl->m_bContourOutside));
m_pImpl->applyMargins(xGraphicObject);
}
xGraphicObject->setPropertyValue(getPropertyName( PROP_ADJUST_CONTRAST ),
uno::Any(static_cast<sal_Int16>(m_pImpl->m_nContrast)));
xGraphicObject->setPropertyValue(getPropertyName( PROP_ADJUST_LUMINANCE ),
uno::Any(static_cast<sal_Int16>(m_pImpl->m_nBrightness)));
if(m_pImpl->m_eColorMode != drawing::ColorMode_STANDARD)
{
xGraphicObject->setPropertyValue(getPropertyName( PROP_GRAPHIC_COLOR_MODE ),
uno::Any(m_pImpl->m_eColorMode));
}
// copy the image fill area properties
xGraphicObject->setPropertyValue(u"FillBackground"_ustr,
xShapeProps->getPropertyValue(u"FillBackground"_ustr));
xGraphicObject->setPropertyValue(u"FillBitmap"_ustr,
xShapeProps->getPropertyValue(u"FillBitmap"_ustr));
xGraphicObject->setPropertyValue(
u"FillBitmapLogicalSize"_ustr, xShapeProps->getPropertyValue(u"FillBitmapLogicalSize"_ustr));
xGraphicObject->setPropertyValue(u"FillBitmapMode"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapMode"_ustr));
xGraphicObject->setPropertyValue(u"FillBitmapOffsetX"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapOffsetX"_ustr));
xGraphicObject->setPropertyValue(
u"FillBitmapPositionOffsetX"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapPositionOffsetX"_ustr));
xGraphicObject->setPropertyValue(
u"FillBitmapPositionOffsetY"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapPositionOffsetY"_ustr));
xGraphicObject->setPropertyValue(
u"FillBitmapRectanglePoint"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapRectanglePoint"_ustr));
xGraphicObject->setPropertyValue(u"FillBitmapSizeX"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapSizeX"_ustr));
xGraphicObject->setPropertyValue(u"FillBitmapSizeY"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapSizeY"_ustr));
xGraphicObject->setPropertyValue(u"FillBitmapStretch"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapStretch"_ustr));
xGraphicObject->setPropertyValue(u"FillBitmapTile"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapTile"_ustr));
xGraphicObject->setPropertyValue(u"FillBitmapURL"_ustr,
xShapeProps->getPropertyValue(u"FillBitmapURL"_ustr));
xGraphicObject->setPropertyValue(u"FillColor"_ustr,
xShapeProps->getPropertyValue(u"FillColor"_ustr));
xGraphicObject->setPropertyValue(u"FillColor2"_ustr,
xShapeProps->getPropertyValue(u"FillColor2"_ustr));
xGraphicObject->setPropertyValue(u"FillComplexColor"_ustr,
xShapeProps->getPropertyValue(u"FillComplexColor"_ustr));
xGraphicObject->setPropertyValue(u"FillGradient"_ustr,
xShapeProps->getPropertyValue(u"FillGradient"_ustr));
xGraphicObject->setPropertyValue(
u"FillGradientStepCount"_ustr, xShapeProps->getPropertyValue(u"FillGradientStepCount"_ustr));
xGraphicObject->setPropertyValue(u"FillHatch"_ustr,
xShapeProps->getPropertyValue(u"FillHatch"_ustr));
xGraphicObject->setPropertyValue(u"FillStyle"_ustr,
xShapeProps->getPropertyValue(u"FillStyle"_ustr));
xGraphicObject->setPropertyValue(u"FillTransparence"_ustr,
xShapeProps->getPropertyValue(u"FillTransparence"_ustr));
xGraphicObject->setPropertyValue(
u"FillTransparenceGradient"_ustr,
xShapeProps->getPropertyValue(u"FillTransparenceGradient"_ustr));
m_pImpl->applyZOrder(xGraphicObject);
//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(u"GraphicCrop"_ustr) >>= 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();
xGraphicObject->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
xGraphicObject->setPropertyValue(u"Opaque"_ustr, uno::Any(true));
}
}
if (m_pImpl->m_rGraphicImportType == IMPORT_AS_DETECTED_INLINE
|| m_pImpl->m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
{
if( m_pImpl->getXSize() && m_pImpl->getYSize() )
xGraphicObject->setPropertyValue(getPropertyName(PROP_SIZE),
uno::Any( awt::Size( m_pImpl->getXSize(), m_pImpl->getYSize() )));
m_pImpl->applyMargins(xGraphicObject);
m_pImpl->applyName(xGraphicObject);
m_pImpl->applyHyperlink(xGraphicObject, false);
}
// Handle horizontal flip.
bool bMirrored = false;
xShapeProps->getPropertyValue(u"IsMirrored"_ustr) >>= bMirrored;
if (bMirrored)
{
xGraphicObject->setPropertyValue(u"HoriMirroredOnEvenPages"_ustr,
uno::Any(true));
xGraphicObject->setPropertyValue(u"HoriMirroredOnOddPages"_ustr,
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_Unicode * /*_data*/, size_t /*len*/)
{
}
void GraphicImport::lcl_props(const writerfilter::Reference<Properties>::Pointer_t& /*ref*/)
{
}
void GraphicImport::lcl_table(Id /*name*/, const writerfilter::Reference<Table>::Pointer_t& /*ref*/)
{
}
void GraphicImport::lcl_substream(Id /*name*/, const 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->m_bIsGraphic;
}
sal_Int32 GraphicImport::GetLeftMarginOrig() const
{
return m_pImpl->m_nLeftMarginOrig;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V516 Consider inspecting an odd expression. Non-null function pointer is compared to null: 'aGraphicSize.Height'.
↑ V516 Consider inspecting an odd expression. Non-null function pointer is compared to null: 'aGraphicSize.Width'.
↑ V516 Consider inspecting an odd expression. Non-null function pointer is compared to null: 'aGraphicSize.Height'.
↑ V516 Consider inspecting an odd expression. Non-null function pointer is compared to null: 'aGraphicSize.Width'.
↑ V530 The return value of function 'padToLength' is required to be utilized.
↑ V547 Expression 'nOOXAngle == 0' is always true.
↑ V547 Expression 'nOOXAngle != 0' is always false.
↑ V547 Expression 'nOOXAngle != 0' is always false.
↑ V547 Expression '!bIsWordprocessingCanvas' is always true.
↑ V547 Expression 'nRotation' is always false.
↑ V547 Expression 'nRotation' is always false.
↑ V547 Expression 'bMirrored' is always false.
↑ V560 A part of conditional expression is always true: nRotation == 0.
↑ V560 A part of conditional expression is always true: !bIsLockedCanvas.
↑ V560 A part of conditional expression is always true: !bIsWordprocessingCanvas.
↑ V560 A part of conditional expression is always true: !nOOXAngle.
↑ V560 A part of conditional expression is always false: nOOXAngle != 0.
↑ V560 A part of conditional expression is always true: !bIsWordprocessingCanvas.
↑ V560 A part of conditional expression is always true: !bIsWordprocessingCanvas.
↑ V560 A part of conditional expression is always true: !bIsWordprocessingCanvas.