/* -*- 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 "svgfilter.hxx" #include "svgfontexport.hxx" #include "svgwriter.hxx" #include #include #include #include #include #include using ::rtl::OUString; // ----------- // - statics - // ----------- static const char aXMLElemG[] = "g"; static const char aXMLElemDefs[] = "defs"; static const char aXMLElemLine[] = "line"; static const char aXMLElemRect[] = "rect"; static const char aXMLElemEllipse[] = "ellipse"; static const char aXMLElemPath[] = "path"; static const char aXMLElemText[] = "text"; static const char aXMLElemTspan[] = "tspan"; static const char aXMLElemImage[] = "image"; static const char aXMLElemMask[] = "mask"; static const char aXMLElemPattern[] = "pattern"; static const char aXMLElemLinearGradient[] = "linearGradient"; static const char aXMLElemRadialGradient[] = "radialGradient"; static const char aXMLElemStop[] = "stop"; // ----------------------------------------------------------------------------- static const char aXMLAttrTransform[] = "transform"; static const char aXMLAttrStyle[] = "style"; static const char aXMLAttrId[] = "id"; static const char aXMLAttrDX[] = "dx"; static const char aXMLAttrDY[] = "dy"; static const char aXMLAttrD[] = "d"; static const char aXMLAttrX[] = "x"; static const char aXMLAttrY[] = "y"; static const char aXMLAttrX1[] = "x1"; static const char aXMLAttrY1[] = "y1"; static const char aXMLAttrX2[] = "x2"; static const char aXMLAttrY2[] = "y2"; static const char aXMLAttrCX[] = "cx"; static const char aXMLAttrCY[] = "cy"; static const char aXMLAttrR[] = "r"; static const char aXMLAttrRX[] = "rx"; static const char aXMLAttrRY[] = "ry"; static const char aXMLAttrWidth[] = "width"; static const char aXMLAttrHeight[] = "height"; static const char aXMLAttrStroke[] = "stroke"; static const char aXMLAttrStrokeOpacity[] = "stroke-opacity"; static const char aXMLAttrStrokeWidth[] = "stroke-width"; static const char aXMLAttrStrokeDashArray[] = "stroke-dasharray"; static const char aXMLAttrFill[] = "fill"; static const char aXMLAttrFillOpacity[] = "fill-opacity"; static const char aXMLAttrFontFamily[] = "font-family"; static const char aXMLAttrFontSize[] = "font-size"; static const char aXMLAttrFontStyle[] = "font-style"; static const char aXMLAttrFontWeight[] = "font-weight"; static const char aXMLAttrTextDecoration[] = "text-decoration"; static const char aXMLAttrXLinkHRef[] = "xlink:href"; static const char aXMLAttrGradientUnits[] = "gradientUnits"; static const char aXMLAttrPatternUnits[] = "patternUnits"; static const char aXMLAttrOffset[] = "offset"; static const char aXMLAttrStopColor[] = "stop-color"; static const char aXMLAttrStrokeLinejoin[] = "stroke-linejoin"; static const char aXMLAttrStrokeLinecap[] = "stroke-linecap"; #define NSPREFIX "ooo:" static const char aOOOAttrNumberingType[] = NSPREFIX "numbering-type"; static sal_Char const XML_UNO_NAME_NRULE_NUMBERINGTYPE[] = "NumberingType"; static sal_Char const XML_UNO_NAME_NRULE_BULLET_CHAR[] = "BulletChar"; // ----------------------------------------------------------------------------- // ---------------------- // - SVGAttributeWriter - // ---------------------- SVGAttributeWriter::SVGAttributeWriter( SVGExport& rExport, SVGFontExport& rFontExport ) : mrExport( rExport ), mrFontExport( rFontExport ), mpElemFont( NULL ), mpElemPaint( NULL ) { } // ----------------------------------------------------------------------------- SVGAttributeWriter::~SVGAttributeWriter() { if( mpElemPaint ) delete mpElemPaint; if( mpElemFont ) delete mpElemFont; } // ----------------------------------------------------------------------------- double SVGAttributeWriter::ImplRound( double fValue, sal_Int32 nDecs ) { return( floor( fValue * pow( 10.0, (int)nDecs ) + 0.5 ) / pow( 10.0, (int)nDecs ) ); } // ----------------------------------------------------------------------------- void SVGAttributeWriter::ImplGetColorStr( const Color& rColor, ::rtl::OUString& rColorStr ) { if( rColor.GetTransparency() == 255 ) rColorStr = B2UCONST( "none" ); else { ::rtl::OUStringBuffer aStyle; aStyle.appendAscii( "rgb(" ); aStyle.append( (sal_Int32) rColor.GetRed() ); aStyle.appendAscii( "," ); aStyle.append( (sal_Int32) rColor.GetGreen() ); aStyle.appendAscii( "," ); aStyle.append( (sal_Int32) rColor.GetBlue() ); aStyle.appendAscii( ")" ); rColorStr = aStyle.makeStringAndClear(); } } // ----------------------------------------------------------------------------- void SVGAttributeWriter::AddColorAttr( const char* pColorAttrName, const char* pColorOpacityAttrName, const Color& rColor ) { ::rtl::OUString aColor, aColorOpacity; ImplGetColorStr( rColor, aColor ); if( rColor.GetTransparency() > 0 && rColor.GetTransparency() < 255 ) aColorOpacity = ::rtl::OUString::valueOf( ImplRound( ( 255.0 - rColor.GetTransparency() ) / 255.0 ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, pColorAttrName, aColor ); if( !aColorOpacity.isEmpty() && mrExport.IsUseOpacity() ) mrExport.AddAttribute( XML_NAMESPACE_NONE, pColorOpacityAttrName, aColorOpacity ); } // ----------------------------------------------------------------------------- void SVGAttributeWriter::AddPaintAttr( const Color& rLineColor, const Color& rFillColor, const Rectangle* pObjBoundRect, const Gradient* pFillGradient ) { // Fill if( pObjBoundRect && pFillGradient ) { ::rtl::OUString aGradientId; AddGradientDef( *pObjBoundRect, *pFillGradient, aGradientId ); if( !aGradientId.isEmpty() ) { ::rtl::OUString aGradientURL( B2UCONST( "url(#" ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFill, ( aGradientURL += aGradientId ) += B2UCONST( ")" ) ); } } else AddColorAttr( aXMLAttrFill, aXMLAttrFillOpacity, rFillColor ); // Stroke AddColorAttr( aXMLAttrStroke, aXMLAttrStrokeOpacity, rLineColor ); } // ----------------------------------------------------------------------------- void SVGAttributeWriter::AddGradientDef( const Rectangle& rObjRect, const Gradient& rGradient, ::rtl::OUString& rGradientId ) { if( rObjRect.GetWidth() && rObjRect.GetHeight() && ( rGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL || rGradient.GetStyle() == GradientStyle_RADIAL || rGradient.GetStyle() == GradientStyle_ELLIPTICAL ) ) { SvXMLElementExport aDesc( mrExport, XML_NAMESPACE_NONE, aXMLElemDefs, sal_True, sal_True ); Color aStartColor( rGradient.GetStartColor() ), aEndColor( rGradient.GetEndColor() ); sal_uInt16 nAngle = rGradient.GetAngle() % 3600; Point aObjRectCenter( rObjRect.Center() ); Polygon aPoly( rObjRect ); static sal_Int32 nCurGradientId = 1; aPoly.Rotate( aObjRectCenter, nAngle ); Rectangle aRect( aPoly.GetBoundRect() ); // adjust start/end colors with intensities aStartColor.SetRed( (sal_uInt8)( (long) aStartColor.GetRed() * rGradient.GetStartIntensity() ) / 100 ); aStartColor.SetGreen( (sal_uInt8)( (long) aStartColor.GetGreen() * rGradient.GetStartIntensity() ) / 100 ); aStartColor.SetBlue( (sal_uInt8)( (long) aStartColor.GetBlue() * rGradient.GetStartIntensity() ) / 100 ); aEndColor.SetRed( (sal_uInt8)( (long) aEndColor.GetRed() * rGradient.GetEndIntensity() ) / 100 ); aEndColor.SetGreen( (sal_uInt8)( (long) aEndColor.GetGreen() * rGradient.GetEndIntensity() ) / 100 ); aEndColor.SetBlue( (sal_uInt8)( (long) aEndColor.GetBlue() * rGradient.GetEndIntensity() ) / 100 ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, ( rGradientId = B2UCONST( "Gradient_" ) ) += ::rtl::OUString::valueOf( nCurGradientId++ ) ); { ::std::auto_ptr< SvXMLElementExport > apGradient; ::rtl::OUString aColorStr; if( rGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL ) { Polygon aLinePoly( 2 ); aLinePoly[ 0 ] = Point( aObjRectCenter.X(), aRect.Top() ); aLinePoly[ 1 ] = Point( aObjRectCenter.X(), aRect.Bottom() ); aLinePoly.Rotate( aObjRectCenter, nAngle ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrGradientUnits, B2UCONST( "userSpaceOnUse" ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX1, ::rtl::OUString::valueOf( aLinePoly[ 0 ].X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY1, ::rtl::OUString::valueOf( aLinePoly[ 0 ].Y() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX2, ::rtl::OUString::valueOf( aLinePoly[ 1 ].X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY2, ::rtl::OUString::valueOf( aLinePoly[ 1 ].Y() ) ); apGradient.reset( new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemLinearGradient, sal_True, sal_True ) ); // write stop values double fBorder = static_cast< double >( rGradient.GetBorder() ) * ( ( rGradient.GetStyle() == GradientStyle_AXIAL ) ? 0.005 : 0.01 ); ImplGetColorStr( ( rGradient.GetStyle() == GradientStyle_AXIAL ) ? aEndColor : aStartColor, aColorStr ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, ::rtl::OUString::valueOf( fBorder ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc2( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, sal_True, sal_True ); } if( rGradient.GetStyle() == GradientStyle_AXIAL ) { ImplGetColorStr( aStartColor, aColorStr ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, ::rtl::OUString::valueOf( 0.5 ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc3( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, sal_True, sal_True ); } } if( rGradient.GetStyle() != GradientStyle_AXIAL ) fBorder = 0.0; ImplGetColorStr( aEndColor, aColorStr ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, ::rtl::OUString::valueOf( ImplRound( 1.0 - fBorder ) ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc4( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, sal_True, sal_True ); } } else { const double fCenterX = rObjRect.Left() + rObjRect.GetWidth() * rGradient.GetOfsX() * 0.01; const double fCenterY = rObjRect.Top() + rObjRect.GetHeight() * rGradient.GetOfsY() * 0.01; const double fRadius = sqrt( static_cast< double >( rObjRect.GetWidth() ) * rObjRect.GetWidth() + rObjRect.GetHeight() * rObjRect.GetHeight() ) * 0.5; mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrGradientUnits, B2UCONST( "userSpaceOnUse" ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrCX, ::rtl::OUString::valueOf( ImplRound( fCenterX ) ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrCY, ::rtl::OUString::valueOf( ImplRound( fCenterY ) ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrR, ::rtl::OUString::valueOf( ImplRound( fRadius ) ) ); apGradient.reset( new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemRadialGradient, sal_True, sal_True ) ); // write stop values ImplGetColorStr( aEndColor, aColorStr ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, ::rtl::OUString::valueOf( 0.0 ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc5( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, sal_True, sal_True ); } ImplGetColorStr( aStartColor, aColorStr ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, ::rtl::OUString::valueOf( ImplRound( 1.0 - rGradient.GetBorder() * 0.01 ) ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc6( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, sal_True, sal_True ); } } } } else rGradientId = ::rtl::OUString(); } // ----------------------------------------------------------------------------- void SVGAttributeWriter::SetFontAttr( const Font& rFont ) { if( rFont != maCurFont ) { ::rtl::OUString aFontStyle, aFontWeight, aTextDecoration; sal_Int32 nFontWeight; maCurFont = rFont; // Font Family setFontFamily(); // Font Size mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontSize, ::rtl::OUString::valueOf( rFont.GetHeight() ) + B2UCONST( "px" ) ); // Font Style if( rFont.GetItalic() != ITALIC_NONE ) { if( rFont.GetItalic() == ITALIC_OBLIQUE ) aFontStyle = B2UCONST( "oblique" ); else aFontStyle = B2UCONST( "italic" ); } else aFontStyle = B2UCONST( "normal" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontStyle, aFontStyle ); // Font Weight switch( rFont.GetWeight() ) { case WEIGHT_THIN: nFontWeight = 100; break; case WEIGHT_ULTRALIGHT: nFontWeight = 200; break; case WEIGHT_LIGHT: nFontWeight = 300; break; case WEIGHT_SEMILIGHT: nFontWeight = 400; break; case WEIGHT_NORMAL: nFontWeight = 400; break; case WEIGHT_MEDIUM: nFontWeight = 500; break; case WEIGHT_SEMIBOLD: nFontWeight = 600; break; case WEIGHT_BOLD: nFontWeight = 700; break; case WEIGHT_ULTRABOLD: nFontWeight = 800; break; case WEIGHT_BLACK: nFontWeight = 900; break; default: nFontWeight = 400; break; } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontWeight, ::rtl::OUString::valueOf( nFontWeight ) ); if( mrExport.IsUseNativeTextDecoration() ) { if( rFont.GetUnderline() != UNDERLINE_NONE || rFont.GetStrikeout() != STRIKEOUT_NONE ) { if( rFont.GetUnderline() != UNDERLINE_NONE ) aTextDecoration = B2UCONST( "underline " ); if( rFont.GetStrikeout() != STRIKEOUT_NONE ) aTextDecoration += B2UCONST( "line-through " ); } else aTextDecoration = B2UCONST( "none" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTextDecoration, aTextDecoration ); } startFontSettings(); } } void SVGAttributeWriter::startFontSettings() { endFontSettings(); if( mrExport.IsUsePositionedCharacters() ) { mpElemFont = new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); } else { mpElemFont = new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, sal_True, sal_True ); } } void SVGAttributeWriter::endFontSettings() { if( mpElemFont ) { delete mpElemFont; mpElemFont = NULL; } } void SVGAttributeWriter::setFontFamily() { if( mrExport.IsUsePositionedCharacters() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontFamily, mrFontExport.GetMappedFontName( maCurFont.GetName() ) ); } else { sal_Int32 nNextTokenPos( 0 ); const ::rtl::OUString& rsFontName = maCurFont.GetName(); ::rtl::OUString sFontFamily( rsFontName.getToken( 0, ';', nNextTokenPos ) ); FontPitch ePitch = maCurFont.GetPitch(); if( ePitch == PITCH_FIXED ) { sFontFamily += B2UCONST( ", monospace" ); } else { FontFamily eFamily = maCurFont.GetFamily(); if( eFamily == FAMILY_ROMAN ) sFontFamily += B2UCONST( ", serif" ); else if( eFamily == FAMILY_SWISS ) sFontFamily += B2UCONST( ", sans-serif" ); } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontFamily, sFontFamily ); } } // ------------------- // - SVGTextWriter - // ------------------- SVGTextWriter::SVGTextWriter( SVGExport& rExport, SVGFontExport& rFontExport ) : mrExport( rExport ), mrFontExport( rFontExport ), mpContext( NULL ), mpVDev( NULL ), mbIsTextShapeStarted( sal_False ), mrTextShape(), msShapeId(), mrParagraphEnumeration(), mrCurrentTextParagraph(), mrTextPortionEnumeration(), mrCurrentTextPortion(), mpTextEmbeddedBitmapMtf( NULL ), mpTargetMapMode( NULL ), mpTextShapeElem( NULL ), mpTextParagraphElem( NULL ), mpTextPositionElem( NULL ), mnLeftTextPortionLength( 0 ), maTextPos(0,0), mnTextWidth(0), mbPositioningNeeded( sal_False ), mbIsNewListItem( sal_False ), maBulletListItemMap(), mbIsListLevelStyleImage( sal_False ), mbLineBreak( sal_False ), mbIsURLField( sal_False ), msUrl(), mbIsPlacehlolderShape( sal_False ), mbIWS( sal_False ), maCurrentFont(), maParentFont() { } // ----------------------------------------------------------------------------- SVGTextWriter::~SVGTextWriter() { endTextParagraph(); } // ----------------------------------------------------------------------------- void SVGTextWriter::implRegisterInterface( const Reference< XInterface >& rxIf ) { if( rxIf.is() ) (mrExport.getInterfaceToIdentifierMapper()).registerReference( rxIf ); } // ----------------------------------------------------------------------------- const ::rtl::OUString & SVGTextWriter::implGetValidIDFromInterface( const Reference< XInterface >& rxIf ) { return (mrExport.getInterfaceToIdentifierMapper()).getIdentifier( rxIf ); } // ----------------------------------------------------------------------------- void SVGTextWriter::implMap( const Size& rSz, Size& rDstSz ) const { if( mpVDev && mpTargetMapMode ) rDstSz = mpVDev->LogicToLogic( rSz, mpVDev->GetMapMode(), *mpTargetMapMode ); else OSL_FAIL( "SVGTextWriter::implMap: invalid virtual device or map mode." ); } // ----------------------------------------------------------------------------- void SVGTextWriter::implMap( const Point& rPt, Point& rDstPt ) const { if( mpVDev && mpTargetMapMode ) rDstPt = mpVDev->LogicToLogic( rPt, mpVDev->GetMapMode(), *mpTargetMapMode ); else OSL_FAIL( "SVGTextWriter::implMap: invalid virtual device or map mode." ); } // ----------------------------------------------------------------------------- void SVGTextWriter::implSetCurrentFont() { if( mpVDev ) { maCurrentFont = mpVDev->GetFont(); Size aSz; implMap( Size( 0, maCurrentFont.GetHeight() ), aSz ); maCurrentFont.SetHeight( aSz.Height() ); } else { OSL_FAIL( "SVGTextWriter::implSetCorrectFontHeight: invalid virtual device." ); } } // ----------------------------------------------------------------------------- template< typename SubType > sal_Bool SVGTextWriter::implGetTextPosition( const MetaAction* pAction, Point& raPos, sal_Bool& rbEmpty ) { const SubType* pA = (const SubType*) pAction; sal_uInt16 nLength = pA->GetLen(); rbEmpty = ( nLength == 0 ); if( !rbEmpty ) { raPos = pA->GetPoint(); return sal_True; } return sal_False; } // ----------------------------------------------------------------------------- template<> sal_Bool SVGTextWriter::implGetTextPosition( const MetaAction* pAction, Point& raPos, sal_Bool& rbEmpty ) { const MetaTextRectAction* pA = (const MetaTextRectAction*) pAction; sal_uInt16 nLength = pA->GetText().getLength(); rbEmpty = ( nLength == 0 ); if( !rbEmpty ) { raPos = pA->GetRect().TopLeft(); return sal_True; } return sal_False; } // ----------------------------------------------------------------------------- template< typename SubType > sal_Bool SVGTextWriter::implGetTextPositionFromBitmap( const MetaAction* pAction, Point& raPos, sal_Bool& rbEmpty ) { const SubType* pA = (const SubType*) pAction; raPos = pA->GetPoint(); rbEmpty = sal_False; return sal_True; } // ----------------------------------------------------------------------------- /** setTextPosition * Set the start position of the next line of text. In case no text is found * the current action index is updated to the index value we reached while * searching for text. * * @returns {sal_Int32} * -2 if no text found and end of line is reached * -1 if no text found and end of paragraph is reached * 0 if no text found and end of text shape is reached * 1 if text found! */ sal_Int32 SVGTextWriter::setTextPosition( const GDIMetaFile& rMtf, sal_uLong& nCurAction ) { Point aPos; sal_uLong nCount = rMtf.GetActionSize(); sal_Bool bEOL = sal_False; sal_Bool bEOP = sal_False; sal_Bool bETS = sal_False; sal_Bool bConfigured = sal_False; sal_Bool bEmpty = sal_True; sal_uLong nActionIndex = nCurAction + 1; for( ; nActionIndex < nCount; ++nActionIndex ) { const MetaAction* pAction = rMtf.GetAction( nActionIndex ); const sal_uInt16 nType = pAction->GetType(); switch( nType ) { case( META_TEXT_ACTION ): { bConfigured = implGetTextPosition( pAction, aPos, bEmpty ); } break; case( META_TEXTRECT_ACTION ): { bConfigured = implGetTextPosition( pAction, aPos, bEmpty ); } break; case( META_TEXTARRAY_ACTION ): { bConfigured = implGetTextPosition( pAction, aPos, bEmpty ); } break; case( META_STRETCHTEXT_ACTION ): { bConfigured = implGetTextPosition( pAction, aPos, bEmpty ); } break; case( META_BMPSCALE_ACTION ): { bConfigured = implGetTextPositionFromBitmap( pAction, aPos, bEmpty ); } break; case( META_BMPEXSCALE_ACTION ): { bConfigured = implGetTextPositionFromBitmap( pAction, aPos, bEmpty ); } break; // If we reach the end of the current line, paragraph or text shape // without finding any text we stop searching case( META_COMMENT_ACTION ): { const MetaCommentAction* pA = (const MetaCommentAction*) pAction; const ::rtl::OString& rsComment = pA->GetComment(); if( rsComment.equalsIgnoreAsciiCaseL( RTL_CONSTASCII_STRINGPARAM( "XTEXT_EOL" ) ) ) { bEOL = true; } else if( rsComment.equalsIgnoreAsciiCaseL( RTL_CONSTASCII_STRINGPARAM( "XTEXT_EOP" ) ) ) { bEOP = true; ::rtl::OUString sContent; while( nextTextPortion() ) { sContent = mrCurrentTextPortion->getString(); if( sContent.isEmpty() ) { continue; } else { if( sContent.equalsAscii( "\n" ) ) mbLineBreak = sal_True; } } if( nextParagraph() ) { while( nextTextPortion() ) { sContent = mrCurrentTextPortion->getString(); if( sContent.isEmpty() ) { continue; } else { if( sContent.equalsAscii( "\n" ) ) mbLineBreak = sal_True; } } } } else if( rsComment.equalsIgnoreAsciiCaseL( RTL_CONSTASCII_STRINGPARAM( "XTEXT_PAINTSHAPE_END" ) ) ) { bETS = true; } } break; } if( bConfigured || bEOL || bEOP || bETS ) break; } implMap( aPos, maTextPos ); if( bEmpty ) { nCurAction = nActionIndex; return ( (bEOL) ? -2 : ( (bEOP) ? -1 : 0 ) ); } else { return 1; } } // ----------------------------------------------------------------------------- void SVGTextWriter::setTextProperties( const GDIMetaFile& rMtf, sal_uLong nCurAction ) { sal_uLong nCount = rMtf.GetActionSize(); sal_Bool bEOP = sal_False; sal_Bool bConfigured = sal_False; for( sal_uLong nActionIndex = nCurAction + 1; nActionIndex < nCount; ++nActionIndex ) { const MetaAction* pAction = rMtf.GetAction( nActionIndex ); const sal_uInt16 nType = pAction->GetType(); switch( nType ) { case( META_TEXTLINECOLOR_ACTION ): case( META_TEXTFILLCOLOR_ACTION ): case( META_TEXTCOLOR_ACTION ): case( META_TEXTALIGN_ACTION ): case( META_FONT_ACTION ): case( META_LAYOUTMODE_ACTION ): { ( (MetaAction*) pAction )->Execute( mpVDev ); } break; case( META_TEXT_ACTION ): { const MetaTextAction* pA = (const MetaTextAction*) pAction; if( pA->GetLen() > 2 ) bConfigured = true; } break; case( META_TEXTRECT_ACTION ): { const MetaTextRectAction* pA = (const MetaTextRectAction*) pAction; if( pA->GetText().getLength() > 2 ) bConfigured = true; } break; case( META_TEXTARRAY_ACTION ): { const MetaTextArrayAction* pA = (const MetaTextArrayAction*) pAction; if( pA->GetLen() > 2 ) bConfigured = true; } break; case( META_STRETCHTEXT_ACTION ): { const MetaStretchTextAction* pA = (const MetaStretchTextAction*) pAction; if( pA->GetLen() > 2 ) bConfigured = true; } break; // If we reach the end of the paragraph without finding any text // we stop searching case( META_COMMENT_ACTION ): { const MetaCommentAction* pA = (const MetaCommentAction*) pAction; const ::rtl::OString& rsComment = pA->GetComment(); if( rsComment.equalsIgnoreAsciiCaseL( RTL_CONSTASCII_STRINGPARAM( "XTEXT_EOP" ) ) ) { bEOP = true; } } break; } if( bConfigured || bEOP ) break; } } // ----------------------------------------------------------------------------- void SVGTextWriter::addFontAttributes( sal_Bool bIsTextContainer ) { implSetCurrentFont(); if( maCurrentFont != maParentFont ) { const String& rsCurFontName = maCurrentFont.GetName(); long int nCurFontSize = maCurrentFont.GetHeight(); FontItalic eCurFontItalic = maCurrentFont.GetItalic(); FontWeight eCurFontWeight = maCurrentFont.GetWeight(); const String& rsParFontName = maParentFont.GetName(); long int nParFontSize = maParentFont.GetHeight(); FontItalic eParFontItalic = maParentFont.GetItalic(); FontWeight eParFontWeight = maParentFont.GetWeight(); // Font Family if( rsCurFontName != rsParFontName ) { implSetFontFamily(); } // Font Size if( nCurFontSize != nParFontSize ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontSize, ::rtl::OUString::valueOf( nCurFontSize ) + B2UCONST( "px" ) ); } // Font Style if( eCurFontItalic != eParFontItalic ) { ::rtl::OUString sFontStyle; if( eCurFontItalic != ITALIC_NONE ) { if( eCurFontItalic == ITALIC_OBLIQUE ) sFontStyle = B2UCONST( "oblique" ); else sFontStyle = B2UCONST( "italic" ); } else { sFontStyle = B2UCONST( "normal" ); } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontStyle, sFontStyle ); } // Font Weight if( eCurFontWeight != eParFontWeight ) { sal_Int32 nFontWeight; switch( eCurFontWeight ) { case WEIGHT_THIN: nFontWeight = 100; break; case WEIGHT_ULTRALIGHT: nFontWeight = 200; break; case WEIGHT_LIGHT: nFontWeight = 300; break; case WEIGHT_SEMILIGHT: nFontWeight = 400; break; case WEIGHT_NORMAL: nFontWeight = 400; break; case WEIGHT_MEDIUM: nFontWeight = 500; break; case WEIGHT_SEMIBOLD: nFontWeight = 600; break; case WEIGHT_BOLD: nFontWeight = 700; break; case WEIGHT_ULTRABOLD: nFontWeight = 800; break; case WEIGHT_BLACK: nFontWeight = 900; break; default: nFontWeight = 400; break; } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontWeight, ::rtl::OUString::valueOf( nFontWeight ) ); } if( bIsTextContainer ) maParentFont = maCurrentFont; } if( mrExport.IsUseNativeTextDecoration() ) { FontUnderline eCurFontUnderline = maCurrentFont.GetUnderline(); FontStrikeout eCurFontStrikeout = maCurrentFont.GetStrikeout(); FontUnderline eParFontUnderline = maParentFont.GetUnderline(); FontStrikeout eParFontStrikeout = maParentFont.GetStrikeout(); ::rtl::OUString sTextDecoration; if( eCurFontUnderline != eParFontUnderline ) { if( eCurFontUnderline != UNDERLINE_NONE ) sTextDecoration = B2UCONST( "underline " ); } if( eCurFontStrikeout != eParFontStrikeout ) { if( eCurFontStrikeout != STRIKEOUT_NONE ) sTextDecoration += B2UCONST( "line-through " ); } if( !sTextDecoration.isEmpty() ) mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTextDecoration, sTextDecoration ); } } // ----------------------------------------------------------------------------- void SVGTextWriter::implSetFontFamily() { sal_Int32 nNextTokenPos( 0 ); const ::rtl::OUString& rsFontName = maCurrentFont.GetName(); ::rtl::OUString sFontFamily( rsFontName.getToken( 0, ';', nNextTokenPos ) ); FontPitch ePitch = maCurrentFont.GetPitch(); if( ePitch == PITCH_FIXED ) { sFontFamily += B2UCONST( ", monospace" ); } else { FontFamily eFamily = maCurrentFont.GetFamily(); if( eFamily == FAMILY_ROMAN ) sFontFamily += B2UCONST( ", serif" ); else if( eFamily == FAMILY_SWISS ) sFontFamily += B2UCONST( ", sans-serif" ); } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontFamily, sFontFamily ); } // ----------------------------------------------------------------------------- sal_Bool SVGTextWriter::createParagraphEnumeration() { if( mrTextShape.is() ) { Reference< XInterface > xRef( mrTextShape, UNO_QUERY ); msShapeId = implGetValidIDFromInterface( xRef ); Reference< XEnumerationAccess > xEnumerationAccess( mrTextShape, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); if( xEnumeration.is() ) { mrParagraphEnumeration.set( xEnumeration ); return sal_True; } else { OSL_FAIL( "SVGTextWriter::createParagraphEnumeration: no valid xEnumeration interface found." ); } } else { OSL_FAIL( "SVGTextWriter::createParagraphEnumeration: no valid XText interface found." ); } return sal_False; } // ----------------------------------------------------------------------------- sal_Bool SVGTextWriter::nextParagraph() { mrTextPortionEnumeration.clear(); mrCurrentTextParagraph.clear(); mbIsNewListItem = sal_False; mbIsListLevelStyleImage = sal_False; #if OSL_DEBUG_LEVEL > 0 if( mrParagraphEnumeration.is() && mrParagraphEnumeration->hasMoreElements() ) { Reference < XTextContent > xTextContent( mrParagraphEnumeration->nextElement(), UNO_QUERY_THROW ); if( xTextContent.is() ) { Reference< XServiceInfo > xServiceInfo( xTextContent, UNO_QUERY_THROW ); if( xServiceInfo.is() ) { OUString sInfo; if( xServiceInfo->supportsService( B2UCONST( "com.sun.star.text.Paragraph" ) ) ) { mrCurrentTextParagraph.set( xTextContent ); Reference< XPropertySet > xPropSet( xTextContent, UNO_QUERY_THROW ); Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); if( xPropSetInfo->hasPropertyByName( B2UCONST( "NumberingLevel" ) ) ) { sal_Int16 nListLevel = 0; if( xPropSet->getPropertyValue( B2UCONST( "NumberingLevel" ) ) >>= nListLevel ) { mbIsNewListItem = sal_True; sInfo = B2UCONST( "NumberingLevel: " ); sInfo += OUString::valueOf( (sal_Int32)nListLevel ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "style", sInfo ); Reference< XIndexReplace > xNumRules; if( xPropSetInfo->hasPropertyByName( B2UCONST( "NumberingRules" ) ) ) { xPropSet->getPropertyValue( B2UCONST( "NumberingRules" ) ) >>= xNumRules; } if( xNumRules.is() && ( nListLevel < xNumRules->getCount() ) ) { sal_Bool bIsNumbered = sal_True; OUString msNumberingIsNumber(RTL_CONSTASCII_USTRINGPARAM("NumberingIsNumber")); if( xPropSetInfo->hasPropertyByName( msNumberingIsNumber ) ) { if( !(xPropSet->getPropertyValue( msNumberingIsNumber ) >>= bIsNumbered ) ) { OSL_FAIL( "numbered paragraph without number info" ); bIsNumbered = sal_False; } if( bIsNumbered ) { sInfo = B2UCONST( "true" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "is-numbered", sInfo ); } } mbIsNewListItem = bIsNumbered; if( bIsNumbered ) { Sequence aProps; if( xNumRules->getByIndex( nListLevel ) >>= aProps ) { sal_Int16 eType = NumberingType::CHAR_SPECIAL; sal_Unicode cBullet = 0xf095; const sal_Int32 nCount = aProps.getLength(); const PropertyValue* pPropArray = aProps.getConstArray(); for( sal_Int32 i = 0; i < nCount; ++i ) { const PropertyValue& rProp = pPropArray[i]; if( rProp.Name.equalsAsciiL( XML_UNO_NAME_NRULE_NUMBERINGTYPE, sizeof(XML_UNO_NAME_NRULE_NUMBERINGTYPE)-1 ) ) { rProp.Value >>= eType; } else if( rProp.Name.equalsAsciiL( XML_UNO_NAME_NRULE_BULLET_CHAR, sizeof(XML_UNO_NAME_NRULE_BULLET_CHAR)-1 ) ) { OUString sValue; rProp.Value >>= sValue; if( !sValue.isEmpty() ) { cBullet = (sal_Unicode)sValue[0]; } } } meNumberingType = eType; mbIsListLevelStyleImage = ( NumberingType::BITMAP == meNumberingType ); if( NumberingType::CHAR_SPECIAL == meNumberingType ) { if( cBullet ) { if( cBullet < ' ' ) { cBullet = 0xF000 + 149; } mcBulletChar = cBullet; sInfo = OUString::valueOf( (sal_Int32) cBullet ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "bullet-char", sInfo ); } } } } } } } Reference< XEnumerationAccess > xEnumerationAccess( xTextContent, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); if( xEnumeration.is() && xEnumeration->hasMoreElements() ) { mrTextPortionEnumeration.set( xEnumeration ); } sInfo = B2UCONST( "Paragraph" ); } else if( xServiceInfo->supportsService( B2UCONST( "com.sun.star.text.Table" ) ) ) { OSL_FAIL( "SVGTextWriter::nextParagraph: text tables are not handled." ); sInfo = B2UCONST( "Table" ); } else { OSL_FAIL( "SVGTextWriter::nextParagraph: Unknown text content." ); return sal_False; } mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", sInfo ); SvXMLElementExport aParaElem( mrExport, XML_NAMESPACE_NONE, "desc", mbIWS, mbIWS ); } else { OSL_FAIL( "SVGTextWriter::nextParagraph: no XServiceInfo interface available for text content." ); return sal_False; } Reference< XInterface > xRef( xTextContent, UNO_QUERY ); const OUString& rParagraphId = implGetValidIDFromInterface( xRef ); if( !rParagraphId.isEmpty() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", rParagraphId ); } return sal_True; } } #else if( mrParagraphEnumeration.is() && mrParagraphEnumeration->hasMoreElements() ) { Reference < XTextContent > xTextContent( mrParagraphEnumeration->nextElement(), UNO_QUERY_THROW ); if( xTextContent.is() ) { Reference< XServiceInfo > xServiceInfo( xTextContent, UNO_QUERY_THROW ); if( xServiceInfo.is() ) { if( xServiceInfo->supportsService( B2UCONST( "com.sun.star.text.Paragraph" ) ) ) { mrCurrentTextParagraph.set( xTextContent ); Reference< XPropertySet > xPropSet( xTextContent, UNO_QUERY_THROW ); Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); if( xPropSetInfo->hasPropertyByName( B2UCONST( "NumberingLevel" ) ) ) { sal_Int16 nListLevel = 0; if( xPropSet->getPropertyValue( B2UCONST( "NumberingLevel" ) ) >>= nListLevel ) { mbIsNewListItem = sal_True; Reference< XIndexReplace > xNumRules; if( xPropSetInfo->hasPropertyByName( B2UCONST( "NumberingRules" ) ) ) { xPropSet->getPropertyValue( B2UCONST( "NumberingRules" ) ) >>= xNumRules; } if( xNumRules.is() && ( nListLevel < xNumRules->getCount() ) ) { sal_Bool bIsNumbered = sal_True; OUString msNumberingIsNumber(RTL_CONSTASCII_USTRINGPARAM("NumberingIsNumber")); if( xPropSetInfo->hasPropertyByName( msNumberingIsNumber ) ) { if( !(xPropSet->getPropertyValue( msNumberingIsNumber ) >>= bIsNumbered ) ) { OSL_FAIL( "numbered paragraph without number info" ); bIsNumbered = sal_False; } } mbIsNewListItem = bIsNumbered; if( bIsNumbered ) { Sequence aProps; if( xNumRules->getByIndex( nListLevel ) >>= aProps ) { sal_Int16 eType = NumberingType::CHAR_SPECIAL; sal_Unicode cBullet = 0xf095; const sal_Int32 nCount = aProps.getLength(); const PropertyValue* pPropArray = aProps.getConstArray(); for( sal_Int32 i = 0; i < nCount; ++i ) { const PropertyValue& rProp = pPropArray[i]; if( rProp.Name.equalsAsciiL( XML_UNO_NAME_NRULE_NUMBERINGTYPE, sizeof(XML_UNO_NAME_NRULE_NUMBERINGTYPE)-1 ) ) { rProp.Value >>= eType; } else if( rProp.Name.equalsAsciiL( XML_UNO_NAME_NRULE_BULLET_CHAR, sizeof(XML_UNO_NAME_NRULE_BULLET_CHAR)-1 ) ) { OUString sValue; rProp.Value >>= sValue; if( !sValue.isEmpty() ) { cBullet = (sal_Unicode)sValue[0]; } } } meNumberingType = eType; mbIsListLevelStyleImage = ( NumberingType::BITMAP == meNumberingType ); if( NumberingType::CHAR_SPECIAL == meNumberingType ) { if( cBullet ) { if( cBullet < ' ' ) { cBullet = 0xF000 + 149; } mcBulletChar = cBullet; } } } } } } } Reference< XEnumerationAccess > xEnumerationAccess( xTextContent, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); if( xEnumeration.is() && xEnumeration->hasMoreElements() ) { mrTextPortionEnumeration.set( xEnumeration ); } } else if( xServiceInfo->supportsService( B2UCONST( "com.sun.star.text.Table" ) ) ) { OSL_FAIL( "SVGTextWriter::nextParagraph: text tables are not handled." ); } else { OSL_FAIL( "SVGTextWriter::nextParagraph: Unknown text content." ); return sal_False; } } else { OSL_FAIL( "SVGTextWriter::nextParagraph: no XServiceInfo interface available for text content." ); return sal_False; } Reference< XInterface > xRef( xTextContent, UNO_QUERY ); const OUString& rParagraphId = implGetValidIDFromInterface( xRef ); if( !rParagraphId.isEmpty() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", rParagraphId ); } return sal_True; } } #endif return sal_False; } // ----------------------------------------------------------------------------- sal_Bool SVGTextWriter::nextTextPortion() { mrCurrentTextPortion.clear(); mbIsURLField = sal_False; mbIsPlacehlolderShape = sal_False; #if OSL_DEBUG_LEVEL > 0 if( mrTextPortionEnumeration.is() && mrTextPortionEnumeration->hasMoreElements() ) { OUString sInfo; Reference< XPropertySet > xPortionPropSet( mrTextPortionEnumeration->nextElement(), UNO_QUERY ); Reference< XPropertySetInfo > xPortionPropInfo( xPortionPropSet->getPropertySetInfo() ); Reference < XTextRange > xPortionTextRange( xPortionPropSet, UNO_QUERY); if( xPortionPropSet.is() && xPortionPropInfo.is() && xPortionPropInfo->hasPropertyByName( B2UCONST( "TextPortionType" ) ) ) { ::rtl::OUString sPortionType; if( xPortionPropSet->getPropertyValue( B2UCONST( "TextPortionType" ) ) >>= sPortionType ) { sInfo = B2UCONST( "type: " ); sInfo += sPortionType; sInfo += B2UCONST( "; " ); } if( xPortionTextRange.is() ) { sInfo += B2UCONST( "content: " ); sInfo += xPortionTextRange->getString(); sInfo += B2UCONST( "; " ); mrCurrentTextPortion.set( xPortionTextRange ); Reference < XPropertySet > xRangePropSet( xPortionTextRange, UNO_QUERY ); if( xRangePropSet.is() && xRangePropSet->getPropertySetInfo()->hasPropertyByName( B2UCONST( "TextField" ) ) ) { Reference < XTextField > xTextField( xRangePropSet->getPropertyValue( B2UCONST( "TextField" ) ), UNO_QUERY ); if( xTextField.is() ) { const ::rtl::OUString sServicePrefix( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.text.textfield.") ); const ::rtl::OUString sPresentationServicePrefix( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.presentation.TextField.") ); Reference< XServiceInfo > xService( xTextField, UNO_QUERY ); const Sequence< OUString > aServices = xService->getSupportedServiceNames(); const OUString* pNames = aServices.getConstArray(); sal_Int32 nCount = aServices.getLength(); OUString sFieldName; // service name postfix of current field // search for TextField service name while( nCount-- ) { if ( pNames->matchIgnoreAsciiCase( sServicePrefix ) ) { // TextField found => postfix is field type! sFieldName = pNames->copy( sServicePrefix.getLength() ); break; } else if( 0 == pNames->compareTo( sPresentationServicePrefix, sPresentationServicePrefix.getLength() ) ) { // TextField found => postfix is field type! sFieldName = pNames->copy( sPresentationServicePrefix.getLength() ); break; } ++pNames; } sInfo += B2UCONST( "text field type: " ); sInfo += sFieldName; sInfo += B2UCONST( "; " ); sInfo += B2UCONST( "content: " ); sInfo += xTextField->getPresentation( /* show command: */ sal_False ); sInfo += B2UCONST( "; " ); if( sFieldName.equalsAscii( "DateTime" ) || sFieldName.equalsAscii( "Header" ) || sFieldName.equalsAscii( "Footer" ) || sFieldName.equalsAscii( "PageNumber" ) ) { mbIsPlacehlolderShape = sal_True; } else { mbIsURLField = sFieldName.equalsAscii( "URL" ); if( mbIsURLField ) { Reference xTextFieldPropSet(xTextField, UNO_QUERY); if( xTextFieldPropSet.is() ) { OUString sURL; if( ( xTextFieldPropSet->getPropertyValue( sFieldName ) ) >>= sURL ) { sInfo += B2UCONST( "url: " ); sInfo += mrExport.GetRelativeReference( sURL ); msUrl = mrExport.GetRelativeReference( sURL ); if( !msUrl.isEmpty() ) { implRegisterInterface( xPortionTextRange ); Reference< XInterface > xRef( xPortionTextRange, UNO_QUERY ); const OUString& rTextPortionId = implGetValidIDFromInterface( xRef ); if( !rTextPortionId.isEmpty() ) { msHyperlinkIdList += rTextPortionId; msHyperlinkIdList += B2UCONST( " " ); } } } } } } } } } mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextPortion" ) ); SvXMLElementExport aPortionElem( mrExport, XML_NAMESPACE_NONE, "desc", mbIWS, mbIWS ); mrExport.GetDocHandler()->characters( sInfo ); return sal_True; } } #else if( mrTextPortionEnumeration.is() && mrTextPortionEnumeration->hasMoreElements() ) { OUString sInfo; Reference< XPropertySet > xPortionPropSet( mrTextPortionEnumeration->nextElement(), UNO_QUERY ); Reference< XPropertySetInfo > xPortionPropInfo( xPortionPropSet->getPropertySetInfo() ); Reference < XTextRange > xPortionTextRange( xPortionPropSet, UNO_QUERY); if( xPortionPropSet.is() && xPortionPropInfo.is() && xPortionPropInfo->hasPropertyByName( B2UCONST( "TextPortionType" ) ) ) { if( xPortionTextRange.is() ) { mrCurrentTextPortion.set( xPortionTextRange ); Reference < XPropertySet > xRangePropSet( xPortionTextRange, UNO_QUERY ); if( xRangePropSet.is() && xRangePropSet->getPropertySetInfo()->hasPropertyByName( B2UCONST( "TextField" ) ) ) { Reference < XTextField > xTextField( xRangePropSet->getPropertyValue( B2UCONST( "TextField" ) ), UNO_QUERY ); if( xTextField.is() ) { const ::rtl::OUString sServicePrefix( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.text.textfield.") ); const ::rtl::OUString sPresentationServicePrefix( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.presentation.TextField.") ); Reference< XServiceInfo > xService( xTextField, UNO_QUERY ); const Sequence< OUString > aServices = xService->getSupportedServiceNames(); const OUString* pNames = aServices.getConstArray(); sal_Int32 nCount = aServices.getLength(); OUString sFieldName; // service name postfix of current field // search for TextField service name while( nCount-- ) { if ( pNames->matchIgnoreAsciiCase( sServicePrefix ) ) { // TextField found => postfix is field type! sFieldName = pNames->copy( sServicePrefix.getLength() ); break; } else if( 0 == pNames->compareTo( sPresentationServicePrefix, sPresentationServicePrefix.getLength() ) ) { // TextField found => postfix is field type! sFieldName = pNames->copy( sPresentationServicePrefix.getLength() ); break; } ++pNames; } if( sFieldName.equalsAscii( "DateTime" ) || sFieldName.equalsAscii( "Header" ) || sFieldName.equalsAscii( "Footer" ) || sFieldName.equalsAscii( "PageNumber" ) ) { mbIsPlacehlolderShape = sal_True; } else { mbIsURLField = sFieldName.equalsAscii( "URL" ); if( mbIsURLField ) { Reference xTextFieldPropSet(xTextField, UNO_QUERY); if( xTextFieldPropSet.is() ) { OUString sURL; if( ( xTextFieldPropSet->getPropertyValue( sFieldName ) ) >>= sURL ) { msUrl = mrExport.GetRelativeReference( sURL ); if( !msUrl.isEmpty() ) { implRegisterInterface( xPortionTextRange ); Reference< XInterface > xRef( xPortionTextRange, UNO_QUERY ); const OUString& rTextPortionId = implGetValidIDFromInterface( xRef ); if( !rTextPortionId.isEmpty() ) { msHyperlinkIdList += rTextPortionId; msHyperlinkIdList += B2UCONST( " " ); } } } } } } } } } return sal_True; } } #endif return sal_False; } // ----------------------------------------------------------------------------- void SVGTextWriter::startTextShape() { if( mpTextShapeElem ) { OSL_FAIL( "SVGTextWriter::startTextShape: text shape already defined." ); } { mbIsTextShapeStarted = sal_True; maParentFont = Font(); mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextShape" ) ); mpTextShapeElem = new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemText, sal_True, mbIWS ); startTextParagraph(); } } // ----------------------------------------------------------------------------- void SVGTextWriter::endTextShape() { endTextParagraph(); if( mrTextShape.is() ) mrTextShape.clear(); if( mrParagraphEnumeration.is() ) mrParagraphEnumeration.clear(); if( mrCurrentTextParagraph.is() ) mrCurrentTextParagraph.clear(); if( mpTextShapeElem ) { delete mpTextShapeElem; mpTextShapeElem = NULL; } mbIsTextShapeStarted = sal_False; // these need to be invoked after the element has been closed implExportHyperlinkIds(); implWriteBulletChars(); implWriteEmbeddedBitmaps(); } // ----------------------------------------------------------------------------- void SVGTextWriter::startTextParagraph() { endTextParagraph(); nextParagraph(); if( mbIsNewListItem ) { OUString sNumberingType; switch( meNumberingType ) { case( NumberingType::CHAR_SPECIAL ): sNumberingType = B2UCONST( "bullet-style" ); break; case( NumberingType::BITMAP ): sNumberingType = B2UCONST( "image-style" ); break; default: sNumberingType = B2UCONST( "number-style" ); break; } mrExport.AddAttribute( XML_NAMESPACE_NONE, aOOOAttrNumberingType, sNumberingType ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "ListItem" ) ); } else { mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextParagraph" ) ); } maParentFont = Font(); addFontAttributes( /* isTexTContainer: */ true ); mpTextParagraphElem = new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS ); if( !mbIsListLevelStyleImage ) { startTextPosition(); } } // ----------------------------------------------------------------------------- void SVGTextWriter::endTextParagraph() { mrCurrentTextPortion.clear(); endTextPosition(); mbIsNewListItem = sal_False; mbIsListLevelStyleImage = sal_False; mbPositioningNeeded = sal_False; if( mpTextParagraphElem ) { delete mpTextParagraphElem; mpTextParagraphElem = NULL; } } // ----------------------------------------------------------------------------- void SVGTextWriter::startTextPosition( sal_Bool bExportX, sal_Bool bExportY ) { endTextPosition(); mnTextWidth = 0; mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextPosition" ) ); if( bExportX ) mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, ::rtl::OUString::valueOf( maTextPos.X() ) ); if( bExportY ) mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, ::rtl::OUString::valueOf( maTextPos.Y() ) ); mpTextPositionElem = new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS ); } // ----------------------------------------------------------------------------- void SVGTextWriter::endTextPosition() { if( mpTextPositionElem ) { delete mpTextPositionElem; mpTextPositionElem = NULL; } } // ----------------------------------------------------------------------------- void SVGTextWriter::implExportHyperlinkIds() { if( !msHyperlinkIdList.isEmpty() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "HyperlinkIdList" ) ); SvXMLElementExport aDescElem( mrExport, XML_NAMESPACE_NONE, "desc", sal_True, sal_False ); mrExport.GetDocHandler()->characters( msHyperlinkIdList.trim() ); msHyperlinkIdList = OUString(); } } // ----------------------------------------------------------------------------- void SVGTextWriter::implWriteBulletChars() { if( maBulletListItemMap.empty() ) return; mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "BulletChars" ) ); SvXMLElementExport aGroupElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); BulletListItemInfoMap::const_iterator it = maBulletListItemMap.begin(); BulletListItemInfoMap::const_iterator end = maBulletListItemMap.end(); OUString sId, sPosition, sScaling, sRefId; for( ; it != end; ++it ) { // (used by animations) { // As id we use the id of the text portion placeholder wrapped // by bullet-char(*) sId = B2UCONST( "bullet-char(" ); sId += it->first; sId += B2UCONST( ")" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", sId ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "BulletChar" ) ); SvXMLElementExport aBulletCharElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); // { const BulletListItemInfo& rInfo = it->second; // Add positioning attribute through a translation sPosition = B2UCONST( "translate(" ); sPosition += OUString::valueOf( rInfo.aPos.X() ); sPosition += B2UCONST( "," ); sPosition += OUString::valueOf( rInfo.aPos.Y() ); sPosition += B2UCONST( ")" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "transform", sPosition ); mpContext->AddPaintAttr( COL_TRANSPARENT, rInfo.aColor ); SvXMLElementExport aPositioningElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); // { // Add size attribute through a scaling sScaling = B2UCONST( "scale(" ); sScaling += OUString::valueOf( rInfo.nFontSize ); sScaling += B2UCONST( "," ); sScaling += OUString::valueOf( rInfo.nFontSize ); sScaling += B2UCONST( ")" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "transform", sScaling ); // Add ref attribute sRefId = B2UCONST( "#bullet-char-template(" ); sRefId += OUString::valueOf( (sal_Int32)( rInfo.cBulletChar ) ); sRefId += B2UCONST( ")" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, sRefId ); SvXMLElementExport aRefElem( mrExport, XML_NAMESPACE_NONE, "use", sal_True, sal_True ); } } // close aPositioningElem } // close aBulletCharElem } // clear the map maBulletListItemMap.clear(); } // ----------------------------------------------------------------------------- template< typename MetaBitmapActionType > void SVGTextWriter::writeBitmapPlaceholder( const MetaBitmapActionType* pAction ) { // text position element const Point& rPos = pAction->GetPoint(); implMap( rPos, maTextPos ); startTextPosition(); mbPositioningNeeded = sal_True; if( mbIsNewListItem ) { mbIsNewListItem = sal_False; mbIsListLevelStyleImage = sal_False; } // bitmap placeholder element sal_uLong nId = SVGActionWriter::GetChecksum( pAction ); OUString sId = B2UCONST( "bitmap-placeholder(" ); sId += msShapeId; sId += B2UCONST( "." ); sId += OUString::valueOf( (sal_Int64)nId ); sId += B2UCONST( ")" ); { mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", sId ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "BitmapPlaceholder" ) ); SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS ); } endTextPosition(); } // ----------------------------------------------------------------------------- void SVGTextWriter::implWriteEmbeddedBitmaps() { if( mpTextEmbeddedBitmapMtf && mpTextEmbeddedBitmapMtf->GetActionSize() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "EmbeddedBitmaps" ) ); SvXMLElementExport aEmbBitmapGroupElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); const GDIMetaFile& rMtf = *mpTextEmbeddedBitmapMtf; OUString sId, sRefId; sal_uLong nId, nChecksum = 0; Point aPt; Size aSz; sal_uLong nCount = rMtf.GetActionSize(); for( sal_uLong nCurAction = 0; nCurAction < nCount; nCurAction++ ) { const MetaAction* pAction = rMtf.GetAction( nCurAction ); const sal_uInt16 nType = pAction->GetType(); switch( nType ) { case( META_BMPSCALE_ACTION ): { const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*) pAction; nChecksum = pA->GetBitmap().GetChecksum(); aPt = pA->GetPoint(); aSz = pA->GetSize(); } break; case( META_BMPEXSCALE_ACTION ): { const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pAction; nChecksum = pA->GetBitmapEx().GetChecksum(); aPt = pA->GetPoint(); aSz = pA->GetSize(); } break; } // (used by animations) { // embedded bitmap id nId = SVGActionWriter::GetChecksum( pAction ); sId = B2UCONST( "embedded-bitmap(" ); sId += msShapeId; sId += B2UCONST( "." ); sId += OUString::valueOf( (sal_Int64)nId ); sId += B2UCONST( ")" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", sId ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "EmbeddedBitmap" ) ); SvXMLElementExport aEmbBitmapElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); // { // referenced bitmap template sRefId = B2UCONST( "#bitmap(" ); sRefId += OUString::valueOf( (sal_Int64)nChecksum ); sRefId += B2UCONST( ")" ); Point aPoint; Size aSize; implMap( aPt, aPoint ); implMap( aSz, aSize ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, OUString::valueOf( aPoint.X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, OUString::valueOf( aPoint.Y() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, sRefId ); SvXMLElementExport aRefElem( mrExport, XML_NAMESPACE_NONE, "use", sal_True, sal_True ); } } // close aEmbBitmapElem } } } // ----------------------------------------------------------------------------- void SVGTextWriter::writeTextPortion( const Point& rPos, const String& rText, sal_Bool bApplyMapping ) { if( rText.Len() == 0 ) return; mbLineBreak = sal_False; if( !mbIsNewListItem || mbIsListLevelStyleImage ) { bool bNotSync = true; OUString sContent; sal_Int32 nStartPos; while( bNotSync ) { if( mnLeftTextPortionLength <= 0 ) { if( !nextTextPortion() ) break; else { sContent = mrCurrentTextPortion->getString(); if( mbIsURLField && sContent.isEmpty() ) { Reference < XPropertySet > xPropSet( mrCurrentTextPortion, UNO_QUERY ); Reference < XTextField > xTextField( xPropSet->getPropertyValue( B2UCONST( "TextField" ) ), UNO_QUERY ); sContent = xTextField->getPresentation( /* show command: */ sal_False ); if( sContent.isEmpty() ) OSL_FAIL( "SVGTextWriter::writeTextPortion: content of URL TextField is empty." ); } mnLeftTextPortionLength = sContent.getLength(); } } else { sContent = mrCurrentTextPortion->getString(); } nStartPos = sContent.getLength() - mnLeftTextPortionLength; if( nStartPos < 0 ) nStartPos = 0; mnLeftTextPortionLength -= rText.Len(); if( sContent.isEmpty() ) continue; if( sContent.equalsAscii( "\n" ) ) mbLineBreak = sal_True; if( sContent.match( rText, nStartPos ) ) bNotSync = false; } } if( !mpVDev ) OSL_FAIL( "SVGTextWriter::writeTextPortion: invalid virtual device." ); const FontMetric aMetric( mpVDev->GetFontMetric() ); bool bTextSpecial = aMetric.IsShadow() || aMetric.IsOutline() || (aMetric.GetRelief() != RELIEF_NONE); if( true || !bTextSpecial ) { implWriteTextPortion( rPos, rText, mpVDev->GetTextColor(), bApplyMapping ); } else { // to be implemented } } // ----------------------------------------------------------------------------- void SVGTextWriter::implWriteTextPortion( const Point& rPos, const String& rText, Color aTextColor, sal_Bool bApplyMapping ) { if( !mpContext ) OSL_FAIL( "SVGTextWriter::implWriteTextPortion: invalid context object." ); Point aPos; Point aBaseLinePos( rPos ); const FontMetric aMetric( mpVDev->GetFontMetric() ); const Font& rFont = mpVDev->GetFont(); if( rFont.GetAlign() == ALIGN_TOP ) aBaseLinePos.Y() += aMetric.GetAscent(); else if( rFont.GetAlign() == ALIGN_BOTTOM ) aBaseLinePos.Y() -= aMetric.GetDescent(); if( bApplyMapping ) implMap( rPos, aPos ); else aPos = rPos; if( mbPositioningNeeded ) { mbPositioningNeeded = sal_False; maTextPos.setX( aPos.X() ); maTextPos.setY( aPos.Y() ); startTextPosition(); } else if( maTextPos.Y() != aPos.Y() ) { // In case the text position moved backward we could have a line break // so we end the current line and start a new one. if( mbLineBreak || ( ( maTextPos.X() + mnTextWidth ) > aPos.X() ) ) { mbLineBreak = sal_False; maTextPos.setX( aPos.X() ); maTextPos.setY( aPos.Y() ); startTextPosition(); } else // superscript, subscript, list item numbering { maTextPos.setY( aPos.Y() ); startTextPosition( sal_False /* do not export x attribute */ ); } } // we are dealing with a bullet, so set up this for the next text portion if( mbIsNewListItem ) { mbIsNewListItem = sal_False; mbPositioningNeeded = sal_True; if( meNumberingType == NumberingType::CHAR_SPECIAL ) { // Create an id for the current text portion implRegisterInterface( mrCurrentTextParagraph ); // Add the needed info to the BulletListItemMap Reference< XInterface > xRef( mrCurrentTextParagraph, UNO_QUERY ); OUString sId = implGetValidIDFromInterface( xRef ); if( !sId.isEmpty() ) { sId += ".bp"; BulletListItemInfo& aBulletListItemInfo = maBulletListItemMap[ sId ]; aBulletListItemInfo.nFontSize = rFont.GetHeight(); aBulletListItemInfo.aColor = aTextColor; aBulletListItemInfo.aPos = maTextPos; aBulletListItemInfo.cBulletChar = mcBulletChar; // Make this text portion a bullet placeholder mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", sId ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "BulletPlaceholder" ) ); SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS ); return; } } } Reference< XInterface > xRef( mrCurrentTextPortion, UNO_QUERY ); const OUString& rTextPortionId = implGetValidIDFromInterface( xRef ); if( !rTextPortionId.isEmpty() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", rTextPortionId ); } if( mbIsPlacehlolderShape ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "PlaceholderText" ) ); mbIsPlacehlolderShape = sal_False; } else if( mbIsURLField && !msUrl.isEmpty() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "UrlField" ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, msUrl ); mbIsURLField = sal_False; } addFontAttributes( /* isTexTContainer: */ false ); mpContext->AddPaintAttr( COL_TRANSPARENT, aTextColor ); SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS ); OUString sTextContent = rText; mrExport.GetDocHandler()->characters( sTextContent ); mnTextWidth += mpVDev->GetTextWidth( sTextContent ); } // ------------------- // - SVGActionWriter - // ------------------- SVGActionWriter::SVGActionWriter( SVGExport& rExport, SVGFontExport& rFontExport ) : mnCurGradientId( 1 ), mnCurMaskId( 1 ), mnCurPatternId( 1 ), mrExport( rExport ), mrFontExport( rFontExport ), mpContext( NULL ), maTextWriter( rExport, rFontExport ), mnInnerMtfCount( 0 ), mbClipAttrChanged( sal_False ) { mpVDev = new VirtualDevice; mpVDev->EnableOutput( sal_False ); maTargetMapMode = MAP_100TH_MM; maTextWriter.setVirtualDevice( mpVDev, maTargetMapMode ); } // ----------------------------------------------------------------------------- SVGActionWriter::~SVGActionWriter() { DBG_ASSERT( !mpContext, "Not all contexts are closed" ); delete mpVDev; } // ----------------------------------------------------------------------------- long SVGActionWriter::ImplMap( sal_Int32 nVal ) const { Size aSz( nVal, nVal ); return( ImplMap( aSz, aSz ).Width() ); } // ----------------------------------------------------------------------------- Point& SVGActionWriter::ImplMap( const Point& rPt, Point& rDstPt ) const { return( rDstPt = mpVDev->LogicToLogic( rPt, mpVDev->GetMapMode(), maTargetMapMode ) ); } // ----------------------------------------------------------------------------- Size& SVGActionWriter::ImplMap( const Size& rSz, Size& rDstSz ) const { return( rDstSz = mpVDev->LogicToLogic( rSz, mpVDev->GetMapMode(), maTargetMapMode ) ); } // ----------------------------------------------------------------------------- Rectangle& SVGActionWriter::ImplMap( const Rectangle& rRect, Rectangle& rDstRect ) const { Point aTL( rRect.TopLeft() ); Size aSz( rRect.GetSize() ); return( rDstRect = Rectangle( ImplMap( aTL, aTL ), ImplMap( aSz, aSz ) ) ); } // ----------------------------------------------------------------------------- Polygon& SVGActionWriter::ImplMap( const Polygon& rPoly, Polygon& rDstPoly ) const { rDstPoly = Polygon( rPoly.GetSize() ); for( sal_uInt16 i = 0, nSize = rPoly.GetSize(); i < nSize; ++i ) { ImplMap( rPoly[ i ], rDstPoly[ i ] ); rDstPoly.SetFlags( i, rPoly.GetFlags( i ) ); } return( rDstPoly ); } // ----------------------------------------------------------------------------- PolyPolygon& SVGActionWriter::ImplMap( const PolyPolygon& rPolyPoly, PolyPolygon& rDstPolyPoly ) const { Polygon aPoly; rDstPolyPoly = PolyPolygon(); for( sal_uInt16 i = 0, nCount = rPolyPoly.Count(); i < nCount; ++i ) { rDstPolyPoly.Insert( ImplMap( rPolyPoly[ i ], aPoly ) ); } return( rDstPolyPoly ); } // ----------------------------------------------------------------------------- ::rtl::OUString SVGActionWriter::GetPathString( const PolyPolygon& rPolyPoly, sal_Bool bLine ) { ::rtl::OUString aPathData; const ::rtl::OUString aBlank( B2UCONST( " " ) ); const ::rtl::OUString aComma( B2UCONST( "," ) ); Point aPolyPoint; for( long i = 0, nCount = rPolyPoly.Count(); i < nCount; i++ ) { const Polygon& rPoly = rPolyPoly[ (sal_uInt16) i ]; sal_uInt16 n = 1, nSize = rPoly.GetSize(); if( nSize > 1 ) { aPathData += B2UCONST( "M " ); aPathData += ::rtl::OUString::valueOf( ( aPolyPoint = rPoly[ 0 ] ).X() ); aPathData += aComma; aPathData += ::rtl::OUString::valueOf( aPolyPoint.Y() ); sal_Char nCurrentMode = 0; const bool bClose(!bLine || rPoly[0] == rPoly[nSize - 1]); while( n < nSize ) { aPathData += aBlank; if ( ( rPoly.GetFlags( n ) == POLY_CONTROL ) && ( ( n + 2 ) < nSize ) ) { if ( nCurrentMode != 'C' ) { nCurrentMode = 'C'; aPathData += B2UCONST( "C " ); } for ( int j = 0; j < 3; j++ ) { if ( j ) aPathData += aBlank; aPathData += ::rtl::OUString::valueOf( ( aPolyPoint = rPoly[ n++ ] ).X() ); aPathData += aComma; aPathData += ::rtl::OUString::valueOf( aPolyPoint.Y() ); } } else { if ( nCurrentMode != 'L' ) { nCurrentMode = 'L'; aPathData += B2UCONST( "L " ); } aPathData += ::rtl::OUString::valueOf( ( aPolyPoint = rPoly[ n++ ] ).X() ); aPathData += aComma; aPathData += ::rtl::OUString::valueOf( aPolyPoint.Y() ); } } if(bClose) aPathData += B2UCONST( " Z" ); if( i < ( nCount - 1 ) ) aPathData += aBlank; } } return aPathData; } // ----------------------------------------------------------------------------- sal_uLong SVGActionWriter::GetChecksum( const MetaAction* pAction ) { GDIMetaFile aMtf; MetaAction* pA = (MetaAction*)pAction; pA->Duplicate(); aMtf.AddAction( pA ); return aMtf.GetChecksum(); } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteLine( const Point& rPt1, const Point& rPt2, const Color* pLineColor, sal_Bool bApplyMapping ) { Point aPt1, aPt2; if( bApplyMapping ) { ImplMap( rPt1, aPt1 ); ImplMap( rPt2, aPt2 ); } else { aPt1 = rPt1; aPt2 = rPt2; } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX1, ::rtl::OUString::valueOf( aPt1.X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY1, ::rtl::OUString::valueOf( aPt1.Y() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX2, ::rtl::OUString::valueOf( aPt2.X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY2, ::rtl::OUString::valueOf( aPt2.Y() ) ); if( pLineColor ) { // !!! mrExport.AddAttribute( XML_NAMESPACE_NONE, ... ) OSL_FAIL( "SVGActionWriter::ImplWriteLine: Line color not implemented" ); } { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, aXMLElemLine, sal_True, sal_True ); } } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteRect( const Rectangle& rRect, long nRadX, long nRadY, sal_Bool bApplyMapping ) { Rectangle aRect; if( bApplyMapping ) ImplMap( rRect, aRect ); else aRect = rRect; mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, ::rtl::OUString::valueOf( aRect.Left() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, ::rtl::OUString::valueOf( aRect.Top() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrWidth, ::rtl::OUString::valueOf( aRect.GetWidth() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrHeight, ::rtl::OUString::valueOf( aRect.GetHeight() ) ); if( nRadX ) mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrRX, ::rtl::OUString::valueOf( bApplyMapping ? ImplMap( nRadX ) : nRadX ) ); if( nRadY ) mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrRY, ::rtl::OUString::valueOf( bApplyMapping ? ImplMap( nRadY ) : nRadY ) ); { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, aXMLElemRect, sal_True, sal_True ); } } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteEllipse( const Point& rCenter, long nRadX, long nRadY, sal_Bool bApplyMapping ) { Point aCenter; if( bApplyMapping ) ImplMap( rCenter, aCenter ); else aCenter = rCenter; mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrCX, ::rtl::OUString::valueOf( aCenter.X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrCY, ::rtl::OUString::valueOf( aCenter.Y() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrRX, ::rtl::OUString::valueOf( bApplyMapping ? ImplMap( nRadX ) : nRadX ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrRY, ::rtl::OUString::valueOf( bApplyMapping ? ImplMap( nRadY ) : nRadY ) ); { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, aXMLElemEllipse, sal_True, sal_True ); } } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWritePolyPolygon( const PolyPolygon& rPolyPoly, sal_Bool bLineOnly, sal_Bool bApplyMapping ) { PolyPolygon aPolyPoly; if( bApplyMapping ) ImplMap( rPolyPoly, aPolyPoly ); else aPolyPoly = rPolyPoly; // add path data attribute mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrD, GetPathString( aPolyPoly, bLineOnly ) ); { // write polyline/polygon element SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, aXMLElemPath, sal_True, sal_True ); } } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteShape( const SVGShapeDescriptor& rShape, sal_Bool bApplyMapping ) { PolyPolygon aPolyPoly; if( bApplyMapping ) ImplMap( rShape.maShapePolyPoly, aPolyPoly ); else aPolyPoly = rShape.maShapePolyPoly; const sal_Bool bLineOnly = ( rShape.maShapeFillColor == Color( COL_TRANSPARENT ) ) && ( !rShape.mapShapeGradient.get() ); Rectangle aBoundRect( aPolyPoly.GetBoundRect() ); mpContext->AddPaintAttr( rShape.maShapeLineColor, rShape.maShapeFillColor, &aBoundRect, rShape.mapShapeGradient.get() ); if( !rShape.maId.isEmpty() ) mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, rShape.maId ); if( rShape.mnStrokeWidth ) { sal_Int32 nStrokeWidth = ( bApplyMapping ? ImplMap( rShape.mnStrokeWidth ) : rShape.mnStrokeWidth ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStrokeWidth, ::rtl::OUString::valueOf( nStrokeWidth ) ); } // support for LineJoin switch(rShape.maLineJoin) { default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE case basegfx::B2DLINEJOIN_MITER: { // miter is Svg default, so no need to write until the exporter might write styles. // If this happens, activate here // mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, ::rtl::OUString::createFromAscii("miter")); break; } case basegfx::B2DLINEJOIN_BEVEL: { mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, ::rtl::OUString::createFromAscii("bevel")); break; } case basegfx::B2DLINEJOIN_ROUND: { mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, ::rtl::OUString::createFromAscii("round")); break; } } // support for LineCap switch(rShape.maLineCap) { default: /* com::sun::star::drawing::LineCap_BUTT */ { // butt is Svg default, so no need to write until the exporter might write styles. // If this happens, activate here // mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, ::rtl::OUString::createFromAscii("butt")); break; } case com::sun::star::drawing::LineCap_ROUND: { mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, ::rtl::OUString::createFromAscii("round")); break; } case com::sun::star::drawing::LineCap_SQUARE: { mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, ::rtl::OUString::createFromAscii("square")); break; } } if( rShape.maDashArray.size() ) { const ::rtl::OUString aComma( B2UCONST( "," ) ); ::rtl::OUString aDashArrayStr; for( unsigned int k = 0; k < rShape.maDashArray.size(); ++k ) { const sal_Int32 nDash = ( bApplyMapping ? ImplMap( FRound( rShape.maDashArray[ k ] ) ) : FRound( rShape.maDashArray[ k ] ) ); if( k ) aDashArrayStr += aComma; aDashArrayStr += ::rtl::OUString::valueOf( nDash ); } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStrokeDashArray, aDashArrayStr ); } ImplWritePolyPolygon( aPolyPoly, bLineOnly, sal_False ); } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWritePattern( const PolyPolygon& rPolyPoly, const Hatch* pHatch, const Gradient* pGradient, sal_uInt32 nWriteFlags ) { if( rPolyPoly.Count() ) { SvXMLElementExport aElemG( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); ::rtl::OUString aPatternId; aPatternId += B2UCONST( "pattern" ); aPatternId += OUString::valueOf( mnCurPatternId++ ); { SvXMLElementExport aElemDefs( mrExport, XML_NAMESPACE_NONE, aXMLElemDefs, sal_True, sal_True ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, aPatternId ); Rectangle aRect; ImplMap( rPolyPoly.GetBoundRect(), aRect ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, OUString::valueOf( aRect.Left() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, OUString::valueOf( aRect.Top() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrWidth, OUString::valueOf( aRect.GetWidth() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrHeight, OUString::valueOf( aRect.GetHeight() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrPatternUnits, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "userSpaceOnUse") ) ); { SvXMLElementExport aElemPattern( mrExport, XML_NAMESPACE_NONE, aXMLElemPattern, sal_True, sal_True ); // The origin of a pattern is positioned at (aRect.Left(), aRect.Top()). // So we need to adjust the pattern coordinate. ::rtl::OUString aTransform; aTransform += B2UCONST( "translate" ); aTransform += B2UCONST( "(" ); aTransform += OUString::valueOf( -aRect.Left() ); aTransform += B2UCONST( "," ); aTransform += OUString::valueOf( -aRect.Top() ); aTransform += B2UCONST( ")" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTransform, aTransform ); { SvXMLElementExport aElemG2( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); GDIMetaFile aTmpMtf; if( pHatch ) mpVDev->AddHatchActions( rPolyPoly, *pHatch, aTmpMtf ); else if ( pGradient ) mpVDev->AddGradientActions( rPolyPoly.GetBoundRect(), *pGradient, aTmpMtf ); ImplWriteActions( aTmpMtf, nWriteFlags, NULL ); } } } ::rtl::OUString aPatternStyle; aPatternStyle += B2UCONST( "fill:url(#" ); aPatternStyle += aPatternId; aPatternStyle += B2UCONST( ")" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStyle, aPatternStyle ); ImplWritePolyPolygon( rPolyPoly, sal_False ); } } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteGradientEx( const PolyPolygon& rPolyPoly, const Gradient& rGradient, sal_uInt32 nWriteFlags, sal_Bool bApplyMapping ) { PolyPolygon aPolyPoly; if( bApplyMapping ) ImplMap( rPolyPoly, aPolyPoly ); else aPolyPoly = rPolyPoly; if ( rGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL ) { ImplWriteGradientLinear( aPolyPoly, rGradient ); } else { ImplWritePattern( aPolyPoly, NULL, &rGradient, nWriteFlags ); } } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteGradientLinear( const PolyPolygon& rPolyPoly, const Gradient& rGradient ) { if( rPolyPoly.Count() ) { SvXMLElementExport aElemG( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); ::rtl::OUString aGradientId; aGradientId += B2UCONST( "gradient" ); aGradientId += OUString::valueOf( mnCurGradientId++ ); { SvXMLElementExport aElemDefs( mrExport, XML_NAMESPACE_NONE, aXMLElemDefs, sal_True, sal_True ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, aGradientId ); { Rectangle aTmpRect, aRect; Point aTmpCenter, aCenter; rGradient.GetBoundRect( rPolyPoly.GetBoundRect(), aTmpRect, aTmpCenter ); ImplMap( aTmpRect, aRect ); ImplMap( aTmpCenter, aCenter ); const sal_uInt16 nAngle = rGradient.GetAngle() % 3600; Polygon aPoly( 2 ); // Setting x value of a gradient vector to rotation center to // place a gradient vector in a target polygon. // This would help editing it in SVG editors like inkscape. aPoly[ 0 ].X() = aPoly[ 1 ].X() = aCenter.X(); aPoly[ 0 ].Y() = aRect.Top(); aPoly[ 1 ].Y() = aRect.Bottom(); aPoly.Rotate( aCenter, nAngle ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX1, OUString::valueOf( aPoly[ 0 ].X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY1, OUString::valueOf( aPoly[ 0 ].Y() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX2, OUString::valueOf( aPoly[ 1 ].X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY2, OUString::valueOf( aPoly[ 1 ].Y() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrGradientUnits, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "userSpaceOnUse" ) ) ); } { SvXMLElementExport aElemLinearGradient( mrExport, XML_NAMESPACE_NONE, aXMLElemLinearGradient, sal_True, sal_True ); const Color aStartColor = ImplGetColorWithIntensity( rGradient.GetStartColor(), rGradient.GetStartIntensity() ); const Color aEndColor = ImplGetColorWithIntensity( rGradient.GetEndColor(), rGradient.GetEndIntensity() ); double fBorderOffset = rGradient.GetBorder() / 100.0; const sal_uInt16 nSteps = rGradient.GetSteps(); if( rGradient.GetStyle() == GradientStyle_LINEAR ) { // Emulate non-smooth gradient if( 0 < nSteps && nSteps < 100 ) { double fOffsetStep = ( 1.0 - fBorderOffset ) / (double)nSteps; for( sal_uInt16 i = 0; i < nSteps; i++ ) { Color aColor = ImplGetGradientColor( aStartColor, aEndColor, i / (double) nSteps ); ImplWriteGradientStop( aColor, fBorderOffset + ( i + 1 ) * fOffsetStep ); aColor = ImplGetGradientColor( aStartColor, aEndColor, ( i + 1 ) / (double) nSteps ); ImplWriteGradientStop( aColor, fBorderOffset + ( i + 1 ) * fOffsetStep ); } } else { ImplWriteGradientStop( aStartColor, fBorderOffset ); ImplWriteGradientStop( aEndColor, 1.0 ); } } else { fBorderOffset /= 2; // Emulate non-smooth gradient if( 0 < nSteps && nSteps < 100 ) { double fOffsetStep = ( 0.5 - fBorderOffset ) / (double)nSteps; // Upper half for( sal_uInt16 i = 0; i < nSteps; i++ ) { Color aColor = ImplGetGradientColor( aEndColor, aStartColor, i / (double) nSteps ); ImplWriteGradientStop( aColor, fBorderOffset + i * fOffsetStep ); aColor = ImplGetGradientColor( aEndColor, aStartColor, (i + 1 ) / (double) nSteps ); ImplWriteGradientStop( aColor, fBorderOffset + i * fOffsetStep ); } // Lower half for( sal_uInt16 i = 0; i < nSteps; i++ ) { Color aColor = ImplGetGradientColor( aStartColor, aEndColor, i / (double) nSteps ); ImplWriteGradientStop( aColor, 0.5 + (i + 1) * fOffsetStep ); aColor = ImplGetGradientColor( aStartColor, aEndColor, (i + 1 ) / (double) nSteps ); ImplWriteGradientStop( aColor, 0.5 + (i + 1) * fOffsetStep ); } } else { ImplWriteGradientStop( aEndColor, fBorderOffset ); ImplWriteGradientStop( aStartColor, 0.5 ); ImplWriteGradientStop( aEndColor, 1.0 - fBorderOffset ); } } } } ::rtl::OUString aGradientStyle; aGradientStyle += B2UCONST( "fill:" ); aGradientStyle += B2UCONST( "url(#" ); aGradientStyle += aGradientId; aGradientStyle += B2UCONST( ")" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStyle, aGradientStyle ); ImplWritePolyPolygon( rPolyPoly, sal_False ); } } void SVGActionWriter::ImplWriteGradientStop( const Color& rColor, double fOffset ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, rtl::OUString::valueOf( fOffset ) ); ::rtl::OUString aStyle, aColor; aStyle += B2UCONST( "stop-color:" ); SVGAttributeWriter::ImplGetColorStr ( rColor, aColor ); aStyle += aColor; mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStyle, aStyle ); { SvXMLElementExport aElemStartStop( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, sal_True, sal_True ); } } Color SVGActionWriter::ImplGetColorWithIntensity( const Color& rColor, sal_uInt16 nIntensity ) { sal_uInt8 nNewRed = (sal_uInt8)( (long)rColor.GetRed() * nIntensity / 100L ); sal_uInt8 nNewGreen = (sal_uInt8)( (long)rColor.GetGreen() * nIntensity / 100L ); sal_uInt8 nNewBlue = (sal_uInt8)( (long)rColor.GetBlue() * nIntensity / 100L ); return Color( nNewRed, nNewGreen, nNewBlue); } Color SVGActionWriter::ImplGetGradientColor( const Color& rStartColor, const Color& rEndColor, double fOffset ) { long nRedStep = rEndColor.GetRed() - rStartColor.GetRed(); long nNewRed = rStartColor.GetRed() + (long)( nRedStep * fOffset ); nNewRed = ( nNewRed < 0 ) ? 0 : ( nNewRed > 0xFF) ? 0xFF : nNewRed; long nGreenStep = rEndColor.GetGreen() - rStartColor.GetGreen(); long nNewGreen = rStartColor.GetGreen() + (long)( nGreenStep * fOffset ); nNewGreen = ( nNewGreen < 0 ) ? 0 : ( nNewGreen > 0xFF) ? 0xFF : nNewGreen; long nBlueStep = rEndColor.GetBlue() - rStartColor.GetBlue(); long nNewBlue = rStartColor.GetBlue() + (long)( nBlueStep * fOffset ); nNewBlue = ( nNewBlue < 0 ) ? 0 : ( nNewBlue > 0xFF) ? 0xFF : nNewBlue; return Color( (sal_uInt8)nNewRed, (sal_uInt8)nNewGreen, (sal_uInt8)nNewBlue ); } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteMask( GDIMetaFile& rMtf, const Point& rDestPt, const Size& rDestSize, const Gradient& rGradient, sal_uInt32 nWriteFlags ) { Point aSrcPt( rMtf.GetPrefMapMode().GetOrigin() ); const Size aSrcSize( rMtf.GetPrefSize() ); const double fScaleX = aSrcSize.Width() ? (double) rDestSize.Width() / aSrcSize.Width() : 1.0; const double fScaleY = aSrcSize.Height() ? (double) rDestSize.Height() / aSrcSize.Height() : 1.0; long nMoveX, nMoveY; if( fScaleX != 1.0 || fScaleY != 1.0 ) { rMtf.Scale( fScaleX, fScaleY ); aSrcPt.X() = FRound( aSrcPt.X() * fScaleX ), aSrcPt.Y() = FRound( aSrcPt.Y() * fScaleY ); } nMoveX = rDestPt.X() - aSrcPt.X(), nMoveY = rDestPt.Y() - aSrcPt.Y(); if( nMoveX || nMoveY ) rMtf.Move( nMoveX, nMoveY ); ::rtl::OUString aMaskId; aMaskId += B2UCONST( "mask" ); aMaskId += OUString::valueOf( mnCurMaskId++ ); { SvXMLElementExport aElemDefs( mrExport, XML_NAMESPACE_NONE, aXMLElemDefs, sal_True, sal_True ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, aMaskId ); { SvXMLElementExport aElemMask( mrExport, XML_NAMESPACE_NONE, aXMLElemMask, sal_True, sal_True ); const PolyPolygon aPolyPolygon( PolyPolygon( Rectangle( rDestPt, rDestSize ) ) ); Gradient aGradient( rGradient ); // swap gradient stops to adopt SVG mask Color aTmpColor( aGradient.GetStartColor() ); sal_uInt16 nTmpIntensity( aGradient.GetStartIntensity() ); aGradient.SetStartColor( aGradient.GetEndColor() ); aGradient.SetStartIntensity( aGradient.GetEndIntensity() ) ; aGradient.SetEndColor( aTmpColor ); aGradient.SetEndIntensity( nTmpIntensity ); ImplWriteGradientEx( aPolyPolygon, aGradient, nWriteFlags ); } } ::rtl::OUString aMaskStyle; aMaskStyle += B2UCONST( "mask:url(#" ); aMaskStyle += aMaskId; aMaskStyle += B2UCONST( ")" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStyle, aMaskStyle ); { SvXMLElementExport aElemG( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); mpVDev->Push(); ImplWriteActions( rMtf, nWriteFlags, NULL ); mpVDev->Pop(); } } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteText( const Point& rPos, const String& rText, const sal_Int32* pDXArray, long nWidth, sal_Bool bApplyMapping ) { const FontMetric aMetric( mpVDev->GetFontMetric() ); bool bTextSpecial = aMetric.IsShadow() || aMetric.IsOutline() || (aMetric.GetRelief() != RELIEF_NONE); if( !bTextSpecial ) { ImplWriteText( rPos, rText, pDXArray, nWidth, mpVDev->GetTextColor(), bApplyMapping ); } else { if( aMetric.GetRelief() != RELIEF_NONE ) { Color aReliefColor( COL_LIGHTGRAY ); Color aTextColor( mpVDev->GetTextColor() ); if ( aTextColor.GetColor() == COL_BLACK ) aTextColor = Color( COL_WHITE ); if ( aTextColor.GetColor() == COL_WHITE ) aReliefColor = Color( COL_BLACK ); Point aPos( rPos ); Point aOffset( 6, 6 ); if ( aMetric.GetRelief() == RELIEF_ENGRAVED ) { aPos -= aOffset; } else { aPos += aOffset; } ImplWriteText( aPos, rText, pDXArray, nWidth, aReliefColor, bApplyMapping ); ImplWriteText( rPos, rText, pDXArray, nWidth, aTextColor, bApplyMapping ); } else { if( aMetric.IsShadow() ) { long nOff = 1 + ((aMetric.GetLineHeight()-24)/24); if ( aMetric.IsOutline() ) nOff += 6; Color aTextColor( mpVDev->GetTextColor() ); Color aShadowColor = Color( COL_BLACK ); if ( (aTextColor.GetColor() == COL_BLACK) || (aTextColor.GetLuminance() < 8) ) aShadowColor = Color( COL_LIGHTGRAY ); Point aPos( rPos ); aPos += Point( nOff, nOff ); ImplWriteText( aPos, rText, pDXArray, nWidth, aShadowColor, bApplyMapping ); if( !aMetric.IsOutline() ) { ImplWriteText( rPos, rText, pDXArray, nWidth, aTextColor, bApplyMapping ); } } if( aMetric.IsOutline() ) { Point aPos = rPos + Point( -6, -6 ); ImplWriteText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor(), bApplyMapping ); aPos = rPos + Point( +6, +6); ImplWriteText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor(), bApplyMapping ); aPos = rPos + Point( -6, +0); ImplWriteText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor(), bApplyMapping ); aPos = rPos + Point( -6, +6); ImplWriteText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor(), bApplyMapping ); aPos = rPos + Point( +0, +6); ImplWriteText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor(), bApplyMapping ); aPos = rPos + Point( +0, -6); ImplWriteText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor(), bApplyMapping ); aPos = rPos + Point( +6, -1); ImplWriteText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor(), bApplyMapping ); aPos = rPos + Point( +6, +0); ImplWriteText( aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor(), bApplyMapping ); ImplWriteText( rPos, rText, pDXArray, nWidth, Color( COL_WHITE ), bApplyMapping ); } } } } void SVGActionWriter::ImplWriteText( const Point& rPos, const String& rText, const sal_Int32* pDXArray, long nWidth, Color aTextColor, sal_Bool bApplyMapping ) { sal_Int32 nLen = rText.Len(); Size aNormSize; Point aPos; Point aBaseLinePos( rPos ); const FontMetric aMetric( mpVDev->GetFontMetric() ); const Font& rFont = mpVDev->GetFont(); if( rFont.GetAlign() == ALIGN_TOP ) aBaseLinePos.Y() += aMetric.GetAscent(); else if( rFont.GetAlign() == ALIGN_BOTTOM ) aBaseLinePos.Y() -= aMetric.GetDescent(); if( bApplyMapping ) ImplMap( rPos, aPos ); else aPos = rPos; boost::shared_array xTmpArray(new sal_Int32[nLen]); // get text sizes if( pDXArray ) { aNormSize = Size( mpVDev->GetTextWidth( rText ), 0 ); memcpy(xTmpArray.get(), pDXArray, nLen * sizeof(sal_Int32)); } else { aNormSize = Size( mpVDev->GetTextArray( rText, xTmpArray.get() ), 0 ); } sal_Int32* pDX = xTmpArray.get(); // if text is rotated, set transform matrix at new g element if( rFont.GetOrientation() ) { Point aRot( aPos ); String aTransform; aTransform = String( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "translate" ) ) ); aTransform += '('; aTransform += String( ::rtl::OUString::valueOf( aRot.X() ) ); aTransform += ','; aTransform += String( ::rtl::OUString::valueOf( aRot.Y() ) ); aTransform += ')'; aTransform += String( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( " rotate" ) ) ); aTransform += '('; aTransform += String( ::rtl::OUString::valueOf( rFont.GetOrientation() * -0.1 ) ); aTransform += ')'; aTransform += String( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( " translate" ) ) ); aTransform += '('; aTransform += String( ::rtl::OUString::valueOf( -aRot.X() ) ); aTransform += ','; aTransform += String( ::rtl::OUString::valueOf( -aRot.Y() ) ); aTransform += ')'; mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTransform, aTransform ); } mpContext->AddPaintAttr( COL_TRANSPARENT, aTextColor ); // for each line of text there should be at least one group element SvXMLElementExport aSVGGElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_False ); sal_Bool bIsPlaceholderField = sal_False; if( mbIsPlacehlolderShape ) { OUString sTextContent = rText; bIsPlaceholderField = sTextContent.match( sPlaceholderTag ); // for a placeholder text field we export only one svg element if( bIsPlaceholderField ) { OUString sCleanTextContent; static const sal_Int32 nFrom = sPlaceholderTag.getLength(); if( sTextContent.getLength() > nFrom ) { sCleanTextContent = sTextContent.copy( nFrom ); } mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "PlaceholderText" ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, ::rtl::OUString::valueOf( aPos.X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, ::rtl::OUString::valueOf( aPos.Y() ) ); { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, aXMLElemText, sal_True, sal_False ); // At least for the single slide case we need really to export placeholder text mrExport.GetDocHandler()->characters( sCleanTextContent ); } } } if( !bIsPlaceholderField ) { if( nLen > 1 ) { aNormSize.Width() = pDX[ nLen - 2 ] + mpVDev->GetTextWidth( rtl::OUString(rText.GetChar(nLen - 1)) ); if( nWidth && aNormSize.Width() && ( nWidth != aNormSize.Width() ) ) { long i; const double fFactor = (double) nWidth / aNormSize.Width(); for( i = 0; i < ( nLen - 1 ); i++ ) pDX[ i ] = FRound( pDX[ i ] * fFactor ); } else { ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xBI( ::vcl::unohelper::CreateBreakIterator() ); const ::com::sun::star::lang::Locale& rLocale = Application::GetSettings().GetLanguageTag().getLocale(); sal_Int32 nCurPos = 0, nLastPos = 0, nX = aPos.X(); // write single glyphs at absolute text positions for( sal_Bool bCont = sal_True; bCont; ) { sal_Int32 nCount = 1; nLastPos = nCurPos; nCurPos = xBI->nextCharacters( rText, nCurPos, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount ); nCount = nCurPos - nLastPos; bCont = ( nCurPos < rText.Len() ) && nCount; if( nCount ) { const ::rtl::OUString aGlyph( rText.Copy( nLastPos, nCount ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, ::rtl::OUString::valueOf( nX ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, ::rtl::OUString::valueOf( aPos.Y() ) ); { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, aXMLElemText, sal_True, sal_False ); mrExport.GetDocHandler()->characters( aGlyph ); } if( bCont ) { // #118796# do NOT access pDXArray, it may be zero (!) nX = aPos.X() + pDX[ nCurPos - 1 ]; } } } } } else { mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, ::rtl::OUString::valueOf( aPos.X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, ::rtl::OUString::valueOf( aPos.Y() ) ); { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, aXMLElemText, sal_True, sal_False ); mrExport.GetDocHandler()->characters( rText ); } } } if( !mrExport.IsUseNativeTextDecoration() ) { if( rFont.GetStrikeout() != STRIKEOUT_NONE || rFont.GetUnderline() != UNDERLINE_NONE ) { Polygon aPoly( 4 ); const long nLineHeight = Max( (long) FRound( aMetric.GetLineHeight() * 0.05 ), (long) 1 ); if( rFont.GetStrikeout() ) { const long nYLinePos = aBaseLinePos.Y() - FRound( aMetric.GetAscent() * 0.26 ); aPoly[ 0 ].X() = aBaseLinePos.X(); aPoly[ 0 ].Y() = nYLinePos - ( nLineHeight >> 1 ); aPoly[ 1 ].X() = aBaseLinePos.X() + aNormSize.Width() - 1; aPoly[ 1 ].Y() = aPoly[ 0 ].Y(); aPoly[ 2 ].X() = aPoly[ 1 ].X(); aPoly[ 2 ].Y() = aPoly[ 0 ].Y() + nLineHeight - 1; aPoly[ 3 ].X() = aPoly[ 0 ].X(); aPoly[ 3 ].Y() = aPoly[ 2 ].Y(); ImplWritePolyPolygon( aPoly, sal_False ); } if( rFont.GetUnderline() ) { const long nYLinePos = aBaseLinePos.Y() + ( nLineHeight << 1 ); aPoly[ 0 ].X() = aBaseLinePos.X(); aPoly[ 0 ].Y() = nYLinePos - ( nLineHeight >> 1 ); aPoly[ 1 ].X() = aBaseLinePos.X() + aNormSize.Width() - 1; aPoly[ 1 ].Y() = aPoly[ 0 ].Y(); aPoly[ 2 ].X() = aPoly[ 1 ].X(); aPoly[ 2 ].Y() = aPoly[ 0 ].Y() + nLineHeight - 1; aPoly[ 3 ].X() = aPoly[ 0 ].X(); aPoly[ 3 ].Y() = aPoly[ 2 ].Y(); ImplWritePolyPolygon( aPoly, sal_False ); } } } } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteBmp( const BitmapEx& rBmpEx, const Point& rPt, const Size& rSz, const Point& rSrcPt, const Size& rSrcSz, sal_Bool bApplyMapping ) { if( !!rBmpEx ) { BitmapEx aBmpEx( rBmpEx ); Point aPoint = Point(); const Rectangle aBmpRect( aPoint, rBmpEx.GetSizePixel() ); const Rectangle aSrcRect( rSrcPt, rSrcSz ); if( aSrcRect != aBmpRect ) aBmpEx.Crop( aSrcRect ); if( !!aBmpEx ) { SvMemoryStream aOStm( 65535, 65535 ); if( GraphicConverter::Export( aOStm, rBmpEx, CVT_PNG ) == ERRCODE_NONE ) { Point aPt; Size aSz; Sequence< sal_Int8 > aSeq( (sal_Int8*) aOStm.GetData(), aOStm.Tell() ); rtl::OUStringBuffer aBuffer; aBuffer.appendAscii( "data:image/png;base64," ); ::sax::Converter::encodeBase64( aBuffer, aSeq ); if( bApplyMapping ) { ImplMap( rPt, aPt ); ImplMap( rSz, aSz ); } else { aPt = rPt; aSz = rSz; } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, OUString::valueOf( aPt.X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, OUString::valueOf( aPt.Y() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrWidth, OUString::valueOf( aSz.Width() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrHeight, OUString::valueOf( aSz.Height() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, aBuffer.makeStringAndClear() ); { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, aXMLElemImage, sal_True, sal_True ); } } } } } // ----------------------------------------------------------------------------- void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, sal_uInt32 nWriteFlags, const ::rtl::OUString* pElementId, const Reference< XShape >* pxShape, const GDIMetaFile* pTextEmbeddedBitmapMtf ) { // need a counter fo rthe actions written per shape to avoid double ID // generation sal_Int32 nEntryCount(0); if( mnInnerMtfCount ) nWriteFlags |= SVGWRITER_NO_SHAPE_COMMENTS; #if OSL_DEBUG_LEVEL > 0 bool bIsTextShape = false; if( !mrExport.IsUsePositionedCharacters() && pxShape && Reference< XText >( *pxShape, UNO_QUERY ).is() ) { bIsTextShape = true; } #endif mbIsPlacehlolderShape = false; if( ( pElementId != NULL ) && ( *pElementId == sPlaceholderTag ) ) { mbIsPlacehlolderShape = true; // since we utilize pElementId in an improper way we reset it to NULL before to go on pElementId = NULL; } for( sal_uLong nCurAction = 0, nCount = rMtf.GetActionSize(); nCurAction < nCount; nCurAction++ ) { const MetaAction* pAction = rMtf.GetAction( nCurAction ); const sal_uInt16 nType = pAction->GetType(); #if OSL_DEBUG_LEVEL > 0 if( bIsTextShape ) { try { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_NONE, "desc", sal_False, sal_False ); OUStringBuffer sType; sType.append(static_cast(nType)); if (pAction && (nType == META_COMMENT_ACTION)) { sType.append(": "); const MetaCommentAction* pA = (const MetaCommentAction*) pAction; rtl::OString sComment = pA->GetComment(); if (!sComment.isEmpty()) { OUString ssComment = OUString( sComment.getStr(), sComment.getLength(), RTL_TEXTENCODING_UTF8 ); sType.append(ssComment); } if (sComment.equalsIgnoreAsciiCaseL( RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_BEGIN"))) { sal_uInt8 const*const pData = pA->GetData(); if (pData && (pA->GetDataSize())) { sal_uInt16 sz = (sal_uInt16)((pA->GetDataSize()) / 2); if (sz) { sType.append("; "); sType.append( reinterpret_cast(pData), sz); } } } } if (sType.getLength()) { mrExport.GetDocHandler()->characters( sType.makeStringAndClear()); } } catch( ... ) { const MetaCommentAction* pA = (const MetaCommentAction*) pAction; OSL_FAIL( pA->GetComment().getStr() ); } } #endif switch( nType ) { case( META_PIXEL_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaPixelAction* pA = (const MetaPixelAction*) pAction; mpContext->AddPaintAttr( pA->GetColor(), pA->GetColor() ); ImplWriteLine( pA->GetPoint(), pA->GetPoint(), &pA->GetColor() ); } } break; case( META_POINT_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaPointAction* pA = (const MetaPointAction*) pAction; mpContext->AddPaintAttr( mpVDev->GetLineColor(), mpVDev->GetLineColor() ); ImplWriteLine( pA->GetPoint(), pA->GetPoint(), NULL ); } } break; case( META_LINE_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaLineAction* pA = (const MetaLineAction*) pAction; mpContext->AddPaintAttr( mpVDev->GetLineColor(), mpVDev->GetLineColor() ); ImplWriteLine( pA->GetStartPoint(), pA->GetEndPoint(), NULL ); } } break; case( META_RECT_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { mpContext->AddPaintAttr( mpVDev->GetLineColor(), mpVDev->GetFillColor() ); ImplWriteRect( ( (const MetaRectAction*) pAction )->GetRect(), 0, 0 ); } } break; case( META_ROUNDRECT_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pAction; mpContext->AddPaintAttr( mpVDev->GetLineColor(), mpVDev->GetFillColor() ); ImplWriteRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() ); } } break; case( META_ELLIPSE_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaEllipseAction* pA = (const MetaEllipseAction*) pAction; const Rectangle& rRect = pA->GetRect(); mpContext->AddPaintAttr( mpVDev->GetLineColor(), mpVDev->GetFillColor() ); ImplWriteEllipse( rRect.Center(), rRect.GetWidth() >> 1, rRect.GetHeight() >> 1 ); } } break; case( META_ARC_ACTION ): case( META_PIE_ACTION ): case( META_CHORD_ACTION ): case( META_POLYGON_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { Polygon aPoly; switch( nType ) { case( META_ARC_ACTION ): { const MetaArcAction* pA = (const MetaArcAction*) pAction; aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC ); } break; case( META_PIE_ACTION ): { const MetaPieAction* pA = (const MetaPieAction*) pAction; aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE ); } break; case( META_CHORD_ACTION ): { const MetaChordAction* pA = (const MetaChordAction*) pAction; aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD ); } break; case( META_POLYGON_ACTION ): aPoly = ( (const MetaPolygonAction*) pAction )->GetPolygon(); break; } if( aPoly.GetSize() ) { mpContext->AddPaintAttr( mpVDev->GetLineColor(), mpVDev->GetFillColor() ); ImplWritePolyPolygon( aPoly, sal_False ); } } } break; case( META_POLYLINE_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaPolyLineAction* pA = (const MetaPolyLineAction*) pAction; const Polygon& rPoly = pA->GetPolygon(); if( rPoly.GetSize() ) { mpContext->AddPaintAttr( mpVDev->GetLineColor(), Color( COL_TRANSPARENT ) ); ImplWritePolyPolygon( rPoly, sal_True ); } } } break; case( META_POLYPOLYGON_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*) pAction; const PolyPolygon& rPolyPoly = pA->GetPolyPolygon(); if( rPolyPoly.Count() ) { mpContext->AddPaintAttr( mpVDev->GetLineColor(), mpVDev->GetFillColor() ); ImplWritePolyPolygon( rPolyPoly, sal_False ); } } } break; case( META_GRADIENT_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaGradientAction* pA = (const MetaGradientAction*) pAction; const Polygon aRectPoly( pA->GetRect() ); const PolyPolygon aRectPolyPoly( aRectPoly ); ImplWriteGradientEx( aRectPolyPoly, pA->GetGradient(), nWriteFlags ); } } break; case( META_GRADIENTEX_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaGradientExAction* pA = (const MetaGradientExAction*) pAction; ImplWriteGradientEx( pA->GetPolyPolygon(), pA->GetGradient(), nWriteFlags ); } } break; case META_HATCH_ACTION: { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaHatchAction* pA = (const MetaHatchAction*) pAction; ImplWritePattern( pA->GetPolyPolygon(), &pA->GetHatch(), NULL, nWriteFlags ); } } break; case( META_TRANSPARENT_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaTransparentAction* pA = (const MetaTransparentAction*) pAction; const PolyPolygon& rPolyPoly = pA->GetPolyPolygon(); if( rPolyPoly.Count() ) { Color aNewLineColor( mpVDev->GetLineColor() ), aNewFillColor( mpVDev->GetFillColor() ); aNewLineColor.SetTransparency( sal::static_int_cast( FRound( pA->GetTransparence() * 2.55 ) ) ); aNewFillColor.SetTransparency( sal::static_int_cast( FRound( pA->GetTransparence() * 2.55 ) ) ); mpContext->AddPaintAttr( aNewLineColor, aNewFillColor ); ImplWritePolyPolygon( rPolyPoly, sal_False ); } } } break; case( META_FLOATTRANSPARENT_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*) pAction; GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() ); ImplWriteMask( aTmpMtf, pA->GetPoint(), pA->GetSize(), pA->GetGradient(), nWriteFlags ); } } break; case( META_EPS_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaEPSAction* pA = (const MetaEPSAction*) pAction; const GDIMetaFile aGDIMetaFile( pA->GetSubstitute() ); sal_Bool bFound = sal_False; for( sal_uInt32 k = 0, nCount2 = aGDIMetaFile.GetActionSize(); ( k < nCount2 ) && !bFound; ++k ) { const MetaAction* pSubstAct = aGDIMetaFile.GetAction( k ); if( pSubstAct->GetType() == META_BMPSCALE_ACTION ) { bFound = sal_True; const MetaBmpScaleAction* pBmpScaleAction = (const MetaBmpScaleAction*) pSubstAct; ImplWriteBmp( pBmpScaleAction->GetBitmap(), pA->GetPoint(), pA->GetSize(), Point(), pBmpScaleAction->GetBitmap().GetSizePixel() ); } } } } break; case( META_COMMENT_ACTION ): { const MetaCommentAction* pA = (const MetaCommentAction*) pAction; String aSkipComment; if( ( pA->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_BEGIN")) ) && ( nWriteFlags & SVGWRITER_WRITE_FILL ) ) { const MetaGradientExAction* pGradAction = NULL; sal_Bool bDone = sal_False; while( !bDone && ( ++nCurAction < nCount ) ) { pAction = rMtf.GetAction( nCurAction ); if( pAction->GetType() == META_GRADIENTEX_ACTION ) pGradAction = (const MetaGradientExAction*) pAction; else if( ( pAction->GetType() == META_COMMENT_ACTION ) && ( ( (const MetaCommentAction*) pAction )->GetComment(). equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_END")) ) ) { bDone = sal_True; } } if( pGradAction ) ImplWriteGradientEx( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), nWriteFlags ); } else if( ( pA->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XPATHFILL_SEQ_BEGIN")) ) && ( nWriteFlags & SVGWRITER_WRITE_FILL ) && !( nWriteFlags & SVGWRITER_NO_SHAPE_COMMENTS ) && pA->GetDataSize() ) { // write open shape in every case if( mapCurShape.get() ) { ImplWriteShape( *mapCurShape ); mapCurShape.reset(); } SvMemoryStream aMemStm( (void*) pA->GetData(), pA->GetDataSize(), STREAM_READ ); SvtGraphicFill aFill; aMemStm >> aFill; sal_Bool bGradient = SvtGraphicFill::fillGradient == aFill.getFillType() && ( SvtGraphicFill::gradientLinear == aFill.getGradientType() || SvtGraphicFill::gradientRadial == aFill.getGradientType() ); sal_Bool bSkip = ( SvtGraphicFill::fillSolid == aFill.getFillType() || bGradient ); if( bSkip ) { PolyPolygon aShapePolyPoly; aFill.getPath( aShapePolyPoly ); if( aShapePolyPoly.Count() ) { mapCurShape.reset( new SVGShapeDescriptor ); if( pElementId ) { mapCurShape->maId = *pElementId + B2UCONST("_") + ::rtl::OUString::valueOf(nEntryCount++); } mapCurShape->maShapePolyPoly = aShapePolyPoly; mapCurShape->maShapeFillColor = aFill.getFillColor(); mapCurShape->maShapeFillColor.SetTransparency( (sal_uInt8) FRound( 255.0 * aFill.getTransparency() ) ); if( bGradient ) { // step through following actions until the first Gradient/GradientEx action is found while( !mapCurShape->mapShapeGradient.get() && bSkip && ( ++nCurAction < nCount ) ) { pAction = rMtf.GetAction( nCurAction ); if( ( pAction->GetType() == META_COMMENT_ACTION ) && ( ( (const MetaCommentAction*) pAction )->GetComment(). equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XPATHFILL_SEQ_END")) ) ) { bSkip = sal_False; } else if( pAction->GetType() == META_GRADIENTEX_ACTION ) { mapCurShape->mapShapeGradient.reset( new Gradient( static_cast< const MetaGradientExAction* >( pAction )->GetGradient() ) ); } else if( pAction->GetType() == META_GRADIENT_ACTION ) { mapCurShape->mapShapeGradient.reset( new Gradient( static_cast< const MetaGradientAction* >( pAction )->GetGradient() ) ); } } } } else bSkip = sal_False; } // skip rest of comment while( bSkip && ( ++nCurAction < nCount ) ) { pAction = rMtf.GetAction( nCurAction ); if( ( pAction->GetType() == META_COMMENT_ACTION ) && ( ( (const MetaCommentAction*) pAction )->GetComment(). equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XPATHFILL_SEQ_END")) ) ) { bSkip = sal_False; } } } else if( ( pA->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XPATHSTROKE_SEQ_BEGIN")) ) && ( nWriteFlags & SVGWRITER_WRITE_FILL ) && !( nWriteFlags & SVGWRITER_NO_SHAPE_COMMENTS ) && pA->GetDataSize() ) { SvMemoryStream aMemStm( (void*) pA->GetData(), pA->GetDataSize(), STREAM_READ ); SvtGraphicStroke aStroke; PolyPolygon aStartArrow, aEndArrow; aMemStm >> aStroke; aStroke.getStartArrow( aStartArrow ); aStroke.getEndArrow( aEndArrow ); // Currently no support for strokes with start/end arrow(s) sal_Bool bSkip = ( !aStartArrow.Count() && !aEndArrow.Count() ); if( bSkip ) { Polygon aPoly; aStroke.getPath(aPoly); if(mapCurShape.get()) { if(1 != mapCurShape->maShapePolyPoly.Count() || !mapCurShape->maShapePolyPoly[0].IsEqual(aPoly)) { // this path action is not covering the same path than the already existing // fill polypolygon, so write out the fill polygon ImplWriteShape( *mapCurShape ); mapCurShape.reset(); } } if( !mapCurShape.get() ) { mapCurShape.reset( new SVGShapeDescriptor ); if( pElementId ) { mapCurShape->maId = *pElementId + B2UCONST("_") + ::rtl::OUString::valueOf(nEntryCount++); } aStroke.getPath( aPoly ); mapCurShape->maShapePolyPoly = aPoly; } mapCurShape->maShapeLineColor = mpVDev->GetLineColor(); mapCurShape->maShapeLineColor.SetTransparency( (sal_uInt8) FRound( aStroke.getTransparency() * 255.0 ) ); mapCurShape->mnStrokeWidth = FRound( aStroke.getStrokeWidth() ); aStroke.getDashArray( mapCurShape->maDashArray ); } // support for LineJoin switch(aStroke.getJoinType()) { default: /* SvtGraphicStroke::joinMiter, SvtGraphicStroke::joinNone */ { mapCurShape->maLineJoin = basegfx::B2DLINEJOIN_MITER; break; } case SvtGraphicStroke::joinRound: { mapCurShape->maLineJoin = basegfx::B2DLINEJOIN_ROUND; break; } case SvtGraphicStroke::joinBevel: { mapCurShape->maLineJoin = basegfx::B2DLINEJOIN_BEVEL; break; } } // support for LineCap switch(aStroke.getCapType()) { default: /* SvtGraphicStroke::capButt */ { mapCurShape->maLineCap = com::sun::star::drawing::LineCap_BUTT; break; } case SvtGraphicStroke::capRound: { mapCurShape->maLineCap = com::sun::star::drawing::LineCap_ROUND; break; } case SvtGraphicStroke::capSquare: { mapCurShape->maLineCap = com::sun::star::drawing::LineCap_SQUARE; break; } } // write open shape in every case if( mapCurShape.get() ) { ImplWriteShape( *mapCurShape ); mapCurShape.reset(); } // skip rest of comment while( bSkip && ( ++nCurAction < nCount ) ) { pAction = rMtf.GetAction( nCurAction ); if( ( pAction->GetType() == META_COMMENT_ACTION ) && ( ( (const MetaCommentAction*) pAction )->GetComment(). equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XPATHSTROKE_SEQ_END")) ) ) { bSkip = sal_False; } } } else if( !mrExport.IsUsePositionedCharacters() && ( nWriteFlags & SVGWRITER_WRITE_TEXT ) ) { if( ( pA->GetComment().equalsIgnoreAsciiCaseL( RTL_CONSTASCII_STRINGPARAM( "XTEXT_PAINTSHAPE_BEGIN" ) ) ) ) { if( pxShape ) { Reference< XText > xText( *pxShape, UNO_QUERY ); if( xText.is() ) maTextWriter.setTextShape( xText, pTextEmbeddedBitmapMtf ); } maTextWriter.createParagraphEnumeration(); { // nTextFound == -1 => no text found // nTextFound == 0 => no text found and end of text shape reached // nTextFound == 1 => text found! sal_Int32 nTextFound = -1; while( ( nTextFound < 0 ) && ( nCurAction < nCount ) ) { nTextFound = maTextWriter.setTextPosition( rMtf, nCurAction ); } // We found some text in the current text shape. if( nTextFound > 0 ) { maTextWriter.setTextProperties( rMtf, nCurAction ); maTextWriter.startTextShape(); } // We reached the end of the current text shape // without finding any text. So we need to go back // by one action in order to handle the // XTEXT_PAINTSHAPE_END action because on the next // loop the nCurAction is incremented by one. else { --nCurAction; } } } else if( ( pA->GetComment().equalsIgnoreAsciiCaseL( RTL_CONSTASCII_STRINGPARAM( "XTEXT_PAINTSHAPE_END" ) ) ) ) { maTextWriter.endTextShape(); } else if( ( pA->GetComment().equalsIgnoreAsciiCaseL( RTL_CONSTASCII_STRINGPARAM( "XTEXT_EOP" ) ) ) ) { const MetaAction* pNextAction = rMtf.GetAction( nCurAction + 1 ); if( !( ( pNextAction->GetType() == META_COMMENT_ACTION ) && ( ( (const MetaCommentAction*) pNextAction )->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XTEXT_PAINTSHAPE_END") ) ) )) { // nTextFound == -1 => no text found and end of paragraph reached // nTextFound == 0 => no text found and end of text shape reached // nTextFound == 1 => text found! sal_Int32 nTextFound = -1; while( ( nTextFound < 0 ) && ( nCurAction < nCount ) ) { nTextFound = maTextWriter.setTextPosition( rMtf, nCurAction ); } // We found a paragraph with some text in the // current text shape. if( nTextFound > 0 ) { maTextWriter.setTextProperties( rMtf, nCurAction ); maTextWriter.startTextParagraph(); } // We reached the end of the current text shape // without finding any text. So we need to go back // by one action in order to handle the // XTEXT_PAINTSHAPE_END action because on the next // loop the nCurAction is incremented by one. else { --nCurAction; } } } else if( ( pA->GetComment().equalsIgnoreAsciiCaseL( RTL_CONSTASCII_STRINGPARAM( "XTEXT_EOL" ) ) ) ) { const MetaAction* pNextAction = rMtf.GetAction( nCurAction + 1 ); if( !( ( pNextAction->GetType() == META_COMMENT_ACTION ) && ( ( (const MetaCommentAction*) pNextAction )->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOP") ) ) ) ) { // nTextFound == -2 => no text found and end of line reached // nTextFound == -1 => no text found and end of paragraph reached // nTextFound == 1 => text found! sal_Int32 nTextFound = -2; while( ( nTextFound < -1 ) && ( nCurAction < nCount ) ) { nTextFound = maTextWriter.setTextPosition( rMtf, nCurAction ); } // We found a line with some text in the current // paragraph. if( nTextFound > 0 ) { maTextWriter.startTextPosition(); } // We reached the end of the current paragraph // without finding any text. So we need to go back // by one action in order to handle the XTEXT_EOP // action because on the next loop the nCurAction is // incremented by one. else { --nCurAction; } } } } } break; case( META_BMP_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaBmpAction* pA = (const MetaBmpAction*) pAction; ImplWriteBmp( pA->GetBitmap(), pA->GetPoint(), mpVDev->PixelToLogic( pA->GetBitmap().GetSizePixel() ), Point(), pA->GetBitmap().GetSizePixel() ); } } break; case( META_BMPSCALE_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*) pAction; // Bitmaps embedded into text shapes are collected and exported elsewhere. if( maTextWriter.isTextShapeStarted() ) { maTextWriter.writeBitmapPlaceholder( pA ); } else { ImplWriteBmp( pA->GetBitmap(), pA->GetPoint(), pA->GetSize(), Point(), pA->GetBitmap().GetSizePixel() ); } } } break; case( META_BMPSCALEPART_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*) pAction; ImplWriteBmp( pA->GetBitmap(), pA->GetDestPoint(), pA->GetDestSize(), pA->GetSrcPoint(), pA->GetSrcSize() ); } } break; case( META_BMPEX_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaBmpExAction* pA = (const MetaBmpExAction*) pAction; ImplWriteBmp( pA->GetBitmapEx(), pA->GetPoint(), mpVDev->PixelToLogic( pA->GetBitmapEx().GetSizePixel() ), Point(), pA->GetBitmapEx().GetSizePixel() ); } } break; case( META_BMPEXSCALE_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pAction; // Bitmaps embedded into text shapes are collected and exported elsewhere. if( maTextWriter.isTextShapeStarted() ) { maTextWriter.writeBitmapPlaceholder( pA ); } else { ImplWriteBmp( pA->GetBitmapEx(), pA->GetPoint(), pA->GetSize(), Point(), pA->GetBitmapEx().GetSizePixel() ); } } } break; case( META_BMPEXSCALEPART_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_FILL ) { const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*) pAction; ImplWriteBmp( pA->GetBitmapEx(), pA->GetDestPoint(), pA->GetDestSize(), pA->GetSrcPoint(), pA->GetSrcSize() ); } } break; case( META_TEXT_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_TEXT ) { const MetaTextAction* pA = (const MetaTextAction*) pAction; const String aText( pA->GetText(), pA->GetIndex(), pA->GetLen() ); if( aText.Len() ) { if( mrExport.IsUsePositionedCharacters() ) { Font aFont = ImplSetCorrectFontHeight(); mpContext->SetFontAttr( aFont ); ImplWriteText( pA->GetPoint(), aText, NULL, 0 ); } else { maTextWriter.writeTextPortion( pA->GetPoint(), aText ); } } } } break; case( META_TEXTRECT_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_TEXT ) { const MetaTextRectAction* pA = (const MetaTextRectAction*) pAction; if (!pA->GetText().isEmpty()) { if( mrExport.IsUsePositionedCharacters() ) { Font aFont = ImplSetCorrectFontHeight(); mpContext->SetFontAttr( aFont ); ImplWriteText( pA->GetRect().TopLeft(), pA->GetText(), NULL, 0 ); } { maTextWriter.writeTextPortion( pA->GetRect().TopLeft(), pA->GetText() ); } } } } break; case( META_TEXTARRAY_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_TEXT ) { const MetaTextArrayAction* pA = (const MetaTextArrayAction*) pAction; const String aText( pA->GetText(), pA->GetIndex(), pA->GetLen() ); if( aText.Len() ) { if( mrExport.IsUsePositionedCharacters() ) { Font aFont = ImplSetCorrectFontHeight(); mpContext->SetFontAttr( aFont ); ImplWriteText( pA->GetPoint(), aText, pA->GetDXArray(), 0 ); } else { maTextWriter.writeTextPortion( pA->GetPoint(), aText ); } } } } break; case( META_STRETCHTEXT_ACTION ): { if( nWriteFlags & SVGWRITER_WRITE_TEXT ) { const MetaStretchTextAction* pA = (const MetaStretchTextAction*) pAction; const String aText( pA->GetText(), pA->GetIndex(), pA->GetLen() ); if( aText.Len() ) { if( mrExport.IsUsePositionedCharacters() ) { Font aFont = ImplSetCorrectFontHeight(); mpContext->SetFontAttr( aFont ); ImplWriteText( pA->GetPoint(), aText, NULL, pA->GetWidth() ); } else { maTextWriter.writeTextPortion( pA->GetPoint(), aText ); } } } } break; case( META_CLIPREGION_ACTION ): case( META_ISECTRECTCLIPREGION_ACTION ): case( META_ISECTREGIONCLIPREGION_ACTION ): case( META_MOVECLIPREGION_ACTION ): { ( (MetaAction*) pAction )->Execute( mpVDev ); mbClipAttrChanged = sal_True; } break; case( META_REFPOINT_ACTION ): case( META_MAPMODE_ACTION ): case( META_LINECOLOR_ACTION ): case( META_FILLCOLOR_ACTION ): case( META_TEXTLINECOLOR_ACTION ): case( META_TEXTFILLCOLOR_ACTION ): case( META_TEXTCOLOR_ACTION ): case( META_TEXTALIGN_ACTION ): case( META_FONT_ACTION ): case( META_PUSH_ACTION ): case( META_POP_ACTION ): case( META_LAYOUTMODE_ACTION ): { ( (MetaAction*) pAction )->Execute( mpVDev ); } break; case( META_RASTEROP_ACTION ): case( META_MASK_ACTION ): case( META_MASKSCALE_ACTION ): case( META_MASKSCALEPART_ACTION ): case( META_WALLPAPER_ACTION ): case( META_TEXTLINE_ACTION ): { // !!! >>> we don't want to support these actions } break; default: OSL_FAIL( "SVGActionWriter::ImplWriteActions: unsupported MetaAction #" ); break; } } } // ----------------------------------------------------------------------------- Font SVGActionWriter::ImplSetCorrectFontHeight() const { Font aFont( mpVDev->GetFont() ); Size aSz; ImplMap( Size( 0, aFont.GetHeight() ), aSz ); aFont.SetHeight( aSz.Height() ); return aFont; } // ----------------------------------------------------------------------------- void SVGActionWriter::WriteMetaFile( const Point& rPos100thmm, const Size& rSize100thmm, const GDIMetaFile& rMtf, sal_uInt32 nWriteFlags, const ::rtl::OUString* pElementId, const Reference< XShape >* pXShape, const GDIMetaFile* pTextEmbeddedBitmapMtf ) { MapMode aMapMode( rMtf.GetPrefMapMode() ); Size aPrefSize( rMtf.GetPrefSize() ); Fraction aFractionX( aMapMode.GetScaleX() ); Fraction aFractionY( aMapMode.GetScaleY() ); mpVDev->Push(); Size aSize( OutputDevice::LogicToLogic( rSize100thmm, MAP_100TH_MM, aMapMode ) ); aMapMode.SetScaleX( aFractionX *= Fraction( aSize.Width(), aPrefSize.Width() ) ); aMapMode.SetScaleY( aFractionY *= Fraction( aSize.Height(), aPrefSize.Height() ) ); Point aOffset( OutputDevice::LogicToLogic( rPos100thmm, MAP_100TH_MM, aMapMode ) ); aMapMode.SetOrigin( aOffset += aMapMode.GetOrigin() ); mpVDev->SetMapMode( aMapMode ); ImplAcquireContext(); mapCurShape.reset(); ImplWriteActions( rMtf, nWriteFlags, pElementId, pXShape, pTextEmbeddedBitmapMtf ); // draw open shape that doesn't have a border if( mapCurShape.get() ) { ImplWriteShape( *mapCurShape ); mapCurShape.reset(); } ImplReleaseContext(); mpVDev->Pop(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */