diff options
author | Daniel Rentz <dr@openoffice.org> | 2010-08-26 18:37:44 +0200 |
---|---|---|
committer | Daniel Rentz <dr@openoffice.org> | 2010-08-26 18:37:44 +0200 |
commit | 04c272eb8396e58376d2b61baee01c4a95f993b4 (patch) | |
tree | 489606116a897ec3c0186161269670b0e8c66146 /oox/source/vml | |
parent | 4d9f54e9f020c91c7fda048bcf3f7549d58bc2b4 (diff) |
dr77: #i114128# import legacy drawing controls
Diffstat (limited to 'oox/source/vml')
-rw-r--r-- | oox/source/vml/makefile.mk | 16 | ||||
-rw-r--r-- | oox/source/vml/vmldrawing.cxx | 123 | ||||
-rw-r--r-- | oox/source/vml/vmldrawingfragment.cxx | 6 | ||||
-rw-r--r-- | oox/source/vml/vmlformatting.cxx | 92 | ||||
-rw-r--r-- | oox/source/vml/vmlinputstream.cxx | 49 | ||||
-rw-r--r-- | oox/source/vml/vmlshape.cxx | 203 | ||||
-rw-r--r-- | oox/source/vml/vmlshapecontext.cxx | 134 | ||||
-rwxr-xr-x | oox/source/vml/vmltextbox.cxx | 82 | ||||
-rwxr-xr-x | oox/source/vml/vmltextboxcontext.cxx | 146 |
9 files changed, 658 insertions, 193 deletions
diff --git a/oox/source/vml/makefile.mk b/oox/source/vml/makefile.mk index 2b47a1c930db..094d37cd8c1c 100644 --- a/oox/source/vml/makefile.mk +++ b/oox/source/vml/makefile.mk @@ -41,13 +41,15 @@ ENABLE_EXCEPTIONS=TRUE # --- Files -------------------------------------------------------- SLOFILES = \ - $(SLO)$/vmldrawing.obj \ - $(SLO)$/vmldrawingfragment.obj \ - $(SLO)$/vmlformatting.obj \ - $(SLO)$/vmlinputstream.obj \ - $(SLO)$/vmlshape.obj \ - $(SLO)$/vmlshapecontainer.obj \ - $(SLO)$/vmlshapecontext.obj + $(SLO)$/vmldrawing.obj \ + $(SLO)$/vmldrawingfragment.obj \ + $(SLO)$/vmlformatting.obj \ + $(SLO)$/vmlinputstream.obj \ + $(SLO)$/vmlshape.obj \ + $(SLO)$/vmlshapecontainer.obj \ + $(SLO)$/vmlshapecontext.obj \ + $(SLO)$/vmltextbox.obj \ + $(SLO)$/vmltextboxcontext.obj # --- Targets ------------------------------------------------------- diff --git a/oox/source/vml/vmldrawing.cxx b/oox/source/vml/vmldrawing.cxx index 56afb8cf76e9..065558d68c9f 100644 --- a/oox/source/vml/vmldrawing.cxx +++ b/oox/source/vml/vmldrawing.cxx @@ -27,12 +27,15 @@ #include "oox/vml/vmldrawing.hxx" +#include <algorithm> +#include <com/sun/star/drawing/XControlShape.hpp> #include <com/sun/star/drawing/XShapes.hpp> -#include "tokens.hxx" +#include <com/sun/star/lang/XMultiServiceFactory.hpp> #include "oox/core/xmlfilterbase.hxx" #include "oox/ole/axcontrol.hxx" #include "oox/vml/vmlshape.hxx" #include "oox/vml/vmlshapecontainer.hxx" +#include "tokens.hxx" namespace oox { namespace vml { @@ -41,6 +44,7 @@ namespace vml { using namespace ::com::sun::star::awt; using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::lang; using namespace ::com::sun::star::uno; using ::oox::core::XmlFilterBase; @@ -57,6 +61,13 @@ OUString lclGetShapeId( sal_Int32 nShapeId ) return CREATE_OUSTRING( "\0s" ) + OUString::valueOf( nShapeId ); } +/** Returns the numeric VML shape identifier from its textual representation. */ +sal_Int32 lclGetShapeId( const OUString& rShapeId ) +{ + // identifier consists of a literal NUL character, a lowercase 's', and the id + return ((rShapeId.getLength() >= 3) && (rShapeId[ 0 ] == '\0') && (rShapeId[ 1 ] == 's')) ? rShapeId.copy( 2 ).toInt32() : -1; +} + } // namespace // ============================================================================ @@ -106,6 +117,18 @@ Drawing::~Drawing() return *mxCtrlForm; } +void Drawing::registerBlockId( sal_Int32 nBlockId ) +{ + OSL_ENSURE( nBlockId > 0, "Drawing::registerBlockId - invalid block index" ); + if( nBlockId > 0 ) + { + // lower_bound() returns iterator pointing to element equal to nBlockId, if existing + BlockIdVector::iterator aIt = ::std::lower_bound( maBlockIds.begin(), maBlockIds.end(), nBlockId ); + if( (aIt == maBlockIds.end()) || (nBlockId != *aIt) ) + maBlockIds.insert( aIt, nBlockId ); + } +} + void Drawing::registerOleObject( const OleObjectInfo& rOleObject ) { OSL_ENSURE( rOleObject.maShapeId.getLength() > 0, "Drawing::registerOleObject - missing OLE object shape id" ); @@ -132,6 +155,47 @@ void Drawing::convertAndInsert() const mxShapes->convertAndInsert( xShapes ); } +sal_Int32 Drawing::getLocalShapeIndex( const OUString& rShapeId ) const +{ + sal_Int32 nShapeId = lclGetShapeId( rShapeId ); + if( nShapeId <= 0 ) return -1; + + /* Shapes in a drawing are counted per registered shape identifier blocks + as stored in the o:idmap element. The contents of this element have + been stored in our member maBlockIds. Each block represents 1024 shape + identifiers, starting with identifier 1 for the block #0. This means, + block #0 represents the identifiers 1-1024, block #1 represents the + identifiers 1025-2048, and so on. The local shape index has to be + calculated according to all blocks registered for this drawing. + + Example: + Registered for this drawing are blocks #1 and #3 (shape identifiers + 1025-2048 and 3073-4096). + Shape identifier 1025 -> local shape index 1. + Shape identifier 1026 -> local shape index 2. + ... + Shape identifier 2048 -> local shape index 1024. + Shape identifier 3073 -> local shape index 1025. + ... + Shape identifier 4096 -> local shape index 2048. + */ + + // get block id from shape id and find its index in the list of used blocks + sal_Int32 nBlockId = (nShapeId - 1) / 1024; + BlockIdVector::iterator aIt = ::std::lower_bound( maBlockIds.begin(), maBlockIds.end(), nBlockId ); + sal_Int32 nIndex = static_cast< sal_Int32 >( aIt - maBlockIds.begin() ); + + // block id not found in set -> register it now (value of nIndex remains valid) + if( (aIt == maBlockIds.end()) || (*aIt != nBlockId) ) + maBlockIds.insert( aIt, nBlockId ); + + // get one-based offset of shape id in its block + sal_Int32 nBlockOffset = (nShapeId - 1) % 1024 + 1; + + // calculate the local shape index + return 1024 * nIndex + nBlockOffset; +} + const OleObjectInfo* Drawing::getOleObjectInfo( const OUString& rShapeId ) const { return ContainerHelper::getMapElement( maOleObjects, rShapeId ); @@ -142,21 +206,72 @@ const ControlInfo* Drawing::getControlInfo( const OUString& rShapeId ) const return ContainerHelper::getMapElement( maControls, rShapeId ); } +Reference< XShape > Drawing::createAndInsertXShape( const OUString& rService, + const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const +{ + OSL_ENSURE( rService.getLength() > 0, "Drawing::createAndInsertXShape - missing UNO shape service name" ); + OSL_ENSURE( rxShapes.is(), "Drawing::createAndInsertXShape - missing XShapes container" ); + Reference< XShape > xShape; + if( (rService.getLength() > 0) && rxShapes.is() ) try + { + Reference< XMultiServiceFactory > xFactory( mrFilter.getModelFactory(), UNO_SET_THROW ); + xShape.set( xFactory->createInstance( rService ), UNO_QUERY_THROW ); + // insert shape into passed shape collection (maybe drawpage or group shape) + rxShapes->add( xShape ); + xShape->setPosition( Point( rShapeRect.X, rShapeRect.Y ) ); + xShape->setSize( Size( rShapeRect.Width, rShapeRect.Height ) ); + } + catch( Exception& ) + { + } + OSL_ENSURE( xShape.is(), "Drawing::createAndInsertXShape - cannot instanciate shape object" ); + return xShape; +} + +Reference< XShape > Drawing::createAndInsertXControlShape( const ::oox::ole::EmbeddedControl& rControl, + const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect, sal_Int32& rnCtrlIndex ) const +{ + Reference< XShape > xShape; + try + { + // create control model and insert it into the form of the draw page + Reference< XControlModel > xCtrlModel( getControlForm().convertAndInsert( rControl, rnCtrlIndex ), UNO_SET_THROW ); + + // create the control shape + xShape = createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.ControlShape" ), rxShapes, rShapeRect ); + + // set the control model at the shape + Reference< XControlShape >( xShape, UNO_QUERY_THROW )->setControl( xCtrlModel ); + } + catch( Exception& ) + { + } + return xShape; +} + bool Drawing::isShapeSupported( const ShapeBase& /*rShape*/ ) const { return true; } -bool Drawing::convertShapeClientAnchor( Rectangle& /*orShapeRect*/, const OUString& /*rShapeAnchor*/ ) const +OUString Drawing::getShapeBaseName( const ShapeBase& /*rShape*/ ) const +{ + return OUString(); +} + +bool Drawing::convertClientAnchor( Rectangle& /*orShapeRect*/, const OUString& /*rShapeAnchor*/ ) const { return false; } -void Drawing::convertControlClientData( const Reference< XControlModel >& /*rxCtrlModel*/, const ShapeClientData& /*rClientData*/ ) const +Reference< XShape > Drawing::createAndInsertClientXShape( const ShapeBase& /*rShape*/, + const Reference< XShapes >& /*rxShapes*/, const Rectangle& /*rShapeRect*/ ) const { + return Reference< XShape >(); } -void Drawing::notifyShapeInserted( const Reference< XShape >& /*rxShape*/, const Rectangle& /*rShapeRect*/ ) +void Drawing::notifyXShapeInserted( const Reference< XShape >& /*rxShape*/, + const Rectangle& /*rShapeRect*/, const ShapeBase& /*rShape*/, bool /*bGroupChild*/ ) { } diff --git a/oox/source/vml/vmldrawingfragment.cxx b/oox/source/vml/vmldrawingfragment.cxx index 60ed554c2cb6..aaa4532e5328 100644 --- a/oox/source/vml/vmldrawingfragment.cxx +++ b/oox/source/vml/vmldrawingfragment.cxx @@ -47,7 +47,7 @@ using ::rtl::OUString; // ============================================================================ DrawingFragment::DrawingFragment( XmlFilterBase& rFilter, const OUString& rFragmentPath, Drawing& rDrawing ) : - FragmentHandler2( rFilter, rFragmentPath ), + FragmentHandler2( rFilter, rFragmentPath, false ), // do not trim whitespace, has been preprocessed by the input stream mrDrawing( rDrawing ) { } @@ -65,7 +65,7 @@ ContextHandlerRef DrawingFragment::onCreateContext( sal_Int32 nElement, const At // DOCX filter handles plain shape elements with this fragment handler case VMLDRAWING_WORD: if( isRootElement() ) - return ShapeContextBase::createShapeContext( *this, nElement, rAttribs, mrDrawing.getShapes() ); + return ShapeContextBase::createShapeContext( *this, mrDrawing.getShapes(), nElement, rAttribs ); break; // XLSX and PPTX filters load the entire VML fragment @@ -77,7 +77,7 @@ ContextHandlerRef DrawingFragment::onCreateContext( sal_Int32 nElement, const At if( nElement == XML_xml ) return this; break; case XML_xml: - return ShapeContextBase::createShapeContext( *this, nElement, rAttribs, mrDrawing.getShapes() ); + return ShapeContextBase::createShapeContext( *this, mrDrawing.getShapes(), nElement, rAttribs ); } break; } diff --git a/oox/source/vml/vmlformatting.cxx b/oox/source/vml/vmlformatting.cxx index e82f78cf7210..9aa91597dcc9 100644 --- a/oox/source/vml/vmlformatting.cxx +++ b/oox/source/vml/vmlformatting.cxx @@ -28,14 +28,14 @@ #include "oox/vml/vmlformatting.hxx" #include <rtl/strbuf.hxx> -#include "tokens.hxx" #include "oox/drawingml/color.hxx" #include "oox/drawingml/drawingmltypes.hxx" #include "oox/drawingml/fillproperties.hxx" #include "oox/drawingml/lineproperties.hxx" +#include "oox/helper/attributelist.hxx" #include "oox/helper/graphichelper.hxx" #include "oox/helper/propertymap.hxx" -#include "oox/token/tokenmap.hxx" +#include "tokens.hxx" namespace oox { namespace vml { @@ -85,8 +85,9 @@ bool lclExtractDouble( double& orfValue, sal_Int32& ornEndPos, const OUString& r /*static*/ bool ConversionHelper::decodeBool( const OUString& rValue ) { + sal_Int32 nToken = AttributeConversion::decodeToken( rValue ); // anything else than 't' or 'true' is considered to be false, as specified - return ((rValue.getLength() == 1) && (rValue[ 0 ] == 't')) || (rValue == CREATE_OUSTRING( "true" )); + return (nToken == XML_t) || (nToken == XML_true); } /*static*/ double ConversionHelper::decodePercent( const OUString& rValue, double fDefValue ) @@ -175,58 +176,35 @@ bool lclExtractDouble( double& orfValue, sal_Int32& ornEndPos, const OUString& r return (decodeMeasureToEmu( rGraphicHelper, rValue, nRefValue, bPixelX, bDefaultAsPixel ) + 180) / 360; } -// ============================================================================ - -namespace { - -/** Converts a VML color attribute to a DrawingML color. - - @param orDmlColor (out-parameter) The destination DrawingML color. - - @param roVmlColor The VML string representation of the color. If existing, - this can be a 6-digit hexadecimal RGB value with leading '#' character, - a predefined color name (e.g. 'black', 'red', etc.), the index into an - application defined color palette in brackets with leading color name - (e.g. 'red [9]' or 'windowColor [64]'), or a color modifier used in - one-color gradients (e.g. 'fill darken(128)' or 'fill lighten(0)'. - - @param roVmlOpacity The opacity of the color. If existing, this should be - a floating-point value in the range [0.0;1.0]. - - @param nDefaultRgb Deafult RGB color used if the parameter roVmlColor is - empty. - - @param nPrimaryRgb If set to something else than API_RGB_TRANSPARENT, - specifies the color to be used to resolve the color modifiers used in - one-color gradients. - */ -void lclGetColor( Color& orDmlColor, const GraphicHelper& rGraphicHelper, +/*static*/ Color ConversionHelper::decodeColor( const GraphicHelper& rGraphicHelper, const OptValue< OUString >& roVmlColor, const OptValue< double >& roVmlOpacity, - sal_Int32 nDefaultRgb, sal_Int32 nPrimaryRgb = API_RGB_TRANSPARENT ) + sal_Int32 nDefaultRgb, sal_Int32 nPrimaryRgb ) { + Color aDmlColor; + // convert opacity const sal_Int32 DML_FULL_OPAQUE = ::oox::drawingml::MAX_PERCENT; double fOpacity = roVmlOpacity.get( 1.0 ); sal_Int32 nOpacity = getLimitedValue< sal_Int32, double >( fOpacity * DML_FULL_OPAQUE, 0, DML_FULL_OPAQUE ); if( nOpacity < DML_FULL_OPAQUE ) - orDmlColor.addTransformation( XML_alpha, nOpacity ); + aDmlColor.addTransformation( XML_alpha, nOpacity ); // color attribute not present - set passed default color if( !roVmlColor.has() ) { - orDmlColor.setSrgbClr( nDefaultRgb ); - return; + aDmlColor.setSrgbClr( nDefaultRgb ); + return aDmlColor; } // separate leading color name or RGB value from following palette index OUString aColorName, aColorIndex; - ConversionHelper::separatePair( aColorName, aColorIndex, roVmlColor.get(), ' ' ); + separatePair( aColorName, aColorIndex, roVmlColor.get(), ' ' ); // RGB colors in the format '#RRGGBB' if( (aColorName.getLength() == 7) && (aColorName[ 0 ] == '#') ) { - orDmlColor.setSrgbClr( aColorName.copy( 1 ).toInt32( 16 ) ); - return; + aDmlColor.setSrgbClr( aColorName.copy( 1 ).toInt32( 16 ) ); + return aDmlColor; } // RGB colors in the format '#RGB' @@ -235,27 +213,27 @@ void lclGetColor( Color& orDmlColor, const GraphicHelper& rGraphicHelper, sal_Int32 nR = aColorName.copy( 1, 1 ).toInt32( 16 ) * 0x11; sal_Int32 nG = aColorName.copy( 2, 1 ).toInt32( 16 ) * 0x11; sal_Int32 nB = aColorName.copy( 3, 1 ).toInt32( 16 ) * 0x11; - orDmlColor.setSrgbClr( (nR << 16) | (nG << 8) | nB ); - return; + aDmlColor.setSrgbClr( (nR << 16) | (nG << 8) | nB ); + return aDmlColor; } /* Predefined color names or system color names (resolve to RGB to detect valid color name). */ - sal_Int32 nColorToken = StaticTokenMap::get().getTokenFromUnicode( aColorName ); + sal_Int32 nColorToken = AttributeConversion::decodeToken( aColorName ); sal_Int32 nRgbValue = Color::getVmlPresetColor( nColorToken, API_RGB_TRANSPARENT ); if( nRgbValue == API_RGB_TRANSPARENT ) nRgbValue = rGraphicHelper.getSystemColor( nColorToken, API_RGB_TRANSPARENT ); if( nRgbValue != API_RGB_TRANSPARENT ) { - orDmlColor.setSrgbClr( nRgbValue ); - return; + aDmlColor.setSrgbClr( nRgbValue ); + return aDmlColor; } // try palette colors enclosed in brackets if( (aColorIndex.getLength() >= 3) && (aColorIndex[ 0 ] == '[') && (aColorIndex[ aColorIndex.getLength() - 1 ] == ']') ) { - orDmlColor.setPaletteClr( aColorIndex.copy( 1, aColorIndex.getLength() - 2 ).toInt32() ); - return; + aDmlColor.setPaletteClr( aColorIndex.copy( 1, aColorIndex.getLength() - 2 ).toInt32() ); + return aDmlColor; } // try fill gradient modificator 'fill <modifier>(<amount>)' @@ -266,7 +244,7 @@ void lclGetColor( Color& orDmlColor, const GraphicHelper& rGraphicHelper, if( (2 <= nOpenParen) && (nOpenParen + 1 < nCloseParen) && (nCloseParen + 1 == aColorIndex.getLength()) ) { sal_Int32 nModToken = XML_TOKEN_INVALID; - switch( StaticTokenMap::get().getTokenFromUnicode( aColorIndex.copy( 0, nOpenParen ) ) ) + switch( AttributeConversion::decodeToken( aColorIndex.copy( 0, nOpenParen ) ) ) { case XML_darken: nModToken = XML_shade; case XML_lighten: nModToken = XML_tint; @@ -277,18 +255,23 @@ void lclGetColor( Color& orDmlColor, const GraphicHelper& rGraphicHelper, /* Simulate this modifier color by a color with related transformation. The modifier amount has to be converted from the range [0;255] to percentage [0;100000] used by DrawingML. */ - orDmlColor.setSrgbClr( nPrimaryRgb ); - orDmlColor.addTransformation( nModToken, static_cast< sal_Int32 >( nValue * ::oox::drawingml::MAX_PERCENT / 255 ) ); - return; + aDmlColor.setSrgbClr( nPrimaryRgb ); + aDmlColor.addTransformation( nModToken, static_cast< sal_Int32 >( nValue * ::oox::drawingml::MAX_PERCENT / 255 ) ); + return aDmlColor; } } } - OSL_ENSURE( false, OStringBuffer( "lclGetColor - invalid VML color name '" ). + OSL_ENSURE( false, OStringBuffer( "ConversionHelper::decodeColor - invalid VML color name '" ). append( OUStringToOString( roVmlColor.get(), RTL_TEXTENCODING_ASCII_US ) ).append( '\'' ).getStr() ); - orDmlColor.setSrgbClr( nDefaultRgb ); + aDmlColor.setSrgbClr( nDefaultRgb ); + return aDmlColor; } +// ============================================================================ + +namespace { + sal_Int32 lclGetEmu( const GraphicHelper& rGraphicHelper, const OptValue< OUString >& roValue, sal_Int32 nDefValue ) { return roValue.has() ? ConversionHelper::decodeMeasureToEmu( rGraphicHelper, roValue.get(), 0, false, false ) : nDefValue; @@ -299,7 +282,7 @@ void lclGetDmlLineDash( OptValue< sal_Int32 >& oroPresetDash, LineProperties::Da if( roDashStyle.has() ) { const OUString& rDashStyle = roDashStyle.get(); - switch( StaticTokenMap::get().getTokenFromUnicode( rDashStyle ) ) + switch( AttributeConversion::decodeToken( rDashStyle ) ) { case XML_solid: oroPresetDash = XML_solid; return; case XML_shortdot: oroPresetDash = XML_sysDot; return; @@ -445,7 +428,7 @@ void StrokeModel::pushToPropMap( PropertyMap& rPropMap, aLineProps.maLineFill.moFillType = XML_solidFill; lclConvertArrow( aLineProps.maStartArrow, maStartArrow ); lclConvertArrow( aLineProps.maEndArrow, maEndArrow ); - lclGetColor( aLineProps.maLineFill.maFillColor, rGraphicHelper, moColor, moOpacity, API_RGB_BLACK ); + aLineProps.maLineFill.maFillColor = ConversionHelper::decodeColor( rGraphicHelper, moColor, moOpacity, API_RGB_BLACK ); aLineProps.moLineWidth = lclGetEmu( rGraphicHelper, moWeight, 1 ); lclGetDmlLineDash( aLineProps.moPresetDash, aLineProps.maCustomDash, moDashStyle ); aLineProps.moLineCompound = lclGetDmlLineCompound( moLineStyle ); @@ -498,9 +481,8 @@ void FillModel::pushToPropMap( PropertyMap& rPropMap, double fFocus = moFocus.get( 0.0 ); // prepare colors - Color aColor1, aColor2; - lclGetColor( aColor1, rGraphicHelper, moColor, moOpacity, API_RGB_WHITE ); - lclGetColor( aColor2, rGraphicHelper, moColor2, moOpacity2, API_RGB_WHITE, aColor1.getColor( rGraphicHelper ) ); + Color aColor1 = ConversionHelper::decodeColor( rGraphicHelper, moColor, moOpacity, API_RGB_WHITE ); + Color aColor2 = ConversionHelper::decodeColor( rGraphicHelper, moColor2, moOpacity2, API_RGB_WHITE, aColor1.getColor( rGraphicHelper ) ); // type XML_gradient is linear or axial gradient if( nFillType == XML_gradient ) @@ -588,7 +570,7 @@ void FillModel::pushToPropMap( PropertyMap& rPropMap, { aFillProps.moFillType = XML_solidFill; // fill color (default is white) - lclGetColor( aFillProps.maFillColor, rGraphicHelper, moColor, moOpacity, API_RGB_WHITE ); + aFillProps.maFillColor = ConversionHelper::decodeColor( rGraphicHelper, moColor, moOpacity, API_RGB_WHITE ); } } } diff --git a/oox/source/vml/vmlinputstream.cxx b/oox/source/vml/vmlinputstream.cxx index 27776c66ca0b..fb2443aba4c4 100644 --- a/oox/source/vml/vmlinputstream.cxx +++ b/oox/source/vml/vmlinputstream.cxx @@ -151,6 +151,48 @@ void lclProcessAttribs( OStringBuffer& rBuffer, const sal_Char* pcBeg, const sal lclAppendToBuffer( rBuffer, pcBeg, pcEnd ); } +void lclProcessContents( OStringBuffer& rBuffer, const sal_Char* pcBeg, const sal_Char* pcEnd ) +{ + /* MSO has a very weird way to store and handle whitespaces. The stream + may contain lots of spaces, tabs, and newlines which have to be handled + as single space character. This will be done in this function. + + If the element text contains a literal line break, it will be stored as + <br> tag (without matching closing </br> element). + + A single space character following another character is stored + literally and must not be stipped away here. Example: The element + <font>abc </font> + contains the three letters a, b, and c, followed by a space character. + + Consecutive space characters, or a leading single space character, are + stored in a <span> element. If there are N space characters (N > 1), + then the <span> element contains exactly (N-1) NBSP characters + (non-breaking space), followed by a regular space character. Example: + The element + <font><span style='mso-spacerun:yes'>\xA0\xA0\xA0 </span></font> + represents 4 consecutive space characters. Has to be handled by the + implementation. + + A single space character for its own is stored in an empty element. + Example: The element + <font></font> + represents a single space character. Has to be handled by the + implementation. + */ + + // skip leading whitespace + const sal_Char* pcContentsBeg = lclFindNonWhiteSpace( pcBeg, pcEnd ); + while( pcContentsBeg < pcEnd ) + { + const sal_Char* pcWhitespaceBeg = lclFindWhiteSpace( pcContentsBeg + 1, pcEnd ); + lclAppendToBuffer( rBuffer, pcContentsBeg, pcWhitespaceBeg ); + if( pcWhitespaceBeg < pcEnd ) + rBuffer.append( ' ' ); + pcContentsBeg = lclFindNonWhiteSpace( pcWhitespaceBeg, pcEnd ); + } +} + } // namespace // ============================================================================ @@ -179,8 +221,9 @@ StreamDataContainer::StreamDataContainer( const Reference< XInputStream >& rxInS { // look for the next opening angle bracket const sal_Char* pcOpen = lclFindCharacter( pcCurr, pcEnd, '<' ); + // copy all characters from current position to opening bracket - lclAppendToBuffer( aBuffer, pcCurr, pcOpen ); + lclProcessContents( aBuffer, pcCurr, pcOpen ); // nothing to do if no opening bracket has been found if( pcOpen < pcEnd ) @@ -217,10 +260,10 @@ StreamDataContainer::StreamDataContainer( const Reference< XInputStream >& rxInS // do nothing } - // replace '<br>' elements with '<br/>' elements + // replace '<br>' element with newline else if( (nElementLen >= 4) && (pcOpen[ 1 ] == 'b') && (pcOpen[ 2 ] == 'r') && (lclFindNonWhiteSpace( pcOpen + 3, pcClose ) == pcClose) ) { - aBuffer.append( RTL_CONSTASCII_STRINGPARAM( "<br/>" ) ); + aBuffer.append( '\n' ); } // check start elements and empty elements for repeated attributes diff --git a/oox/source/vml/vmlshape.cxx b/oox/source/vml/vmlshape.cxx index 290e624698f4..5603d1b541b1 100644 --- a/oox/source/vml/vmlshape.cxx +++ b/oox/source/vml/vmlshape.cxx @@ -27,16 +27,14 @@ #include "oox/vml/vmlshape.hxx" -#include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <com/sun/star/beans/PropertyValues.hpp> #include <com/sun/star/awt/XControlModel.hpp> #include <com/sun/star/drawing/PointSequenceSequence.hpp> -#include <com/sun/star/drawing/XControlShape.hpp> #include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp> #include <com/sun/star/drawing/XShapes.hpp> #include <com/sun/star/graphic/XGraphic.hpp> #include <rtl/math.hxx> -#include "properties.hxx" +#include <rtl/ustrbuf.hxx> #include "oox/core/xmlfilterbase.hxx" #include "oox/helper/graphichelper.hxx" #include "oox/helper/propertymap.hxx" @@ -46,6 +44,8 @@ #include "oox/ole/oleobjecthelper.hxx" #include "oox/vml/vmldrawing.hxx" #include "oox/vml/vmlshapecontainer.hxx" +#include "oox/vml/vmltextbox.hxx" +#include "properties.hxx" namespace oox { namespace vml { @@ -55,16 +55,21 @@ namespace vml { using namespace ::com::sun::star::awt; using namespace ::com::sun::star::drawing; using namespace ::com::sun::star::graphic; -using namespace ::com::sun::star::lang; using namespace ::com::sun::star::uno; using ::oox::core::XmlFilterBase; using ::rtl::OUString; +using ::rtl::OUStringBuffer; // ============================================================================ namespace { +const sal_Int32 VML_SHAPETYPE_PICTUREFRAME = 75; +const sal_Int32 VML_SHAPETYPE_HOSTCONTROL = 201; + +// ---------------------------------------------------------------------------- + Point lclGetAbsPoint( const Point& rRelPoint, const Rectangle& rShapeRect, const Rectangle& rCoordSys ) { double fWidthRatio = static_cast< double >( rShapeRect.Width ) / rCoordSys.Width; @@ -87,55 +92,6 @@ Rectangle lclGetAbsRect( const Rectangle& rRelRect, const Rectangle& rShapeRect, return aAbsRect; } -Reference< XShape > lclCreateXShape( const XmlFilterBase& rFilter, const OUString& rService ) -{ - OSL_ENSURE( rService.getLength() > 0, "lclCreateXShape - missing UNO shape service name" ); - Reference< XShape > xShape; - try - { - Reference< XMultiServiceFactory > xFactory( rFilter.getModelFactory(), UNO_SET_THROW ); - xShape.set( xFactory->createInstance( rService ), UNO_QUERY_THROW ); - } - catch( Exception& ) - { - } - OSL_ENSURE( xShape.is(), "lclCreateXShape - cannot instanciate shape object" ); - return xShape; -} - -void lclInsertXShape( const Reference< XShapes >& rxShapes, const Reference< XShape >& rxShape ) -{ - OSL_ENSURE( rxShapes.is(), "lclInsertXShape - missing XShapes container" ); - OSL_ENSURE( rxShape.is(), "lclInsertXShape - missing XShape" ); - if( rxShapes.is() && rxShape.is() ) try - { - // insert shape into passed shape collection (maybe drawpage or group shape) - rxShapes->add( rxShape ); - } - catch( Exception& ) - { - } -} - -void lclSetXShapeRect( const Reference< XShape >& rxShape, const Rectangle& rShapeRect ) -{ - OSL_ENSURE( rxShape.is(), "lclSetXShapeRect - missing XShape" ); - if( rxShape.is() ) - { - rxShape->setPosition( Point( rShapeRect.X, rShapeRect.Y ) ); - rxShape->setSize( Size( rShapeRect.Width, rShapeRect.Height ) ); - } -} - -Reference< XShape > lclCreateAndInsertXShape( const XmlFilterBase& rFilter, - const Reference< XShapes >& rxShapes, const OUString& rService, const Rectangle& rShapeRect ) -{ - Reference< XShape > xShape = lclCreateXShape( rFilter, rService ); - lclInsertXShape( rxShapes, xShape ); - lclSetXShapeRect( xShape, rShapeRect ); - return xShape; -} - } // namespace // ============================================================================ @@ -168,6 +124,11 @@ ShapeType::~ShapeType() { } +sal_Int32 ShapeType::getShapeType() const +{ + return maTypeModel.moShapeType.get( 0 ); +} + OUString ShapeType::getGraphicPath() const { return maTypeModel.moGraphicPath.get( OUString() ); @@ -208,12 +169,30 @@ Rectangle ShapeType::getRelRectangle() const // ============================================================================ -ShapeClientData::ShapeClientData() : +ClientData::ClientData() : mnObjType( XML_TOKEN_INVALID ), + mnTextHAlign( XML_Left ), + mnTextVAlign( XML_Top ), mnCol( -1 ), mnRow( -1 ), + mnChecked( VML_CLIENTDATA_UNCHECKED ), + mnDropStyle( XML_Combo ), + mnDropLines( 1 ), + mnVal( 0 ), + mnMin( 0 ), + mnMax( 0 ), + mnInc( 0 ), + mnPage( 0 ), + mnSelType( XML_Single ), + mnVTEdit( VML_CLIENTDATA_TEXT ), mbPrintObject( true ), - mbVisible( false ) + mbVisible( false ), + mbDde( false ), + mbNo3D( false ), + mbNo3D2( false ), + mbMultiLine( false ), + mbVScroll( false ), + mbSecretEdit( false ) { } @@ -223,9 +202,19 @@ ShapeModel::ShapeModel() { } -ShapeClientData& ShapeModel::createClientData() +ShapeModel::~ShapeModel() +{ +} + +TextBox& ShapeModel::createTextBox() +{ + mxTextBox.reset( new TextBox ); + return *mxTextBox; +} + +ClientData& ShapeModel::createClientData() { - mxClientData.reset( new ShapeClientData ); + mxClientData.reset( new ClientData ); return *mxClientData; } @@ -244,6 +233,22 @@ void ShapeBase::finalizeFragmentImport() maTypeModel.assignUsed( pShapeType->getTypeModel() ); } +OUString ShapeBase::getShapeName() const +{ + if( maTypeModel.maShapeName.getLength() > 0 ) + return maTypeModel.maShapeName; + + OUString aBaseName = mrDrawing.getShapeBaseName( *this ); + if( aBaseName.getLength() > 0 ) + { + sal_Int32 nShapeIdx = mrDrawing.getLocalShapeIndex( getShapeId() ); + if( nShapeIdx > 0 ) + return OUStringBuffer( aBaseName ).append( sal_Unicode( ' ' ) ).append( nShapeIdx ).makeStringAndClear(); + } + + return OUString(); +} + const ShapeType* ShapeBase::getChildTypeById( const OUString& ) const { return 0; @@ -262,15 +267,23 @@ Reference< XShape > ShapeBase::convertAndInsert( const Reference< XShapes >& rxS /* Calculate shape rectangle. Applications may do something special according to some imported shape client data (e.g. Excel cell anchor). */ Rectangle aShapeRect = calcShapeRectangle( pParentAnchor ); + // convert the shape, if the calculated rectangle is not empty if( ((aShapeRect.Width > 0) || (aShapeRect.Height > 0)) && rxShapes.is() ) { xShape = implConvertAndInsert( rxShapes, aShapeRect ); - /* Notify the drawing that a new shape has been inserted (but not - for children of group shapes). For convenience, pass the - rectangle that contains position and size of the shape. */ - if( !pParentAnchor && xShape.is() ) - mrDrawing.notifyShapeInserted( xShape, aShapeRect ); + if( xShape.is() ) + { + // set shape name (imported or generated) + PropertySet aShapeProp( xShape ); + aShapeProp.setProperty( PROP_Name, getShapeName() ); + + /* Notify the drawing that a new shape has been inserted. For + convenience, pass the rectangle that contains position and + size of the shape. */ + bool bGroupChild = pParentAnchor != 0; + mrDrawing.notifyXShapeInserted( xShape, aShapeRect, *this, bGroupChild ); + } } } return xShape; @@ -283,10 +296,12 @@ void ShapeBase::convertFormatting( const Reference< XShape >& rxShape, const Sha /* Calculate shape rectangle. Applications may do something special according to some imported shape client data (e.g. Excel cell anchor). */ Rectangle aShapeRect = calcShapeRectangle( pParentAnchor ); + // convert the shape, if the calculated rectangle is not empty if( (aShapeRect.Width > 0) || (aShapeRect.Height > 0) ) { - lclSetXShapeRect( rxShape, aShapeRect ); + rxShape->setPosition( Point( aShapeRect.X, aShapeRect.Y ) ); + rxShape->setSize( Size( aShapeRect.Width, aShapeRect.Height ) ); convertShapeProperties( rxShape ); } } @@ -299,7 +314,8 @@ Rectangle ShapeBase::calcShapeRectangle( const ShapeParentAnchor* pParentAnchor /* Calculate shape rectangle. Applications may do something special according to some imported shape client data (e.g. Excel cell anchor). */ Rectangle aShapeRect; - if( !maShapeModel.mxClientData.get() || !mrDrawing.convertShapeClientAnchor( aShapeRect, maShapeModel.mxClientData->maAnchor ) ) + const ClientData* pClientData = getClientData(); + if( !pClientData || !mrDrawing.convertClientAnchor( aShapeRect, pClientData->maAnchor ) ) aShapeRect = getRectangle( pParentAnchor ); return aShapeRect; } @@ -327,7 +343,7 @@ SimpleShape::SimpleShape( Drawing& rDrawing, const OUString& rService ) : Reference< XShape > SimpleShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const { - Reference< XShape > xShape = lclCreateAndInsertXShape( mrDrawing.getFilter(), rxShapes, maService, rShapeRect ); + Reference< XShape > xShape = mrDrawing.createAndInsertXShape( maService, rxShapes, rShapeRect ); convertShapeProperties( xShape ); return xShape; } @@ -386,7 +402,7 @@ Reference< XShape > CustomShape::implConvertAndInsert( const Reference< XShapes { // create the custom shape geometry Reference< XEnhancedCustomShapeDefaulter > xDefaulter( xShape, UNO_QUERY_THROW ); - xDefaulter->createCustomShapeDefaults( OUString::valueOf( maTypeModel.moShapeType.get( 0 ) ) ); + xDefaulter->createCustomShapeDefaults( OUString::valueOf( getShapeType() ) ); // convert common properties convertShapeProperties( xShape ); } @@ -406,11 +422,14 @@ ComplexShape::ComplexShape( Drawing& rDrawing ) : Reference< XShape > ComplexShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const { XmlFilterBase& rFilter = mrDrawing.getFilter(); + sal_Int32 nShapeType = getShapeType(); OUString aGraphicPath = getGraphicPath(); // try to find registered OLE object info if( const OleObjectInfo* pOleObjectInfo = mrDrawing.getOleObjectInfo( maTypeModel.maShapeId ) ) { + OSL_ENSURE( nShapeType == VML_SHAPETYPE_PICTUREFRAME, "ComplexShape::implConvertAndInsert - unexpected shape type" ); + // if OLE object is embedded into a DrawingML shape (PPTX), do not create it here if( pOleObjectInfo->mbDmlShape ) return Reference< XShape >(); @@ -419,7 +438,7 @@ Reference< XShape > ComplexShape::implConvertAndInsert( const Reference< XShapes Size aOleSize( rShapeRect.Width, rShapeRect.Height ); if( rFilter.getOleObjectHelper().importOleObject( aOleProps, *pOleObjectInfo, aOleSize ) ) { - Reference< XShape > xShape = lclCreateAndInsertXShape( rFilter, rxShapes, CREATE_OUSTRING( "com.sun.star.drawing.OLE2Shape" ), rShapeRect ); + Reference< XShape > xShape = mrDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.OLE2Shape" ), rxShapes, rShapeRect ); if( xShape.is() ) { // set the replacement graphic @@ -440,36 +459,40 @@ Reference< XShape > ComplexShape::implConvertAndInsert( const Reference< XShapes // try to find registered form control info const ControlInfo* pControlInfo = mrDrawing.getControlInfo( maTypeModel.maShapeId ); - if( pControlInfo && (pControlInfo->maFragmentPath.getLength() > 0) && (maTypeModel.maName.getLength() > 0) ) + if( pControlInfo && (pControlInfo->maFragmentPath.getLength() > 0) ) { - OSL_ENSURE( maTypeModel.maName == pControlInfo->maName, "ComplexShape::implConvertAndInsert - control name mismatch" ); - ::oox::ole::EmbeddedControl aControl( maTypeModel.maName ); - // load the control properties from fragment - if( rFilter.importFragment( new ::oox::ole::AxControlFragment( rFilter, pControlInfo->maFragmentPath, aControl ) ) ) try - { - // create control model and insert it into the form of the draw page - Reference< XControlModel > xCtrlModel( mrDrawing.getControlForm().convertAndInsert( aControl ), UNO_SET_THROW ); - if( maShapeModel.mxClientData.get() ) - mrDrawing.convertControlClientData( xCtrlModel, *maShapeModel.mxClientData ); - - // create the control shape, set control model at the shape - Reference< XShape > xShape = lclCreateAndInsertXShape( - rFilter, rxShapes, CREATE_OUSTRING( "com.sun.star.drawing.ControlShape" ), rShapeRect ); - Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY ); // do not throw, but always return the shape - if( xCtrlShape.is() ) - xCtrlShape->setControl( xCtrlModel ); - return xShape; - } - catch( Exception& ) + OSL_ENSURE( nShapeType == VML_SHAPETYPE_HOSTCONTROL, "ComplexShape::implConvertAndInsert - unexpected shape type" ); + OUString aShapeName = getShapeName(); + if( aShapeName.getLength() > 0 ) { + OSL_ENSURE( aShapeName == pControlInfo->maName, "ComplexShape::implConvertAndInsert - control name mismatch" ); + // load the control properties from fragment + ::oox::ole::EmbeddedControl aControl( aShapeName ); + if( rFilter.importFragment( new ::oox::ole::AxControlFragment( rFilter, pControlInfo->maFragmentPath, aControl ) ) ) + { + // create and return the control shape (including control model) + sal_Int32 nCtrlIndex = -1; + Reference< XShape > xShape = mrDrawing.createAndInsertXControlShape( aControl, rxShapes, rShapeRect, nCtrlIndex ); + // on error, proceed and try to create picture from replacement image + if( xShape.is() ) + return xShape; + } } - // on error, proceed and try to create picture from replacement image + } + + // host application wants to create the shape (do not try failed OLE controls again) + if( (nShapeType == VML_SHAPETYPE_HOSTCONTROL) && !pControlInfo ) + { + OSL_ENSURE( getClientData(), "ComplexShape::implConvertAndInsert - missing client data" ); + Reference< XShape > xShape = mrDrawing.createAndInsertClientXShape( *this, rxShapes, rShapeRect ); + if( xShape.is() ) + return xShape; } // try to create a picture object if( aGraphicPath.getLength() > 0 ) { - Reference< XShape > xShape = lclCreateAndInsertXShape( rFilter, rxShapes, CREATE_OUSTRING( "com.sun.star.drawing.GraphicObjectShape" ), rShapeRect ); + Reference< XShape > xShape = mrDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.GraphicObjectShape" ), rxShapes, rShapeRect ); if( xShape.is() ) { OUString aGraphicUrl = rFilter.getGraphicHelper().importEmbeddedGraphicObject( aGraphicPath ); @@ -525,7 +548,7 @@ Reference< XShape > GroupShape::implConvertAndInsert( const Reference< XShapes > aParentAnchor.maCoordSys = getCoordSystem(); if( !mxChildren->empty() && (aParentAnchor.maCoordSys.Width > 0) && (aParentAnchor.maCoordSys.Height > 0) ) try { - xGroupShape = lclCreateAndInsertXShape( mrDrawing.getFilter(), rxShapes, CREATE_OUSTRING( "com.sun.star.drawing.GroupShape" ), rShapeRect ); + xGroupShape = mrDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.GroupShape" ), rxShapes, rShapeRect ); Reference< XShapes > xChildShapes( xGroupShape, UNO_QUERY_THROW ); mxChildren->convertAndInsert( xChildShapes, &aParentAnchor ); // no child shape has been created - delete the group shape diff --git a/oox/source/vml/vmlshapecontext.cxx b/oox/source/vml/vmlshapecontext.cxx index ac08c1836c2d..d16644a7908a 100644 --- a/oox/source/vml/vmlshapecontext.cxx +++ b/oox/source/vml/vmlshapecontext.cxx @@ -27,8 +27,10 @@ #include "oox/vml/vmlshapecontext.hxx" +#include "oox/vml/vmldrawing.hxx" #include "oox/vml/vmlshape.hxx" #include "oox/vml/vmlshapecontainer.hxx" +#include "oox/vml/vmltextboxcontext.hxx" namespace oox { namespace vml { @@ -104,39 +106,101 @@ OptValue< DoublePair > lclDecodePercentPair( const AttributeList& rAttribs, sal_ bool lclDecodeVmlxBool( const OUString& rValue, bool bDefaultForEmpty ) { if( rValue.getLength() == 0 ) return bDefaultForEmpty; + sal_Int32 nToken = AttributeConversion::decodeToken( rValue ); // anything else than 't' or 'True' is considered to be false, as specified - return ((rValue.getLength() == 1) && (rValue[ 0 ] == 't')) || (rValue == CREATE_OUSTRING( "True" )); + return (nToken == XML_t) || (nToken == XML_True); } } // namespace // ============================================================================ -ShapeClientDataContext::ShapeClientDataContext( ContextHandler2Helper& rParent, - const AttributeList& rAttribs, ShapeClientData& rClientData ) : +ShapeLayoutContext::ShapeLayoutContext( ContextHandler2Helper& rParent, Drawing& rDrawing ) : + ContextHandler2( rParent ), + mrDrawing( rDrawing ) +{ +} + + +ContextHandlerRef ShapeLayoutContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + switch( nElement ) + { + case O_TOKEN( idmap ): + { + OUString aBlockIds = rAttribs.getString( XML_data, OUString() ); + sal_Int32 nIndex = 0; + while( nIndex >= 0 ) + { + OUString aToken = aBlockIds.getToken( 0, ' ', nIndex ).trim(); + if( aToken.getLength() > 0 ) + mrDrawing.registerBlockId( aToken.toInt32() ); + } + } + break; + } + return 0; +} + +// ============================================================================ + +ClientDataContext::ClientDataContext( ContextHandler2Helper& rParent, + ClientData& rClientData, const AttributeList& rAttribs ) : ContextHandler2( rParent ), mrClientData( rClientData ) { mrClientData.mnObjType = rAttribs.getToken( XML_ObjectType, XML_TOKEN_INVALID ); } -ContextHandlerRef ShapeClientDataContext::onCreateContext( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ ) +ContextHandlerRef ClientDataContext::onCreateContext( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ ) { - return isRootElement() ? this : 0; + if( isRootElement() ) + { + maElementText = OUString(); + return this; + } + return 0; } -void ShapeClientDataContext::onEndElement( const OUString& rChars ) +void ClientDataContext::onCharacters( const OUString& rChars ) +{ + /* Empty but existing elements have special meaning, e.g. 'true'. Collect + existing text and convert it in onEndElement(). */ + maElementText = rChars; +} + +void ClientDataContext::onEndElement() { switch( getCurrentElement() ) { - case VMLX_TOKEN( Anchor ): mrClientData.maAnchor = rChars; break; - case VMLX_TOKEN( FmlaPict ): mrClientData.maPictureLink = rChars; break; - case VMLX_TOKEN( FmlaLink ): mrClientData.maLinkedCell = rChars; break; - case VMLX_TOKEN( FmlaRange ): mrClientData.maSourceRange = rChars; break; - case VMLX_TOKEN( Column ): mrClientData.mnCol = rChars.toInt32(); break; - case VMLX_TOKEN( Row ): mrClientData.mnRow = rChars.toInt32(); break; - case VMLX_TOKEN( PrintObject ): mrClientData.mbPrintObject = lclDecodeVmlxBool( rChars, true ); break; - case VMLX_TOKEN( Visible ): mrClientData.mbVisible = true; break; + case VMLX_TOKEN( Anchor ): mrClientData.maAnchor = maElementText; break; + case VMLX_TOKEN( FmlaMacro ): mrClientData.maFmlaMacro = maElementText; break; + case VMLX_TOKEN( FmlaPict ): mrClientData.maFmlaPict = maElementText; break; + case VMLX_TOKEN( FmlaLink ): mrClientData.maFmlaLink = maElementText; break; + case VMLX_TOKEN( FmlaRange ): mrClientData.maFmlaRange = maElementText; break; + case VMLX_TOKEN( FmlaGroup ): mrClientData.maFmlaGroup = maElementText; break; + case VMLX_TOKEN( TextHAlign ): mrClientData.mnTextHAlign = AttributeConversion::decodeToken( maElementText ); break; + case VMLX_TOKEN( TextVAlign ): mrClientData.mnTextVAlign = AttributeConversion::decodeToken( maElementText ); break; + case VMLX_TOKEN( Column ): mrClientData.mnCol = maElementText.toInt32(); break; + case VMLX_TOKEN( Row ): mrClientData.mnRow = maElementText.toInt32(); break; + case VMLX_TOKEN( Checked ): mrClientData.mnChecked = maElementText.toInt32(); break; + case VMLX_TOKEN( DropStyle ): mrClientData.mnDropStyle = AttributeConversion::decodeToken( maElementText ); break; + case VMLX_TOKEN( DropLines ): mrClientData.mnDropLines = maElementText.toInt32(); break; + case VMLX_TOKEN( Val ): mrClientData.mnVal = maElementText.toInt32(); break; + case VMLX_TOKEN( Min ): mrClientData.mnMin = maElementText.toInt32(); break; + case VMLX_TOKEN( Max ): mrClientData.mnMax = maElementText.toInt32(); break; + case VMLX_TOKEN( Inc ): mrClientData.mnInc = maElementText.toInt32(); break; + case VMLX_TOKEN( Page ): mrClientData.mnPage = maElementText.toInt32(); break; + case VMLX_TOKEN( SelType ): mrClientData.mnSelType = AttributeConversion::decodeToken( maElementText ); break; + case VMLX_TOKEN( VTEdit ): mrClientData.mnVTEdit = maElementText.toInt32(); break; + case VMLX_TOKEN( PrintObject ): mrClientData.mbPrintObject = lclDecodeVmlxBool( maElementText, true ); break; + case VMLX_TOKEN( Visible ): mrClientData.mbVisible = lclDecodeVmlxBool( maElementText, true ); break; + case VMLX_TOKEN( DDE ): mrClientData.mbDde = lclDecodeVmlxBool( maElementText, true ); break; + case VMLX_TOKEN( NoThreeD ): mrClientData.mbNo3D = lclDecodeVmlxBool( maElementText, true ); break; + case VMLX_TOKEN( NoThreeD2 ): mrClientData.mbNo3D2 = lclDecodeVmlxBool( maElementText, true ); break; + case VMLX_TOKEN( MultiLine ): mrClientData.mbMultiLine = lclDecodeVmlxBool( maElementText, true ); break; + case VMLX_TOKEN( VScroll ): mrClientData.mbVScroll = lclDecodeVmlxBool( maElementText, true ); break; + case VMLX_TOKEN( SecretEdit ): mrClientData.mbSecretEdit = lclDecodeVmlxBool( maElementText, true ); break; } } @@ -148,23 +212,26 @@ ShapeContextBase::ShapeContextBase( ContextHandler2Helper& rParent ) : } /*static*/ ContextHandlerRef ShapeContextBase::createShapeContext( ContextHandler2Helper& rParent, - sal_Int32 nElement, const AttributeList& rAttribs, ShapeContainer& rShapes ) + ShapeContainer& rShapes, sal_Int32 nElement, const AttributeList& rAttribs ) { switch( nElement ) { + case O_TOKEN( shapelayout ): + return new ShapeLayoutContext( rParent, rShapes.getDrawing() ); + case VML_TOKEN( shapetype ): - return new ShapeTypeContext( rParent, rAttribs, rShapes.createShapeType() ); + return new ShapeTypeContext( rParent, rShapes.createShapeType(), rAttribs ); case VML_TOKEN( group ): - return new GroupShapeContext( rParent, rAttribs, rShapes.createShape< GroupShape >() ); + return new GroupShapeContext( rParent, rShapes.createShape< GroupShape >(), rAttribs ); case VML_TOKEN( shape ): - return new ShapeContext( rParent, rAttribs, rShapes.createShape< ComplexShape >() ); + return new ShapeContext( rParent, rShapes.createShape< ComplexShape >(), rAttribs ); case VML_TOKEN( rect ): case VML_TOKEN( roundrect ): - return new ShapeContext( rParent, rAttribs, rShapes.createShape< RectangleShape >() ); + return new ShapeContext( rParent, rShapes.createShape< RectangleShape >(), rAttribs ); case VML_TOKEN( oval ): - return new ShapeContext( rParent, rAttribs, rShapes.createShape< EllipseShape >() ); + return new ShapeContext( rParent, rShapes.createShape< EllipseShape >(), rAttribs ); case VML_TOKEN( polyline ): - return new ShapeContext( rParent, rAttribs, rShapes.createShape< PolyLineShape >() ); + return new ShapeContext( rParent, rShapes.createShape< PolyLineShape >(), rAttribs ); // TODO: case VML_TOKEN( arc ): @@ -172,14 +239,14 @@ ShapeContextBase::ShapeContextBase( ContextHandler2Helper& rParent ) : case VML_TOKEN( line ): case VML_TOKEN( diagram ): case VML_TOKEN( image ): - return new ShapeContext( rParent, rAttribs, rShapes.createShape< ComplexShape >() ); + return new ShapeContext( rParent, rShapes.createShape< ComplexShape >(), rAttribs ); } return false; } // ============================================================================ -ShapeTypeContext::ShapeTypeContext( ContextHandler2Helper& rParent, const AttributeList& rAttribs, ShapeType& rShapeType ) : +ShapeTypeContext::ShapeTypeContext( ContextHandler2Helper& rParent, ShapeType& rShapeType, const AttributeList& rAttribs ) : ShapeContextBase( rParent ), mrTypeModel( rShapeType.getTypeModel() ) { @@ -189,7 +256,7 @@ ShapeTypeContext::ShapeTypeContext( ContextHandler2Helper& rParent, const Attrib OSL_ENSURE( mrTypeModel.maShapeId.getLength() > 0, "ShapeTypeContext::ShapeTypeContext - missing shape identifier" ); // if the o:spid attribute exists, the id attribute contains the user-defined shape name if( bHasOspid ) - mrTypeModel.maName = rAttribs.getXString( XML_id, OUString() ); + mrTypeModel.maShapeName = rAttribs.getXString( XML_id, OUString() ); // builtin shape type identifier mrTypeModel.moShapeType = rAttribs.getInteger( O_TOKEN( spt ) ); @@ -280,8 +347,8 @@ void ShapeTypeContext::setStyle( const OUString& rStyle ) // ============================================================================ -ShapeContext::ShapeContext( ContextHandler2Helper& rParent, const AttributeList& rAttribs, ShapeBase& rShape ) : - ShapeTypeContext( rParent, rAttribs, rShape ), +ShapeContext::ShapeContext( ContextHandler2Helper& rParent, ShapeBase& rShape, const AttributeList& rAttribs ) : + ShapeTypeContext( rParent, rShape, rAttribs ), mrShapeModel( rShape.getShapeModel() ) { // collect shape specific attributes @@ -293,8 +360,13 @@ ShapeContext::ShapeContext( ContextHandler2Helper& rParent, const AttributeList& ContextHandlerRef ShapeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) { // Excel specific shape client data - if( isRootElement() && (nElement == VMLX_TOKEN( ClientData )) ) - return new ShapeClientDataContext( *this, rAttribs, mrShapeModel.createClientData() ); + if( isRootElement() ) switch( nElement ) + { + case VML_TOKEN( textbox ): + return new TextBoxContext( *this, mrShapeModel.createTextBox(), rAttribs ); + case VMLX_TOKEN( ClientData ): + return new ClientDataContext( *this, mrShapeModel.createClientData(), rAttribs ); + } // handle remaining stuff in base class return ShapeTypeContext::onCreateContext( nElement, rAttribs ); } @@ -313,8 +385,8 @@ void ShapeContext::setPoints( const OUString& rPoints ) // ============================================================================ -GroupShapeContext::GroupShapeContext( ContextHandler2Helper& rParent, const AttributeList& rAttribs, GroupShape& rShape ) : - ShapeContext( rParent, rAttribs, rShape ), +GroupShapeContext::GroupShapeContext( ContextHandler2Helper& rParent, GroupShape& rShape, const AttributeList& rAttribs ) : + ShapeContext( rParent, rShape, rAttribs ), mrShapes( rShape.getChildren() ) { } @@ -322,7 +394,7 @@ GroupShapeContext::GroupShapeContext( ContextHandler2Helper& rParent, const Attr ContextHandlerRef GroupShapeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) { // try to create a context of an embedded shape - ContextHandlerRef xContext = createShapeContext( *this, nElement, rAttribs, mrShapes ); + ContextHandlerRef xContext = createShapeContext( *this, mrShapes, nElement, rAttribs ); // handle remaining stuff of this shape in base class return xContext.get() ? xContext : ShapeContext::onCreateContext( nElement, rAttribs ); } diff --git a/oox/source/vml/vmltextbox.cxx b/oox/source/vml/vmltextbox.cxx new file mode 100755 index 000000000000..e684490f39af --- /dev/null +++ b/oox/source/vml/vmltextbox.cxx @@ -0,0 +1,82 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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/vml/vmltextbox.hxx" + +#include <rtl/ustrbuf.hxx> +#include "tokens.hxx" + +namespace oox { +namespace vml { + +// ============================================================================ + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +// ============================================================================ + +TextFontModel::TextFontModel() +{ +} + +// ============================================================================ + +TextPortionModel::TextPortionModel( const TextFontModel& rFont, const OUString& rText ) : + maFont( rFont ), + maText( rText ) +{ +} + +// ============================================================================ + +TextBox::TextBox() +{ +} + +void TextBox::appendPortion( const TextFontModel& rFont, const OUString& rText ) +{ + maPortions.push_back( TextPortionModel( rFont, rText ) ); +} + +const TextFontModel* TextBox::getFirstFont() const +{ + return maPortions.empty() ? 0 : &maPortions.front().maFont; +} + +OUString TextBox::getText() const +{ + OUStringBuffer aBuffer; + for( PortionVector::const_iterator aIt = maPortions.begin(), aEnd = maPortions.end(); aIt != aEnd; ++aIt ) + aBuffer.append( aIt->maText ); + return aBuffer.makeStringAndClear(); +} + +// ============================================================================ + +} // namespace vml +} // namespace oox diff --git a/oox/source/vml/vmltextboxcontext.cxx b/oox/source/vml/vmltextboxcontext.cxx new file mode 100755 index 000000000000..4b57656b4f78 --- /dev/null +++ b/oox/source/vml/vmltextboxcontext.cxx @@ -0,0 +1,146 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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/vml/vmltextboxcontext.hxx" + +namespace oox { +namespace vml { + +// ============================================================================ + +using ::oox::core::ContextHandler2; +using ::oox::core::ContextHandler2Helper; +using ::oox::core::ContextHandlerRef; +using ::rtl::OUString; + +// ============================================================================ + +TextPortionContext::TextPortionContext( ContextHandler2Helper& rParent, + TextBox& rTextBox, const TextFontModel& rParentFont, + sal_Int32 nElement, const AttributeList& rAttribs ) : + ContextHandler2( rParent ), + mrTextBox( rTextBox ), + maFont( rParentFont ), + mnInitialPortions( rTextBox.getPortionCount() ) +{ + switch( nElement ) + { + case XML_font: + maFont.moName = rAttribs.getXString( XML_face ); + maFont.moColor = rAttribs.getXString( XML_color ); + maFont.monSize = rAttribs.getInteger( XML_size ); + break; + case XML_u: + OSL_ENSURE( !maFont.monUnderline, "TextPortionContext::TextPortionContext - nested <u> elements" ); + maFont.monUnderline = (rAttribs.getToken( XML_class, XML_TOKEN_INVALID ) == XML_font4) ? XML_double : XML_single; + break; + case XML_sub: + case XML_sup: + OSL_ENSURE( !maFont.monEscapement, "TextPortionContext::TextPortionContext - nested <sub> or <sup> elements" ); + maFont.monEscapement = nElement; + break; + case XML_b: + OSL_ENSURE( !maFont.mobBold, "TextPortionContext::TextPortionContext - nested <b> elements" ); + maFont.mobBold = true; + break; + case XML_i: + OSL_ENSURE( !maFont.mobItalic, "TextPortionContext::TextPortionContext - nested <i> elements" ); + maFont.mobItalic = true; + break; + case XML_s: + OSL_ENSURE( !maFont.mobStrikeout, "TextPortionContext::TextPortionContext - nested <s> elements" ); + maFont.mobStrikeout = true; + break; + default: + OSL_ENSURE( false, "TextPortionContext::TextPortionContext - unknown element" ); + } +} + +ContextHandlerRef TextPortionContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + OSL_ENSURE( nElement != XML_font, "TextPortionContext::onCreateContext - nested <font> elements" ); + return new TextPortionContext( *this, mrTextBox, maFont, nElement, rAttribs ); +} + +void TextPortionContext::onCharacters( const OUString& rChars ) +{ + switch( getCurrentElement() ) + { + case XML_span: + // replace all NBSP characters with SP + mrTextBox.appendPortion( maFont, rChars.replace( 160, ' ' ) ); + break; + default: + mrTextBox.appendPortion( maFont, rChars ); + } +} + +void TextPortionContext::onEndElement() +{ + /* An empty child element without own child elements represents a single + space character, for example: + + <font> + <i>abc</i> + <font></font> + <b>def</b> + </font> + + represents the italic text 'abc', an unformatted space character, and + the bold text 'def'. + */ + if( (mnInitialPortions > 0) && (mrTextBox.getPortionCount() == mnInitialPortions) ) + mrTextBox.appendPortion( maFont, OUString( sal_Unicode( ' ' ) ) ); +} + +// ============================================================================ + +TextBoxContext::TextBoxContext( ContextHandler2Helper& rParent, TextBox& rTextBox, const AttributeList& /*rAttribs*/ ) : + ContextHandler2( rParent ), + mrTextBox( rTextBox ) +{ +} + +ContextHandlerRef TextBoxContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + switch( getCurrentElement() ) + { + case VML_TOKEN( textbox ): + if( nElement == XML_div ) return this; + break; + case XML_div: + if( nElement == XML_font ) return new TextPortionContext( *this, mrTextBox, TextFontModel(), nElement, rAttribs ); + break; + } + return 0; +} + +// ============================================================================ + +} // namespace vml +} // namespace oox + |