Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / filter / source / svg / svgwriter.cxx
blob9253bbace5f3e6ca6f6a33d2ca565dd862d9ed09
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
25 #include <rtl/crc.h>
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>
35 #include <memory>
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())
112 return;
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;
127 maStateStack.pop();
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 )
152 rColorStr = "none";
153 else
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 )
182 // Fill
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 );
195 else
196 AddColorAttr( aXMLAttrFill, "fill-opacity", rFillColor );
198 // Stroke
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;
233 OUString aColorStr;
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 ) );
252 // write stop values
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 )
276 fBorder = 0.0;
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 );
286 else
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 ) );
300 // write stop values
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 );
320 else
321 rGradientId.clear();
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;
334 rCurFont = rFont;
336 // Font Family
337 setFontFamily();
339 // Font Size
340 mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontSize,
341 OUString::number( rFont.GetFontHeight() ) + "px" );
343 // Font Style
344 if( rFont.GetItalic() != ITALIC_NONE )
346 if( rFont.GetItalic() == ITALIC_OBLIQUE )
347 aFontStyle = "oblique";
348 else
349 aFontStyle = "italic";
351 else
352 aFontStyle = "normal";
354 mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontStyle, aFontStyle );
356 // Font Weight
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 ";
384 else
385 aTextDecoration = "none";
387 mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTextDecoration, aTextDecoration );
390 startFontSettings();
395 void SVGAttributeWriter::startFontSettings()
397 endFontSettings();
398 if( mrExport.IsUsePositionedCharacters() )
400 mpElemFont.reset( new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemG, true, true ) );
402 else
404 mpElemFont.reset( new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, true, true ) );
409 void SVGAttributeWriter::endFontSettings()
411 mpElemFont.reset();
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() ) );
423 else
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";
432 else
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 ),
447 mpVDev( nullptr ),
448 mbIsTextShapeStarted( false ),
449 mrTextShape(),
450 msShapeId(),
451 mrParagraphEnumeration(),
452 mrCurrentTextParagraph(),
453 mrTextPortionEnumeration(),
454 mrCurrentTextPortion(),
455 mpTextEmbeddedBitmapMtf( nullptr ),
456 mpTargetMapMode( nullptr ),
457 mnLeftTextPortionLength( 0 ),
458 maTextPos(0,0),
459 mnTextWidth(0),
460 mbPositioningNeeded( false ),
461 mbIsNewListItem( false ),
462 meNumberingType(0),
463 mcBulletChar(0),
464 maBulletListItemMap(),
465 mbIsListLevelStyleImage( false ),
466 mbLineBreak( false ),
467 mbIsURLField( false ),
468 msUrl(),
469 mbIsPlaceholderShape( false ),
470 maCurrentFont(),
471 maParentFont()
476 SVGTextWriter::~SVGTextWriter()
478 endTextParagraph();
482 void SVGTextWriter::implRegisterInterface( const Reference< XInterface >& rxIf )
484 if( rxIf.is() )
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 );
499 else
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 );
508 else
509 OSL_FAIL( "SVGTextWriter::implMap: invalid virtual device or map mode." );
513 void SVGTextWriter::implSetCurrentFont()
515 if( mpVDev )
517 maCurrentFont = mpVDev->GetFont();
518 Size aSz;
520 implMap( Size( 0, maCurrentFont.GetFontHeight() ), aSz );
522 maCurrentFont.SetFontHeight( aSz.Height() );
524 else
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 );
537 if( !rbEmpty )
539 raPos = pA->GetPoint();
540 return true;
542 return false;
546 template<>
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 );
552 if( !rbEmpty )
554 raPos = pA->GetRect().TopLeft();
555 return true;
557 return false;
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();
566 rbEmpty = false;
567 return true;
571 /** setTextPosition
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
580 * 1 if text found!
582 sal_Int32 SVGTextWriter::setTextPosition( const GDIMetaFile& rMtf, sal_uLong& nCurAction )
584 Point aPos;
585 sal_uLong nCount = rMtf.GetActionSize();
586 bool bEOL = false;
587 bool bEOP = false;
588 bool bETS = false;
589 bool bConfigured = false;
590 bool bEmpty = true;
592 sal_uLong nActionIndex = nCurAction + 1;
593 for( ; nActionIndex < nCount; ++nActionIndex )
595 const MetaAction* pAction = rMtf.GetAction( nActionIndex );
596 const MetaActionType nType = pAction->GetType();
598 switch( nType )
600 case MetaActionType::TEXT:
602 bConfigured = implGetTextPosition<MetaTextAction>( pAction, aPos, bEmpty );
604 break;
606 case MetaActionType::TEXTRECT:
608 bConfigured = implGetTextPosition<MetaTextRectAction>( pAction, aPos, bEmpty );
610 break;
612 case MetaActionType::TEXTARRAY:
614 bConfigured = implGetTextPosition<MetaTextArrayAction>( pAction, aPos, bEmpty );
616 break;
618 case MetaActionType::STRETCHTEXT:
620 bConfigured = implGetTextPosition<MetaStretchTextAction>( pAction, aPos, bEmpty );
622 break;
624 case MetaActionType::BMPSCALE:
626 bConfigured = implGetTextPositionFromBitmap<MetaBmpScaleAction>( pAction, aPos, bEmpty );
628 break;
630 case MetaActionType::BMPEXSCALE:
632 bConfigured = implGetTextPositionFromBitmap<MetaBmpExScaleAction>( pAction, aPos, bEmpty );
634 break;
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" ) )
644 bEOL = true;
646 else if( rsComment.equalsIgnoreAsciiCase( "XTEXT_EOP" ) )
648 bEOP = true;
650 OUString sContent;
651 while( nextTextPortion() )
653 sContent = mrCurrentTextPortion->getString();
654 if( sContent.isEmpty() )
656 continue;
658 else
660 if( sContent == "\n" )
661 mbLineBreak = true;
664 if( nextParagraph() )
666 while( nextTextPortion() )
668 sContent = mrCurrentTextPortion->getString();
669 if( sContent.isEmpty() )
671 continue;
673 else
675 if( sContent == "\n" )
676 mbLineBreak = true;
681 else if( rsComment.equalsIgnoreAsciiCase( "XTEXT_PAINTSHAPE_END" ) )
683 bETS = true;
686 break;
687 default: break;
689 if( bConfigured || bEOL || bEOP || bETS ) break;
691 implMap( aPos, maTextPos );
693 if( bEmpty )
695 nCurAction = nActionIndex;
696 return ( bEOL ? -2 : ( bEOP ? -1 : 0 ) );
698 else
700 return 1;
705 void SVGTextWriter::setTextProperties( const GDIMetaFile& rMtf, sal_uLong nCurAction )
707 sal_uLong nCount = rMtf.GetActionSize();
708 bool bEOP = false;
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();
714 switch( nType )
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 );
725 break;
727 case MetaActionType::TEXT:
729 const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
730 if( pA->GetLen() > 2 )
731 bConfigured = true;
733 break;
734 case MetaActionType::TEXTRECT:
736 const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
737 if( pA->GetText().getLength() > 2 )
738 bConfigured = true;
740 break;
741 case MetaActionType::TEXTARRAY:
743 const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
744 if( pA->GetLen() > 2 )
745 bConfigured = true;
747 break;
748 case MetaActionType::STRETCHTEXT:
750 const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
751 if( pA->GetLen() > 2 )
752 bConfigured = true;
754 break;
755 // If we reach the end of the paragraph without finding any text
756 // we stop searching
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" ) )
763 bEOP = true;
766 break;
767 default: break;
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();
791 // Font Family
792 if( rsCurFontName != rsParFontName )
794 implSetFontFamily();
797 // Font Size
798 if( nCurFontSize != nParFontSize )
800 mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontSize,
801 OUString::number( nCurFontSize ) + "px" );
804 // Font Style
805 if( eCurFontItalic != eParFontItalic )
807 OUString sFontStyle;
808 if( eCurFontItalic != ITALIC_NONE )
810 if( eCurFontItalic == ITALIC_OBLIQUE )
811 sFontStyle = "oblique";
812 else
813 sFontStyle = "italic";
815 else
817 sFontStyle = "normal";
819 mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrFontStyle, sFontStyle );
822 // Font Weight
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";
898 else
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 );
922 else
924 OSL_FAIL( "SVGTextWriter::createParagraphEnumeration: no valid xEnumeration interface found." );
927 else
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
948 OUString sInfo;
949 #endif
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 );
964 #endif
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" );
979 bIsNumbered = false;
981 #if OSL_DEBUG_LEVEL > 0
982 if( bIsNumbered )
984 sInfo = "true";
985 mrExport.AddAttribute( XML_NAMESPACE_NONE, "is-numbered", sInfo );
987 #endif
989 mbIsNewListItem = bIsNumbered;
991 if( 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" )
1009 OUString sValue;
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 )
1021 if( cBullet )
1023 if( cBullet < ' ' )
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 );
1031 #endif
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";
1050 #endif
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
1056 sInfo = "Table";
1057 #endif
1059 else
1061 OSL_FAIL( "SVGTextWriter::nextParagraph: Unknown text content." );
1062 return false;
1064 #if OSL_DEBUG_LEVEL > 0
1065 mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", sInfo );
1066 SvXMLElementExport aParaElem( mrExport, XML_NAMESPACE_NONE, "desc", mbIWS, mbIWS );
1067 #endif
1069 else
1071 OSL_FAIL( "SVGTextWriter::nextParagraph: no XServiceInfo interface available for text content." );
1072 return false;
1075 const OUString& rParagraphId = implGetValidIDFromInterface( Reference<XInterface>(xTextContent, UNO_QUERY) );
1076 if( !rParagraphId.isEmpty() )
1078 mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", rParagraphId );
1080 return true;
1083 return false;
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
1095 OUString sInfo;
1096 #endif
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 + "; ";
1109 #endif
1110 if( xPortionTextRange.is() )
1112 #if OSL_DEBUG_LEVEL > 0
1113 sInfo += "content: " + xPortionTextRange->getString() + "; ";
1114 #endif
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
1135 while( nCount-- )
1137 if ( pNames->matchIgnoreAsciiCase( sServicePrefix ) )
1139 // TextField found => postfix is field type!
1140 sFieldName = pNames->copy( sServicePrefix.getLength() );
1141 break;
1143 else if( pNames->startsWith( sPresentationServicePrefix ) )
1145 // TextField found => postfix is field type!
1146 sFieldName = pNames->copy( sPresentationServicePrefix.getLength() );
1147 break;
1150 ++pNames;
1153 #if OSL_DEBUG_LEVEL > 0
1154 sInfo += "text field type: " + sFieldName + "; content: " + xTextField->getPresentation( /* show command: */ false ) + "; ";
1155 #endif
1156 if( sFieldName == "DateTime" || sFieldName == "Header"
1157 || sFieldName == "Footer" || sFieldName == "PageNumber" )
1159 mbIsPlaceholderShape = true;
1161 else
1163 mbIsURLField = sFieldName == "URL";
1165 if( mbIsURLField )
1167 Reference<XPropertySet> xTextFieldPropSet(xTextField, UNO_QUERY);
1168 if( xTextFieldPropSet.is() )
1170 OUString sURL;
1171 if( ( xTextFieldPropSet->getPropertyValue( sFieldName ) ) >>= sURL )
1173 #if OSL_DEBUG_LEVEL > 0
1174 sInfo += "url: " + mrExport.GetRelativeReference( sURL );
1175 #endif
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 );
1198 #endif
1199 return true;
1203 return false;
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()
1239 endTextParagraph();
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()
1255 endTextParagraph();
1256 nextParagraph();
1257 if( mbIsNewListItem )
1259 OUString sNumberingType;
1260 switch( meNumberingType )
1262 case NumberingType::CHAR_SPECIAL:
1263 sNumberingType = "bullet-style";
1264 break;
1265 case NumberingType::BITMAP:
1266 sNumberingType = "image-style";
1267 break;
1268 default:
1269 sNumberingType = "number-style";
1270 break;
1272 mrExport.AddAttribute( XML_NAMESPACE_NONE, "ooo:numbering-type", sNumberingType );
1273 mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", "ListItem" );
1275 else
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();
1293 endTextPosition();
1294 mbIsNewListItem = false;
1295 mbIsListLevelStyleImage = false;
1296 mbPositioningNeeded = false;
1297 mpTextParagraphElem.reset();
1301 void SVGTextWriter::startTextPosition( bool bExportX, bool bExportY )
1303 endTextPosition();
1304 mnTextWidth = 0;
1305 mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", "TextPosition" );
1306 if( bExportX )
1307 mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, OUString::number( maTextPos.X() ) );
1308 if( bExportY )
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() )
1336 return;
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
1346 // bullet-char-*
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
1383 // clear the map
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 );
1412 endTextPosition();
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;
1426 Point aPt;
1427 Size aSz;
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();
1435 switch( nType )
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();
1444 break;
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();
1452 break;
1453 default: break;
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 ) + ")";
1471 Point aPoint;
1472 Size aSize;
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() )
1492 return;
1494 bool bStandAloneTextPortion = false;
1495 if( !isTextShapeStarted() )
1497 bStandAloneTextPortion = true;
1498 startTextShape();
1501 mbLineBreak = false;
1503 if( !mbIsNewListItem || mbIsListLevelStyleImage )
1505 bool bNotSync = true;
1506 OUString sContent;
1507 sal_Int32 nStartPos;
1508 while( bNotSync )
1510 if( mnLeftTextPortionLength <= 0 || !mrCurrentTextPortion.is() )
1512 if( !nextTextPortion() )
1513 break;
1514 else
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();
1528 else
1530 sContent = mrCurrentTextPortion->getString();
1533 nStartPos = sContent.getLength() - mnLeftTextPortionLength;
1534 if( nStartPos < 0 ) nStartPos = 0;
1535 mnLeftTextPortionLength -= rText.getLength();
1537 if( sContent.isEmpty() )
1538 continue;
1539 if( sContent == "\n" )
1540 mbLineBreak = true;
1541 if( sContent.match( rText, nStartPos ) )
1542 bNotSync = false;
1546 assert(mpVDev); //invalid virtual device
1548 #if 0
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() );
1557 else
1559 // to be implemented
1561 #else
1562 implWriteTextPortion( rPos, rText, mpVDev->GetTextColor() );
1563 #endif
1565 if( bStandAloneTextPortion )
1567 endTextShape();
1572 void SVGTextWriter::implWriteTextPortion( const Point& rPos,
1573 const OUString& rText,
1574 Color aTextColor )
1576 Point aPos;
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() )
1627 sId += ".bp";
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 );
1638 return;
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 );
1672 else
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 ),
1684 mnCurMaskId( 1 ),
1685 mnCurPatternId( 1 ),
1686 mnCurClipPathId( 1 ),
1687 mpCurrentClipRegionElem(),
1688 mrExport( rExport ),
1689 maContextHandler(),
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 ) );
1748 return rDstPoly;
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( "," );
1772 Point aPolyPoint;
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();
1779 if( nSize > 1 )
1781 aPolyPoint = rPoly[ 0 ];
1782 aPathData.append("M ")
1783 .append(OUString::number( aPolyPoint.X() ))
1784 .append(aComma)
1785 .append(OUString::number( aPolyPoint.Y() ));
1787 sal_Char nCurrentMode = 0;
1788 const bool bClose(!bLine || rPoly[0] == rPoly[nSize - 1]);
1789 while( n < nSize )
1791 aPathData.append(aBlank);
1793 if ( ( rPoly.GetFlags( n ) == PolyFlags::Control ) && ( ( n + 2 ) < nSize ) )
1795 if ( nCurrentMode != 'C' )
1797 nCurrentMode = 'C';
1798 aPathData.append("C ");
1800 for ( int j = 0; j < 3; j++ )
1802 if ( j )
1803 aPathData.append(aBlank);
1805 aPolyPoint = rPoly[ n++ ];
1806 aPathData.append(OUString::number( aPolyPoint.X() ))
1807 .append(aComma)
1808 .append(OUString::number( aPolyPoint.Y() ));
1811 else
1813 if ( nCurrentMode != 'L' )
1815 nCurrentMode = 'L';
1816 aPathData.append("L ");
1819 aPolyPoint = rPoly[ n++ ];
1820 aPathData.append(OUString::number( aPolyPoint.X() ))
1821 .append(aComma)
1822 .append(OUString::number( aPolyPoint.Y() ));
1826 if(bClose)
1827 aPathData.append(" Z");
1829 if( i < ( nCount - 1 ) )
1830 aPathData.append(aBlank);
1834 return aPathData.makeStringAndClear();
1838 BitmapChecksum SVGActionWriter::GetChecksum( const MetaAction* pAction )
1840 GDIMetaFile aMtf;
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 )
1850 Point aPt1, aPt2;
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() ) );
1860 if( pLineColor )
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() ) );
1883 if( nRadX )
1884 mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrRX, OUString::number( ImplMap( nRadX ) ) );
1886 if( nRadY )
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 )
1895 Point aCenter;
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");
1924 break;
1926 case basegfx::B2DLineJoin::Bevel:
1928 mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, "bevel");
1929 break;
1931 case basegfx::B2DLineJoin::Round:
1933 mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, "round");
1934 break;
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");
1946 break;
1948 case css::drawing::LineCap_ROUND:
1950 mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, "round");
1951 break;
1953 case css::drawing::LineCap_SQUARE:
1955 mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, "square");
1956 break;
1965 void SVGActionWriter::ImplWritePolyPolygon( const tools::PolyPolygon& rPolyPoly, bool bLineOnly,
1966 bool bApplyMapping )
1968 tools::PolyPolygon aPolyPoly;
1970 if( bApplyMapping )
1971 ImplMap( rPolyPoly, aPolyPoly );
1972 else
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");
2015 break;
2017 case basegfx::B2DLineJoin::Bevel:
2019 mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, "bevel");
2020 break;
2022 case basegfx::B2DLineJoin::Round:
2024 mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinejoin, "round");
2025 break;
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");
2037 break;
2039 case css::drawing::LineCap_ROUND:
2041 mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, "round");
2042 break;
2044 case css::drawing::LineCap_SQUARE:
2046 mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStrokeLinecap, "square");
2047 break;
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 ] ) );
2059 if( 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)
2093 return;
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 )
2113 return;
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() ) +
2154 ")";
2155 mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTransform, aTransform );
2158 SvXMLElementExport aElemG2( mrExport, XML_NAMESPACE_NONE, aXMLElemG, true, true );
2160 GDIMetaFile aTmpMtf;
2161 if( pHatch )
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 );
2186 else
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 );
2254 else
2256 ImplWriteGradientStop( aStartColor, fBorderOffset );
2257 ImplWriteGradientStop( aEndColor, 1.0 );
2260 else
2262 fBorderOffset /= 2;
2263 // Emulate non-smooth gradient
2264 if( 0 < nSteps && nSteps < 100 )
2266 double fOffsetStep = ( 0.5 - fBorderOffset ) / static_cast<double>(nSteps);
2267 // Upper half
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 );
2275 // Lower half
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 );
2284 else
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 );
2309 aStyle += 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,
2330 double fOffset )
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 );
2403 mpVDev->Push();
2404 ImplWriteActions( rMtf, nWriteFlags, nullptr );
2405 mpVDev->Pop();
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);
2417 if( !bTextSpecial )
2419 ImplWriteText( rPos, rText, pDXArray, nWidth, mpVDev->GetTextColor() );
2421 else
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;
2435 Point aPos( rPos );
2436 Point aOffset( 6, 6 );
2438 if ( aMetric.GetRelief() == FontRelief::Engraved )
2440 aPos -= aOffset;
2442 else
2444 aPos += aOffset;
2447 ImplWriteText( aPos, rText, pDXArray, nWidth, aReliefColor );
2448 ImplWriteText( rPos, rText, pDXArray, nWidth, aTextColor );
2450 else
2452 if( aMetric.IsShadow() )
2454 long nOff = 1 + ((aMetric.GetLineHeight()-24)/24);
2455 if ( aMetric.IsOutline() )
2456 nOff += 6;
2458 Color aTextColor( mpVDev->GetTextColor() );
2459 Color aShadowColor( COL_BLACK );
2461 if ( (aTextColor == COL_BLACK) || (aTextColor.GetLuminance() < 8) )
2462 aShadowColor = COL_LIGHTGRAY;
2464 Point aPos( rPos );
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,
2502 Color aTextColor )
2504 sal_Int32 nLen = rText.getLength();
2505 Size aNormSize;
2506 Point aPos;
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]);
2519 // get text sizes
2520 if( pDXArray )
2522 aNormSize = Size( mpVDev->GetTextWidth( rText ), 0 );
2523 memcpy(xTmpArray.get(), pDXArray, nLen * sizeof(long));
2525 else
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() )
2534 Point aRot( aPos );
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 )
2575 if( nLen > 1 )
2577 aNormSize.setWidth( pDX[ nLen - 2 ] + mpVDev->GetTextWidth( OUString(rText[nLen - 1]) ) );
2579 if( nWidth && aNormSize.Width() && ( nWidth != aNormSize.Width() ) )
2581 long i;
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 );
2587 else
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;
2598 nLastPos = nCurPos;
2599 nCurPos = xBI->nextCharacters( rText, nCurPos, rLocale,
2600 css::i18n::CharacterIteratorMode::SKIPCELL,
2601 nCount, nCount );
2603 nCount = nCurPos - nLastPos;
2604 bCont = ( nCurPos < rText.getLength() ) && nCount;
2606 if( 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 );
2618 if( bCont )
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;
2629 else
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 )
2681 if( !!rBmpEx )
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 );
2690 if( !!aBmpEx )
2692 SvMemoryStream aOStm( 65535, 65535 );
2694 if( GraphicConverter::Export( aOStm, rBmpEx, ConvertDataFormat::PNG ) == ERRCODE_NONE )
2696 Point aPt;
2697 Size aSz;
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
2730 // generation
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;
2740 #endif
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
2755 if( bIsTextShape )
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))
2764 sType.append(": ");
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);
2778 if (sz)
2780 sType.append("; ");
2781 sType.append(
2782 reinterpret_cast<sal_Unicode const*>(pData),
2783 sz);
2788 if (sType.getLength())
2790 mrExport.GetDocHandler()->characters(
2791 sType.makeStringAndClear());
2794 catch( ... )
2796 const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
2797 SAL_WARN( "filter.svg", pA->GetComment() );
2801 #endif
2802 switch( nType )
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() );
2814 break;
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() );
2826 break;
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() );
2838 break;
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() );
2848 break;
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() );
2860 break;
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 );
2873 break;
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;
2884 switch( nType )
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 );
2891 break;
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 );
2898 break;
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 );
2905 break;
2907 case MetaActionType::POLYGON:
2908 aPoly = static_cast<const MetaPolygonAction*>(pAction)->GetPolygon();
2909 break;
2910 default: break;
2913 if( aPoly.GetSize() )
2915 maAttributeWriter.AddPaintAttr( mpVDev->GetLineColor(), mpVDev->GetFillColor() );
2916 ImplWritePolyPolygon( aPoly, false );
2920 break;
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 );
2937 break;
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 );
2953 break;
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 );
2966 break;
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 );
2976 break;
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 );
2986 break;
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 );
3007 break;
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 );
3019 break;
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 )
3035 bFound = true;
3036 const MetaBmpScaleAction* pBmpScaleAction = static_cast<const MetaBmpScaleAction*>(pSubstAct);
3037 ImplWriteBmp( BitmapEx(pBmpScaleAction->GetBitmap()),
3038 pA->GetPoint(), pA->GetSize(),
3039 Point(), pBmpScaleAction->GetBitmap().GetSizePixel() );
3044 break;
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;
3054 bool bDone = false;
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") ) )
3066 bDone = true;
3070 if( pGradAction )
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 ) &&
3075 pA->GetDataSize() )
3077 // write open shape in every case
3078 if (mapCurShape)
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 );
3094 if( bSkip )
3096 tools::PolyPolygon aShapePolyPoly;
3098 aFill.getPath( aShapePolyPoly );
3100 if( aShapePolyPoly.Count() )
3102 mapCurShape.reset( new SVGShapeDescriptor );
3104 if( pElementId )
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() )) );
3113 if( bGradient )
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") ) )
3125 bSkip = false;
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() ) );
3140 else
3141 bSkip = false;
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") ) )
3153 bSkip = false;
3157 else if( ( pA->GetComment().equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_BEGIN") ) &&
3158 ( nWriteFlags & SVGWRITER_WRITE_FILL ) && !( nWriteFlags & SVGWRITER_NO_SHAPE_COMMENTS ) &&
3159 pA->GetDataSize() )
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);
3175 if (mapCurShape)
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();
3187 if (!mapCurShape)
3190 mapCurShape.reset( new SVGShapeDescriptor );
3192 if( pElementId )
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;
3211 break;
3213 case SvtGraphicStroke::joinRound:
3215 mapCurShape->maLineJoin = basegfx::B2DLineJoin::Round;
3216 break;
3218 case SvtGraphicStroke::joinBevel:
3220 mapCurShape->maLineJoin = basegfx::B2DLineJoin::Bevel;
3221 break;
3225 // added support for LineCap
3226 switch(aStroke.getCapType())
3228 default: /* SvtGraphicStroke::capButt */
3230 mapCurShape->maLineCap = css::drawing::LineCap_BUTT;
3231 break;
3233 case SvtGraphicStroke::capRound:
3235 mapCurShape->maLineCap = css::drawing::LineCap_ROUND;
3236 break;
3238 case SvtGraphicStroke::capSquare:
3240 mapCurShape->maLineCap = css::drawing::LineCap_SQUARE;
3241 break;
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
3284 if (mapCurShape)
3286 ImplWriteShape( *mapCurShape );
3287 mapCurShape.reset();
3290 // skip rest of comment
3291 bool bSkip = true;
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") ) )
3301 bSkip = false;
3305 else if( !mrExport.IsUsePositionedCharacters() && ( nWriteFlags & SVGWRITER_WRITE_TEXT ) )
3307 if( pA->GetComment().equalsIgnoreAsciiCase( "XTEXT_PAINTSHAPE_BEGIN" ) )
3309 if( pxShape )
3311 Reference< XText > xText( *pxShape, UNO_QUERY );
3312 if( xText.is() )
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.
3336 else
3338 --nCurAction;
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.
3372 else
3374 --nCurAction;
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
3394 // paragraph.
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.
3404 else
3406 --nCurAction;
3412 break;
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() );
3425 break;
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 );
3438 else
3440 ImplWriteBmp( BitmapEx(pA->GetBitmap()),
3441 pA->GetPoint(), pA->GetSize(),
3442 Point(), pA->GetBitmap().GetSizePixel() );
3446 break;
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() );
3459 break;
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() );
3472 break;
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 );
3485 else
3487 ImplWriteBmp( pA->GetBitmapEx(),
3488 pA->GetPoint(), pA->GetSize(),
3489 Point(), pA->GetBitmapEx().GetSizePixel() );
3493 break;
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() );
3506 break;
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 );
3524 else
3526 maTextWriter.writeTextPortion( pA->GetPoint(), aText );
3531 break;
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() );
3551 break;
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 );
3569 else
3571 maTextWriter.writeTextPortion( pA->GetPoint(), aText );
3576 break;
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() );
3594 else
3596 maTextWriter.writeTextPortion( pA->GetPoint(), aText );
3601 break;
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;
3614 break;
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 );
3625 break;
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 );
3641 break;
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 );
3656 break;
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
3667 break;
3669 default:
3670 OSL_FAIL( "SVGActionWriter::ImplWriteActions: unsupported MetaAction #" );
3671 break;
3677 vcl::Font SVGActionWriter::ImplSetCorrectFontHeight() const
3679 vcl::Font aFont( mpVDev->GetFont() );
3680 Size aSz;
3682 ImplMap( Size( 0, aFont.GetFontHeight() ), aSz );
3684 aFont.SetFontHeight( aSz.Height() );
3686 return aFont;
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() );
3703 mpVDev->Push();
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
3724 if (mapCurShape)
3726 ImplWriteShape( *mapCurShape );
3727 mapCurShape.reset();
3730 mpVDev->Pop();
3734 SVGWriter::SVGWriter( const Sequence<Any>& args, const Reference< XComponentContext >& rxCtx )
3735 : mxContext(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 );
3751 GDIMetaFile aMtf;
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: */