1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "svgfilter.hxx"
21 #include "svgfontexport.hxx"
22 #include "svgwriter.hxx"
24 #include <comphelper/base64.hxx>
26 #include <sal/log.hxx>
27 #include <vcl/unohelp.hxx>
28 #include <vcl/outdev.hxx>
29 #include <vcl/settings.hxx>
30 #include <tools/helpers.hxx>
31 #include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
32 #include <sax/tools/converter.hxx>
33 #include <i18nlangtag/languagetag.hxx>
38 static const char aPrefixClipPathId
[] = "clip_path_";
40 static const char aXMLElemG
[] = "g";
41 static const char aXMLElemDefs
[] = "defs";
42 static const char aXMLElemText
[] = "text";
43 static const char aXMLElemTspan
[] = "tspan";
44 static const char aXMLElemLinearGradient
[] = "linearGradient";
45 static const char aXMLElemStop
[] = "stop";
47 static const char aXMLAttrTransform
[] = "transform";
48 static const char aXMLAttrStyle
[] = "style";
49 static const char aXMLAttrId
[] = "id";
50 static const char aXMLAttrX
[] = "x";
51 static const char aXMLAttrY
[] = "y";
52 static const char aXMLAttrX1
[] = "x1";
53 static const char aXMLAttrY1
[] = "y1";
54 static const char aXMLAttrX2
[] = "x2";
55 static const char aXMLAttrY2
[] = "y2";
56 static const char aXMLAttrCX
[] = "cx";
57 static const char aXMLAttrCY
[] = "cy";
58 static const char aXMLAttrRX
[] = "rx";
59 static const char aXMLAttrRY
[] = "ry";
60 static const char aXMLAttrWidth
[] = "width";
61 static const char aXMLAttrHeight
[] = "height";
62 static const char aXMLAttrStrokeWidth
[] = "stroke-width";
63 static const char aXMLAttrFill
[] = "fill";
64 static const char aXMLAttrFontFamily
[] = "font-family";
65 static const char aXMLAttrFontSize
[] = "font-size";
66 static const char aXMLAttrFontStyle
[] = "font-style";
67 static const char aXMLAttrFontWeight
[] = "font-weight";
68 static const char aXMLAttrTextDecoration
[] = "text-decoration";
69 static const char aXMLAttrXLinkHRef
[] = "xlink:href";
70 static const char aXMLAttrGradientUnits
[] = "gradientUnits";
71 static const char aXMLAttrOffset
[] = "offset";
72 static const char aXMLAttrStopColor
[] = "stop-color";
73 static const char aXMLAttrStrokeLinejoin
[] = "stroke-linejoin";
74 static const char aXMLAttrStrokeLinecap
[] = "stroke-linecap";
77 PushFlags
SVGContextHandler::getPushFlags() const
79 if (maStateStack
.empty())
80 return PushFlags::NONE
;
82 const PartialState
& rPartialState
= maStateStack
.top();
83 return rPartialState
.meFlags
;
86 SVGState
& SVGContextHandler::getCurrentState()
88 return maCurrentState
;
91 void SVGContextHandler::pushState( PushFlags eFlags
)
93 PartialState aPartialState
;
94 aPartialState
.meFlags
= eFlags
;
96 if (eFlags
& PushFlags::FONT
)
98 aPartialState
.setFont( maCurrentState
.aFont
);
101 if (eFlags
& PushFlags::CLIPREGION
)
103 aPartialState
.mnRegionClipPathId
= maCurrentState
.nRegionClipPathId
;
106 maStateStack
.push( std::move(aPartialState
) );
109 void SVGContextHandler::popState()
111 if (maStateStack
.empty())
114 const PartialState
& rPartialState
= maStateStack
.top();
115 PushFlags eFlags
= rPartialState
.meFlags
;
117 if (eFlags
& PushFlags::FONT
)
119 maCurrentState
.aFont
= rPartialState
.getFont( vcl::Font() );
122 if (eFlags
& PushFlags::CLIPREGION
)
124 maCurrentState
.nRegionClipPathId
= rPartialState
.mnRegionClipPathId
;
130 SVGAttributeWriter::SVGAttributeWriter( SVGExport
& rExport
, SVGFontExport
& rFontExport
, SVGState
& rCurState
)
131 : mrExport( rExport
)
132 , mrFontExport( rFontExport
)
133 , mrCurrentState( rCurState
)
138 SVGAttributeWriter::~SVGAttributeWriter()
143 double SVGAttributeWriter::ImplRound( double fValue
)
145 return floor( fValue
* pow( 10.0, 3 ) + 0.5 ) / pow( 10.0, 3 );
149 void SVGAttributeWriter::ImplGetColorStr( const Color
& rColor
, OUString
& rColorStr
)
151 if( rColor
.GetTransparency() == 255 )
155 rColorStr
= "rgb(" + OUString::number(rColor
.GetRed()) + "," + OUString::number(rColor
.GetGreen()) +
156 "," + OUString::number(rColor
.GetBlue()) + ")";
161 void SVGAttributeWriter::AddColorAttr( const char* pColorAttrName
,
162 const char* pColorOpacityAttrName
,
163 const Color
& rColor
)
165 OUString aColor
, aColorOpacity
;
167 ImplGetColorStr( rColor
, aColor
);
169 if( rColor
.GetTransparency() > 0 && rColor
.GetTransparency() < 255 )
170 aColorOpacity
= OUString::number( ImplRound( ( 255.0 - rColor
.GetTransparency() ) / 255.0 ) );
172 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, pColorAttrName
, aColor
);
174 if( !aColorOpacity
.isEmpty() && mrExport
.IsUseOpacity() )
175 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, pColorOpacityAttrName
, aColorOpacity
);
179 void SVGAttributeWriter::AddPaintAttr( const Color
& rLineColor
, const Color
& rFillColor
,
180 const tools::Rectangle
* pObjBoundRect
, const Gradient
* pFillGradient
)
183 if( pObjBoundRect
&& pFillGradient
)
185 OUString aGradientId
;
187 AddGradientDef( *pObjBoundRect
, *pFillGradient
, aGradientId
);
189 if( !aGradientId
.isEmpty() )
191 OUString aGradientURL
= "url(#" + aGradientId
+ ")";
192 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFill
, aGradientURL
);
196 AddColorAttr( aXMLAttrFill
, "fill-opacity", rFillColor
);
199 AddColorAttr( "stroke", "stroke-opacity", rLineColor
);
203 void SVGAttributeWriter::AddGradientDef( const tools::Rectangle
& rObjRect
, const Gradient
& rGradient
, OUString
& rGradientId
)
205 if( rObjRect
.GetWidth() && rObjRect
.GetHeight() &&
206 ( rGradient
.GetStyle() == GradientStyle::Linear
|| rGradient
.GetStyle() == GradientStyle::Axial
||
207 rGradient
.GetStyle() == GradientStyle::Radial
|| rGradient
.GetStyle() == GradientStyle::Elliptical
) )
209 SvXMLElementExport
aDesc( mrExport
, XML_NAMESPACE_NONE
, aXMLElemDefs
, true, true );
210 Color
aStartColor( rGradient
.GetStartColor() ), aEndColor( rGradient
.GetEndColor() );
211 sal_uInt16 nAngle
= rGradient
.GetAngle() % 3600;
212 Point
aObjRectCenter( rObjRect
.Center() );
213 tools::Polygon
aPoly( rObjRect
);
214 static sal_Int32 nCurGradientId
= 1;
216 aPoly
.Rotate( aObjRectCenter
, nAngle
);
217 tools::Rectangle
aRect( aPoly
.GetBoundRect() );
219 // adjust start/end colors with intensities
220 aStartColor
.SetRed( static_cast<sal_uInt8
>( ( aStartColor
.GetRed() * rGradient
.GetStartIntensity() ) / 100 ) );
221 aStartColor
.SetGreen( static_cast<sal_uInt8
>( ( aStartColor
.GetGreen() * rGradient
.GetStartIntensity() ) / 100 ) );
222 aStartColor
.SetBlue( static_cast<sal_uInt8
>( ( aStartColor
.GetBlue() * rGradient
.GetStartIntensity() ) / 100 ) );
224 aEndColor
.SetRed( static_cast<sal_uInt8
>( ( aEndColor
.GetRed() * rGradient
.GetEndIntensity() ) / 100 ) );
225 aEndColor
.SetGreen( static_cast<sal_uInt8
>( ( aEndColor
.GetGreen() * rGradient
.GetEndIntensity() ) / 100 ) );
226 aEndColor
.SetBlue( static_cast<sal_uInt8
>( ( aEndColor
.GetBlue() * rGradient
.GetEndIntensity() ) / 100 ) );
228 rGradientId
= "Gradient_" + OUString::number( nCurGradientId
++ );
229 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrId
, rGradientId
);
232 std::unique_ptr
< SvXMLElementExport
> apGradient
;
235 if( rGradient
.GetStyle() == GradientStyle::Linear
|| rGradient
.GetStyle() == GradientStyle::Axial
)
237 tools::Polygon
aLinePoly( 2 );
239 aLinePoly
[ 0 ] = Point( aObjRectCenter
.X(), aRect
.Top() );
240 aLinePoly
[ 1 ] = Point( aObjRectCenter
.X(), aRect
.Bottom() );
242 aLinePoly
.Rotate( aObjRectCenter
, nAngle
);
244 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrGradientUnits
, "userSpaceOnUse" );
245 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX1
, OUString::number( aLinePoly
[ 0 ].X() ) );
246 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY1
, OUString::number( aLinePoly
[ 0 ].Y() ) );
247 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX2
, OUString::number( aLinePoly
[ 1 ].X() ) );
248 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY2
, OUString::number( aLinePoly
[ 1 ].Y() ) );
250 apGradient
.reset( new SvXMLElementExport( mrExport
, XML_NAMESPACE_NONE
, aXMLElemLinearGradient
, true, true ) );
253 double fBorder
= static_cast< double >( rGradient
.GetBorder() ) *
254 ( ( rGradient
.GetStyle() == GradientStyle::Axial
) ? 0.005 : 0.01 );
256 ImplGetColorStr( ( rGradient
.GetStyle() == GradientStyle::Axial
) ? aEndColor
: aStartColor
, aColorStr
);
257 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrOffset
, OUString::number( fBorder
) );
258 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStopColor
, aColorStr
);
261 SvXMLElementExport
aDesc2( mrExport
, XML_NAMESPACE_NONE
, aXMLElemStop
, true, true );
264 if( rGradient
.GetStyle() == GradientStyle::Axial
)
266 ImplGetColorStr( aStartColor
, aColorStr
);
267 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrOffset
, OUString::number( 0.5 ) );
268 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStopColor
, aColorStr
);
271 SvXMLElementExport
aDesc3( mrExport
, XML_NAMESPACE_NONE
, aXMLElemStop
, true, true );
275 if( rGradient
.GetStyle() != GradientStyle::Axial
)
278 ImplGetColorStr( aEndColor
, aColorStr
);
279 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrOffset
, OUString::number( ImplRound( 1.0 - fBorder
) ) );
280 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStopColor
, aColorStr
);
283 SvXMLElementExport
aDesc4( mrExport
, XML_NAMESPACE_NONE
, aXMLElemStop
, true, true );
288 const double fCenterX
= rObjRect
.Left() + rObjRect
.GetWidth() * rGradient
.GetOfsX() * 0.01;
289 const double fCenterY
= rObjRect
.Top() + rObjRect
.GetHeight() * rGradient
.GetOfsY() * 0.01;
290 const double fRadius
= sqrt( static_cast< double >( rObjRect
.GetWidth() ) * rObjRect
.GetWidth() +
291 rObjRect
.GetHeight() * rObjRect
.GetHeight() ) * 0.5;
293 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrGradientUnits
, "userSpaceOnUse" );
294 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrCX
, OUString::number( ImplRound( fCenterX
) ) );
295 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrCY
, OUString::number( ImplRound( fCenterY
) ) );
296 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "r", OUString::number( ImplRound( fRadius
) ) );
298 apGradient
.reset( new SvXMLElementExport( mrExport
, XML_NAMESPACE_NONE
, "radialGradient", true, true ) );
301 ImplGetColorStr( aEndColor
, aColorStr
);
302 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrOffset
, OUString::number( 0.0 ) );
303 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStopColor
, aColorStr
);
306 SvXMLElementExport
aDesc5( mrExport
, XML_NAMESPACE_NONE
, aXMLElemStop
, true, true );
309 ImplGetColorStr( aStartColor
, aColorStr
);
310 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrOffset
,
311 OUString::number( ImplRound( 1.0 - rGradient
.GetBorder() * 0.01 ) ) );
312 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStopColor
, aColorStr
);
315 SvXMLElementExport
aDesc6( mrExport
, XML_NAMESPACE_NONE
, aXMLElemStop
, true, true );
325 void SVGAttributeWriter::SetFontAttr( const vcl::Font
& rFont
)
327 vcl::Font
& rCurFont
= mrCurrentState
.aFont
;
329 if( rFont
!= rCurFont
)
331 OUString aFontStyle
, aTextDecoration
;
332 sal_Int32 nFontWeight
;
340 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFontSize
,
341 OUString::number( rFont
.GetFontHeight() ) + "px" );
344 if( rFont
.GetItalic() != ITALIC_NONE
)
346 if( rFont
.GetItalic() == ITALIC_OBLIQUE
)
347 aFontStyle
= "oblique";
349 aFontStyle
= "italic";
352 aFontStyle
= "normal";
354 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFontStyle
, aFontStyle
);
357 switch( rFont
.GetWeight() )
359 case WEIGHT_THIN
: nFontWeight
= 100; break;
360 case WEIGHT_ULTRALIGHT
: nFontWeight
= 200; break;
361 case WEIGHT_LIGHT
: nFontWeight
= 300; break;
362 case WEIGHT_SEMILIGHT
: nFontWeight
= 400; break;
363 case WEIGHT_NORMAL
: nFontWeight
= 400; break;
364 case WEIGHT_MEDIUM
: nFontWeight
= 500; break;
365 case WEIGHT_SEMIBOLD
: nFontWeight
= 600; break;
366 case WEIGHT_BOLD
: nFontWeight
= 700; break;
367 case WEIGHT_ULTRABOLD
: nFontWeight
= 800; break;
368 case WEIGHT_BLACK
: nFontWeight
= 900; break;
369 default: nFontWeight
= 400; break;
372 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFontWeight
, OUString::number( nFontWeight
) );
374 if( mrExport
.IsUseNativeTextDecoration() )
376 if( rFont
.GetUnderline() != LINESTYLE_NONE
|| rFont
.GetStrikeout() != STRIKEOUT_NONE
)
378 if( rFont
.GetUnderline() != LINESTYLE_NONE
)
379 aTextDecoration
= "underline ";
381 if( rFont
.GetStrikeout() != STRIKEOUT_NONE
)
382 aTextDecoration
+= "line-through ";
385 aTextDecoration
= "none";
387 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrTextDecoration
, aTextDecoration
);
395 void SVGAttributeWriter::startFontSettings()
398 if( mrExport
.IsUsePositionedCharacters() )
400 mpElemFont
.reset( new SvXMLElementExport( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true ) );
404 mpElemFont
.reset( new SvXMLElementExport( mrExport
, XML_NAMESPACE_NONE
, aXMLElemTspan
, true, true ) );
409 void SVGAttributeWriter::endFontSettings()
415 void SVGAttributeWriter::setFontFamily()
417 vcl::Font
& rCurFont
= mrCurrentState
.aFont
;
419 if( mrExport
.IsUsePositionedCharacters() )
421 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFontFamily
, mrFontExport
.GetMappedFontName( rCurFont
.GetFamilyName() ) );
425 const OUString
& rsFontName
= rCurFont
.GetFamilyName();
426 OUString
sFontFamily( rsFontName
.getToken( 0, ';' ) );
427 FontPitch ePitch
= rCurFont
.GetPitch();
428 if( ePitch
== PITCH_FIXED
)
430 sFontFamily
+= ", monospace";
434 FontFamily eFamily
= rCurFont
.GetFamilyType();
435 if( eFamily
== FAMILY_ROMAN
)
436 sFontFamily
+= ", serif";
437 else if( eFamily
== FAMILY_SWISS
)
438 sFontFamily
+= ", sans-serif";
440 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFontFamily
, sFontFamily
);
444 SVGTextWriter::SVGTextWriter( SVGExport
& rExport
, SVGAttributeWriter
& rAttributeWriter
)
445 : mrExport( rExport
),
446 mrAttributeWriter( rAttributeWriter
),
448 mbIsTextShapeStarted( false ),
451 mrParagraphEnumeration(),
452 mrCurrentTextParagraph(),
453 mrTextPortionEnumeration(),
454 mrCurrentTextPortion(),
455 mpTextEmbeddedBitmapMtf( nullptr ),
456 mpTargetMapMode( nullptr ),
457 mnLeftTextPortionLength( 0 ),
460 mbPositioningNeeded( false ),
461 mbIsNewListItem( false ),
464 maBulletListItemMap(),
465 mbIsListLevelStyleImage( false ),
466 mbLineBreak( false ),
467 mbIsURLField( false ),
469 mbIsPlaceholderShape( false ),
476 SVGTextWriter::~SVGTextWriter()
482 void SVGTextWriter::implRegisterInterface( const Reference
< XInterface
>& rxIf
)
485 mrExport
.getInterfaceToIdentifierMapper().registerReference( rxIf
);
489 const OUString
& SVGTextWriter::implGetValidIDFromInterface( const Reference
< XInterface
>& rxIf
)
491 return mrExport
.getInterfaceToIdentifierMapper().getIdentifier( rxIf
);
495 void SVGTextWriter::implMap( const Size
& rSz
, Size
& rDstSz
) const
497 if( mpVDev
&& mpTargetMapMode
)
498 rDstSz
= OutputDevice::LogicToLogic( rSz
, mpVDev
->GetMapMode(), *mpTargetMapMode
);
500 OSL_FAIL( "SVGTextWriter::implMap: invalid virtual device or map mode." );
504 void SVGTextWriter::implMap( const Point
& rPt
, Point
& rDstPt
) const
506 if( mpVDev
&& mpTargetMapMode
)
507 rDstPt
= OutputDevice::LogicToLogic( rPt
, mpVDev
->GetMapMode(), *mpTargetMapMode
);
509 OSL_FAIL( "SVGTextWriter::implMap: invalid virtual device or map mode." );
513 void SVGTextWriter::implSetCurrentFont()
517 maCurrentFont
= mpVDev
->GetFont();
520 implMap( Size( 0, maCurrentFont
.GetFontHeight() ), aSz
);
522 maCurrentFont
.SetFontHeight( aSz
.Height() );
526 OSL_FAIL( "SVGTextWriter::implSetCorrectFontHeight: invalid virtual device." );
531 template< typename SubType
>
532 bool SVGTextWriter::implGetTextPosition( const MetaAction
* pAction
, Point
& raPos
, bool& rbEmpty
)
534 const SubType
* pA
= static_cast<const SubType
*>(pAction
);
535 sal_uInt16 nLength
= pA
->GetLen();
536 rbEmpty
= ( nLength
== 0 );
539 raPos
= pA
->GetPoint();
547 bool SVGTextWriter::implGetTextPosition
<MetaTextRectAction
>( const MetaAction
* pAction
, Point
& raPos
, bool& rbEmpty
)
549 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
550 sal_uInt16 nLength
= pA
->GetText().getLength();
551 rbEmpty
= ( nLength
== 0 );
554 raPos
= pA
->GetRect().TopLeft();
561 template< typename SubType
>
562 bool SVGTextWriter::implGetTextPositionFromBitmap( const MetaAction
* pAction
, Point
& raPos
, bool& rbEmpty
)
564 const SubType
* pA
= static_cast<const SubType
*>(pAction
);
565 raPos
= pA
->GetPoint();
572 * Set the start position of the next line of text. In case no text is found
573 * the current action index is updated to the index value we reached while
574 * searching for text.
576 * @returns {sal_Int32}
577 * -2 if no text found and end of line is reached
578 * -1 if no text found and end of paragraph is reached
579 * 0 if no text found and end of text shape is reached
582 sal_Int32
SVGTextWriter::setTextPosition( const GDIMetaFile
& rMtf
, sal_uLong
& nCurAction
)
585 sal_uLong nCount
= rMtf
.GetActionSize();
589 bool bConfigured
= false;
592 sal_uLong nActionIndex
= nCurAction
+ 1;
593 for( ; nActionIndex
< nCount
; ++nActionIndex
)
595 const MetaAction
* pAction
= rMtf
.GetAction( nActionIndex
);
596 const MetaActionType nType
= pAction
->GetType();
600 case MetaActionType::TEXT
:
602 bConfigured
= implGetTextPosition
<MetaTextAction
>( pAction
, aPos
, bEmpty
);
606 case MetaActionType::TEXTRECT
:
608 bConfigured
= implGetTextPosition
<MetaTextRectAction
>( pAction
, aPos
, bEmpty
);
612 case MetaActionType::TEXTARRAY
:
614 bConfigured
= implGetTextPosition
<MetaTextArrayAction
>( pAction
, aPos
, bEmpty
);
618 case MetaActionType::STRETCHTEXT
:
620 bConfigured
= implGetTextPosition
<MetaStretchTextAction
>( pAction
, aPos
, bEmpty
);
624 case MetaActionType::BMPSCALE
:
626 bConfigured
= implGetTextPositionFromBitmap
<MetaBmpScaleAction
>( pAction
, aPos
, bEmpty
);
630 case MetaActionType::BMPEXSCALE
:
632 bConfigured
= implGetTextPositionFromBitmap
<MetaBmpExScaleAction
>( pAction
, aPos
, bEmpty
);
636 // If we reach the end of the current line, paragraph or text shape
637 // without finding any text we stop searching
638 case MetaActionType::COMMENT
:
640 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
641 const OString
& rsComment
= pA
->GetComment();
642 if( rsComment
.equalsIgnoreAsciiCase( "XTEXT_EOL" ) )
646 else if( rsComment
.equalsIgnoreAsciiCase( "XTEXT_EOP" ) )
651 while( nextTextPortion() )
653 sContent
= mrCurrentTextPortion
->getString();
654 if( sContent
.isEmpty() )
660 if( sContent
== "\n" )
664 if( nextParagraph() )
666 while( nextTextPortion() )
668 sContent
= mrCurrentTextPortion
->getString();
669 if( sContent
.isEmpty() )
675 if( sContent
== "\n" )
681 else if( rsComment
.equalsIgnoreAsciiCase( "XTEXT_PAINTSHAPE_END" ) )
689 if( bConfigured
|| bEOL
|| bEOP
|| bETS
) break;
691 implMap( aPos
, maTextPos
);
695 nCurAction
= nActionIndex
;
696 return ( bEOL
? -2 : ( bEOP
? -1 : 0 ) );
705 void SVGTextWriter::setTextProperties( const GDIMetaFile
& rMtf
, sal_uLong nCurAction
)
707 sal_uLong nCount
= rMtf
.GetActionSize();
709 bool bConfigured
= false;
710 for( sal_uLong nActionIndex
= nCurAction
+ 1; nActionIndex
< nCount
; ++nActionIndex
)
712 const MetaAction
* pAction
= rMtf
.GetAction( nActionIndex
);
713 const MetaActionType nType
= pAction
->GetType();
716 case MetaActionType::TEXTLINECOLOR
:
717 case MetaActionType::TEXTFILLCOLOR
:
718 case MetaActionType::TEXTCOLOR
:
719 case MetaActionType::TEXTALIGN
:
720 case MetaActionType::FONT
:
721 case MetaActionType::LAYOUTMODE
:
723 const_cast<MetaAction
*>(pAction
)->Execute( mpVDev
);
727 case MetaActionType::TEXT
:
729 const MetaTextAction
* pA
= static_cast<const MetaTextAction
*>(pAction
);
730 if( pA
->GetLen() > 2 )
734 case MetaActionType::TEXTRECT
:
736 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
737 if( pA
->GetText().getLength() > 2 )
741 case MetaActionType::TEXTARRAY
:
743 const MetaTextArrayAction
* pA
= static_cast<const MetaTextArrayAction
*>(pAction
);
744 if( pA
->GetLen() > 2 )
748 case MetaActionType::STRETCHTEXT
:
750 const MetaStretchTextAction
* pA
= static_cast<const MetaStretchTextAction
*>(pAction
);
751 if( pA
->GetLen() > 2 )
755 // If we reach the end of the paragraph without finding any text
757 case MetaActionType::COMMENT
:
759 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
760 const OString
& rsComment
= pA
->GetComment();
761 if( rsComment
.equalsIgnoreAsciiCase( "XTEXT_EOP" ) )
769 if( bConfigured
|| bEOP
) break;
774 void SVGTextWriter::addFontAttributes( bool bIsTextContainer
)
776 implSetCurrentFont();
778 if( maCurrentFont
!= maParentFont
)
780 const OUString
& rsCurFontName
= maCurrentFont
.GetFamilyName();
781 long int nCurFontSize
= maCurrentFont
.GetFontHeight();
782 FontItalic eCurFontItalic
= maCurrentFont
.GetItalic();
783 FontWeight eCurFontWeight
= maCurrentFont
.GetWeight();
785 const OUString
& rsParFontName
= maParentFont
.GetFamilyName();
786 long int nParFontSize
= maParentFont
.GetFontHeight();
787 FontItalic eParFontItalic
= maParentFont
.GetItalic();
788 FontWeight eParFontWeight
= maParentFont
.GetWeight();
792 if( rsCurFontName
!= rsParFontName
)
798 if( nCurFontSize
!= nParFontSize
)
800 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFontSize
,
801 OUString::number( nCurFontSize
) + "px" );
805 if( eCurFontItalic
!= eParFontItalic
)
808 if( eCurFontItalic
!= ITALIC_NONE
)
810 if( eCurFontItalic
== ITALIC_OBLIQUE
)
811 sFontStyle
= "oblique";
813 sFontStyle
= "italic";
817 sFontStyle
= "normal";
819 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFontStyle
, sFontStyle
);
823 if( eCurFontWeight
!= eParFontWeight
)
825 sal_Int32 nFontWeight
;
826 switch( eCurFontWeight
)
828 case WEIGHT_THIN
: nFontWeight
= 100; break;
829 case WEIGHT_ULTRALIGHT
: nFontWeight
= 200; break;
830 case WEIGHT_LIGHT
: nFontWeight
= 300; break;
831 case WEIGHT_SEMILIGHT
: nFontWeight
= 400; break;
832 case WEIGHT_NORMAL
: nFontWeight
= 400; break;
833 case WEIGHT_MEDIUM
: nFontWeight
= 500; break;
834 case WEIGHT_SEMIBOLD
: nFontWeight
= 600; break;
835 case WEIGHT_BOLD
: nFontWeight
= 700; break;
836 case WEIGHT_ULTRABOLD
: nFontWeight
= 800; break;
837 case WEIGHT_BLACK
: nFontWeight
= 900; break;
838 default: nFontWeight
= 400; break;
840 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFontWeight
, OUString::number( nFontWeight
) );
844 if( mrExport
.IsUseNativeTextDecoration() )
846 FontLineStyle eCurFontLineStyle
= maCurrentFont
.GetUnderline();
847 FontStrikeout eCurFontStrikeout
= maCurrentFont
.GetStrikeout();
849 FontLineStyle eParFontLineStyle
= maParentFont
.GetUnderline();
850 FontStrikeout eParFontStrikeout
= maParentFont
.GetStrikeout();
852 OUString sTextDecoration
;
853 bool bIsDecorationChanged
= false;
854 if( eCurFontLineStyle
!= eParFontLineStyle
)
856 if( eCurFontLineStyle
!= LINESTYLE_NONE
)
857 sTextDecoration
= "underline";
858 bIsDecorationChanged
= true;
860 if( eCurFontStrikeout
!= eParFontStrikeout
)
862 if( eCurFontStrikeout
!= STRIKEOUT_NONE
)
864 if( !sTextDecoration
.isEmpty() )
865 sTextDecoration
+= " ";
866 sTextDecoration
+= "line-through";
868 bIsDecorationChanged
= true;
871 if( !sTextDecoration
.isEmpty() )
873 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrTextDecoration
, sTextDecoration
);
875 else if( bIsDecorationChanged
)
877 sTextDecoration
= "none";
878 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrTextDecoration
, sTextDecoration
);
882 if( bIsTextContainer
)
883 maParentFont
= maCurrentFont
;
889 void SVGTextWriter::implSetFontFamily()
891 const OUString
& rsFontName
= maCurrentFont
.GetFamilyName();
892 OUString
sFontFamily( rsFontName
.getToken( 0, ';' ) );
893 FontPitch ePitch
= maCurrentFont
.GetPitch();
894 if( ePitch
== PITCH_FIXED
)
896 sFontFamily
+= ", monospace";
900 FontFamily eFamily
= maCurrentFont
.GetFamilyType();
901 if( eFamily
== FAMILY_ROMAN
)
902 sFontFamily
+= ", serif";
903 else if( eFamily
== FAMILY_SWISS
)
904 sFontFamily
+= ", sans-serif";
906 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrFontFamily
, sFontFamily
);
910 void SVGTextWriter::createParagraphEnumeration()
912 if( mrTextShape
.is() )
914 msShapeId
= implGetValidIDFromInterface( Reference
<XInterface
>(mrTextShape
, UNO_QUERY
) );
916 Reference
< XEnumerationAccess
> xEnumerationAccess( mrTextShape
, UNO_QUERY_THROW
);
917 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
918 if( xEnumeration
.is() )
920 mrParagraphEnumeration
.set( xEnumeration
);
924 OSL_FAIL( "SVGTextWriter::createParagraphEnumeration: no valid xEnumeration interface found." );
929 OSL_FAIL( "SVGTextWriter::createParagraphEnumeration: no valid XText interface found." );
934 bool SVGTextWriter::nextParagraph()
936 mrTextPortionEnumeration
.clear();
937 mrCurrentTextParagraph
.clear();
938 mbIsNewListItem
= false;
939 mbIsListLevelStyleImage
= false;
941 if( mrParagraphEnumeration
.is() && mrParagraphEnumeration
->hasMoreElements() )
943 Reference
< XTextContent
> xTextContent( mrParagraphEnumeration
->nextElement(), UNO_QUERY_THROW
);
944 if( xTextContent
.is() )
946 Reference
< XServiceInfo
> xServiceInfo( xTextContent
, UNO_QUERY_THROW
);
947 #if OSL_DEBUG_LEVEL > 0
950 if( xServiceInfo
->supportsService( "com.sun.star.text.Paragraph" ) )
952 mrCurrentTextParagraph
.set( xTextContent
);
953 Reference
< XPropertySet
> xPropSet( xTextContent
, UNO_QUERY_THROW
);
954 Reference
< XPropertySetInfo
> xPropSetInfo
= xPropSet
->getPropertySetInfo();
955 if( xPropSetInfo
->hasPropertyByName( "NumberingLevel" ) )
957 sal_Int16 nListLevel
= 0;
958 if( xPropSet
->getPropertyValue( "NumberingLevel" ) >>= nListLevel
)
960 mbIsNewListItem
= true;
961 #if OSL_DEBUG_LEVEL > 0
962 sInfo
= "NumberingLevel: " + OUString::number( nListLevel
);
963 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "style", sInfo
);
965 Reference
< XIndexReplace
> xNumRules
;
966 if( xPropSetInfo
->hasPropertyByName( "NumberingRules" ) )
968 xPropSet
->getPropertyValue( "NumberingRules" ) >>= xNumRules
;
970 if( xNumRules
.is() && ( nListLevel
< xNumRules
->getCount() ) )
972 bool bIsNumbered
= true;
973 OUString
sNumberingIsNumber("NumberingIsNumber");
974 if( xPropSetInfo
->hasPropertyByName( sNumberingIsNumber
) )
976 if( !(xPropSet
->getPropertyValue( sNumberingIsNumber
) >>= bIsNumbered
) )
978 OSL_FAIL( "numbered paragraph without number info" );
981 #if OSL_DEBUG_LEVEL > 0
985 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "is-numbered", sInfo
);
989 mbIsNewListItem
= bIsNumbered
;
993 Sequence
<PropertyValue
> aProps
;
994 if( xNumRules
->getByIndex( nListLevel
) >>= aProps
)
996 sal_Int16 eType
= NumberingType::CHAR_SPECIAL
;
997 sal_Unicode cBullet
= 0xf095;
998 const sal_Int32 nCount
= aProps
.getLength();
999 const PropertyValue
* pPropArray
= aProps
.getConstArray();
1000 for( sal_Int32 i
= 0; i
< nCount
; ++i
)
1002 const PropertyValue
& rProp
= pPropArray
[i
];
1003 if( rProp
.Name
== "NumberingType" )
1005 rProp
.Value
>>= eType
;
1007 else if( rProp
.Name
== "BulletChar" )
1010 rProp
.Value
>>= sValue
;
1011 if( !sValue
.isEmpty() )
1013 cBullet
= sValue
[0];
1017 meNumberingType
= eType
;
1018 mbIsListLevelStyleImage
= ( NumberingType::BITMAP
== meNumberingType
);
1019 if( NumberingType::CHAR_SPECIAL
== meNumberingType
)
1025 cBullet
= 0xF000 + 149;
1027 mcBulletChar
= cBullet
;
1028 #if OSL_DEBUG_LEVEL > 0
1029 sInfo
= OUString::number( static_cast<sal_Int32
>(cBullet
) );
1030 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "bullet-char", sInfo
);
1042 Reference
< XEnumerationAccess
> xEnumerationAccess( xTextContent
, UNO_QUERY_THROW
);
1043 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1044 if( xEnumeration
.is() && xEnumeration
->hasMoreElements() )
1046 mrTextPortionEnumeration
.set( xEnumeration
);
1048 #if OSL_DEBUG_LEVEL > 0
1049 sInfo
= "Paragraph";
1052 else if( xServiceInfo
->supportsService( "com.sun.star.text.Table" ) )
1054 OSL_FAIL( "SVGTextWriter::nextParagraph: text tables are not handled." );
1055 #if OSL_DEBUG_LEVEL > 0
1061 OSL_FAIL( "SVGTextWriter::nextParagraph: Unknown text content." );
1064 #if OSL_DEBUG_LEVEL > 0
1065 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", sInfo
);
1066 SvXMLElementExport
aParaElem( mrExport
, XML_NAMESPACE_NONE
, "desc", mbIWS
, mbIWS
);
1071 OSL_FAIL( "SVGTextWriter::nextParagraph: no XServiceInfo interface available for text content." );
1075 const OUString
& rParagraphId
= implGetValidIDFromInterface( Reference
<XInterface
>(xTextContent
, UNO_QUERY
) );
1076 if( !rParagraphId
.isEmpty() )
1078 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "id", rParagraphId
);
1087 bool SVGTextWriter::nextTextPortion()
1089 mrCurrentTextPortion
.clear();
1090 mbIsURLField
= false;
1091 mbIsPlaceholderShape
= false;
1092 if( mrTextPortionEnumeration
.is() && mrTextPortionEnumeration
->hasMoreElements() )
1094 #if OSL_DEBUG_LEVEL > 0
1097 Reference
< XPropertySet
> xPortionPropSet( mrTextPortionEnumeration
->nextElement(), UNO_QUERY
);
1098 Reference
< XPropertySetInfo
> xPortionPropInfo( xPortionPropSet
->getPropertySetInfo() );
1099 Reference
< XTextRange
> xPortionTextRange( xPortionPropSet
, UNO_QUERY
);
1100 if( xPortionPropSet
.is() && xPortionPropInfo
.is()
1101 && xPortionPropInfo
->hasPropertyByName( "TextPortionType" ) )
1103 #if OSL_DEBUG_LEVEL > 0
1104 OUString sPortionType
;
1105 if( xPortionPropSet
->getPropertyValue( "TextPortionType" ) >>= sPortionType
)
1107 sInfo
= "type: " + sPortionType
+ "; ";
1110 if( xPortionTextRange
.is() )
1112 #if OSL_DEBUG_LEVEL > 0
1113 sInfo
+= "content: " + xPortionTextRange
->getString() + "; ";
1115 mrCurrentTextPortion
.set( xPortionTextRange
);
1117 Reference
< XPropertySet
> xRangePropSet( xPortionTextRange
, UNO_QUERY
);
1118 if( xRangePropSet
.is() && xRangePropSet
->getPropertySetInfo()->hasPropertyByName( "TextField" ) )
1120 Reference
< XTextField
> xTextField( xRangePropSet
->getPropertyValue( "TextField" ), UNO_QUERY
);
1121 if( xTextField
.is() )
1123 const OUString
sServicePrefix("com.sun.star.text.textfield.");
1124 const OUString
sPresentationServicePrefix("com.sun.star.presentation.TextField.");
1126 Reference
< XServiceInfo
> xService( xTextField
, UNO_QUERY
);
1127 const Sequence
< OUString
> aServices
= xService
->getSupportedServiceNames();
1129 const OUString
* pNames
= aServices
.getConstArray();
1130 sal_Int32 nCount
= aServices
.getLength();
1132 OUString sFieldName
; // service name postfix of current field
1134 // search for TextField service name
1137 if ( pNames
->matchIgnoreAsciiCase( sServicePrefix
) )
1139 // TextField found => postfix is field type!
1140 sFieldName
= pNames
->copy( sServicePrefix
.getLength() );
1143 else if( pNames
->startsWith( sPresentationServicePrefix
) )
1145 // TextField found => postfix is field type!
1146 sFieldName
= pNames
->copy( sPresentationServicePrefix
.getLength() );
1153 #if OSL_DEBUG_LEVEL > 0
1154 sInfo
+= "text field type: " + sFieldName
+ "; content: " + xTextField
->getPresentation( /* show command: */ false ) + "; ";
1156 if( sFieldName
== "DateTime" || sFieldName
== "Header"
1157 || sFieldName
== "Footer" || sFieldName
== "PageNumber" )
1159 mbIsPlaceholderShape
= true;
1163 mbIsURLField
= sFieldName
== "URL";
1167 Reference
<XPropertySet
> xTextFieldPropSet(xTextField
, UNO_QUERY
);
1168 if( xTextFieldPropSet
.is() )
1171 if( ( xTextFieldPropSet
->getPropertyValue( sFieldName
) ) >>= sURL
)
1173 #if OSL_DEBUG_LEVEL > 0
1174 sInfo
+= "url: " + mrExport
.GetRelativeReference( sURL
);
1176 msUrl
= mrExport
.GetRelativeReference( sURL
);
1177 if( !msUrl
.isEmpty() )
1179 implRegisterInterface( xPortionTextRange
);
1181 const OUString
& rTextPortionId
= implGetValidIDFromInterface( Reference
<XInterface
>(xPortionTextRange
, UNO_QUERY
) );
1182 if( !rTextPortionId
.isEmpty() )
1184 msHyperlinkIdList
+= rTextPortionId
+ " ";
1194 #if OSL_DEBUG_LEVEL > 0
1195 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "TextPortion" );
1196 SvXMLElementExport
aPortionElem( mrExport
, XML_NAMESPACE_NONE
, "desc", mbIWS
, mbIWS
);
1197 mrExport
.GetDocHandler()->characters( sInfo
);
1207 void SVGTextWriter::startTextShape()
1209 if( mpTextShapeElem
)
1211 OSL_FAIL( "SVGTextWriter::startTextShape: text shape already defined." );
1215 mbIsTextShapeStarted
= true;
1216 maParentFont
= vcl::Font();
1217 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "TextShape" );
1219 // if text is rotated, set transform matrix at text element
1220 const vcl::Font
& rFont
= mpVDev
->GetFont();
1221 if( rFont
.GetOrientation() )
1223 Point
aRot( maTextPos
);
1224 OUString aTransform
= "rotate(" +
1225 OUString::number( rFont
.GetOrientation() * -0.1 ) + " " +
1226 OUString::number( aRot
.X() ) + " " +
1227 OUString::number( aRot
.Y() ) + ")";
1228 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrTransform
, aTransform
);
1231 mpTextShapeElem
.reset(new SvXMLElementExport( mrExport
, XML_NAMESPACE_NONE
, aXMLElemText
, true, mbIWS
));
1232 startTextParagraph();
1237 void SVGTextWriter::endTextShape()
1240 mrTextShape
.clear();
1241 mrParagraphEnumeration
.clear();
1242 mrCurrentTextParagraph
.clear();
1243 mpTextShapeElem
.reset();
1244 mbIsTextShapeStarted
= false;
1245 // these need to be invoked after the <text> element has been closed
1246 implExportHyperlinkIds();
1247 implWriteBulletChars();
1248 implWriteEmbeddedBitmaps();
1253 void SVGTextWriter::startTextParagraph()
1257 if( mbIsNewListItem
)
1259 OUString sNumberingType
;
1260 switch( meNumberingType
)
1262 case NumberingType::CHAR_SPECIAL
:
1263 sNumberingType
= "bullet-style";
1265 case NumberingType::BITMAP
:
1266 sNumberingType
= "image-style";
1269 sNumberingType
= "number-style";
1272 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "ooo:numbering-type", sNumberingType
);
1273 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "ListItem" );
1277 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "TextParagraph" );
1279 maParentFont
= vcl::Font();
1280 addFontAttributes( /* isTexTContainer: */ true );
1281 mpTextParagraphElem
.reset(new SvXMLElementExport( mrExport
, XML_NAMESPACE_NONE
, aXMLElemTspan
, mbIWS
, mbIWS
));
1283 if( !mbIsListLevelStyleImage
)
1285 mbPositioningNeeded
= true;
1290 void SVGTextWriter::endTextParagraph()
1292 mrCurrentTextPortion
.clear();
1294 mbIsNewListItem
= false;
1295 mbIsListLevelStyleImage
= false;
1296 mbPositioningNeeded
= false;
1297 mpTextParagraphElem
.reset();
1301 void SVGTextWriter::startTextPosition( bool bExportX
, bool bExportY
)
1305 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "TextPosition" );
1307 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX
, OUString::number( maTextPos
.X() ) );
1309 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY
, OUString::number( maTextPos
.Y() ) );
1311 mpTextPositionElem
.reset( new SvXMLElementExport( mrExport
, XML_NAMESPACE_NONE
, aXMLElemTspan
, mbIWS
, mbIWS
) );
1315 void SVGTextWriter::endTextPosition()
1317 mpTextPositionElem
.reset();
1321 void SVGTextWriter::implExportHyperlinkIds()
1323 if( !msHyperlinkIdList
.isEmpty() )
1325 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "HyperlinkIdList" );
1326 SvXMLElementExport
aDescElem( mrExport
, XML_NAMESPACE_NONE
, "desc", true, false );
1327 mrExport
.GetDocHandler()->characters( msHyperlinkIdList
.trim() );
1328 msHyperlinkIdList
.clear();
1333 void SVGTextWriter::implWriteBulletChars()
1335 if( maBulletListItemMap
.empty() )
1338 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "BulletChars" );
1339 SvXMLElementExport
aGroupElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true );
1341 OUString sId
, sPosition
, sScaling
, sRefId
;
1342 for (auto const& bulletListItem
: maBulletListItemMap
)
1344 // <g id="?" > (used by animations)
1345 // As id we use the id of the text portion placeholder with prefix
1347 sId
= "bullet-char-" + bulletListItem
.first
;
1348 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "id", sId
);
1349 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "BulletChar" );
1350 SvXMLElementExport
aBulletCharElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true );
1352 // <g transform="translate(x,y)" >
1354 const BulletListItemInfo
& rInfo
= bulletListItem
.second
;
1356 // Add positioning attribute through a translation
1357 sPosition
= "translate(" +
1358 OUString::number( rInfo
.aPos
.X() ) +
1359 "," + OUString::number( rInfo
.aPos
.Y() ) + ")";
1360 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "transform", sPosition
);
1362 mrAttributeWriter
.AddPaintAttr( COL_TRANSPARENT
, rInfo
.aColor
);
1364 SvXMLElementExport
aPositioningElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true );
1366 // <use transform="scale(font-size)" xlink:ref="/" >
1368 // Add size attribute through a scaling
1369 sScaling
= "scale(" + OUString::number( rInfo
.nFontSize
) +
1370 "," + OUString::number( rInfo
.nFontSize
)+ ")";
1371 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "transform", sScaling
);
1373 // Add ref attribute
1374 sRefId
= "#bullet-char-template-" +
1375 OUString::number( ( rInfo
.cBulletChar
) );
1376 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrXLinkHRef
, sRefId
);
1378 SvXMLElementExport
aRefElem( mrExport
, XML_NAMESPACE_NONE
, "use", true, true );
1380 } // close aPositioningElem
1384 maBulletListItemMap
.clear();
1388 template< typename MetaBitmapActionType
>
1389 void SVGTextWriter::writeBitmapPlaceholder( const MetaBitmapActionType
* pAction
)
1391 // text position element
1392 const Point
& rPos
= pAction
->GetPoint();
1393 implMap( rPos
, maTextPos
);
1394 startTextPosition();
1395 mbPositioningNeeded
= true;
1396 if( mbIsNewListItem
)
1398 mbIsNewListItem
= false;
1399 mbIsListLevelStyleImage
= false;
1402 // bitmap placeholder element
1403 BitmapChecksum nId
= SVGActionWriter::GetChecksum( pAction
);
1404 OUString sId
= "bitmap-placeholder(" + msShapeId
+ "." +
1405 OUString::number( nId
) + ")";
1408 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "id", sId
);
1409 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "BitmapPlaceholder" );
1410 SvXMLElementExport
aSVGTspanElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemTspan
, mbIWS
, mbIWS
);
1416 void SVGTextWriter::implWriteEmbeddedBitmaps()
1418 if( mpTextEmbeddedBitmapMtf
&& mpTextEmbeddedBitmapMtf
->GetActionSize() )
1420 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "EmbeddedBitmaps" );
1421 SvXMLElementExport
aEmbBitmapGroupElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true );
1423 const GDIMetaFile
& rMtf
= *mpTextEmbeddedBitmapMtf
;
1425 BitmapChecksum nId
, nChecksum
= 0;
1428 sal_uLong nCount
= rMtf
.GetActionSize();
1429 for( sal_uLong nCurAction
= 0; nCurAction
< nCount
; nCurAction
++ )
1432 const MetaAction
* pAction
= rMtf
.GetAction( nCurAction
);
1433 const MetaActionType nType
= pAction
->GetType();
1437 case MetaActionType::BMPSCALE
:
1439 const MetaBmpScaleAction
* pA
= static_cast<const MetaBmpScaleAction
*>(pAction
);
1440 nChecksum
= pA
->GetBitmap().GetChecksum();
1441 aPt
= pA
->GetPoint();
1442 aSz
= pA
->GetSize();
1445 case MetaActionType::BMPEXSCALE
:
1447 const MetaBmpExScaleAction
* pA
= static_cast<const MetaBmpExScaleAction
*>(pAction
);
1448 nChecksum
= pA
->GetBitmapEx().GetChecksum();
1449 aPt
= pA
->GetPoint();
1450 aSz
= pA
->GetSize();
1456 // <g id="?" > (used by animations)
1458 // embedded bitmap id
1459 nId
= SVGActionWriter::GetChecksum( pAction
);
1460 OUString sId
= "embedded-bitmap(" + msShapeId
+ "." + OUString::number( nId
) + ")";
1461 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "id", sId
);
1462 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "EmbeddedBitmap" );
1464 SvXMLElementExport
aEmbBitmapElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true );
1466 // <use x="?" y="?" xlink:ref="?" >
1468 // referenced bitmap template
1469 OUString sRefId
= "#bitmap(" + OUString::number( nChecksum
) + ")";
1473 implMap( aPt
, aPoint
);
1474 implMap( aSz
, aSize
);
1476 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX
, OUString::number( aPoint
.X() ) );
1477 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY
, OUString::number( aPoint
.Y() ) );
1478 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrXLinkHRef
, sRefId
);
1480 SvXMLElementExport
aRefElem( mrExport
, XML_NAMESPACE_NONE
, "use", true, true );
1482 } // close aEmbBitmapElem
1488 void SVGTextWriter::writeTextPortion( const Point
& rPos
,
1489 const OUString
& rText
)
1491 if( rText
.isEmpty() )
1494 bool bStandAloneTextPortion
= false;
1495 if( !isTextShapeStarted() )
1497 bStandAloneTextPortion
= true;
1501 mbLineBreak
= false;
1503 if( !mbIsNewListItem
|| mbIsListLevelStyleImage
)
1505 bool bNotSync
= true;
1507 sal_Int32 nStartPos
;
1510 if( mnLeftTextPortionLength
<= 0 || !mrCurrentTextPortion
.is() )
1512 if( !nextTextPortion() )
1516 sContent
= mrCurrentTextPortion
->getString();
1517 if( mbIsURLField
&& sContent
.isEmpty() )
1519 Reference
< XPropertySet
> xPropSet( mrCurrentTextPortion
, UNO_QUERY
);
1520 Reference
< XTextField
> xTextField( xPropSet
->getPropertyValue( "TextField" ), UNO_QUERY
);
1521 sContent
= xTextField
->getPresentation( /* show command: */ false );
1522 if( sContent
.isEmpty() )
1523 OSL_FAIL( "SVGTextWriter::writeTextPortion: content of URL TextField is empty." );
1525 mnLeftTextPortionLength
= sContent
.getLength();
1530 sContent
= mrCurrentTextPortion
->getString();
1533 nStartPos
= sContent
.getLength() - mnLeftTextPortionLength
;
1534 if( nStartPos
< 0 ) nStartPos
= 0;
1535 mnLeftTextPortionLength
-= rText
.getLength();
1537 if( sContent
.isEmpty() )
1539 if( sContent
== "\n" )
1541 if( sContent
.match( rText
, nStartPos
) )
1546 assert(mpVDev
); //invalid virtual device
1549 const FontMetric
aMetric( mpVDev
->GetFontMetric() );
1551 bool bTextSpecial
= aMetric
.IsShadow() || aMetric
.IsOutline() || (aMetric
.GetRelief() != FontRelief::NONE
);
1553 if( true || !bTextSpecial
)
1555 implWriteTextPortion( rPos
, rText
, mpVDev
->GetTextColor() );
1559 // to be implemented
1562 implWriteTextPortion( rPos
, rText
, mpVDev
->GetTextColor() );
1565 if( bStandAloneTextPortion
)
1572 void SVGTextWriter::implWriteTextPortion( const Point
& rPos
,
1573 const OUString
& rText
,
1577 Point
aBaseLinePos( rPos
);
1578 const FontMetric
aMetric( mpVDev
->GetFontMetric() );
1579 const vcl::Font
& rFont
= mpVDev
->GetFont();
1581 if( rFont
.GetAlignment() == ALIGN_TOP
)
1582 aBaseLinePos
.AdjustY(aMetric
.GetAscent() );
1583 else if( rFont
.GetAlignment() == ALIGN_BOTTOM
)
1584 aBaseLinePos
.AdjustY( -(aMetric
.GetDescent()) );
1586 implMap( rPos
, aPos
);
1588 if( mbPositioningNeeded
)
1590 mbPositioningNeeded
= false;
1591 maTextPos
.setX( aPos
.X() );
1592 maTextPos
.setY( aPos
.Y() );
1593 startTextPosition();
1595 else if( maTextPos
.Y() != aPos
.Y() )
1597 // In case the text position moved backward we could have a line break
1598 // so we end the current line and start a new one.
1599 if( mbLineBreak
|| ( ( maTextPos
.X() + mnTextWidth
) > aPos
.X() ) )
1601 mbLineBreak
= false;
1602 maTextPos
.setX( aPos
.X() );
1603 maTextPos
.setY( aPos
.Y() );
1604 startTextPosition();
1606 else // superscript, subscript, list item numbering
1608 maTextPos
.setY( aPos
.Y() );
1609 startTextPosition( false /* do not export x attribute */ );
1612 // we are dealing with a bullet, so set up this for the next text portion
1613 if( mbIsNewListItem
)
1615 mbIsNewListItem
= false;
1616 mbPositioningNeeded
= true;
1618 if( meNumberingType
== NumberingType::CHAR_SPECIAL
)
1620 // Create an id for the current text portion
1621 implRegisterInterface( mrCurrentTextParagraph
);
1623 // Add the needed info to the BulletListItemMap
1624 OUString sId
= implGetValidIDFromInterface( Reference
<XInterface
>(mrCurrentTextParagraph
, UNO_QUERY
) );
1625 if( !sId
.isEmpty() )
1628 BulletListItemInfo
& aBulletListItemInfo
= maBulletListItemMap
[ sId
];
1629 aBulletListItemInfo
.nFontSize
= rFont
.GetFontHeight();
1630 aBulletListItemInfo
.aColor
= aTextColor
;
1631 aBulletListItemInfo
.aPos
= maTextPos
;
1632 aBulletListItemInfo
.cBulletChar
= mcBulletChar
;
1634 // Make this text portion a bullet placeholder
1635 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "id", sId
);
1636 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "BulletPlaceholder" );
1637 SvXMLElementExport
aSVGTspanElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemTspan
, mbIWS
, mbIWS
);
1643 const OUString
& rTextPortionId
= implGetValidIDFromInterface( Reference
<XInterface
>(mrCurrentTextPortion
, UNO_QUERY
) );
1644 if( !rTextPortionId
.isEmpty() )
1646 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "id", rTextPortionId
);
1649 if( mbIsPlaceholderShape
)
1651 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "PlaceholderText" );
1652 mbIsPlaceholderShape
= false;
1655 addFontAttributes( /* isTexTContainer: */ false );
1657 mrAttributeWriter
.AddPaintAttr( COL_TRANSPARENT
, aTextColor
);
1659 // <a> tag for link should be the innermost tag, inside <tspan>
1660 if( !mbIsPlaceholderShape
&& mbIsURLField
&& !msUrl
.isEmpty() )
1662 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "UrlField" );
1663 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrXLinkHRef
, msUrl
);
1665 SvXMLElementExport
aSVGTspanElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemTspan
, mbIWS
, mbIWS
);
1666 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrXLinkHRef
, msUrl
);
1668 SvXMLElementExport
aSVGAElem( mrExport
, XML_NAMESPACE_NONE
, "a", mbIWS
, mbIWS
);
1669 mrExport
.GetDocHandler()->characters( rText
);
1674 SvXMLElementExport
aSVGTspanElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemTspan
, mbIWS
, mbIWS
);
1675 mrExport
.GetDocHandler()->characters( rText
);
1678 mnTextWidth
+= mpVDev
->GetTextWidth( rText
);
1682 SVGActionWriter::SVGActionWriter( SVGExport
& rExport
, SVGFontExport
& rFontExport
) :
1683 mnCurGradientId( 1 ),
1685 mnCurPatternId( 1 ),
1686 mnCurClipPathId( 1 ),
1687 mpCurrentClipRegionElem(),
1688 mrExport( rExport
),
1690 mrCurrentState( maContextHandler
.getCurrentState() ),
1691 maAttributeWriter( rExport
, rFontExport
, mrCurrentState
),
1692 maTextWriter( rExport
, maAttributeWriter
),
1693 mbClipAttrChanged( false ),
1694 mbIsPlaceholderShape( false )
1696 mpVDev
= VclPtr
<VirtualDevice
>::Create();
1697 mpVDev
->EnableOutput( false );
1698 maTargetMapMode
= MapMode(MapUnit::Map100thMM
);
1699 maTextWriter
.setVirtualDevice( mpVDev
, maTargetMapMode
);
1703 SVGActionWriter::~SVGActionWriter()
1705 mpVDev
.disposeAndClear();
1709 long SVGActionWriter::ImplMap( sal_Int32 nVal
) const
1711 Size
aSz( nVal
, nVal
);
1713 return ImplMap( aSz
, aSz
).Width();
1717 Point
& SVGActionWriter::ImplMap( const Point
& rPt
, Point
& rDstPt
) const
1719 return( rDstPt
= OutputDevice::LogicToLogic( rPt
, mpVDev
->GetMapMode(), maTargetMapMode
) );
1723 Size
& SVGActionWriter::ImplMap( const Size
& rSz
, Size
& rDstSz
) const
1725 return( rDstSz
= OutputDevice::LogicToLogic( rSz
, mpVDev
->GetMapMode(), maTargetMapMode
) );
1729 void SVGActionWriter::ImplMap( const tools::Rectangle
& rRect
, tools::Rectangle
& rDstRect
) const
1731 Point
aTL( rRect
.TopLeft() );
1732 Size
aSz( rRect
.GetSize() );
1734 rDstRect
= tools::Rectangle( ImplMap( aTL
, aTL
), ImplMap( aSz
, aSz
) );
1738 tools::Polygon
& SVGActionWriter::ImplMap( const tools::Polygon
& rPoly
, tools::Polygon
& rDstPoly
) const
1740 rDstPoly
= tools::Polygon( rPoly
.GetSize() );
1742 for( sal_uInt16 i
= 0, nSize
= rPoly
.GetSize(); i
< nSize
; ++i
)
1744 ImplMap( rPoly
[ i
], rDstPoly
[ i
] );
1745 rDstPoly
.SetFlags( i
, rPoly
.GetFlags( i
) );
1752 tools::PolyPolygon
& SVGActionWriter::ImplMap( const tools::PolyPolygon
& rPolyPoly
, tools::PolyPolygon
& rDstPolyPoly
) const
1754 tools::Polygon aPoly
;
1756 rDstPolyPoly
= tools::PolyPolygon();
1758 for( sal_uInt16 i
= 0, nCount
= rPolyPoly
.Count(); i
< nCount
; ++i
)
1760 rDstPolyPoly
.Insert( ImplMap( rPolyPoly
[ i
], aPoly
) );
1763 return rDstPolyPoly
;
1767 OUString
SVGActionWriter::GetPathString( const tools::PolyPolygon
& rPolyPoly
, bool bLine
)
1769 OUStringBuffer aPathData
;
1770 const OUString
aBlank( " " );
1771 const OUString
aComma( "," );
1774 for( long i
= 0, nCount
= rPolyPoly
.Count(); i
< nCount
; i
++ )
1776 const tools::Polygon
& rPoly
= rPolyPoly
[ static_cast<sal_uInt16
>(i
) ];
1777 sal_uInt16 n
= 1, nSize
= rPoly
.GetSize();
1781 aPolyPoint
= rPoly
[ 0 ];
1782 aPathData
.append("M ")
1783 .append(OUString::number( aPolyPoint
.X() ))
1785 .append(OUString::number( aPolyPoint
.Y() ));
1787 sal_Char nCurrentMode
= 0;
1788 const bool bClose(!bLine
|| rPoly
[0] == rPoly
[nSize
- 1]);
1791 aPathData
.append(aBlank
);
1793 if ( ( rPoly
.GetFlags( n
) == PolyFlags::Control
) && ( ( n
+ 2 ) < nSize
) )
1795 if ( nCurrentMode
!= 'C' )
1798 aPathData
.append("C ");
1800 for ( int j
= 0; j
< 3; j
++ )
1803 aPathData
.append(aBlank
);
1805 aPolyPoint
= rPoly
[ n
++ ];
1806 aPathData
.append(OUString::number( aPolyPoint
.X() ))
1808 .append(OUString::number( aPolyPoint
.Y() ));
1813 if ( nCurrentMode
!= 'L' )
1816 aPathData
.append("L ");
1819 aPolyPoint
= rPoly
[ n
++ ];
1820 aPathData
.append(OUString::number( aPolyPoint
.X() ))
1822 .append(OUString::number( aPolyPoint
.Y() ));
1827 aPathData
.append(" Z");
1829 if( i
< ( nCount
- 1 ) )
1830 aPathData
.append(aBlank
);
1834 return aPathData
.makeStringAndClear();
1838 BitmapChecksum
SVGActionWriter::GetChecksum( const MetaAction
* pAction
)
1841 MetaAction
* pA
= const_cast<MetaAction
*>(pAction
);
1842 aMtf
.AddAction( pA
);
1843 return aMtf
.GetChecksum();
1847 void SVGActionWriter::ImplWriteLine( const Point
& rPt1
, const Point
& rPt2
,
1848 const Color
* pLineColor
)
1852 ImplMap( rPt1
, aPt1
);
1853 ImplMap( rPt2
, aPt2
);
1855 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX1
, OUString::number( aPt1
.X() ) );
1856 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY1
, OUString::number( aPt1
.Y() ) );
1857 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX2
, OUString::number( aPt2
.X() ) );
1858 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY2
, OUString::number( aPt2
.Y() ) );
1862 // !!! mrExport.AddAttribute( XML_NAMESPACE_NONE, ... )
1863 OSL_FAIL( "SVGActionWriter::ImplWriteLine: Line color not implemented" );
1867 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_NONE
, "line", true, true );
1872 void SVGActionWriter::ImplWriteRect( const tools::Rectangle
& rRect
, long nRadX
, long nRadY
)
1874 tools::Rectangle aRect
;
1876 ImplMap( rRect
, aRect
);
1878 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX
, OUString::number( aRect
.Left() ) );
1879 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY
, OUString::number( aRect
.Top() ) );
1880 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrWidth
, OUString::number( aRect
.GetWidth() ) );
1881 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrHeight
, OUString::number( aRect
.GetHeight() ) );
1884 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrRX
, OUString::number( ImplMap( nRadX
) ) );
1887 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrRY
, OUString::number( ImplMap( nRadY
) ) );
1889 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_NONE
, "rect", true, true );
1893 void SVGActionWriter::ImplWriteEllipse( const Point
& rCenter
, long nRadX
, long nRadY
)
1897 ImplMap( rCenter
, aCenter
);
1899 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrCX
, OUString::number( aCenter
.X() ) );
1900 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrCY
, OUString::number( aCenter
.Y() ) );
1901 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrRX
, OUString::number( ImplMap( nRadX
) ) );
1902 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrRY
, OUString::number( ImplMap( nRadY
) ) );
1905 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_NONE
, "ellipse", true, true );
1910 void SVGActionWriter::ImplAddLineAttr( const LineInfo
&rAttrs
)
1912 if ( !rAttrs
.IsDefault() )
1914 sal_Int32 nStrokeWidth
= ImplMap( rAttrs
.GetWidth() );
1915 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStrokeWidth
,
1916 OUString::number( nStrokeWidth
) );
1917 // support for LineJoint
1918 switch(rAttrs
.GetLineJoin())
1920 case basegfx::B2DLineJoin::NONE
:
1921 case basegfx::B2DLineJoin::Miter
:
1923 mrExport
.AddAttribute(XML_NAMESPACE_NONE
, aXMLAttrStrokeLinejoin
, "miter");
1926 case basegfx::B2DLineJoin::Bevel
:
1928 mrExport
.AddAttribute(XML_NAMESPACE_NONE
, aXMLAttrStrokeLinejoin
, "bevel");
1931 case basegfx::B2DLineJoin::Round
:
1933 mrExport
.AddAttribute(XML_NAMESPACE_NONE
, aXMLAttrStrokeLinejoin
, "round");
1938 // support for LineCap
1939 switch(rAttrs
.GetLineCap())
1941 default: /* css::drawing::LineCap_BUTT */
1943 // butt is Svg default, so no need to write until the exporter might write styles.
1944 // If this happens, activate here
1945 // mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, "butt");
1948 case css::drawing::LineCap_ROUND
:
1950 mrExport
.AddAttribute(XML_NAMESPACE_NONE
, aXMLAttrStrokeLinecap
, "round");
1953 case css::drawing::LineCap_SQUARE
:
1955 mrExport
.AddAttribute(XML_NAMESPACE_NONE
, aXMLAttrStrokeLinecap
, "square");
1965 void SVGActionWriter::ImplWritePolyPolygon( const tools::PolyPolygon
& rPolyPoly
, bool bLineOnly
,
1966 bool bApplyMapping
)
1968 tools::PolyPolygon aPolyPoly
;
1971 ImplMap( rPolyPoly
, aPolyPoly
);
1973 aPolyPoly
= rPolyPoly
;
1975 // add path data attribute
1976 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "d", GetPathString( aPolyPoly
, bLineOnly
) );
1979 // write polyline/polygon element
1980 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_NONE
, "path", true, true );
1985 void SVGActionWriter::ImplWriteShape( const SVGShapeDescriptor
& rShape
)
1987 tools::PolyPolygon aPolyPoly
;
1989 ImplMap( rShape
.maShapePolyPoly
, aPolyPoly
);
1991 const bool bLineOnly
1992 = (rShape
.maShapeFillColor
== COL_TRANSPARENT
) && (!rShape
.mapShapeGradient
);
1993 tools::Rectangle
aBoundRect( aPolyPoly
.GetBoundRect() );
1995 maAttributeWriter
.AddPaintAttr( rShape
.maShapeLineColor
, rShape
.maShapeFillColor
, &aBoundRect
, rShape
.mapShapeGradient
.get() );
1997 if( !rShape
.maId
.isEmpty() )
1998 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrId
, rShape
.maId
);
2000 if( rShape
.mnStrokeWidth
)
2002 sal_Int32 nStrokeWidth
= ImplMap( rShape
.mnStrokeWidth
);
2003 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStrokeWidth
, OUString::number( nStrokeWidth
) );
2006 // support for LineJoin
2007 switch(rShape
.maLineJoin
)
2009 case basegfx::B2DLineJoin::NONE
:
2010 case basegfx::B2DLineJoin::Miter
:
2012 // miter is Svg default, so no need to write until the exporter might write styles.
2013 // If this happens, activate here
2014 // mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, "miter");
2017 case basegfx::B2DLineJoin::Bevel
:
2019 mrExport
.AddAttribute(XML_NAMESPACE_NONE
, aXMLAttrStrokeLinejoin
, "bevel");
2022 case basegfx::B2DLineJoin::Round
:
2024 mrExport
.AddAttribute(XML_NAMESPACE_NONE
, aXMLAttrStrokeLinejoin
, "round");
2029 // support for LineCap
2030 switch(rShape
.maLineCap
)
2032 default: /* css::drawing::LineCap_BUTT */
2034 // butt is Svg default, so no need to write until the exporter might write styles.
2035 // If this happens, activate here
2036 // mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, "butt");
2039 case css::drawing::LineCap_ROUND
:
2041 mrExport
.AddAttribute(XML_NAMESPACE_NONE
, aXMLAttrStrokeLinecap
, "round");
2044 case css::drawing::LineCap_SQUARE
:
2046 mrExport
.AddAttribute(XML_NAMESPACE_NONE
, aXMLAttrStrokeLinecap
, "square");
2051 if( !rShape
.maDashArray
.empty() )
2053 OUStringBuffer aDashArrayStr
;
2055 for( size_t k
= 0; k
< rShape
.maDashArray
.size(); ++k
)
2057 const sal_Int32 nDash
= ImplMap( FRound( rShape
.maDashArray
[ k
] ) );
2060 aDashArrayStr
.append(",");
2062 aDashArrayStr
.append(OUString::number( nDash
));
2065 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "stroke-dasharray", aDashArrayStr
.makeStringAndClear() );
2068 ImplWritePolyPolygon( aPolyPoly
, bLineOnly
, false );
2073 void SVGActionWriter::ImplCreateClipPathDef( const tools::PolyPolygon
& rPolyPoly
)
2075 OUString aClipPathId
= aPrefixClipPathId
+ OUString::number( mnCurClipPathId
++ );
2077 SvXMLElementExport
aElemDefs( mrExport
, XML_NAMESPACE_NONE
, aXMLElemDefs
, true, true );
2080 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrId
, aClipPathId
);
2081 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "clipPathUnits", "userSpaceOnUse" );
2082 SvXMLElementExport
aElemClipPath( mrExport
, XML_NAMESPACE_NONE
, "clipPath", true, true );
2084 ImplWritePolyPolygon(rPolyPoly
, false);
2088 void SVGActionWriter::ImplStartClipRegion(sal_Int32 nClipPathId
)
2090 assert(!mpCurrentClipRegionElem
);
2092 if (nClipPathId
== 0)
2095 OUString aUrl
= OUStringLiteral("url(#") + aPrefixClipPathId
+ OUString::number( nClipPathId
) + ")";
2096 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "clip-path", aUrl
);
2097 mpCurrentClipRegionElem
.reset( new SvXMLElementExport( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true ) );
2100 void SVGActionWriter::ImplEndClipRegion()
2102 if (mpCurrentClipRegionElem
)
2104 mpCurrentClipRegionElem
.reset();
2108 void SVGActionWriter::ImplWriteClipPath( const tools::PolyPolygon
& rPolyPoly
)
2110 ImplEndClipRegion();
2112 if( rPolyPoly
.Count() == 0 )
2115 ImplCreateClipPathDef(rPolyPoly
);
2116 mrCurrentState
.nRegionClipPathId
= mnCurClipPathId
- 1;
2117 ImplStartClipRegion( mrCurrentState
.nRegionClipPathId
);
2120 void SVGActionWriter::ImplWritePattern( const tools::PolyPolygon
& rPolyPoly
,
2121 const Hatch
* pHatch
,
2122 const Gradient
* pGradient
,
2123 sal_uInt32 nWriteFlags
)
2125 if( rPolyPoly
.Count() )
2127 SvXMLElementExport
aElemG( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true );
2129 OUString aPatternId
= "pattern" + OUString::number( mnCurPatternId
++ );
2132 SvXMLElementExport
aElemDefs( mrExport
, XML_NAMESPACE_NONE
, aXMLElemDefs
, true, true );
2134 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrId
, aPatternId
);
2136 tools::Rectangle aRect
;
2137 ImplMap( rPolyPoly
.GetBoundRect(), aRect
);
2139 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX
, OUString::number( aRect
.Left() ) );
2140 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY
, OUString::number( aRect
.Top() ) );
2141 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrWidth
, OUString::number( aRect
.GetWidth() ) );
2142 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrHeight
, OUString::number( aRect
.GetHeight() ) );
2144 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "patternUnits", OUString( "userSpaceOnUse") );
2147 SvXMLElementExport
aElemPattern( mrExport
, XML_NAMESPACE_NONE
, "pattern", true, true );
2149 // The origin of a pattern is positioned at (aRect.Left(), aRect.Top()).
2150 // So we need to adjust the pattern coordinate.
2151 OUString aTransform
= "translate(" +
2152 OUString::number( -aRect
.Left() ) +
2153 "," + OUString::number( -aRect
.Top() ) +
2155 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrTransform
, aTransform
);
2158 SvXMLElementExport
aElemG2( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true );
2160 GDIMetaFile aTmpMtf
;
2162 mpVDev
->AddHatchActions( rPolyPoly
, *pHatch
, aTmpMtf
);
2163 else if ( pGradient
)
2164 mpVDev
->AddGradientActions( rPolyPoly
.GetBoundRect(), *pGradient
, aTmpMtf
);
2165 ImplWriteActions( aTmpMtf
, nWriteFlags
, nullptr );
2170 OUString aPatternStyle
= "fill:url(#" + aPatternId
+ ")";
2172 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStyle
, aPatternStyle
);
2173 ImplWritePolyPolygon( rPolyPoly
, false );
2178 void SVGActionWriter::ImplWriteGradientEx( const tools::PolyPolygon
& rPolyPoly
, const Gradient
& rGradient
,
2179 sal_uInt32 nWriteFlags
)
2181 if ( rGradient
.GetStyle() == GradientStyle::Linear
||
2182 rGradient
.GetStyle() == GradientStyle::Axial
)
2184 ImplWriteGradientLinear( rPolyPoly
, rGradient
);
2188 ImplWritePattern( rPolyPoly
, nullptr, &rGradient
, nWriteFlags
);
2193 void SVGActionWriter::ImplWriteGradientLinear( const tools::PolyPolygon
& rPolyPoly
,
2194 const Gradient
& rGradient
)
2196 if( rPolyPoly
.Count() )
2198 SvXMLElementExport
aElemG( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true );
2200 OUString aGradientId
= "gradient" + OUString::number( mnCurGradientId
++ );
2203 SvXMLElementExport
aElemDefs( mrExport
, XML_NAMESPACE_NONE
, aXMLElemDefs
, true, true );
2205 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrId
, aGradientId
);
2207 tools::Rectangle aTmpRect
, aRect
;
2208 Point aTmpCenter
, aCenter
;
2210 rGradient
.GetBoundRect( rPolyPoly
.GetBoundRect(), aTmpRect
, aTmpCenter
);
2211 ImplMap( aTmpRect
, aRect
);
2212 ImplMap( aTmpCenter
, aCenter
);
2213 const sal_uInt16 nAngle
= rGradient
.GetAngle() % 3600;
2215 tools::Polygon
aPoly( 2 );
2216 // Setting x value of a gradient vector to rotation center to
2217 // place a gradient vector in a target polygon.
2218 // This would help editing it in SVG editors like inkscape.
2219 aPoly
[ 0 ].setX( aCenter
.X() );
2220 aPoly
[ 1 ].setX( aCenter
.X() );
2221 aPoly
[ 0 ].setY( aRect
.Top() );
2222 aPoly
[ 1 ].setY( aRect
.Bottom() );
2223 aPoly
.Rotate( aCenter
, nAngle
);
2225 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX1
, OUString::number( aPoly
[ 0 ].X() ) );
2226 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY1
, OUString::number( aPoly
[ 0 ].Y() ) );
2227 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX2
, OUString::number( aPoly
[ 1 ].X() ) );
2228 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY2
, OUString::number( aPoly
[ 1 ].Y() ) );
2230 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrGradientUnits
,
2231 OUString( "userSpaceOnUse" ) );
2235 SvXMLElementExport
aElemLinearGradient( mrExport
, XML_NAMESPACE_NONE
, aXMLElemLinearGradient
, true, true );
2237 const Color aStartColor
= ImplGetColorWithIntensity( rGradient
.GetStartColor(), rGradient
.GetStartIntensity() );
2238 const Color aEndColor
= ImplGetColorWithIntensity( rGradient
.GetEndColor(), rGradient
.GetEndIntensity() );
2239 double fBorderOffset
= rGradient
.GetBorder() / 100.0;
2240 const sal_uInt16 nSteps
= rGradient
.GetSteps();
2241 if( rGradient
.GetStyle() == GradientStyle::Linear
)
2243 // Emulate non-smooth gradient
2244 if( 0 < nSteps
&& nSteps
< 100 )
2246 double fOffsetStep
= ( 1.0 - fBorderOffset
) / static_cast<double>(nSteps
);
2247 for( sal_uInt16 i
= 0; i
< nSteps
; i
++ ) {
2248 Color aColor
= ImplGetGradientColor( aStartColor
, aEndColor
, i
/ static_cast<double>(nSteps
) );
2249 ImplWriteGradientStop( aColor
, fBorderOffset
+ ( i
+ 1 ) * fOffsetStep
);
2250 aColor
= ImplGetGradientColor( aStartColor
, aEndColor
, ( i
+ 1 ) / static_cast<double>(nSteps
) );
2251 ImplWriteGradientStop( aColor
, fBorderOffset
+ ( i
+ 1 ) * fOffsetStep
);
2256 ImplWriteGradientStop( aStartColor
, fBorderOffset
);
2257 ImplWriteGradientStop( aEndColor
, 1.0 );
2263 // Emulate non-smooth gradient
2264 if( 0 < nSteps
&& nSteps
< 100 )
2266 double fOffsetStep
= ( 0.5 - fBorderOffset
) / static_cast<double>(nSteps
);
2268 for( sal_uInt16 i
= 0; i
< nSteps
; i
++ )
2270 Color aColor
= ImplGetGradientColor( aEndColor
, aStartColor
, i
/ static_cast<double>(nSteps
) );
2271 ImplWriteGradientStop( aColor
, fBorderOffset
+ i
* fOffsetStep
);
2272 aColor
= ImplGetGradientColor( aEndColor
, aStartColor
, (i
+ 1 ) / static_cast<double>(nSteps
) );
2273 ImplWriteGradientStop( aColor
, fBorderOffset
+ i
* fOffsetStep
);
2276 for( sal_uInt16 i
= 0; i
< nSteps
; i
++ )
2278 Color aColor
= ImplGetGradientColor( aStartColor
, aEndColor
, i
/ static_cast<double>(nSteps
) );
2279 ImplWriteGradientStop( aColor
, 0.5 + (i
+ 1) * fOffsetStep
);
2280 aColor
= ImplGetGradientColor( aStartColor
, aEndColor
, (i
+ 1 ) / static_cast<double>(nSteps
) );
2281 ImplWriteGradientStop( aColor
, 0.5 + (i
+ 1) * fOffsetStep
);
2286 ImplWriteGradientStop( aEndColor
, fBorderOffset
);
2287 ImplWriteGradientStop( aStartColor
, 0.5 );
2288 ImplWriteGradientStop( aEndColor
, 1.0 - fBorderOffset
);
2294 OUString aGradientStyle
= "fill:url(#" + aGradientId
+ ")";
2296 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStyle
, aGradientStyle
);
2297 ImplWritePolyPolygon( rPolyPoly
, false );
2302 void SVGActionWriter::ImplWriteGradientStop( const Color
& rColor
, double fOffset
)
2304 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrOffset
, OUString::number( fOffset
) );
2306 OUString aStyle
, aColor
;
2307 aStyle
+= "stop-color:";
2308 SVGAttributeWriter::ImplGetColorStr ( rColor
, aColor
);
2311 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStyle
, aStyle
);
2313 SvXMLElementExport
aElemStartStop( mrExport
, XML_NAMESPACE_NONE
, aXMLElemStop
, true, true );
2318 Color
SVGActionWriter::ImplGetColorWithIntensity( const Color
& rColor
,
2319 sal_uInt16 nIntensity
)
2321 sal_uInt8 nNewRed
= static_cast<sal_uInt8
>( static_cast<long>(rColor
.GetRed()) * nIntensity
/ 100L );
2322 sal_uInt8 nNewGreen
= static_cast<sal_uInt8
>( static_cast<long>(rColor
.GetGreen()) * nIntensity
/ 100L );
2323 sal_uInt8 nNewBlue
= static_cast<sal_uInt8
>( static_cast<long>(rColor
.GetBlue()) * nIntensity
/ 100L );
2324 return Color( nNewRed
, nNewGreen
, nNewBlue
);
2328 Color
SVGActionWriter::ImplGetGradientColor( const Color
& rStartColor
,
2329 const Color
& rEndColor
,
2332 long nRedStep
= rEndColor
.GetRed() - rStartColor
.GetRed();
2333 long nNewRed
= rStartColor
.GetRed() + static_cast<long>( nRedStep
* fOffset
);
2334 nNewRed
= ( nNewRed
< 0 ) ? 0 : ( nNewRed
> 0xFF) ? 0xFF : nNewRed
;
2336 long nGreenStep
= rEndColor
.GetGreen() - rStartColor
.GetGreen();
2337 long nNewGreen
= rStartColor
.GetGreen() + static_cast<long>( nGreenStep
* fOffset
);
2338 nNewGreen
= ( nNewGreen
< 0 ) ? 0 : ( nNewGreen
> 0xFF) ? 0xFF : nNewGreen
;
2340 long nBlueStep
= rEndColor
.GetBlue() - rStartColor
.GetBlue();
2341 long nNewBlue
= rStartColor
.GetBlue() + static_cast<long>( nBlueStep
* fOffset
);
2342 nNewBlue
= ( nNewBlue
< 0 ) ? 0 : ( nNewBlue
> 0xFF) ? 0xFF : nNewBlue
;
2344 return Color( static_cast<sal_uInt8
>(nNewRed
), static_cast<sal_uInt8
>(nNewGreen
), static_cast<sal_uInt8
>(nNewBlue
) );
2348 void SVGActionWriter::ImplWriteMask( GDIMetaFile
& rMtf
,
2349 const Point
& rDestPt
,
2350 const Size
& rDestSize
,
2351 const Gradient
& rGradient
,
2352 sal_uInt32 nWriteFlags
)
2354 Point
aSrcPt( rMtf
.GetPrefMapMode().GetOrigin() );
2355 const Size
aSrcSize( rMtf
.GetPrefSize() );
2356 const double fScaleX
= aSrcSize
.Width() ? static_cast<double>(rDestSize
.Width()) / aSrcSize
.Width() : 1.0;
2357 const double fScaleY
= aSrcSize
.Height() ? static_cast<double>(rDestSize
.Height()) / aSrcSize
.Height() : 1.0;
2358 long nMoveX
, nMoveY
;
2360 if( fScaleX
!= 1.0 || fScaleY
!= 1.0 )
2362 rMtf
.Scale( fScaleX
, fScaleY
);
2363 aSrcPt
.setX( FRound( aSrcPt
.X() * fScaleX
) );
2364 aSrcPt
.setY( FRound( aSrcPt
.Y() * fScaleY
) );
2367 nMoveX
= rDestPt
.X() - aSrcPt
.X();
2368 nMoveY
= rDestPt
.Y() - aSrcPt
.Y();
2370 if( nMoveX
|| nMoveY
)
2371 rMtf
.Move( nMoveX
, nMoveY
);
2373 OUString aMaskId
= "mask" + OUString::number( mnCurMaskId
++ );
2376 SvXMLElementExport
aElemDefs( mrExport
, XML_NAMESPACE_NONE
, aXMLElemDefs
, true, true );
2378 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrId
, aMaskId
);
2380 SvXMLElementExport
aElemMask( mrExport
, XML_NAMESPACE_NONE
, "mask", true, true );
2382 const tools::PolyPolygon
aPolyPolygon( tools::PolyPolygon( tools::Rectangle( rDestPt
, rDestSize
) ) );
2383 Gradient
aGradient( rGradient
);
2385 // swap gradient stops to adopt SVG mask
2386 Color
aTmpColor( aGradient
.GetStartColor() );
2387 sal_uInt16
nTmpIntensity( aGradient
.GetStartIntensity() );
2388 aGradient
.SetStartColor( aGradient
.GetEndColor() );
2389 aGradient
.SetStartIntensity( aGradient
.GetEndIntensity() ) ;
2390 aGradient
.SetEndColor( aTmpColor
);
2391 aGradient
.SetEndIntensity( nTmpIntensity
);
2393 ImplWriteGradientEx( aPolyPolygon
, aGradient
, nWriteFlags
);
2397 OUString aMaskStyle
= "mask:url(#" + aMaskId
+ ")";
2398 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrStyle
, aMaskStyle
);
2401 SvXMLElementExport
aElemG( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, true );
2404 ImplWriteActions( rMtf
, nWriteFlags
, nullptr );
2410 void SVGActionWriter::ImplWriteText( const Point
& rPos
, const OUString
& rText
,
2411 const long* pDXArray
, long nWidth
)
2413 const FontMetric
aMetric( mpVDev
->GetFontMetric() );
2415 bool bTextSpecial
= aMetric
.IsShadow() || aMetric
.IsOutline() || (aMetric
.GetRelief() != FontRelief::NONE
);
2419 ImplWriteText( rPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
2423 if( aMetric
.GetRelief() != FontRelief::NONE
)
2425 Color
aReliefColor( COL_LIGHTGRAY
);
2426 Color
aTextColor( mpVDev
->GetTextColor() );
2428 if ( aTextColor
== COL_BLACK
)
2429 aTextColor
= COL_WHITE
;
2431 if ( aTextColor
== COL_WHITE
)
2432 aReliefColor
= COL_BLACK
;
2436 Point
aOffset( 6, 6 );
2438 if ( aMetric
.GetRelief() == FontRelief::Engraved
)
2447 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, aReliefColor
);
2448 ImplWriteText( rPos
, rText
, pDXArray
, nWidth
, aTextColor
);
2452 if( aMetric
.IsShadow() )
2454 long nOff
= 1 + ((aMetric
.GetLineHeight()-24)/24);
2455 if ( aMetric
.IsOutline() )
2458 Color
aTextColor( mpVDev
->GetTextColor() );
2459 Color
aShadowColor( COL_BLACK
);
2461 if ( (aTextColor
== COL_BLACK
) || (aTextColor
.GetLuminance() < 8) )
2462 aShadowColor
= COL_LIGHTGRAY
;
2465 aPos
+= Point( nOff
, nOff
);
2466 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, aShadowColor
);
2468 if( !aMetric
.IsOutline() )
2470 ImplWriteText( rPos
, rText
, pDXArray
, nWidth
, aTextColor
);
2474 if( aMetric
.IsOutline() )
2476 Point aPos
= rPos
+ Point( -6, -6 );
2477 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
2478 aPos
= rPos
+ Point( +6, +6);
2479 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
2480 aPos
= rPos
+ Point( -6, +0);
2481 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
2482 aPos
= rPos
+ Point( -6, +6);
2483 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
2484 aPos
= rPos
+ Point( +0, +6);
2485 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
2486 aPos
= rPos
+ Point( +0, -6);
2487 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
2488 aPos
= rPos
+ Point( +6, -1);
2489 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
2490 aPos
= rPos
+ Point( +6, +0);
2491 ImplWriteText( aPos
, rText
, pDXArray
, nWidth
, mpVDev
->GetTextColor() );
2493 ImplWriteText( rPos
, rText
, pDXArray
, nWidth
, COL_WHITE
);
2500 void SVGActionWriter::ImplWriteText( const Point
& rPos
, const OUString
& rText
,
2501 const long* pDXArray
, long nWidth
,
2504 sal_Int32 nLen
= rText
.getLength();
2507 Point
aBaseLinePos( rPos
);
2508 const FontMetric
aMetric( mpVDev
->GetFontMetric() );
2509 const vcl::Font
& rFont
= mpVDev
->GetFont();
2511 if( rFont
.GetAlignment() == ALIGN_TOP
)
2512 aBaseLinePos
.AdjustY(aMetric
.GetAscent() );
2513 else if( rFont
.GetAlignment() == ALIGN_BOTTOM
)
2514 aBaseLinePos
.AdjustY( -(aMetric
.GetDescent()) );
2516 ImplMap( rPos
, aPos
);
2518 std::unique_ptr
<long[]> xTmpArray(new long[nLen
]);
2522 aNormSize
= Size( mpVDev
->GetTextWidth( rText
), 0 );
2523 memcpy(xTmpArray
.get(), pDXArray
, nLen
* sizeof(long));
2527 aNormSize
= Size( mpVDev
->GetTextArray( rText
, xTmpArray
.get() ), 0 );
2529 long* pDX
= xTmpArray
.get();
2531 // if text is rotated, set transform matrix at new g element
2532 if( rFont
.GetOrientation() )
2535 OUString aTransform
= "rotate(" +
2536 OUString::number( rFont
.GetOrientation() * -0.1 ) + " " +
2537 OUString::number( aRot
.X() ) + " " +
2538 OUString::number( aRot
.Y() ) + ")";
2539 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrTransform
, aTransform
);
2543 maAttributeWriter
.AddPaintAttr( COL_TRANSPARENT
, aTextColor
);
2545 // for each line of text there should be at least one group element
2546 SvXMLElementExport
aSVGGElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemG
, true, false );
2548 bool bIsPlaceholderField
= false;
2550 if( mbIsPlaceholderShape
)
2552 bIsPlaceholderField
= rText
.match( sPlaceholderTag
);
2553 // for a placeholder text field we export only one <text> svg element
2554 if( bIsPlaceholderField
)
2556 OUString sCleanTextContent
;
2557 static const sal_Int32 nFrom
= sPlaceholderTag
.getLength();
2558 if( rText
.getLength() > nFrom
)
2560 sCleanTextContent
= rText
.copy( nFrom
);
2562 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "class", "PlaceholderText" );
2563 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX
, OUString::number( aPos
.X() ) );
2564 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY
, OUString::number( aPos
.Y() ) );
2566 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemText
, true, false );
2567 // At least for the single slide case we need really to export placeholder text
2568 mrExport
.GetDocHandler()->characters( sCleanTextContent
);
2573 if( !bIsPlaceholderField
)
2577 aNormSize
.setWidth( pDX
[ nLen
- 2 ] + mpVDev
->GetTextWidth( OUString(rText
[nLen
- 1]) ) );
2579 if( nWidth
&& aNormSize
.Width() && ( nWidth
!= aNormSize
.Width() ) )
2582 const double fFactor
= static_cast<double>(nWidth
) / aNormSize
.Width();
2584 for( i
= 0; i
< ( nLen
- 1 ); i
++ )
2585 pDX
[ i
] = FRound( pDX
[ i
] * fFactor
);
2589 css::uno::Reference
< css::i18n::XBreakIterator
> xBI( vcl::unohelper::CreateBreakIterator() );
2590 const css::lang::Locale
& rLocale
= Application::GetSettings().GetLanguageTag().getLocale();
2591 sal_Int32 nCurPos
= 0, nLastPos
= 0, nX
= aPos
.X();
2593 // write single glyphs at absolute text positions
2594 for( bool bCont
= true; bCont
; )
2596 sal_Int32 nCount
= 1;
2599 nCurPos
= xBI
->nextCharacters( rText
, nCurPos
, rLocale
,
2600 css::i18n::CharacterIteratorMode::SKIPCELL
,
2603 nCount
= nCurPos
- nLastPos
;
2604 bCont
= ( nCurPos
< rText
.getLength() ) && nCount
;
2608 const OUString
aGlyph( rText
.copy( nLastPos
, nCount
) );
2610 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX
, OUString::number( nX
) );
2611 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY
, OUString::number( aPos
.Y() ) );
2614 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemText
, true, false );
2615 mrExport
.GetDocHandler()->characters( aGlyph
);
2620 // #118796# do NOT access pDXArray, it may be zero (!)
2621 sal_Int32 nDXWidth
= pDX
[ nCurPos
- 1 ];
2622 nDXWidth
= ImplMap( nDXWidth
);
2623 nX
= aPos
.X() + nDXWidth
;
2631 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX
, OUString::number( aPos
.X() ) );
2632 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY
, OUString::number( aPos
.Y() ) );
2635 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_NONE
, aXMLElemText
, true, false );
2636 mrExport
.GetDocHandler()->characters( rText
);
2642 if( !mrExport
.IsUseNativeTextDecoration() )
2644 if( rFont
.GetStrikeout() != STRIKEOUT_NONE
|| rFont
.GetUnderline() != LINESTYLE_NONE
)
2646 tools::Polygon
aPoly( 4 );
2647 const long nLineHeight
= std::max
<long>( FRound( aMetric
.GetLineHeight() * 0.05 ), 1 );
2649 if( rFont
.GetStrikeout() )
2651 const long nYLinePos
= aBaseLinePos
.Y() - FRound( aMetric
.GetAscent() * 0.26 );
2653 aPoly
[ 0 ].setX( aBaseLinePos
.X() ); aPoly
[ 0 ].setY( nYLinePos
- ( nLineHeight
>> 1 ) );
2654 aPoly
[ 1 ].setX( aBaseLinePos
.X() + aNormSize
.Width() - 1 ); aPoly
[ 1 ].setY( aPoly
[ 0 ].Y() );
2655 aPoly
[ 2 ].setX( aPoly
[ 1 ].X() ); aPoly
[ 2 ].setY( aPoly
[ 0 ].Y() + nLineHeight
- 1 );
2656 aPoly
[ 3 ].setX( aPoly
[ 0 ].X() ); aPoly
[ 3 ].setY( aPoly
[ 2 ].Y() );
2658 ImplWritePolyPolygon( aPoly
, false );
2661 if( rFont
.GetUnderline() )
2663 const long nYLinePos
= aBaseLinePos
.Y() + ( nLineHeight
<< 1 );
2665 aPoly
[ 0 ].setX( aBaseLinePos
.X() ); aPoly
[ 0 ].setY( nYLinePos
- ( nLineHeight
>> 1 ) );
2666 aPoly
[ 1 ].setX( aBaseLinePos
.X() + aNormSize
.Width() - 1 ); aPoly
[ 1 ].setY( aPoly
[ 0 ].Y() );
2667 aPoly
[ 2 ].setX( aPoly
[ 1 ].X() ); aPoly
[ 2 ].setY( aPoly
[ 0 ].Y() + nLineHeight
- 1 );
2668 aPoly
[ 3 ].setX( aPoly
[ 0 ].X() ); aPoly
[ 3 ].setY( aPoly
[ 2 ].Y() );
2670 ImplWritePolyPolygon( aPoly
, false );
2677 void SVGActionWriter::ImplWriteBmp( const BitmapEx
& rBmpEx
,
2678 const Point
& rPt
, const Size
& rSz
,
2679 const Point
& rSrcPt
, const Size
& rSrcSz
)
2683 BitmapEx
aBmpEx( rBmpEx
);
2684 const tools::Rectangle
aBmpRect( Point(), rBmpEx
.GetSizePixel() );
2685 const tools::Rectangle
aSrcRect( rSrcPt
, rSrcSz
);
2687 if( aSrcRect
!= aBmpRect
)
2688 aBmpEx
.Crop( aSrcRect
);
2692 SvMemoryStream
aOStm( 65535, 65535 );
2694 if( GraphicConverter::Export( aOStm
, rBmpEx
, ConvertDataFormat::PNG
) == ERRCODE_NONE
)
2698 Sequence
< sal_Int8
> aSeq( static_cast<sal_Int8
const *>(aOStm
.GetData()), aOStm
.Tell() );
2699 OUStringBuffer
aBuffer( "data:image/png;base64," );
2700 ::comphelper::Base64::encode( aBuffer
, aSeq
);
2702 ImplMap( rPt
, aPt
);
2703 ImplMap( rSz
, aSz
);
2705 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrX
, OUString::number( aPt
.X() ) );
2706 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrY
, OUString::number( aPt
.Y() ) );
2707 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrWidth
, OUString::number( aSz
.Width() ) );
2708 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrHeight
, OUString::number( aSz
.Height() ) );
2710 // the image must be scaled to aSz in a non-uniform way
2711 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, "preserveAspectRatio", "none" );
2713 mrExport
.AddAttribute( XML_NAMESPACE_NONE
, aXMLAttrXLinkHRef
, aBuffer
.makeStringAndClear() );
2715 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_NONE
, "image", true, true );
2723 void SVGActionWriter::ImplWriteActions( const GDIMetaFile
& rMtf
,
2724 sal_uInt32 nWriteFlags
,
2725 const OUString
* pElementId
,
2726 const Reference
< css::drawing::XShape
>* pxShape
,
2727 const GDIMetaFile
* pTextEmbeddedBitmapMtf
)
2729 // need a counter for the actions written per shape to avoid double ID
2731 sal_Int32
nEntryCount(0);
2733 #if OSL_DEBUG_LEVEL > 0
2734 bool bIsTextShape
= false;
2735 if( !mrExport
.IsUsePositionedCharacters() && pxShape
2736 && Reference
< XText
>( *pxShape
, UNO_QUERY
).is() )
2738 bIsTextShape
= true;
2741 mbIsPlaceholderShape
= false;
2742 if( ( pElementId
!= nullptr ) && ( *pElementId
== sPlaceholderTag
) )
2744 mbIsPlaceholderShape
= true;
2745 // since we utilize pElementId in an improper way we reset it to NULL before to go on
2746 pElementId
= nullptr;
2749 for( sal_uLong nCurAction
= 0, nCount
= rMtf
.GetActionSize(); nCurAction
< nCount
; nCurAction
++ )
2751 const MetaAction
* pAction
= rMtf
.GetAction( nCurAction
);
2752 const MetaActionType nType
= pAction
->GetType();
2754 #if OSL_DEBUG_LEVEL > 0
2759 SvXMLElementExport
aElem( mrExport
,
2760 XML_NAMESPACE_NONE
, "desc", false, false );
2761 OUStringBuffer
sType(OUString::number(static_cast<sal_uInt16
>(nType
)));
2762 if (pAction
&& (nType
== MetaActionType::COMMENT
))
2765 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
2766 OString sComment
= pA
->GetComment();
2767 if (!sComment
.isEmpty())
2769 sType
.append(OStringToOUString(
2770 sComment
, RTL_TEXTENCODING_UTF8
));
2772 if (sComment
.equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN"))
2774 sal_uInt8
const*const pData
= pA
->GetData();
2775 if (pData
&& (pA
->GetDataSize()))
2777 sal_uInt16 sz
= static_cast<sal_uInt16
>((pA
->GetDataSize()) / 2);
2782 reinterpret_cast<sal_Unicode
const*>(pData
),
2788 if (sType
.getLength())
2790 mrExport
.GetDocHandler()->characters(
2791 sType
.makeStringAndClear());
2796 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
2797 SAL_WARN( "filter.svg", pA
->GetComment() );
2804 case MetaActionType::PIXEL
:
2806 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2808 const MetaPixelAction
* pA
= static_cast<const MetaPixelAction
*>(pAction
);
2810 maAttributeWriter
.AddPaintAttr( pA
->GetColor(), pA
->GetColor() );
2811 ImplWriteLine( pA
->GetPoint(), pA
->GetPoint(), &pA
->GetColor() );
2816 case MetaActionType::POINT
:
2818 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2820 const MetaPointAction
* pA
= static_cast<const MetaPointAction
*>(pAction
);
2822 maAttributeWriter
.AddPaintAttr( mpVDev
->GetLineColor(), mpVDev
->GetLineColor() );
2823 ImplWriteLine( pA
->GetPoint(), pA
->GetPoint() );
2828 case MetaActionType::LINE
:
2830 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2832 const MetaLineAction
* pA
= static_cast<const MetaLineAction
*>(pAction
);
2834 maAttributeWriter
.AddPaintAttr( mpVDev
->GetLineColor(), mpVDev
->GetLineColor() );
2835 ImplWriteLine( pA
->GetStartPoint(), pA
->GetEndPoint() );
2840 case MetaActionType::RECT
:
2842 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2844 maAttributeWriter
.AddPaintAttr( mpVDev
->GetLineColor(), mpVDev
->GetFillColor() );
2845 ImplWriteRect( static_cast<const MetaRectAction
*>(pAction
)->GetRect() );
2850 case MetaActionType::ROUNDRECT
:
2852 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2854 const MetaRoundRectAction
* pA
= static_cast<const MetaRoundRectAction
*>(pAction
);
2856 maAttributeWriter
.AddPaintAttr( mpVDev
->GetLineColor(), mpVDev
->GetFillColor() );
2857 ImplWriteRect( pA
->GetRect(), pA
->GetHorzRound(), pA
->GetVertRound() );
2862 case MetaActionType::ELLIPSE
:
2864 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2866 const MetaEllipseAction
* pA
= static_cast<const MetaEllipseAction
*>(pAction
);
2867 const tools::Rectangle
& rRect
= pA
->GetRect();
2869 maAttributeWriter
.AddPaintAttr( mpVDev
->GetLineColor(), mpVDev
->GetFillColor() );
2870 ImplWriteEllipse( rRect
.Center(), rRect
.GetWidth() >> 1, rRect
.GetHeight() >> 1 );
2875 case MetaActionType::ARC
:
2876 case MetaActionType::PIE
:
2877 case MetaActionType::CHORD
:
2878 case MetaActionType::POLYGON
:
2880 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2882 tools::Polygon aPoly
;
2886 case MetaActionType::ARC
:
2888 const MetaArcAction
* pA
= static_cast<const MetaArcAction
*>(pAction
);
2889 aPoly
= tools::Polygon( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), PolyStyle::Arc
);
2893 case MetaActionType::PIE
:
2895 const MetaPieAction
* pA
= static_cast<const MetaPieAction
*>(pAction
);
2896 aPoly
= tools::Polygon( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), PolyStyle::Pie
);
2900 case MetaActionType::CHORD
:
2902 const MetaChordAction
* pA
= static_cast<const MetaChordAction
*>(pAction
);
2903 aPoly
= tools::Polygon( pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), PolyStyle::Chord
);
2907 case MetaActionType::POLYGON
:
2908 aPoly
= static_cast<const MetaPolygonAction
*>(pAction
)->GetPolygon();
2913 if( aPoly
.GetSize() )
2915 maAttributeWriter
.AddPaintAttr( mpVDev
->GetLineColor(), mpVDev
->GetFillColor() );
2916 ImplWritePolyPolygon( aPoly
, false );
2922 case MetaActionType::POLYLINE
:
2924 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2926 const MetaPolyLineAction
* pA
= static_cast<const MetaPolyLineAction
*>(pAction
);
2927 const tools::Polygon
& rPoly
= pA
->GetPolygon();
2929 if( rPoly
.GetSize() )
2931 maAttributeWriter
.AddPaintAttr( mpVDev
->GetLineColor(), COL_TRANSPARENT
);
2932 ImplAddLineAttr( pA
->GetLineInfo() );
2933 ImplWritePolyPolygon( rPoly
, true );
2939 case MetaActionType::POLYPOLYGON
:
2941 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2943 const MetaPolyPolygonAction
* pA
= static_cast<const MetaPolyPolygonAction
*>(pAction
);
2944 const tools::PolyPolygon
& rPolyPoly
= pA
->GetPolyPolygon();
2946 if( rPolyPoly
.Count() )
2948 maAttributeWriter
.AddPaintAttr( mpVDev
->GetLineColor(), mpVDev
->GetFillColor() );
2949 ImplWritePolyPolygon( rPolyPoly
, false );
2955 case MetaActionType::GRADIENT
:
2957 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2959 const MetaGradientAction
* pA
= static_cast<const MetaGradientAction
*>(pAction
);
2960 const tools::Polygon
aRectPoly( pA
->GetRect() );
2961 const tools::PolyPolygon
aRectPolyPoly( aRectPoly
);
2963 ImplWriteGradientEx( aRectPolyPoly
, pA
->GetGradient(), nWriteFlags
);
2968 case MetaActionType::GRADIENTEX
:
2970 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2972 const MetaGradientExAction
* pA
= static_cast<const MetaGradientExAction
*>(pAction
);
2973 ImplWriteGradientEx( pA
->GetPolyPolygon(), pA
->GetGradient(), nWriteFlags
);
2978 case MetaActionType::HATCH
:
2980 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2982 const MetaHatchAction
* pA
= static_cast<const MetaHatchAction
*>(pAction
);
2983 ImplWritePattern( pA
->GetPolyPolygon(), &pA
->GetHatch(), nullptr, nWriteFlags
);
2988 case MetaActionType::Transparent
:
2990 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
2992 const MetaTransparentAction
* pA
= static_cast<const MetaTransparentAction
*>(pAction
);
2993 const tools::PolyPolygon
& rPolyPoly
= pA
->GetPolyPolygon();
2995 if( rPolyPoly
.Count() )
2997 Color
aNewLineColor( mpVDev
->GetLineColor() ), aNewFillColor( mpVDev
->GetFillColor() );
2999 aNewLineColor
.SetTransparency( sal::static_int_cast
<sal_uInt8
>( FRound( pA
->GetTransparence() * 2.55 ) ) );
3000 aNewFillColor
.SetTransparency( sal::static_int_cast
<sal_uInt8
>( FRound( pA
->GetTransparence() * 2.55 ) ) );
3002 maAttributeWriter
.AddPaintAttr( aNewLineColor
, aNewFillColor
);
3003 ImplWritePolyPolygon( rPolyPoly
, false );
3009 case MetaActionType::FLOATTRANSPARENT
:
3011 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
3013 const MetaFloatTransparentAction
* pA
= static_cast<const MetaFloatTransparentAction
*>(pAction
);
3014 GDIMetaFile
aTmpMtf( pA
->GetGDIMetaFile() );
3015 ImplWriteMask( aTmpMtf
, pA
->GetPoint(), pA
->GetSize(),
3016 pA
->GetGradient(), nWriteFlags
);
3021 case MetaActionType::EPS
:
3023 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
3025 const MetaEPSAction
* pA
= static_cast<const MetaEPSAction
*>(pAction
);
3026 const GDIMetaFile
& aGDIMetaFile( pA
->GetSubstitute() );
3027 bool bFound
= false;
3029 for( sal_uInt32 k
= 0, nCount2
= aGDIMetaFile
.GetActionSize(); ( k
< nCount2
) && !bFound
; ++k
)
3031 const MetaAction
* pSubstAct
= aGDIMetaFile
.GetAction( k
);
3033 if( pSubstAct
->GetType() == MetaActionType::BMPSCALE
)
3036 const MetaBmpScaleAction
* pBmpScaleAction
= static_cast<const MetaBmpScaleAction
*>(pSubstAct
);
3037 ImplWriteBmp( BitmapEx(pBmpScaleAction
->GetBitmap()),
3038 pA
->GetPoint(), pA
->GetSize(),
3039 Point(), pBmpScaleAction
->GetBitmap().GetSizePixel() );
3046 case MetaActionType::COMMENT
:
3048 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
3050 if( ( pA
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN") ) &&
3051 ( nWriteFlags
& SVGWRITER_WRITE_FILL
) )
3053 const MetaGradientExAction
* pGradAction
= nullptr;
3056 while( !bDone
&& ( ++nCurAction
< nCount
) )
3058 pAction
= rMtf
.GetAction( nCurAction
);
3060 if( pAction
->GetType() == MetaActionType::GRADIENTEX
)
3061 pGradAction
= static_cast<const MetaGradientExAction
*>(pAction
);
3062 else if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
3063 ( static_cast<const MetaCommentAction
*>( pAction
)->GetComment().
3064 equalsIgnoreAsciiCase("XGRAD_SEQ_END") ) )
3071 ImplWriteGradientEx( pGradAction
->GetPolyPolygon(), pGradAction
->GetGradient(), nWriteFlags
);
3073 else if( ( pA
->GetComment().equalsIgnoreAsciiCase("XPATHFILL_SEQ_BEGIN") ) &&
3074 ( nWriteFlags
& SVGWRITER_WRITE_FILL
) && !( nWriteFlags
& SVGWRITER_NO_SHAPE_COMMENTS
) &&
3077 // write open shape in every case
3080 ImplWriteShape( *mapCurShape
);
3081 mapCurShape
.reset();
3084 SvMemoryStream
aMemStm( const_cast<sal_uInt8
*>(pA
->GetData()), pA
->GetDataSize(), StreamMode::READ
);
3085 SvtGraphicFill aFill
;
3087 ReadSvtGraphicFill( aMemStm
, aFill
);
3089 bool bGradient
= SvtGraphicFill::fillGradient
== aFill
.getFillType() &&
3090 ( SvtGraphicFill::GradientType::Linear
== aFill
.getGradientType() ||
3091 SvtGraphicFill::GradientType::Radial
== aFill
.getGradientType() );
3092 bool bSkip
= ( SvtGraphicFill::fillSolid
== aFill
.getFillType() || bGradient
);
3096 tools::PolyPolygon aShapePolyPoly
;
3098 aFill
.getPath( aShapePolyPoly
);
3100 if( aShapePolyPoly
.Count() )
3102 mapCurShape
.reset( new SVGShapeDescriptor
);
3106 mapCurShape
->maId
= *pElementId
+ "_" + OUString::number(nEntryCount
++);
3109 mapCurShape
->maShapePolyPoly
= aShapePolyPoly
;
3110 mapCurShape
->maShapeFillColor
= aFill
.getFillColor();
3111 mapCurShape
->maShapeFillColor
.SetTransparency( static_cast<sal_uInt8
>(FRound( 255.0 * aFill
.getTransparency() )) );
3115 // step through following actions until the first Gradient/GradientEx action is found
3116 while (!mapCurShape
->mapShapeGradient
&& bSkip
3117 && (++nCurAction
< nCount
))
3119 pAction
= rMtf
.GetAction( nCurAction
);
3121 if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
3122 ( static_cast<const MetaCommentAction
*>(pAction
)->GetComment().
3123 equalsIgnoreAsciiCase("XPATHFILL_SEQ_END") ) )
3127 else if( pAction
->GetType() == MetaActionType::GRADIENTEX
)
3129 mapCurShape
->mapShapeGradient
.reset( new Gradient(
3130 static_cast< const MetaGradientExAction
* >( pAction
)->GetGradient() ) );
3132 else if( pAction
->GetType() == MetaActionType::GRADIENT
)
3134 mapCurShape
->mapShapeGradient
.reset( new Gradient(
3135 static_cast< const MetaGradientAction
* >( pAction
)->GetGradient() ) );
3144 // skip rest of comment
3145 while( bSkip
&& ( ++nCurAction
< nCount
) )
3147 pAction
= rMtf
.GetAction( nCurAction
);
3149 if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
3150 ( static_cast<const MetaCommentAction
*>( pAction
)->GetComment().
3151 equalsIgnoreAsciiCase("XPATHFILL_SEQ_END") ) )
3157 else if( ( pA
->GetComment().equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_BEGIN") ) &&
3158 ( nWriteFlags
& SVGWRITER_WRITE_FILL
) && !( nWriteFlags
& SVGWRITER_NO_SHAPE_COMMENTS
) &&
3161 SvMemoryStream
aMemStm( const_cast<sal_uInt8
*>(pA
->GetData()), pA
->GetDataSize(), StreamMode::READ
);
3162 SvtGraphicStroke aStroke
;
3163 tools::PolyPolygon aStartArrow
, aEndArrow
;
3165 ReadSvtGraphicStroke( aMemStm
, aStroke
);
3166 aStroke
.getStartArrow( aStartArrow
);
3167 aStroke
.getEndArrow( aEndArrow
);
3169 // Currently no support for strokes with start/end arrow(s)
3170 // added that support
3171 tools::Polygon aPoly
;
3173 aStroke
.getPath(aPoly
);
3177 if(1 != mapCurShape
->maShapePolyPoly
.Count()
3178 || !mapCurShape
->maShapePolyPoly
[0].IsEqual(aPoly
))
3180 // this path action is not covering the same path than the already existing
3181 // fill polypolygon, so write out the fill polygon
3182 ImplWriteShape( *mapCurShape
);
3183 mapCurShape
.reset();
3190 mapCurShape
.reset( new SVGShapeDescriptor
);
3194 mapCurShape
->maId
= *pElementId
+ "_" + OUString::number(nEntryCount
++);
3197 mapCurShape
->maShapePolyPoly
= aPoly
;
3200 mapCurShape
->maShapeLineColor
= mpVDev
->GetLineColor();
3201 mapCurShape
->maShapeLineColor
.SetTransparency( static_cast<sal_uInt8
>(FRound( aStroke
.getTransparency() * 255.0 )) );
3202 mapCurShape
->mnStrokeWidth
= FRound( aStroke
.getStrokeWidth() );
3203 aStroke
.getDashArray( mapCurShape
->maDashArray
);
3205 // added support for LineJoin
3206 switch(aStroke
.getJoinType())
3208 default: /* SvtGraphicStroke::joinMiter, SvtGraphicStroke::joinNone */
3210 mapCurShape
->maLineJoin
= basegfx::B2DLineJoin::Miter
;
3213 case SvtGraphicStroke::joinRound
:
3215 mapCurShape
->maLineJoin
= basegfx::B2DLineJoin::Round
;
3218 case SvtGraphicStroke::joinBevel
:
3220 mapCurShape
->maLineJoin
= basegfx::B2DLineJoin::Bevel
;
3225 // added support for LineCap
3226 switch(aStroke
.getCapType())
3228 default: /* SvtGraphicStroke::capButt */
3230 mapCurShape
->maLineCap
= css::drawing::LineCap_BUTT
;
3233 case SvtGraphicStroke::capRound
:
3235 mapCurShape
->maLineCap
= css::drawing::LineCap_ROUND
;
3238 case SvtGraphicStroke::capSquare
:
3240 mapCurShape
->maLineCap
= css::drawing::LineCap_SQUARE
;
3245 if(mapCurShape
.get() && mapCurShape
->maShapePolyPoly
.Count() && (aStartArrow
.Count() || aEndArrow
.Count()))
3247 ImplWriteShape( *mapCurShape
);
3249 mapCurShape
->maShapeFillColor
= mapCurShape
->maShapeLineColor
;
3250 mapCurShape
->maShapeLineColor
= COL_TRANSPARENT
;
3251 mapCurShape
->mnStrokeWidth
= 0;
3252 mapCurShape
->maDashArray
.clear();
3253 mapCurShape
->maLineJoin
= basegfx::B2DLineJoin::Miter
;
3254 mapCurShape
->maLineCap
= css::drawing::LineCap_BUTT
;
3256 if(aStartArrow
.Count())
3258 mapCurShape
->maShapePolyPoly
= aStartArrow
;
3260 if( pElementId
) // #i124825# pElementId is optional, may be zero
3262 mapCurShape
->maId
= *pElementId
+ "_" + OUString::number(nEntryCount
++);
3265 ImplWriteShape( *mapCurShape
);
3268 if(aEndArrow
.Count())
3270 mapCurShape
->maShapePolyPoly
= aEndArrow
;
3272 if( pElementId
) // #i124825# pElementId is optional, may be zero
3274 mapCurShape
->maId
= *pElementId
+ "_" + OUString::number(nEntryCount
++);
3277 ImplWriteShape( *mapCurShape
);
3280 mapCurShape
.reset();
3283 // write open shape in every case
3286 ImplWriteShape( *mapCurShape
);
3287 mapCurShape
.reset();
3290 // skip rest of comment
3293 while( bSkip
&& ( ++nCurAction
< nCount
) )
3295 pAction
= rMtf
.GetAction( nCurAction
);
3297 if( ( pAction
->GetType() == MetaActionType::COMMENT
) &&
3298 ( static_cast<const MetaCommentAction
*>(pAction
)->GetComment().
3299 equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_END") ) )
3305 else if( !mrExport
.IsUsePositionedCharacters() && ( nWriteFlags
& SVGWRITER_WRITE_TEXT
) )
3307 if( pA
->GetComment().equalsIgnoreAsciiCase( "XTEXT_PAINTSHAPE_BEGIN" ) )
3311 Reference
< XText
> xText( *pxShape
, UNO_QUERY
);
3313 maTextWriter
.setTextShape( xText
, pTextEmbeddedBitmapMtf
);
3315 maTextWriter
.createParagraphEnumeration();
3317 // nTextFound == -1 => no text found
3318 // nTextFound == 0 => no text found and end of text shape reached
3319 // nTextFound == 1 => text found!
3320 sal_Int32 nTextFound
= -1;
3321 while( ( nTextFound
< 0 ) && ( nCurAction
< nCount
) )
3323 nTextFound
= maTextWriter
.setTextPosition( rMtf
, nCurAction
);
3325 // We found some text in the current text shape.
3326 if( nTextFound
> 0 )
3328 maTextWriter
.setTextProperties( rMtf
, nCurAction
);
3329 maTextWriter
.startTextShape();
3331 // We reached the end of the current text shape
3332 // without finding any text. So we need to go back
3333 // by one action in order to handle the
3334 // XTEXT_PAINTSHAPE_END action because on the next
3335 // loop the nCurAction is incremented by one.
3342 else if( pA
->GetComment().equalsIgnoreAsciiCase( "XTEXT_PAINTSHAPE_END" ) )
3344 maTextWriter
.endTextShape();
3346 else if( pA
->GetComment().equalsIgnoreAsciiCase( "XTEXT_EOP" ) )
3348 const MetaAction
* pNextAction
= rMtf
.GetAction( nCurAction
+ 1 );
3349 if( !( ( pNextAction
->GetType() == MetaActionType::COMMENT
) &&
3350 ( static_cast<const MetaCommentAction
*>(pNextAction
)->GetComment().equalsIgnoreAsciiCase("XTEXT_PAINTSHAPE_END") ) ))
3352 // nTextFound == -1 => no text found and end of paragraph reached
3353 // nTextFound == 0 => no text found and end of text shape reached
3354 // nTextFound == 1 => text found!
3355 sal_Int32 nTextFound
= -1;
3356 while( ( nTextFound
< 0 ) && ( nCurAction
< nCount
) )
3358 nTextFound
= maTextWriter
.setTextPosition( rMtf
, nCurAction
);
3360 // We found a paragraph with some text in the
3361 // current text shape.
3362 if( nTextFound
> 0 )
3364 maTextWriter
.setTextProperties( rMtf
, nCurAction
);
3365 maTextWriter
.startTextParagraph();
3367 // We reached the end of the current text shape
3368 // without finding any text. So we need to go back
3369 // by one action in order to handle the
3370 // XTEXT_PAINTSHAPE_END action because on the next
3371 // loop the nCurAction is incremented by one.
3379 else if( pA
->GetComment().equalsIgnoreAsciiCase( "XTEXT_EOL" ) )
3381 const MetaAction
* pNextAction
= rMtf
.GetAction( nCurAction
+ 1 );
3382 if( !( ( pNextAction
->GetType() == MetaActionType::COMMENT
) &&
3383 ( static_cast<const MetaCommentAction
*>(pNextAction
)->GetComment().equalsIgnoreAsciiCase("XTEXT_EOP") ) ) )
3385 // nTextFound == -2 => no text found and end of line reached
3386 // nTextFound == -1 => no text found and end of paragraph reached
3387 // nTextFound == 1 => text found!
3388 sal_Int32 nTextFound
= -2;
3389 while( ( nTextFound
< -1 ) && ( nCurAction
< nCount
) )
3391 nTextFound
= maTextWriter
.setTextPosition( rMtf
, nCurAction
);
3393 // We found a line with some text in the current
3395 if( nTextFound
> 0 )
3397 maTextWriter
.startTextPosition();
3399 // We reached the end of the current paragraph
3400 // without finding any text. So we need to go back
3401 // by one action in order to handle the XTEXT_EOP
3402 // action because on the next loop the nCurAction is
3403 // incremented by one.
3414 case MetaActionType::BMP
:
3416 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
3418 const MetaBmpAction
* pA
= static_cast<const MetaBmpAction
*>(pAction
);
3420 ImplWriteBmp( BitmapEx(pA
->GetBitmap()),
3421 pA
->GetPoint(), mpVDev
->PixelToLogic( pA
->GetBitmap().GetSizePixel() ),
3422 Point(), pA
->GetBitmap().GetSizePixel() );
3427 case MetaActionType::BMPSCALE
:
3429 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
3431 const MetaBmpScaleAction
* pA
= static_cast<const MetaBmpScaleAction
*>(pAction
);
3433 // Bitmaps embedded into text shapes are collected and exported elsewhere.
3434 if( maTextWriter
.isTextShapeStarted() )
3436 maTextWriter
.writeBitmapPlaceholder( pA
);
3440 ImplWriteBmp( BitmapEx(pA
->GetBitmap()),
3441 pA
->GetPoint(), pA
->GetSize(),
3442 Point(), pA
->GetBitmap().GetSizePixel() );
3448 case MetaActionType::BMPSCALEPART
:
3450 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
3452 const MetaBmpScalePartAction
* pA
= static_cast<const MetaBmpScalePartAction
*>(pAction
);
3454 ImplWriteBmp( BitmapEx(pA
->GetBitmap()),
3455 pA
->GetDestPoint(), pA
->GetDestSize(),
3456 pA
->GetSrcPoint(), pA
->GetSrcSize() );
3461 case MetaActionType::BMPEX
:
3463 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
3465 const MetaBmpExAction
* pA
= static_cast<const MetaBmpExAction
*>(pAction
);
3467 ImplWriteBmp( pA
->GetBitmapEx(),
3468 pA
->GetPoint(), mpVDev
->PixelToLogic( pA
->GetBitmapEx().GetSizePixel() ),
3469 Point(), pA
->GetBitmapEx().GetSizePixel() );
3474 case MetaActionType::BMPEXSCALE
:
3476 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
3478 const MetaBmpExScaleAction
* pA
= static_cast<const MetaBmpExScaleAction
*>(pAction
);
3480 // Bitmaps embedded into text shapes are collected and exported elsewhere.
3481 if( maTextWriter
.isTextShapeStarted() )
3483 maTextWriter
.writeBitmapPlaceholder( pA
);
3487 ImplWriteBmp( pA
->GetBitmapEx(),
3488 pA
->GetPoint(), pA
->GetSize(),
3489 Point(), pA
->GetBitmapEx().GetSizePixel() );
3495 case MetaActionType::BMPEXSCALEPART
:
3497 if( nWriteFlags
& SVGWRITER_WRITE_FILL
)
3499 const MetaBmpExScalePartAction
* pA
= static_cast<const MetaBmpExScalePartAction
*>(pAction
);
3501 ImplWriteBmp( pA
->GetBitmapEx(),
3502 pA
->GetDestPoint(), pA
->GetDestSize(),
3503 pA
->GetSrcPoint(), pA
->GetSrcSize() );
3508 case MetaActionType::TEXT
:
3510 if( nWriteFlags
& SVGWRITER_WRITE_TEXT
)
3512 const MetaTextAction
* pA
= static_cast<const MetaTextAction
*>(pAction
);
3513 sal_Int32 aLength
= std::min( pA
->GetText().getLength(), pA
->GetLen() );
3514 const OUString aText
= pA
->GetText().copy( pA
->GetIndex(), aLength
);
3516 if( !aText
.isEmpty() )
3518 if( mrExport
.IsUsePositionedCharacters() )
3520 vcl::Font aFont
= ImplSetCorrectFontHeight();
3521 maAttributeWriter
.SetFontAttr( aFont
);
3522 ImplWriteText( pA
->GetPoint(), aText
, nullptr, 0 );
3526 maTextWriter
.writeTextPortion( pA
->GetPoint(), aText
);
3533 case MetaActionType::TEXTRECT
:
3535 if( nWriteFlags
& SVGWRITER_WRITE_TEXT
)
3537 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
3539 if (!pA
->GetText().isEmpty())
3541 if( mrExport
.IsUsePositionedCharacters() )
3543 vcl::Font aFont
= ImplSetCorrectFontHeight();
3544 maAttributeWriter
.SetFontAttr( aFont
);
3545 ImplWriteText( pA
->GetRect().TopLeft(), pA
->GetText(), nullptr, 0 );
3547 maTextWriter
.writeTextPortion( pA
->GetRect().TopLeft(), pA
->GetText() );
3553 case MetaActionType::TEXTARRAY
:
3555 if( nWriteFlags
& SVGWRITER_WRITE_TEXT
)
3557 const MetaTextArrayAction
* pA
= static_cast<const MetaTextArrayAction
*>(pAction
);
3558 sal_Int32 aLength
= std::min( pA
->GetText().getLength(), pA
->GetLen() );
3559 const OUString aText
= pA
->GetText().copy( pA
->GetIndex(), aLength
);
3561 if( !aText
.isEmpty() )
3563 if( mrExport
.IsUsePositionedCharacters() )
3565 vcl::Font aFont
= ImplSetCorrectFontHeight();
3566 maAttributeWriter
.SetFontAttr( aFont
);
3567 ImplWriteText( pA
->GetPoint(), aText
, pA
->GetDXArray(), 0 );
3571 maTextWriter
.writeTextPortion( pA
->GetPoint(), aText
);
3578 case MetaActionType::STRETCHTEXT
:
3580 if( nWriteFlags
& SVGWRITER_WRITE_TEXT
)
3582 const MetaStretchTextAction
* pA
= static_cast<const MetaStretchTextAction
*>(pAction
);
3583 sal_Int32 aLength
= std::min( pA
->GetText().getLength(), pA
->GetLen() );
3584 const OUString aText
= pA
->GetText().copy( pA
->GetIndex(), aLength
);
3586 if( !aText
.isEmpty() )
3588 if( mrExport
.IsUsePositionedCharacters() )
3590 vcl::Font aFont
= ImplSetCorrectFontHeight();
3591 maAttributeWriter
.SetFontAttr( aFont
);
3592 ImplWriteText( pA
->GetPoint(), aText
, nullptr, pA
->GetWidth() );
3596 maTextWriter
.writeTextPortion( pA
->GetPoint(), aText
);
3603 case MetaActionType::CLIPREGION
:
3604 case MetaActionType::ISECTRECTCLIPREGION
:
3605 case MetaActionType::ISECTREGIONCLIPREGION
:
3606 case MetaActionType::MOVECLIPREGION
:
3608 const_cast<MetaAction
*>(pAction
)->Execute( mpVDev
);
3609 const vcl::Region
& rClipRegion
= mpVDev
->GetActiveClipRegion();
3610 ImplWriteClipPath( rClipRegion
.GetAsPolyPolygon() );
3612 mbClipAttrChanged
= true;
3616 case MetaActionType::PUSH
:
3618 const MetaPushAction
* pA
= static_cast<const MetaPushAction
*>(pAction
);
3619 PushFlags mnFlags
= pA
->GetFlags();
3621 const_cast<MetaAction
*>(pAction
)->Execute( mpVDev
);
3623 maContextHandler
.pushState( mnFlags
);
3627 case MetaActionType::POP
:
3629 const_cast<MetaAction
*>(pAction
)->Execute( mpVDev
);
3631 PushFlags mnFlags
= maContextHandler
.getPushFlags();
3633 maContextHandler
.popState();
3635 if( mnFlags
& PushFlags::CLIPREGION
)
3637 ImplEndClipRegion();
3638 ImplStartClipRegion( mrCurrentState
.nRegionClipPathId
);
3643 case MetaActionType::REFPOINT
:
3644 case MetaActionType::MAPMODE
:
3645 case MetaActionType::LINECOLOR
:
3646 case MetaActionType::FILLCOLOR
:
3647 case MetaActionType::TEXTLINECOLOR
:
3648 case MetaActionType::TEXTFILLCOLOR
:
3649 case MetaActionType::TEXTCOLOR
:
3650 case MetaActionType::TEXTALIGN
:
3651 case MetaActionType::FONT
:
3652 case MetaActionType::LAYOUTMODE
:
3654 const_cast<MetaAction
*>(pAction
)->Execute( mpVDev
);
3658 case MetaActionType::RASTEROP
:
3659 case MetaActionType::MASK
:
3660 case MetaActionType::MASKSCALE
:
3661 case MetaActionType::MASKSCALEPART
:
3662 case MetaActionType::WALLPAPER
:
3663 case MetaActionType::TEXTLINE
:
3665 // !!! >>> we don't want to support these actions
3670 OSL_FAIL( "SVGActionWriter::ImplWriteActions: unsupported MetaAction #" );
3677 vcl::Font
SVGActionWriter::ImplSetCorrectFontHeight() const
3679 vcl::Font
aFont( mpVDev
->GetFont() );
3682 ImplMap( Size( 0, aFont
.GetFontHeight() ), aSz
);
3684 aFont
.SetFontHeight( aSz
.Height() );
3690 void SVGActionWriter::WriteMetaFile( const Point
& rPos100thmm
,
3691 const Size
& rSize100thmm
,
3692 const GDIMetaFile
& rMtf
,
3693 sal_uInt32 nWriteFlags
,
3694 const OUString
* pElementId
,
3695 const Reference
< css::drawing::XShape
>* pXShape
,
3696 const GDIMetaFile
* pTextEmbeddedBitmapMtf
)
3698 MapMode
aMapMode( rMtf
.GetPrefMapMode() );
3699 Size
aPrefSize( rMtf
.GetPrefSize() );
3700 Fraction
aFractionX( aMapMode
.GetScaleX() );
3701 Fraction
aFractionY( aMapMode
.GetScaleY() );
3705 Size
aSize( OutputDevice::LogicToLogic(rSize100thmm
, MapMode(MapUnit::Map100thMM
), aMapMode
) );
3706 aFractionX
*= Fraction( aSize
.Width(), aPrefSize
.Width() );
3707 aMapMode
.SetScaleX( aFractionX
);
3708 aFractionY
*= Fraction( aSize
.Height(), aPrefSize
.Height() );
3709 aMapMode
.SetScaleY( aFractionY
);
3711 Point
aOffset( OutputDevice::LogicToLogic(rPos100thmm
, MapMode(MapUnit::Map100thMM
), aMapMode
) );
3712 aOffset
+= aMapMode
.GetOrigin();
3713 aMapMode
.SetOrigin( aOffset
);
3715 mpVDev
->SetMapMode( aMapMode
);
3717 mapCurShape
.reset();
3719 ImplWriteActions( rMtf
, nWriteFlags
, pElementId
, pXShape
, pTextEmbeddedBitmapMtf
);
3720 maTextWriter
.endTextParagraph();
3721 ImplEndClipRegion();
3723 // draw open shape that doesn't have a border
3726 ImplWriteShape( *mapCurShape
);
3727 mapCurShape
.reset();
3734 SVGWriter::SVGWriter( const Sequence
<Any
>& args
, const Reference
< XComponentContext
>& rxCtx
)
3737 if(args
.getLength()==1)
3738 args
[0]>>=maFilterData
;
3742 SVGWriter::~SVGWriter()
3747 void SAL_CALL
SVGWriter::write( const Reference
<XDocumentHandler
>& rxDocHandler
,
3748 const Sequence
<sal_Int8
>& rMtfSeq
)
3750 SvMemoryStream
aMemStm( const_cast<sal_Int8
*>(rMtfSeq
.getConstArray()), rMtfSeq
.getLength(), StreamMode::READ
);
3753 ReadGDIMetaFile( aMemStm
, aMtf
);
3755 rtl::Reference
<SVGExport
> pWriter(new SVGExport( mxContext
, rxDocHandler
, maFilterData
));
3756 pWriter
->writeMtf( aMtf
);
3759 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */