/* -*- 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 #include #include #include #include #include #include #include static const char aPrefixClipPathId[] = "clip_path_"; static const char aXMLElemG[] = "g"; static const char aXMLElemDefs[] = "defs"; static const char aXMLElemText[] = "text"; static const char aXMLElemTspan[] = "tspan"; static const char aXMLElemLinearGradient[] = "linearGradient"; static const char aXMLElemStop[] = "stop"; static const char aXMLAttrTransform[] = "transform"; static const char aXMLAttrStyle[] = "style"; static const char aXMLAttrId[] = "id"; 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 aXMLAttrRX[] = "rx"; static const char aXMLAttrRY[] = "ry"; static const char aXMLAttrWidth[] = "width"; static const char aXMLAttrHeight[] = "height"; static const char aXMLAttrStrokeWidth[] = "stroke-width"; static const char aXMLAttrFill[] = "fill"; 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 aXMLAttrOffset[] = "offset"; static const char aXMLAttrStopColor[] = "stop-color"; static const char aXMLAttrStrokeLinejoin[] = "stroke-linejoin"; static const char aXMLAttrStrokeLinecap[] = "stroke-linecap"; PushFlags SVGContextHandler::getPushFlags() const { if (maStateStack.empty()) return PushFlags::NONE; const PartialState& rPartialState = maStateStack.top(); return rPartialState.meFlags; } SVGState& SVGContextHandler::getCurrentState() { return maCurrentState; } void SVGContextHandler::pushState( PushFlags eFlags ) { PartialState aPartialState; aPartialState.meFlags = eFlags; if (eFlags & PushFlags::FONT) { aPartialState.setFont( maCurrentState.aFont ); } if (eFlags & PushFlags::CLIPREGION) { aPartialState.mnRegionClipPathId = maCurrentState.nRegionClipPathId; } maStateStack.push( std::move(aPartialState) ); } void SVGContextHandler::popState() { if (maStateStack.empty()) return; const PartialState& rPartialState = maStateStack.top(); PushFlags eFlags = rPartialState.meFlags; if (eFlags & PushFlags::FONT) { maCurrentState.aFont = rPartialState.getFont( vcl::Font() ); } if (eFlags & PushFlags::CLIPREGION) { maCurrentState.nRegionClipPathId = rPartialState.mnRegionClipPathId; } maStateStack.pop(); } SVGAttributeWriter::SVGAttributeWriter( SVGExport& rExport, SVGFontExport& rFontExport, SVGState& rCurState ) : mrExport( rExport ) , mrFontExport( rFontExport ) , mrCurrentState( rCurState ) { } SVGAttributeWriter::~SVGAttributeWriter() { } double SVGAttributeWriter::ImplRound( double fValue ) { return floor( fValue * pow( 10.0, 3 ) + 0.5 ) / pow( 10.0, 3 ); } void SVGAttributeWriter::ImplGetColorStr( const Color& rColor, OUString& rColorStr ) { if( rColor.GetTransparency() == 255 ) rColorStr = "none"; else { rColorStr = "rgb(" + OUString::number(rColor.GetRed()) + "," + OUString::number(rColor.GetGreen()) + "," + OUString::number(rColor.GetBlue()) + ")"; } } void SVGAttributeWriter::AddColorAttr( const char* pColorAttrName, const char* pColorOpacityAttrName, const Color& rColor ) { OUString aColor, aColorOpacity; ImplGetColorStr( rColor, aColor ); if( rColor.GetTransparency() > 0 && rColor.GetTransparency() < 255 ) aColorOpacity = OUString::number( 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 tools::Rectangle* pObjBoundRect, const Gradient* pFillGradient ) { // Fill if( pObjBoundRect && pFillGradient ) { OUString aGradientId; AddGradientDef( *pObjBoundRect, *pFillGradient, aGradientId ); if( !aGradientId.isEmpty() ) { OUString aGradientURL = "url(#" + aGradientId + ")"; mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFill, aGradientURL ); } } else AddColorAttr( aXMLAttrFill, "fill-opacity", rFillColor ); // Stroke AddColorAttr( "stroke", "stroke-opacity", rLineColor ); } void SVGAttributeWriter::AddGradientDef( const tools::Rectangle& rObjRect, const Gradient& rGradient, 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, true, true ); Color aStartColor( rGradient.GetStartColor() ), aEndColor( rGradient.GetEndColor() ); sal_uInt16 nAngle = rGradient.GetAngle() % 3600; Point aObjRectCenter( rObjRect.Center() ); tools::Polygon aPoly( rObjRect ); static sal_Int32 nCurGradientId = 1; aPoly.Rotate( aObjRectCenter, nAngle ); tools::Rectangle aRect( aPoly.GetBoundRect() ); // adjust start/end colors with intensities aStartColor.SetRed( static_cast( ( aStartColor.GetRed() * rGradient.GetStartIntensity() ) / 100 ) ); aStartColor.SetGreen( static_cast( ( aStartColor.GetGreen() * rGradient.GetStartIntensity() ) / 100 ) ); aStartColor.SetBlue( static_cast( ( aStartColor.GetBlue() * rGradient.GetStartIntensity() ) / 100 ) ); aEndColor.SetRed( static_cast( ( aEndColor.GetRed() * rGradient.GetEndIntensity() ) / 100 ) ); aEndColor.SetGreen( static_cast( ( aEndColor.GetGreen() * rGradient.GetEndIntensity() ) / 100 ) ); aEndColor.SetBlue( static_cast( ( aEndColor.GetBlue() * rGradient.GetEndIntensity() ) / 100 ) ); rGradientId = "Gradient_" + OUString::number( nCurGradientId++ ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrId, rGradientId ); { std::unique_ptr< SvXMLElementExport > apGradient; OUString aColorStr; if( rGradient.GetStyle() == GradientStyle::Linear || rGradient.GetStyle() == GradientStyle::Axial ) { tools::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, "userSpaceOnUse" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX1, OUString::number( aLinePoly[ 0 ].X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY1, OUString::number( aLinePoly[ 0 ].Y() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX2, OUString::number( aLinePoly[ 1 ].X() ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY2, OUString::number( aLinePoly[ 1 ].Y() ) ); apGradient.reset( new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemLinearGradient, true, 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, OUString::number( fBorder ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc2( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, true, true ); } if( rGradient.GetStyle() == GradientStyle::Axial ) { ImplGetColorStr( aStartColor, aColorStr ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, OUString::number( 0.5 ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc3( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, true, true ); } } if( rGradient.GetStyle() != GradientStyle::Axial ) fBorder = 0.0; ImplGetColorStr( aEndColor, aColorStr ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, OUString::number( ImplRound( 1.0 - fBorder ) ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc4( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, true, 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, "userSpaceOnUse" ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrCX, OUString::number( ImplRound( fCenterX ) ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrCY, OUString::number( ImplRound( fCenterY ) ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "r", OUString::number( ImplRound( fRadius ) ) ); apGradient.reset( new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, "radialGradient", true, true ) ); // write stop values ImplGetColorStr( aEndColor, aColorStr ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, OUString::number( 0.0 ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc5( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, true, true ); } ImplGetColorStr( aStartColor, aColorStr ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrOffset, OUString::number( ImplRound( 1.0 - rGradient.GetBorder() * 0.01 ) ) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrStopColor, aColorStr ); { SvXMLElementExport aDesc6( mrExport, XML_NAMESPACE_NONE, aXMLElemStop, true, true ); } } } } else rGradientId.clear(); } void SVGAttributeWriter::SetFontAttr( const vcl::Font& rFont ) { vcl::Font& rCurFont = mrCurrentState.aFont; if( rFont != rCurFont ) { OUString aFontStyle, aTextDecoration; sal_Int32 nFontWeight; rCurFont = rFont; // Font Family setFontFamily(); // Font Size mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontSize, OUString::number( rFont.GetFontHeight() ) + "px" ); // Font Style if( rFont.GetItalic() != ITALIC_NONE ) { if( rFont.GetItalic() == ITALIC_OBLIQUE ) aFontStyle = "oblique"; else aFontStyle = "italic"; } else aFontStyle = "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, OUString::number( nFontWeight ) ); if( mrExport.IsUseNativeTextDecoration() ) { if( rFont.GetUnderline() != LINESTYLE_NONE || rFont.GetStrikeout() != STRIKEOUT_NONE ) { if( rFont.GetUnderline() != LINESTYLE_NONE ) aTextDecoration = "underline "; if( rFont.GetStrikeout() != STRIKEOUT_NONE ) aTextDecoration += "line-through "; } else aTextDecoration = "none"; mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTextDecoration, aTextDecoration ); } startFontSettings(); } } void SVGAttributeWriter::startFontSettings() { endFontSettings(); if( mrExport.IsUsePositionedCharacters() ) { mpElemFont.reset( new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemG, true, true ) ); } else { mpElemFont.reset( new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, true, true ) ); } } void SVGAttributeWriter::endFontSettings() { mpElemFont.reset(); } void SVGAttributeWriter::setFontFamily() { vcl::Font& rCurFont = mrCurrentState.aFont; if( mrExport.IsUsePositionedCharacters() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontFamily, mrFontExport.GetMappedFontName( rCurFont.GetFamilyName() ) ); } else { const OUString& rsFontName = rCurFont.GetFamilyName(); OUString sFontFamily( rsFontName.getToken( 0, ';' ) ); FontPitch ePitch = rCurFont.GetPitch(); if( ePitch == PITCH_FIXED ) { sFontFamily += ", monospace"; } else { FontFamily eFamily = rCurFont.GetFamilyType(); if( eFamily == FAMILY_ROMAN ) sFontFamily += ", serif"; else if( eFamily == FAMILY_SWISS ) sFontFamily += ", sans-serif"; } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontFamily, sFontFamily ); } } SVGTextWriter::SVGTextWriter( SVGExport& rExport, SVGAttributeWriter& rAttributeWriter ) : mrExport( rExport ), mrAttributeWriter( rAttributeWriter ), mpVDev( nullptr ), mbIsTextShapeStarted( false ), mrTextShape(), msShapeId(), mrParagraphEnumeration(), mrCurrentTextParagraph(), mrTextPortionEnumeration(), mrCurrentTextPortion(), mpTextEmbeddedBitmapMtf( nullptr ), mpTargetMapMode( nullptr ), mnLeftTextPortionLength( 0 ), maTextPos(0,0), mnTextWidth(0), mbPositioningNeeded( false ), mbIsNewListItem( false ), meNumberingType(0), mcBulletChar(0), maBulletListItemMap(), mbIsListLevelStyleImage( false ), mbLineBreak( false ), mbIsURLField( false ), msUrl(), mbIsPlaceholderShape( false ), maCurrentFont(), maParentFont() { } SVGTextWriter::~SVGTextWriter() { endTextParagraph(); } void SVGTextWriter::implRegisterInterface( const Reference< XInterface >& rxIf ) { if( rxIf.is() ) mrExport.getInterfaceToIdentifierMapper().registerReference( rxIf ); } const 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 = OutputDevice::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 = OutputDevice::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.GetFontHeight() ), aSz ); maCurrentFont.SetFontHeight( aSz.Height() ); } else { OSL_FAIL( "SVGTextWriter::implSetCorrectFontHeight: invalid virtual device." ); } } template< typename SubType > bool SVGTextWriter::implGetTextPosition( const MetaAction* pAction, Point& raPos, bool& rbEmpty ) { const SubType* pA = static_cast(pAction); sal_uInt16 nLength = pA->GetLen(); rbEmpty = ( nLength == 0 ); if( !rbEmpty ) { raPos = pA->GetPoint(); return true; } return false; } template<> bool SVGTextWriter::implGetTextPosition( const MetaAction* pAction, Point& raPos, bool& rbEmpty ) { const MetaTextRectAction* pA = static_cast(pAction); sal_uInt16 nLength = pA->GetText().getLength(); rbEmpty = ( nLength == 0 ); if( !rbEmpty ) { raPos = pA->GetRect().TopLeft(); return true; } return false; } template< typename SubType > bool SVGTextWriter::implGetTextPositionFromBitmap( const MetaAction* pAction, Point& raPos, bool& rbEmpty ) { const SubType* pA = static_cast(pAction); raPos = pA->GetPoint(); rbEmpty = false; return 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(); bool bEOL = false; bool bEOP = false; bool bETS = false; bool bConfigured = false; bool bEmpty = true; sal_uLong nActionIndex = nCurAction + 1; for( ; nActionIndex < nCount; ++nActionIndex ) { const MetaAction* pAction = rMtf.GetAction( nActionIndex ); const MetaActionType nType = pAction->GetType(); switch( nType ) { case MetaActionType::TEXT: { bConfigured = implGetTextPosition( pAction, aPos, bEmpty ); } break; case MetaActionType::TEXTRECT: { bConfigured = implGetTextPosition( pAction, aPos, bEmpty ); } break; case MetaActionType::TEXTARRAY: { bConfigured = implGetTextPosition( pAction, aPos, bEmpty ); } break; case MetaActionType::STRETCHTEXT: { bConfigured = implGetTextPosition( pAction, aPos, bEmpty ); } break; case MetaActionType::BMPSCALE: { bConfigured = implGetTextPositionFromBitmap( pAction, aPos, bEmpty ); } break; case MetaActionType::BMPEXSCALE: { 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 MetaActionType::COMMENT: { const MetaCommentAction* pA = static_cast(pAction); const OString& rsComment = pA->GetComment(); if( rsComment.equalsIgnoreAsciiCase( "XTEXT_EOL" ) ) { bEOL = true; } else if( rsComment.equalsIgnoreAsciiCase( "XTEXT_EOP" ) ) { bEOP = true; OUString sContent; while( nextTextPortion() ) { sContent = mrCurrentTextPortion->getString(); if( sContent.isEmpty() ) { continue; } else { if( sContent == "\n" ) mbLineBreak = true; } } if( nextParagraph() ) { while( nextTextPortion() ) { sContent = mrCurrentTextPortion->getString(); if( sContent.isEmpty() ) { continue; } else { if( sContent == "\n" ) mbLineBreak = true; } } } } else if( rsComment.equalsIgnoreAsciiCase( "XTEXT_PAINTSHAPE_END" ) ) { bETS = true; } } break; default: 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(); bool bEOP = false; bool bConfigured = false; for( sal_uLong nActionIndex = nCurAction + 1; nActionIndex < nCount; ++nActionIndex ) { const MetaAction* pAction = rMtf.GetAction( nActionIndex ); const MetaActionType nType = pAction->GetType(); switch( nType ) { case MetaActionType::TEXTLINECOLOR: case MetaActionType::TEXTFILLCOLOR: case MetaActionType::TEXTCOLOR: case MetaActionType::TEXTALIGN: case MetaActionType::FONT: case MetaActionType::LAYOUTMODE: { const_cast(pAction)->Execute( mpVDev ); } break; case MetaActionType::TEXT: { const MetaTextAction* pA = static_cast(pAction); if( pA->GetLen() > 2 ) bConfigured = true; } break; case MetaActionType::TEXTRECT: { const MetaTextRectAction* pA = static_cast(pAction); if( pA->GetText().getLength() > 2 ) bConfigured = true; } break; case MetaActionType::TEXTARRAY: { const MetaTextArrayAction* pA = static_cast(pAction); if( pA->GetLen() > 2 ) bConfigured = true; } break; case MetaActionType::STRETCHTEXT: { const MetaStretchTextAction* pA = static_cast(pAction); if( pA->GetLen() > 2 ) bConfigured = true; } break; // If we reach the end of the paragraph without finding any text // we stop searching case MetaActionType::COMMENT: { const MetaCommentAction* pA = static_cast(pAction); const OString& rsComment = pA->GetComment(); if( rsComment.equalsIgnoreAsciiCase( "XTEXT_EOP" ) ) { bEOP = true; } } break; default: break; } if( bConfigured || bEOP ) break; } } void SVGTextWriter::addFontAttributes( bool bIsTextContainer ) { implSetCurrentFont(); if( maCurrentFont != maParentFont ) { const OUString& rsCurFontName = maCurrentFont.GetFamilyName(); long int nCurFontSize = maCurrentFont.GetFontHeight(); FontItalic eCurFontItalic = maCurrentFont.GetItalic(); FontWeight eCurFontWeight = maCurrentFont.GetWeight(); const OUString& rsParFontName = maParentFont.GetFamilyName(); long int nParFontSize = maParentFont.GetFontHeight(); 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, OUString::number( nCurFontSize ) + "px" ); } // Font Style if( eCurFontItalic != eParFontItalic ) { OUString sFontStyle; if( eCurFontItalic != ITALIC_NONE ) { if( eCurFontItalic == ITALIC_OBLIQUE ) sFontStyle = "oblique"; else sFontStyle = "italic"; } else { sFontStyle = "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, OUString::number( nFontWeight ) ); } if( mrExport.IsUseNativeTextDecoration() ) { FontLineStyle eCurFontLineStyle = maCurrentFont.GetUnderline(); FontStrikeout eCurFontStrikeout = maCurrentFont.GetStrikeout(); FontLineStyle eParFontLineStyle = maParentFont.GetUnderline(); FontStrikeout eParFontStrikeout = maParentFont.GetStrikeout(); OUString sTextDecoration; bool bIsDecorationChanged = false; if( eCurFontLineStyle != eParFontLineStyle ) { if( eCurFontLineStyle != LINESTYLE_NONE ) sTextDecoration = "underline"; bIsDecorationChanged = true; } if( eCurFontStrikeout != eParFontStrikeout ) { if( eCurFontStrikeout != STRIKEOUT_NONE ) { if( !sTextDecoration.isEmpty() ) sTextDecoration += " "; sTextDecoration += "line-through"; } bIsDecorationChanged = true; } if( !sTextDecoration.isEmpty() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTextDecoration, sTextDecoration ); } else if( bIsDecorationChanged ) { sTextDecoration = "none"; mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTextDecoration, sTextDecoration ); } } if( bIsTextContainer ) maParentFont = maCurrentFont; } } void SVGTextWriter::implSetFontFamily() { const OUString& rsFontName = maCurrentFont.GetFamilyName(); OUString sFontFamily( rsFontName.getToken( 0, ';' ) ); FontPitch ePitch = maCurrentFont.GetPitch(); if( ePitch == PITCH_FIXED ) { sFontFamily += ", monospace"; } else { FontFamily eFamily = maCurrentFont.GetFamilyType(); if( eFamily == FAMILY_ROMAN ) sFontFamily += ", serif"; else if( eFamily == FAMILY_SWISS ) sFontFamily += ", sans-serif"; } mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontFamily, sFontFamily ); } void SVGTextWriter::createParagraphEnumeration() { if( mrTextShape.is() ) { msShapeId = implGetValidIDFromInterface( Reference(mrTextShape, UNO_QUERY) ); Reference< XEnumerationAccess > xEnumerationAccess( mrTextShape, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); if( xEnumeration.is() ) { mrParagraphEnumeration.set( xEnumeration ); } else { OSL_FAIL( "SVGTextWriter::createParagraphEnumeration: no valid xEnumeration interface found." ); } } else { OSL_FAIL( "SVGTextWriter::createParagraphEnumeration: no valid XText interface found." ); } } bool SVGTextWriter::nextParagraph() { mrTextPortionEnumeration.clear(); mrCurrentTextParagraph.clear(); mbIsNewListItem = false; mbIsListLevelStyleImage = false; if( mrParagraphEnumeration.is() && mrParagraphEnumeration->hasMoreElements() ) { Reference < XTextContent > xTextContent( mrParagraphEnumeration->nextElement(), UNO_QUERY_THROW ); if( xTextContent.is() ) { Reference< XServiceInfo > xServiceInfo( xTextContent, UNO_QUERY_THROW ); #if OSL_DEBUG_LEVEL > 0 OUString sInfo; #endif if( xServiceInfo->supportsService( "com.sun.star.text.Paragraph" ) ) { mrCurrentTextParagraph.set( xTextContent ); Reference< XPropertySet > xPropSet( xTextContent, UNO_QUERY_THROW ); Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); if( xPropSetInfo->hasPropertyByName( "NumberingLevel" ) ) { sal_Int16 nListLevel = 0; if( xPropSet->getPropertyValue( "NumberingLevel" ) >>= nListLevel ) { mbIsNewListItem = true; #if OSL_DEBUG_LEVEL > 0 sInfo = "NumberingLevel: " + OUString::number( nListLevel ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "style", sInfo ); #endif Reference< XIndexReplace > xNumRules; if( xPropSetInfo->hasPropertyByName( "NumberingRules" ) ) { xPropSet->getPropertyValue( "NumberingRules" ) >>= xNumRules; } if( xNumRules.is() && ( nListLevel < xNumRules->getCount() ) ) { bool bIsNumbered = true; OUString sNumberingIsNumber("NumberingIsNumber"); if( xPropSetInfo->hasPropertyByName( sNumberingIsNumber ) ) { if( !(xPropSet->getPropertyValue( sNumberingIsNumber ) >>= bIsNumbered ) ) { OSL_FAIL( "numbered paragraph without number info" ); bIsNumbered = false; } #if OSL_DEBUG_LEVEL > 0 if( bIsNumbered ) { sInfo = "true"; mrExport.AddAttribute( XML_NAMESPACE_NONE, "is-numbered", sInfo ); } #endif } 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 == "NumberingType" ) { rProp.Value >>= eType; } else if( rProp.Name == "BulletChar" ) { OUString sValue; rProp.Value >>= sValue; if( !sValue.isEmpty() ) { cBullet = sValue[0]; } } } meNumberingType = eType; mbIsListLevelStyleImage = ( NumberingType::BITMAP == meNumberingType ); if( NumberingType::CHAR_SPECIAL == meNumberingType ) { if( cBullet ) { if( cBullet < ' ' ) { cBullet = 0xF000 + 149; } mcBulletChar = cBullet; #if OSL_DEBUG_LEVEL > 0 sInfo = OUString::number( static_cast(cBullet) ); mrExport.AddAttribute( XML_NAMESPACE_NONE, "bullet-char", sInfo ); #endif } } } } } } } Reference< XEnumerationAccess > xEnumerationAccess( xTextContent, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); if( xEnumeration.is() && xEnumeration->hasMoreElements() ) { mrTextPortionEnumeration.set( xEnumeration ); } #if OSL_DEBUG_LEVEL > 0 sInfo = "Paragraph"; #endif } else if( xServiceInfo->supportsService( "com.sun.star.text.Table" ) ) { OSL_FAIL( "SVGTextWriter::nextParagraph: text tables are not handled." ); #if OSL_DEBUG_LEVEL > 0 sInfo = "Table"; #endif } else { OSL_FAIL( "SVGTextWriter::nextParagraph: Unknown text content." ); return false; } #if OSL_DEBUG_LEVEL > 0 mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", sInfo ); SvXMLElementExport aParaElem( mrExport, XML_NAMESPACE_NONE, "desc", mbIWS, mbIWS ); #endif } else { OSL_FAIL( "SVGTextWriter::nextParagraph: no XServiceInfo interface available for text content." ); return false; } const OUString& rParagraphId = implGetValidIDFromInterface( Reference(xTextContent, UNO_QUERY) ); if( !rParagraphId.isEmpty() ) { mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", rParagraphId ); } return true; } return false; } bool SVGTextWriter::nextTextPortion() { mrCurrentTextPortion.clear(); mbIsURLField = false; if( mrTextPortionEnumeration.is() && mrTextPortionEnumeration->hasMoreElements() ) { mbIsPlaceholderShape = false; #if OSL_DEBUG_LEVEL > 0 OUString sInfo; #endif 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( "TextPortionType" ) ) { #if OSL_DEBUG_LEVEL > 0 OUString sPortionType; if( xPortionPropSet->getPropertyValue( "TextPortionType" ) >>= sPortionType ) { sInfo = "type: " + sPortionType + "; "; } #endif msPageCount = ""; msDateTimeType = ""; msTextFieldType = ""; if( xPortionTextRange.is() ) { #if OSL_DEBUG_LEVEL > 0 sInfo += "content: " + xPortionTextRange->getString() + "; "; #endif mrCurrentTextPortion.set( xPortionTextRange ); Reference < XPropertySet > xRangePropSet( xPortionTextRange, UNO_QUERY ); if( xRangePropSet.is() && xRangePropSet->getPropertySetInfo()->hasPropertyByName( "TextField" ) ) { Reference < XTextField > xTextField( xRangePropSet->getPropertyValue( "TextField" ), UNO_QUERY ); if( xTextField.is() ) { const OUString sServicePrefix("com.sun.star.text.textfield."); const OUString sPresentationServicePrefix("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( pNames->startsWith( sPresentationServicePrefix ) ) { // TextField found => postfix is field type! sFieldName = pNames->copy( sPresentationServicePrefix.getLength() ); break; } ++pNames; } msTextFieldType = sFieldName; #if OSL_DEBUG_LEVEL > 0 sInfo += "text field type: " + sFieldName + "; content: " + xTextField->getPresentation( /* show command: */ false ) + "; "; #endif // This case handle Date or Time text field inserted by the user // on both page/master page. It doesn't handle the standard Date/Time field. if( sFieldName == "DateTime" ) { Reference xTextFieldPropSet(xTextField, UNO_QUERY); if( xTextFieldPropSet.is() ) { Reference xPropSetInfo = xTextFieldPropSet->getPropertySetInfo(); if( xPropSetInfo.is() ) { // The standard Date/Time field has no property. // Trying to get a property value on such field would cause a runtime exception. // So the hasPropertyByName check is needed. bool bIsFixed = true; if( xPropSetInfo->hasPropertyByName("IsFixed") && ( ( xTextFieldPropSet->getPropertyValue( "IsFixed" ) ) >>= bIsFixed ) && !bIsFixed ) { bool bIsDate; if( xPropSetInfo->hasPropertyByName("IsDate") && ( ( xTextFieldPropSet->getPropertyValue( "IsDate" ) ) >>= bIsDate ) ) { msDateTimeType = OUString::createFromAscii( bIsDate ? "" : "