diff options
Diffstat (limited to 'oox/source/drawingml/lineproperties.cxx')
-rw-r--r-- | oox/source/drawingml/lineproperties.cxx | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/oox/source/drawingml/lineproperties.cxx b/oox/source/drawingml/lineproperties.cxx new file mode 100644 index 000000000000..6cee420c167d --- /dev/null +++ b/oox/source/drawingml/lineproperties.cxx @@ -0,0 +1,482 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: lineproperties.cxx,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "oox/drawingml/lineproperties.hxx" +#include <vector> +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/drawing/FlagSequence.hpp> +#include <com/sun/star/drawing/LineDash.hpp> +#include <com/sun/star/drawing/LineJoint.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/drawing/PointSequence.hpp> +#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp> +#include "properties.hxx" +#include "tokens.hxx" +#include "oox/helper/modelobjecthelper.hxx" +#include "oox/helper/propertymap.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/core/filterbase.hxx" +#include "oox/core/namespaces.hxx" +#include "oox/drawingml/drawingmltypes.hxx" + +using namespace ::com::sun::star::drawing; + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::awt::Point; +using ::com::sun::star::container::XNameContainer; +using ::oox::core::FilterBase; + +namespace oox { +namespace drawingml { + +// ============================================================================ + +namespace { + +static const sal_Int32 spnDefaultLineIds[ LineId_END ] = +{ + PROP_LineStyle, + PROP_LineWidth, + PROP_LineColor, + PROP_LineTransparence, + PROP_LineDash, + PROP_LineJoint, + PROP_LineStartName, + PROP_LineStartWidth, + PROP_LineStartCenter, + PROP_LineEndName, + PROP_LineEndWidth, + PROP_LineEndCenter +}; + +// ---------------------------------------------------------------------------- + +void lclSetDashData( LineDash& orLineDash, sal_Int16 nDots, sal_Int32 nDotLen, + sal_Int16 nDashes, sal_Int32 nDashLen, sal_Int32 nDistance ) +{ + orLineDash.Dots = nDots; + orLineDash.DotLen = nDotLen; + orLineDash.Dashes = nDashes; + orLineDash.DashLen = nDashLen; + orLineDash.Distance = nDistance; +} + +/** Converts the specified preset dash to API dash. + + Line length and dot length are set relative to line width and have to be + multiplied by the actual line width after this function. + */ +void lclConvertPresetDash( LineDash& orLineDash, sal_Int32 nPresetDash ) +{ + switch( nPresetDash ) + { + case XML_dot: lclSetDashData( orLineDash, 1, 1, 0, 0, 3 ); break; + case XML_dash: lclSetDashData( orLineDash, 0, 0, 1, 4, 3 ); break; + case XML_dashDot: lclSetDashData( orLineDash, 1, 1, 1, 4, 3 ); break; + + case XML_lgDash: lclSetDashData( orLineDash, 0, 0, 1, 8, 3 ); break; + case XML_lgDashDot: lclSetDashData( orLineDash, 1, 1, 1, 8, 3 ); break; + case XML_lgDashDotDot: lclSetDashData( orLineDash, 2, 1, 1, 8, 3 ); break; + + case XML_sysDot: lclSetDashData( orLineDash, 1, 1, 0, 0, 1 ); break; + case XML_sysDash: lclSetDashData( orLineDash, 0, 0, 1, 3, 1 ); break; + case XML_sysDashDot: lclSetDashData( orLineDash, 1, 1, 1, 3, 1 ); break; + case XML_sysDashDotDot: lclSetDashData( orLineDash, 2, 1, 1, 3, 1 ); break; + + default: + OSL_ENSURE( false, "lclConvertPresetDash - unsupported preset dash" ); + lclSetDashData( orLineDash, 0, 0, 1, 4, 3 ); + } +} + +/** Converts the passed custom dash to API dash. + + Line length and dot length are set relative to line width and have to be + multiplied by the actual line width after this function. + */ +void lclConvertCustomDash( LineDash& orLineDash, const LineProperties::DashStopVector& rCustomDash ) +{ + if( rCustomDash.empty() ) + { + OSL_ENSURE( false, "lclConvertCustomDash - unexpected empty custom dash" ); + lclSetDashData( orLineDash, 0, 0, 1, 4, 3 ); + return; + } + + // count dashes and dots (stops equal or less than 2 are assumed to be dots) + sal_Int16 nDots = 0; + sal_Int32 nDotLen = 0; + sal_Int16 nDashes = 0; + sal_Int32 nDashLen = 0; + sal_Int32 nDistance = 0; + for( LineProperties::DashStopVector::const_iterator aIt = rCustomDash.begin(), aEnd = rCustomDash.end(); aIt != aEnd; ++aIt ) + { + if( aIt->first <= 2 ) + { + ++nDots; + nDotLen += aIt->first; + } + else + { + ++nDashes; + nDashLen += aIt->first; + } + nDistance += aIt->second; + } + orLineDash.DotLen = (nDots > 0) ? ::std::max< sal_Int32 >( nDotLen / nDots, 1 ) : 0; + orLineDash.Dots = nDots; + orLineDash.DashLen = (nDashes > 0) ? ::std::max< sal_Int32 >( nDashLen / nDashes, 1 ) : 0; + orLineDash.Dashes = nDashes; + orLineDash.Distance = ::std::max< sal_Int32 >( nDistance / rCustomDash.size(), 1 ); +} + +DashStyle lclGetDashStyle( sal_Int32 nToken ) +{ + switch( nToken ) + { + case XML_rnd: return DashStyle_ROUNDRELATIVE; + case XML_sq: return DashStyle_RECTRELATIVE; + case XML_flat: return DashStyle_RECT; + } + return DashStyle_ROUNDRELATIVE; +} + +LineJoint lclGetLineJoint( sal_Int32 nToken ) +{ + switch( nToken ) + { + case XML_round: return LineJoint_ROUND; + case XML_bevel: return LineJoint_BEVEL; + case XML_miter: return LineJoint_MITER; + } + return LineJoint_ROUND; +} + +const sal_Int32 OOX_ARROWSIZE_SMALL = 0; +const sal_Int32 OOX_ARROWSIZE_MEDIUM = 1; +const sal_Int32 OOX_ARROWSIZE_LARGE = 2; + +sal_Int32 lclGetArrowSize( sal_Int32 nToken ) +{ + switch( nToken ) + { + case XML_sm: return OOX_ARROWSIZE_SMALL; + case XML_med: return OOX_ARROWSIZE_MEDIUM; + case XML_lg: return OOX_ARROWSIZE_LARGE; + } + return OOX_ARROWSIZE_MEDIUM; +} + +// ---------------------------------------------------------------------------- + +void lclPushMarkerProperties( PropertyMap& rPropMap, const LineArrowProperties& rArrowProps, + ModelObjectHelper& rModelObjHelper, const LinePropertyIds& rPropIds, sal_Int32 nLineWidth, bool bLineEnd ) +{ + PolyPolygonBezierCoords aMarker; + OUString aMarkerName; + sal_Int32 nMarkerWidth = 0; + bool bMarkerCenter = false; + + OUStringBuffer aBuffer; + sal_Int32 nArrowType = rArrowProps.moArrowType.get( XML_none ); + switch( nArrowType ) + { + case XML_triangle: + aBuffer.append( CREATE_OUSTRING( "msArrowEnd" ) ); + break; + case XML_arrow: + aBuffer.append( CREATE_OUSTRING( "msArrowOpenEnd" ) ); + break; + case XML_stealth: + aBuffer.append( CREATE_OUSTRING( "msArrowStealthEnd" ) ); + break; + case XML_diamond: + aBuffer.append( CREATE_OUSTRING( "msArrowDiamondEnd" ) ); + bMarkerCenter = true; + break; + case XML_oval: + aBuffer.append( CREATE_OUSTRING( "msArrowOvalEnd" ) ); + bMarkerCenter = true; + break; + } + + if( aBuffer.getLength() > 0 ) + { + sal_Int32 nLength = lclGetArrowSize( rArrowProps.moArrowLength.get( XML_med ) ); + sal_Int32 nWidth = lclGetArrowSize( rArrowProps.moArrowWidth.get( XML_med ) ); + + sal_Int32 nNameIndex = nWidth * 3 + nLength + 1; + aBuffer.append( sal_Unicode( ' ' ) ).append( nNameIndex ); + aMarkerName = aBuffer.makeStringAndClear(); + + bool bIsArrow = nArrowType == XML_arrow; + double fArrowLength = 1.0; + switch( nLength ) + { + case OOX_ARROWSIZE_SMALL: fArrowLength = (bIsArrow ? 3.5 : 2.0); break; + case OOX_ARROWSIZE_MEDIUM: fArrowLength = (bIsArrow ? 4.5 : 3.0); break; + case OOX_ARROWSIZE_LARGE: fArrowLength = (bIsArrow ? 6.0 : 5.0); break; + } + double fArrowWidth = 1.0; + switch( nWidth ) + { + case OOX_ARROWSIZE_SMALL: fArrowWidth = (bIsArrow ? 3.5 : 2.0); break; + case OOX_ARROWSIZE_MEDIUM: fArrowWidth = (bIsArrow ? 4.5 : 3.0); break; + case OOX_ARROWSIZE_LARGE: fArrowWidth = (bIsArrow ? 6.0 : 5.0); break; + } + // set arrow width relative to line width (convert line width from EMUs to 1/100 mm) + sal_Int32 nApiLineWidth = ::std::max< sal_Int32 >( GetCoordinate( nLineWidth ), 70 ); + nMarkerWidth = static_cast< sal_Int32 >( fArrowWidth * nApiLineWidth ); + + // test if the arrow already exists, do not create it again in this case + if( !rPropIds.mbNamedLineMarker || !rModelObjHelper.hasLineMarker( aMarkerName ) ) + { +// pass X and Y as percentage to OOX_ARROW_POINT +#define OOX_ARROW_POINT( x, y ) Point( static_cast< sal_Int32 >( fArrowWidth * x ), static_cast< sal_Int32 >( fArrowLength * y ) ) + + ::std::vector< Point > aPoints; + switch( rArrowProps.moArrowType.get() ) + { + case XML_triangle: + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + aPoints.push_back( OOX_ARROW_POINT( 100, 100 ) ); + aPoints.push_back( OOX_ARROW_POINT( 0, 100 ) ); + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + break; + case XML_arrow: + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + aPoints.push_back( OOX_ARROW_POINT( 100, 91 ) ); + aPoints.push_back( OOX_ARROW_POINT( 85, 100 ) ); + aPoints.push_back( OOX_ARROW_POINT( 50, 36 ) ); + aPoints.push_back( OOX_ARROW_POINT( 15, 100 ) ); + aPoints.push_back( OOX_ARROW_POINT( 0, 91 ) ); + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + break; + case XML_stealth: + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + aPoints.push_back( OOX_ARROW_POINT( 100, 100 ) ); + aPoints.push_back( OOX_ARROW_POINT( 50, 60 ) ); + aPoints.push_back( OOX_ARROW_POINT( 0, 100 ) ); + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + break; + case XML_diamond: + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + aPoints.push_back( OOX_ARROW_POINT( 100, 50 ) ); + aPoints.push_back( OOX_ARROW_POINT( 50, 100 ) ); + aPoints.push_back( OOX_ARROW_POINT( 0, 50 ) ); + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + break; + case XML_oval: + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + aPoints.push_back( OOX_ARROW_POINT( 75, 7 ) ); + aPoints.push_back( OOX_ARROW_POINT( 93, 25 ) ); + aPoints.push_back( OOX_ARROW_POINT( 100, 50 ) ); + aPoints.push_back( OOX_ARROW_POINT( 93, 75 ) ); + aPoints.push_back( OOX_ARROW_POINT( 75, 93 ) ); + aPoints.push_back( OOX_ARROW_POINT( 50, 100 ) ); + aPoints.push_back( OOX_ARROW_POINT( 25, 93 ) ); + aPoints.push_back( OOX_ARROW_POINT( 7, 75 ) ); + aPoints.push_back( OOX_ARROW_POINT( 0, 50 ) ); + aPoints.push_back( OOX_ARROW_POINT( 7, 25 ) ); + aPoints.push_back( OOX_ARROW_POINT( 25, 7 ) ); + aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); + break; + } +#undef OOX_ARROW_POINT + + OSL_ENSURE( !aPoints.empty(), "ApiLineMarkerProperties::ApiLineMarkerProperties - missing arrow coordinates" ); + if( !aPoints.empty() ) + { + aMarker.Coordinates.realloc( 1 ); + aMarker.Coordinates[ 0 ] = ContainerHelper::vectorToSequence( aPoints ); + + ::std::vector< PolygonFlags > aFlags( aPoints.size(), PolygonFlags_NORMAL ); + aMarker.Flags.realloc( 1 ); + aMarker.Flags[ 0 ] = ContainerHelper::vectorToSequence( aFlags ); + + if( rPropIds.mbNamedLineMarker && !rModelObjHelper.insertLineMarker( aMarkerName, aMarker ) ) + aMarkerName = OUString(); + } + else + { + aMarkerName = OUString(); + } + } + } + + // push the properties (filled aMarkerName indicates valid marker) + if( aMarkerName.getLength() > 0 ) + { + if( bLineEnd ) + { + if( rPropIds.mbNamedLineMarker ) + rPropMap.setProperty( rPropIds[ LineEndId ], aMarkerName ); + else + rPropMap.setProperty( rPropIds[ LineEndId ], aMarker ); + rPropMap.setProperty( rPropIds[ LineEndWidthId ], nMarkerWidth ); + rPropMap.setProperty( rPropIds[ LineEndCenterId ], bMarkerCenter ); + } + else + { + if( rPropIds.mbNamedLineMarker ) + rPropMap.setProperty( rPropIds[ LineStartId ], aMarkerName ); + else + rPropMap.setProperty( rPropIds[ LineStartId ], aMarker ); + rPropMap.setProperty( rPropIds[ LineStartWidthId ], nMarkerWidth ); + rPropMap.setProperty( rPropIds[ LineStartCenterId ], bMarkerCenter ); + } + } +} + +} // namespace + +// ============================================================================ + +LinePropertyIds::LinePropertyIds( const sal_Int32* pnPropertyIds, bool bNamedLineDash, bool bNamedLineMarker ) : + mpnPropertyIds( pnPropertyIds ), + mbNamedLineDash( bNamedLineDash ), + mbNamedLineMarker( bNamedLineMarker ) +{ + OSL_ENSURE( mpnPropertyIds != 0, "LinePropertyIds::LinePropertyIds - missing property identifiers" ); +} + +// ============================================================================ + +void LineArrowProperties::assignUsed( const LineArrowProperties& rSourceProps ) +{ + moArrowType.assignIfUsed( rSourceProps.moArrowType ); + moArrowWidth.assignIfUsed( rSourceProps.moArrowWidth ); + moArrowLength.assignIfUsed( rSourceProps.moArrowLength ); +} + +// ============================================================================ + +LinePropertyIds LineProperties::DEFAULT_IDS( spnDefaultLineIds, false, true ); + +void LineProperties::assignUsed( const LineProperties& rSourceProps ) +{ + maStartArrow.assignUsed( rSourceProps.maStartArrow ); + maEndArrow.assignUsed( rSourceProps.maEndArrow ); + maLineFill.assignUsed( rSourceProps.maLineFill ); + if( !rSourceProps.maCustomDash.empty() ) + maCustomDash = rSourceProps.maCustomDash; + moLineWidth.assignIfUsed( rSourceProps.moLineWidth ); + moPresetDash.assignIfUsed( rSourceProps.moPresetDash ); + moLineCompound.assignIfUsed( rSourceProps.moLineCompound ); + moLineCap.assignIfUsed( rSourceProps.moLineCap ); + moLineJoint.assignIfUsed( rSourceProps.moLineJoint ); +} + +void LineProperties::pushToPropMap( PropertyMap& rPropMap, const FilterBase& rFilter, + ModelObjectHelper& rModelObjHelper, const LinePropertyIds& rPropIds, sal_Int32 nPhClr ) const +{ + // line fill type must exist, otherwise ignore other properties + if( maLineFill.moFillType.has() ) + { + // line style (our core only supports none and solid) + LineStyle eLineStyle = (maLineFill.moFillType.get() == XML_noFill) ? LineStyle_NONE : LineStyle_SOLID; + + // create line dash from preset dash token (not for invisible line) + if( (eLineStyle != LineStyle_NONE) && (moPresetDash.differsFrom( XML_solid ) || (!moPresetDash && !maCustomDash.empty())) ) + { + LineDash aLineDash; + aLineDash.Style = lclGetDashStyle( moLineCap.get( XML_rnd ) ); + + // convert preset dash or custom dash + if( moPresetDash.has() ) + lclConvertPresetDash( aLineDash, moPresetDash.get() ); + else + lclConvertCustomDash( aLineDash, maCustomDash ); + + // convert relative dash/dot length to absolute length + sal_Int32 nLineWidth = GetCoordinate( moLineWidth.get( 103500 ) ); + aLineDash.DotLen *= nLineWidth; + aLineDash.DashLen *= nLineWidth; + aLineDash.Distance *= nLineWidth; + + if( rPropIds.mbNamedLineDash ) + { + OUString aDashName = rModelObjHelper.insertLineDash( aLineDash ); + if( aDashName.getLength() > 0 ) + { + rPropMap.setProperty( rPropIds[ LineDashId ], aDashName ); + eLineStyle = LineStyle_DASH; + } + } + else + { + rPropMap.setProperty( rPropIds[ LineDashId ], aLineDash ); + eLineStyle = LineStyle_DASH; + } + } + + // set final line style property + rPropMap.setProperty( rPropIds[ LineStyleId ], eLineStyle ); + + // line joint type + if( moLineJoint.has() ) + rPropMap.setProperty( rPropIds[ LineJointId ], lclGetLineJoint( moLineJoint.get() ) ); + + // convert line width from EMUs to 1/100 mm + if( moLineWidth.has() ) + rPropMap.setProperty( rPropIds[ LineWidthId ], GetCoordinate( moLineWidth.get() ) ); + + // line color and transparence + Color aLineColor = maLineFill.getBestSolidColor(); + if( aLineColor.isUsed() ) + { + rPropMap.setProperty( rPropIds[ LineColorId ], aLineColor.getColor( rFilter, nPhClr ) ); + if( aLineColor.hasTransparence() ) + rPropMap.setProperty( rPropIds[ LineTransparenceId ], aLineColor.getTransparence() ); + } + + // line markers + lclPushMarkerProperties( rPropMap, maStartArrow, rModelObjHelper, rPropIds, moLineWidth.get( 0 ), false ); + lclPushMarkerProperties( rPropMap, maEndArrow, rModelObjHelper, rPropIds, moLineWidth.get( 0 ), true ); + } +} + +void LineProperties::pushToPropSet( PropertySet& rPropSet, const FilterBase& rFilter, + ModelObjectHelper& rModelObjHelper, const LinePropertyIds& rPropIds, sal_Int32 nPhClr ) const +{ + PropertyMap aPropMap; + pushToPropMap( aPropMap, rFilter, rModelObjHelper, rPropIds, nPhClr ); + rPropSet.setProperties( aPropMap ); +} + +// ============================================================================ + +} // namespace drawingml +} // namespace oox + |