diff options
author | Marco Cecchetti <mrcekets@gmail.com> | 2012-08-11 22:23:22 +0200 |
---|---|---|
committer | Thorsten Behrens <tbehrens@suse.com> | 2012-10-10 12:03:28 +0200 |
commit | b76628acb1ae4fc06f8c1b70ec2e0cf39356deef (patch) | |
tree | 23d2bc9d6e763c601db80e38bf4901dcdd5a4474 | |
parent | 5626e723945eb39ed211a26ba5386ee486b8a4e8 (diff) |
text export support for bullets and hyperlinks
SVGFilter:
- new:
implExportTextShapeIndex, implEmbedBulletGlyphs, implEmbedBulletGlyph,
implExportTextEmbeddedBitmaps
- modified:
implExportShape, implCreateObjectsFromShapes, implCreateObjectsFromShape
SVGActionWriter:
- new:
GetChecksum
- modified:
ImplWriteActions, WriteMetaFile
SVGTextWriter:
-new:
implExportHyperlinkIds, implWriteBulletChars, writeBitmapPlaceholder,
implWriteEmbeddedBitmaps, setTextShape, implRegisterInterface,
implGetValidIDFromInterface
-modified:
nextParagraph, nextTextPortion, startTextShape, endTextShape, startTextParagraph,
endTextParagraph, writeTextPortion, implWriteTextPortion
-rw-r--r-- | filter/source/svg/svgexport.cxx | 234 | ||||
-rw-r--r-- | filter/source/svg/svgfilter.hxx | 79 | ||||
-rw-r--r-- | filter/source/svg/svgwriter.cxx | 641 | ||||
-rw-r--r-- | filter/source/svg/svgwriter.hxx | 103 |
4 files changed, 1022 insertions, 35 deletions
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx index ebb88e0ffc0d..ae2d6dc23403 100644 --- a/filter/source/svg/svgexport.cxx +++ b/filter/source/svg/svgexport.cxx @@ -89,6 +89,7 @@ static const char aOOOAttrDateTimeField[] = NSPREFIX "date-time-field"; static const char aOOOAttrFooterField[] = NSPREFIX "footer-field"; static const char aOOOAttrHeaderField[] = NSPREFIX "header-field"; static const char aOOOAttrHasTransition[] = NSPREFIX "has-transition"; +static const char aOOOAttrIdList[] = NSPREFIX "id-list"; // ooo xml attributes for pages and shapes static const char aOOOAttrName[] = NSPREFIX "name"; @@ -800,12 +801,16 @@ sal_Bool SVGFilter::implExportDocument() mpSVGFontExport = new SVGFontExport( *mpSVGExport, aObjects ); mpSVGWriter = new SVGActionWriter( *mpSVGExport, *mpSVGFontExport ); - if( mpSVGExport->IsEmbedFonts() ) { mpSVGFontExport->EmbedFonts(); } - + if( !mpSVGExport->IsUsePositionedCharacters() ) + { + implExportTextShapeIndex(); + implEmbedBulletGlyphs(); + implExportTextEmbeddedBitmaps(); + } implExportMasterPages( mMasterPageTargets, 0, mMasterPageTargets.getLength() - 1 ); implExportDrawPages( mSelectedPages, 0, nLastPage ); @@ -1138,6 +1143,154 @@ sal_Bool SVGFilter::implExportAnimations() return bRet; } +// ----------------------------------------------------------------------------- + +void SVGFilter::implExportTextShapeIndex() +{ + mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextShapeIndex" ) ); + SvXMLElementExport aDefsContainerElem( *mpSVGExport, XML_NAMESPACE_NONE, "defs", sal_True, sal_True ); + + sal_Int32 nCount = mSelectedPages.getLength(); + for( sal_Int32 i = 0; i < nCount; ++i ) + { + const Reference< XDrawPage > & xDrawPage = mSelectedPages[i]; + if( mTextShapeIdListMap.find( xDrawPage ) != mTextShapeIdListMap.end() ) + { + OUString sTextShapeIdList = mTextShapeIdListMap[xDrawPage].trim(); + + Reference< XInterface > xRef( xDrawPage, UNO_QUERY ); + const OUString& rPageId = implGetValidIDFromInterface( xRef ); + if( !rPageId.isEmpty() && !sTextShapeIdList.isEmpty() ) + { + mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, aOOOAttrSlide, rPageId ); + mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, aOOOAttrIdList, sTextShapeIdList ); + SvXMLElementExport aGElem( *mpSVGExport, XML_NAMESPACE_NONE, "g", sal_True, sal_True ); + } + } + } +} + +// ----------------------------------------------------------------------------- + +void SVGFilter::implEmbedBulletGlyphs() +{ + mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "EmbeddedBulletChars" ) ); + SvXMLElementExport aDefsContainerElem( *mpSVGExport, XML_NAMESPACE_NONE, "defs", sal_True, sal_True ); + + OUString sPathData = B2UCONST( "M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z" ); + implEmbedBulletGlyph( 57356, sPathData ); + sPathData = B2UCONST( "M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z" ); + implEmbedBulletGlyph( 57354, sPathData ); + sPathData = B2UCONST( "M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z" ); + implEmbedBulletGlyph( 10146, sPathData ); + sPathData = B2UCONST( "M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z" ); + implEmbedBulletGlyph( 10132, sPathData ); + sPathData = B2UCONST( "M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z" ); + implEmbedBulletGlyph( 10007, sPathData ); + sPathData = B2UCONST( "M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z" ); + implEmbedBulletGlyph( 10004, sPathData ); + sPathData = B2UCONST( "M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z" ); + implEmbedBulletGlyph( 9679, sPathData ); + sPathData = B2UCONST( "M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z" ); + implEmbedBulletGlyph( 8226, sPathData ); + sPathData = B2UCONST( "M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z" ); + implEmbedBulletGlyph( 8211, sPathData ); +} + +// ----------------------------------------------------------------------------- + +void SVGFilter::implEmbedBulletGlyph( sal_Unicode cBullet, const ::rtl::OUString & sPathData ) +{ + OUString sId = B2UCONST( "bullet-char-template(" ); + sId += OUString::valueOf( (sal_Int32)cBullet ); + sId += B2UCONST( ")" ); + mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "id", sId ); + + double fFactor = 1.0 / 2048; + OUString sFactor = OUString::valueOf( fFactor ); + OUString sTransform = B2UCONST( "scale(" ); + sTransform += sFactor; sTransform += B2UCONST( ",-" ); sTransform += sFactor; + sTransform += B2UCONST( ")" ); + mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "transform", sTransform ); + + SvXMLElementExport aGElem( *mpSVGExport, XML_NAMESPACE_NONE, "g", sal_True, sal_True ); + + mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "d", sPathData ); + SvXMLElementExport aPathElem( *mpSVGExport, XML_NAMESPACE_NONE, "path", sal_True, sal_True ); + +} + +// ----------------------------------------------------------------------------- + +/** SVGFilter::implExportTextEmbeddedBitmaps + * We export bitmaps embedded into text shapes, such as those used by list + * items with image style, only once in a specic <defs> element. + */ +sal_Bool SVGFilter::implExportTextEmbeddedBitmaps() +{ + mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextEmbeddedBitmaps" ) ); + SvXMLElementExport aDefsContainerElem( *mpSVGExport, XML_NAMESPACE_NONE, "defs", sal_True, sal_True ); + + OUString sId; + + MetaBitmapActionSet::const_iterator it = mEmbeddedBitmapActionSet.begin(); + MetaBitmapActionSet::const_iterator end = mEmbeddedBitmapActionSet.end(); + for( ; it != end; ++it) + { + const GDIMetaFile& aMtf = it->GetRepresentation(); + + if( aMtf.GetActionSize() == 1 ) + { + MetaBmpExScaleAction* pAction = (MetaBmpExScaleAction*) aMtf.GetAction( 0 ); + if( pAction ) + { + sal_uLong nId = pAction->GetBitmapEx().GetChecksum(); + sId = B2UCONST( "bitmap(" ); + sId += OUString::valueOf( (sal_Int64)nId ); + sId += B2UCONST( ")" ); + mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "id", sId ); + + const Reference< XShape >& rxShape = (const Reference< XShape >&)( it->GetObject() ); + Reference< XPropertySet > xShapePropSet( rxShape, UNO_QUERY ); + ::com::sun::star::awt::Rectangle aBoundRect; + if( xShapePropSet.is() && ( xShapePropSet->getPropertyValue( B2UCONST( "BoundRect" ) ) >>= aBoundRect ) ) + { + // Origin of the coordinate device must be (0,0). + const Point aTopLeft; + const Size aSize( aBoundRect.Width, aBoundRect.Height ); + + const Point aPt = pAction->GetPoint(); + // The image must be exported with x, y attribute set to 0, + // on the contrary when referenced by a <use> element, + // specifying the wanted position, they will result + // misplaced. + pAction->Move( -aPt.X(), -aPt.Y() ); + mpSVGWriter->WriteMetaFile( aTopLeft, aSize, aMtf, SVGWRITER_WRITE_ALL, NULL ); + // We reset to the original values so that when the <use> + // element is created the x, y attributes are correct. + pAction->Move( aPt.X(), aPt.Y() ); + } + else + { + OSL_FAIL( "implExportTextEmbeddedBitmaps: no shape bounding box." ); + return sal_False; + } + } + else + { + OSL_FAIL( "implExportTextEmbeddedBitmaps: metafile should have MetaBmpExScaleAction only." ); + return sal_False; + } + } + else + { + OSL_FAIL( "implExportTextEmbeddedBitmaps: metafile should have a single action." ); + return sal_False; + } + + } + return sal_True; +} // ----------------------------------------------------------------------------- @@ -1572,11 +1725,21 @@ sal_Bool SVGFilter::implExportShape( const Reference< XShape >& rxShape ) //mpSVGExport->AddAttributeIdLegacy( XML_NAMESPACE_DRAW, rShapeId ); } + const GDIMetaFile* pEmbeddedBitmapsMtf = NULL; + if( mEmbeddedBitmapActionMap.find( rxShape ) != mEmbeddedBitmapActionMap.end() ) + { + pEmbeddedBitmapsMtf = &( mEmbeddedBitmapActionMap[ rxShape ].GetRepresentation() ); + } + { - Reference< XText > xText( rxShape, UNO_QUERY ); - mpSVGWriter->bIsTextShape = xText.is(); +// Reference< XText > xText( rxShape, UNO_QUERY ); +// mpSVGWriter->bIsTextShape = xText.is(); SvXMLElementExport aExp2( *mpSVGExport, XML_NAMESPACE_NONE, "g", sal_True, sal_True ); - mpSVGWriter->WriteMetaFile( aTopLeft, aSize, rMtf, SVGWRITER_WRITE_ALL, pElementId ); + mpSVGWriter->WriteMetaFile( aTopLeft, aSize, rMtf, + SVGWRITER_WRITE_ALL, + pElementId, + &rxShape, + pEmbeddedBitmapsMtf ); } } @@ -1606,7 +1769,7 @@ sal_Bool SVGFilter::implCreateObjects() Reference< XShapes > xShapes( xMasterPage, UNO_QUERY ); if( xShapes.is() ) - implCreateObjectsFromShapes( xShapes ); + implCreateObjectsFromShapes( xMasterPage, xShapes ); } } @@ -1641,7 +1804,7 @@ sal_Bool SVGFilter::implCreateObjects() Reference< XShapes > xShapes( xDrawPage, UNO_QUERY ); if( xShapes.is() ) - implCreateObjectsFromShapes( xShapes ); + implCreateObjectsFromShapes( xDrawPage, xShapes ); } } return sal_True; @@ -1649,7 +1812,7 @@ sal_Bool SVGFilter::implCreateObjects() // ----------------------------------------------------------------------------- -sal_Bool SVGFilter::implCreateObjectsFromShapes( const Reference< XShapes >& rxShapes ) +sal_Bool SVGFilter::implCreateObjectsFromShapes( const Reference< XDrawPage > & rxPage, const Reference< XShapes >& rxShapes ) { Reference< XShape > xShape; sal_Bool bRet = sal_False; @@ -1657,7 +1820,7 @@ sal_Bool SVGFilter::implCreateObjectsFromShapes( const Reference< XShapes >& rxS for( sal_Int32 i = 0, nCount = rxShapes->getCount(); i < nCount; ++i ) { if( ( rxShapes->getByIndex( i ) >>= xShape ) && xShape.is() ) - bRet = implCreateObjectsFromShape( xShape ) || bRet; + bRet = implCreateObjectsFromShape( rxPage, xShape ) || bRet; xShape = NULL; } @@ -1667,15 +1830,16 @@ sal_Bool SVGFilter::implCreateObjectsFromShapes( const Reference< XShapes >& rxS // ----------------------------------------------------------------------------- -sal_Bool SVGFilter::implCreateObjectsFromShape( const Reference< XShape >& rxShape ) +sal_Bool SVGFilter::implCreateObjectsFromShape( const Reference< XDrawPage > & rxPage, const Reference< XShape >& rxShape ) { sal_Bool bRet = sal_False; + if( rxShape->getShapeType().lastIndexOf( B2UCONST( "drawing.GroupShape" ) ) != -1 ) { Reference< XShapes > xShapes( rxShape, UNO_QUERY ); if( xShapes.is() ) - bRet = implCreateObjectsFromShapes( xShapes ); + bRet = implCreateObjectsFromShapes( rxPage, xShapes ); } else { @@ -1700,8 +1864,54 @@ sal_Bool SVGFilter::implCreateObjectsFromShape( const Reference< XShape >& rxSha (*mpObjects)[ rxShape ] = ObjectRepresentation( rxShape, aMtf ); } else - (*mpObjects)[ rxShape ] = ObjectRepresentation( rxShape, aGraphic.GetGDIMetaFile() ); + { + Reference< XText > xText( rxShape, UNO_QUERY ); + sal_Bool bIsTextShape = xText.is(); + + if( !mpSVGExport->IsUsePositionedCharacters() && bIsTextShape ) + { + // We create a map of text shape ids. + implRegisterInterface( rxShape ); + Reference< XInterface > xRef( rxShape, UNO_QUERY ); + const OUString& rShapeId = implGetValidIDFromInterface( xRef ); + if( !rShapeId.isEmpty() ) + { + mTextShapeIdListMap[rxPage] += rShapeId; + mTextShapeIdListMap[rxPage] += B2UCONST( " " ); + } + + // We create a set of bitmaps embedded into text shape. + GDIMetaFile aMtf; + const Point aNullPt; + const Size aSize( pObj->GetCurrentBoundRect().GetSize() ); + MetaAction* pAction; + const GDIMetaFile& rMtf = aGraphic.GetGDIMetaFile(); + sal_uLong nCount = rMtf.GetActionSize(); + for( sal_uLong nCurAction = 0; nCurAction < nCount; ++nCurAction ) + { + pAction = rMtf.GetAction( nCurAction ); + const sal_uInt16 nType = pAction->GetType(); + + if( nType == META_BMPEXSCALE_ACTION ) + { + GDIMetaFile aEmbeddedBitmapMtf; + pAction->Duplicate(); + aEmbeddedBitmapMtf.AddAction( pAction ); + aEmbeddedBitmapMtf.SetPrefSize( aSize ); + aEmbeddedBitmapMtf.SetPrefMapMode( MAP_100TH_MM ); + mEmbeddedBitmapActionSet.insert( ObjectRepresentation( rxShape, aEmbeddedBitmapMtf ) ); + pAction->Duplicate(); + aMtf.AddAction( pAction ); + } + } + aMtf.SetPrefSize( aSize ); + aMtf.SetPrefMapMode( MAP_100TH_MM ); + mEmbeddedBitmapActionMap[ rxShape ] = ObjectRepresentation( rxShape, aMtf ); + } + + (*mpObjects)[ rxShape ] = ObjectRepresentation( rxShape, aGraphic.GetGDIMetaFile() ); + } bRet = sal_True; } } diff --git a/filter/source/svg/svgfilter.hxx b/filter/source/svg/svgfilter.hxx index 50d66f6e81bc..9a0ba8f09ac8 100644 --- a/filter/source/svg/svgfilter.hxx +++ b/filter/source/svg/svgfilter.hxx @@ -226,6 +226,70 @@ struct HashUChar size_t operator()( const sal_Unicode uchar ) const { return static_cast< size_t >( uchar ); } }; +// --------------------------- +// - HashBmpExScale - +// --------------------------- + +struct HashBmpExScale +{ + size_t operator()( const ObjectRepresentation& rObjRep ) const + { + const GDIMetaFile& aMtf = rObjRep.GetRepresentation(); + if( aMtf.GetActionSize() == 1 ) + { + const MetaBmpExScaleAction* pAction = (const MetaBmpExScaleAction*) aMtf.GetAction( 0 ); + if( pAction ) + { + return static_cast< size_t >( pAction->GetBitmapEx().GetChecksum() ); + } + else + { + OSL_FAIL( "HashBmpExScale: metafile should have MetaBmpExScaleAction only." ); + return 0; + } + } + else + { + OSL_FAIL( "HashBmpExScale: metafile should have a single action." ); + return 0; + } + } +}; + +// --------------------------- +// - EqualityBmpExScale - +// --------------------------- + +struct EqualityBmpExScale +{ + bool operator()( const ObjectRepresentation& rObjRep1, + const ObjectRepresentation& rObjRep2 ) const + { + const GDIMetaFile& aMtf1 = rObjRep1.GetRepresentation(); + const GDIMetaFile& aMtf2 = rObjRep2.GetRepresentation(); + if( aMtf1.GetActionSize() == 1 && aMtf2.GetActionSize() == 1 ) + { + const MetaBmpExScaleAction* pA1 = (const MetaBmpExScaleAction*) aMtf1.GetAction( 0 ); + const MetaBmpExScaleAction* pA2 = (const MetaBmpExScaleAction*) aMtf2.GetAction( 0 ); + if( pA1 && pA2 ) + { + return ( pA1->GetBitmapEx().GetChecksum() == pA2->GetBitmapEx().GetChecksum() ); + } + else + { + OSL_FAIL( "EqualityBmpExScale: metafile should have MetaBmpExScaleAction only." ); + return false; + } + } + else + { + OSL_FAIL( "EqualityBmpExScale: metafile should have a single action." ); + return false; + } + + } +}; + // ------------- // - SVGFilter - @@ -250,6 +314,10 @@ public: typedef ::boost::unordered_map< ::rtl::OUString, UCharSet, HashOUString > UCharSetMap; typedef ::boost::unordered_map< Reference< XInterface >, UCharSetMap, HashReferenceXInterface > UCharSetMapMap; + typedef ::boost::unordered_map< Reference< XInterface >, ::rtl::OUString, HashReferenceXInterface > UOStringMap; + + typedef ::boost::unordered_set< ObjectRepresentation, HashBmpExScale, EqualityBmpExScale > MetaBitmapActionSet; + private: Reference< XMultiServiceFactory > mxMSF; @@ -267,6 +335,9 @@ private: ::rtl::OUString msClipPathId; UCharSetMapMap mTextFieldCharSets; Reference< XInterface > mCreateOjectsCurrentMasterPage; + UOStringMap mTextShapeIdListMap; + MetaBitmapActionSet mEmbeddedBitmapActionSet; + ObjectMap mEmbeddedBitmapActionMap; ObjectMap* mpObjects; Reference< XComponent > mxSrcDoc; @@ -285,6 +356,10 @@ private: sal_Bool implGetPagePropSet( const Reference< XDrawPage > & rxPage ); sal_Bool implGenerateMetaData(); + void implExportTextShapeIndex(); + void implEmbedBulletGlyphs(); + void implEmbedBulletGlyph( sal_Unicode cBullet, const ::rtl::OUString & sPathData ); + sal_Bool implExportTextEmbeddedBitmaps(); sal_Bool implGenerateScript(); sal_Bool implExportDocument(); @@ -303,8 +378,8 @@ private: sal_Bool implExportShape( const Reference< XShape >& rxShape ); sal_Bool implCreateObjects(); - sal_Bool implCreateObjectsFromShapes( const Reference< XShapes >& rxShapes ); - sal_Bool implCreateObjectsFromShape( const Reference< XShape >& rxShape ); + sal_Bool implCreateObjectsFromShapes( const Reference< XDrawPage > & rxPage, const Reference< XShapes >& rxShapes ); + sal_Bool implCreateObjectsFromShape( const Reference< XDrawPage > & rxPage, const Reference< XShape >& rxShape ); sal_Bool implCreateObjectsFromBackground( const Reference< XDrawPage >& rxMasterPage ); ::rtl::OUString implGetClassFromShape( const Reference< XShape >& rxShape ); diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index 772867472627..4880161338b9 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -31,9 +31,10 @@ #include "svgfontexport.hxx" #include "svgwriter.hxx" +#include <rtl/crc.h> #include <vcl/unohelp.hxx> #include <tools/helpers.hxx> - +#include <xmloff/unointerfacetouniqueidentifiermapper.hxx> #include <sax/tools/converter.hxx> #include <boost/shared_array.hpp> @@ -97,8 +98,19 @@ static const char aXMLAttrPatternUnits[] = "patternUnits"; static const char aXMLAttrOffset[] = "offset"; static const char aXMLAttrStopColor[] = "stop-color"; + +#define NSPREFIX "ooo:" + +static const char aOOOAttrNumberingType[] = NSPREFIX "numbering-type"; + + +static sal_Char const XML_UNO_NAME_NRULE_NUMBERINGTYPE[] = "NumberingType"; +static sal_Char const XML_UNO_NAME_NRULE_BULLET_CHAR[] = "BulletChar"; // ----------------------------------------------------------------------------- + + + // ---------------------- // - SVGAttributeWriter - // ---------------------- @@ -443,12 +455,25 @@ SVGTextWriter::SVGTextWriter( SVGExport& rExport, SVGFontExport& rFontExport ) mrFontExport( rFontExport ), mpContext( NULL ), mpVDev( NULL ), + mrTextShape(), + mrParagraphEnumeration(), + mrTextPortionEnumeration(), + mrCurrentTextPortion(), + mpTextEmbeddedBitmapMtf( NULL ), mpTargetMapMode( NULL ), mpTextShapeElem( NULL ), mpTextParagraphElem( NULL ), mpTextPositionElem( NULL ), + mnLeftTextPortionLength( 0 ), maTextPos(0,0), mnTextWidth(0), + mbPositioningNeeded( sal_False ), + mbIsNumbering( sal_False ), + maBulletListItemMap(), + mbIsListLevelStyleImage( sal_False ), + mbLineBreak( sal_False ), + mbIsURLField( sal_False ), + msUrl(), mbIsPlacehlolderShape( sal_False ), mbIWS( sal_False ), maCurrentFont(), @@ -465,6 +490,21 @@ SVGTextWriter::~SVGTextWriter() // ----------------------------------------------------------------------------- +void SVGTextWriter::implRegisterInterface( const Reference< XInterface >& rxIf ) +{ + if( rxIf.is() ) + (mrExport.getInterfaceToIdentifierMapper()).registerReference( rxIf ); +} + +// ----------------------------------------------------------------------------- + +const ::rtl::OUString & SVGTextWriter::implGetValidIDFromInterface( const Reference< XInterface >& rxIf ) +{ + return (mrExport.getInterfaceToIdentifierMapper()).getIdentifier( rxIf ); +} + +// ----------------------------------------------------------------------------- + void SVGTextWriter::implMap( const Size& rSz, Size& rDstSz ) const { if( mpVDev && mpTargetMapMode ) @@ -818,8 +858,236 @@ void SVGTextWriter::implSetFontFamily() // ----------------------------------------------------------------------------- +sal_Bool SVGTextWriter::nextParagraph() +{ + mrTextPortionEnumeration.clear(); + if( mrParagraphEnumeration.is() && mrParagraphEnumeration->hasMoreElements() ) + { + Reference < XTextContent > xTextContent( mrParagraphEnumeration->nextElement(), UNO_QUERY_THROW ); + if( xTextContent.is() ) + { + Reference< XServiceInfo > xServiceInfo( xTextContent, UNO_QUERY_THROW ); + if( xServiceInfo.is() ) + { + OUString sInfo; + if( xServiceInfo->supportsService( B2UCONST( "com.sun.star.text.Paragraph" ) ) ) + { + Reference< XPropertySet > xPropSet( xTextContent, UNO_QUERY_THROW ); + Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + mbIsNumbering = sal_False; + mbIsListLevelStyleImage = sal_False; + if( xPropSetInfo->hasPropertyByName( B2UCONST( "NumberingLevel" ) ) ) + { + sal_Int16 nListLevel = 0; + if( xPropSet->getPropertyValue( B2UCONST( "NumberingLevel" ) ) >>= nListLevel ) + { + mbIsNumbering = sal_True; + sInfo = B2UCONST( "NumberingLevel: " ); + sInfo += OUString::valueOf( (sal_Int32)nListLevel ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "style", sInfo ); + + Reference< XIndexReplace > xNumRules; + if( xPropSetInfo->hasPropertyByName( B2UCONST( "NumberingRules" ) ) ) + { + xPropSet->getPropertyValue( B2UCONST( "NumberingRules" ) ) >>= xNumRules; + } + if( xNumRules.is() && ( nListLevel < xNumRules->getCount() ) ) + { + Sequence<PropertyValue> aProps; + if( xNumRules->getByIndex( nListLevel ) >>= aProps ) + { + sal_Int16 eType = NumberingType::CHAR_SPECIAL; + sal_Unicode cBullet = 0xf095; + const sal_Int32 nCount = aProps.getLength(); + const PropertyValue* pPropArray = aProps.getConstArray(); + for( sal_Int32 i = 0; i < nCount; ++i ) + { + const PropertyValue& rProp = pPropArray[i]; + if( rProp.Name.equalsAsciiL( XML_UNO_NAME_NRULE_NUMBERINGTYPE, sizeof(XML_UNO_NAME_NRULE_NUMBERINGTYPE)-1 ) ) + { + rProp.Value >>= eType; + } + else if( rProp.Name.equalsAsciiL( XML_UNO_NAME_NRULE_BULLET_CHAR, sizeof(XML_UNO_NAME_NRULE_BULLET_CHAR)-1 ) ) + { + OUString sValue; + rProp.Value >>= sValue; + if( !sValue.isEmpty() ) + { + cBullet = (sal_Unicode)sValue[0]; + } + } + } + meNumberingType = eType; + mbIsListLevelStyleImage = ( NumberingType::BITMAP == meNumberingType ); + if( NumberingType::CHAR_SPECIAL == meNumberingType ) + { + if( cBullet ) + { + if( cBullet < ' ' ) + { + cBullet = 0xF000 + 149; + } + mcBulletChar = cBullet; + // text:bullet-char="..." + sInfo = OUString::valueOf( (sal_Int32) cBullet ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "bullet-char", sInfo ); + } + + } + } + } + + } + } + + Reference< XEnumerationAccess > xEnumerationAccess( xTextContent, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); + if( xEnumeration.is() && xEnumeration->hasMoreElements() ) + { + mrTextPortionEnumeration.set( xEnumeration ); + } + sInfo = B2UCONST( "Paragraph" ); + } + else if( xServiceInfo->supportsService( B2UCONST( "com.sun.star.text.Table" ) ) ) + { + sInfo = B2UCONST( "Table" ); + } + else + { + OSL_FAIL( "SVGTextWriter::nextParagraph: Unknown text content." ); + return sal_False; + } + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", sInfo ); + SvXMLElementExport aParaElem( mrExport, XML_NAMESPACE_NONE, "desc", mbIWS, mbIWS ); + } + + Reference< XInterface > xRef( xTextContent, UNO_QUERY ); + const OUString& rParagraphId = implGetValidIDFromInterface( xRef ); + if( !rParagraphId.isEmpty() ) + { + mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", rParagraphId ); + } + return sal_True; + } + } + return sal_False; +} + +// ----------------------------------------------------------------------------- + +sal_Bool SVGTextWriter::nextTextPortion() +{ + mrCurrentTextPortion.clear(); + mbIsURLField = sal_False; + if( mrTextPortionEnumeration.is() && mrTextPortionEnumeration->hasMoreElements() ) + { + OUString sInfo; + Reference< XPropertySet > xPortionPropSet( mrTextPortionEnumeration->nextElement(), UNO_QUERY ); + Reference< XPropertySetInfo > xPortionPropInfo( xPortionPropSet->getPropertySetInfo() ); + Reference < XTextRange > xPortionTextRange( xPortionPropSet, UNO_QUERY); + if( xPortionPropSet.is() && xPortionPropInfo.is() + && xPortionPropInfo->hasPropertyByName( B2UCONST( "TextPortionType" ) ) ) + { + ::rtl::OUString sPortionType; + if( xPortionPropSet->getPropertyValue( B2UCONST( "TextPortionType" ) ) >>= sPortionType ) + { + sInfo = B2UCONST( "type: " ); + sInfo += sPortionType; + sInfo += B2UCONST( "; " ); + } + if( xPortionTextRange.is() ) + { + sInfo += B2UCONST( "content: " ); + sInfo += xPortionTextRange->getString(); + sInfo += B2UCONST( "; " ); + + mrCurrentTextPortion.set( xPortionTextRange ); + + Reference < XPropertySet > xRangePropSet( xPortionTextRange, UNO_QUERY ); + if( xRangePropSet.is() && xRangePropSet->getPropertySetInfo()->hasPropertyByName( B2UCONST( "TextField" ) ) ) + { + Reference < XTextField > xTextField( xRangePropSet->getPropertyValue( B2UCONST( "TextField" ) ), UNO_QUERY ); + if( xTextField.is() ) + { + const ::rtl::OUString sServicePrefix( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.text.textfield.") ); + Reference< XServiceInfo > xService( xTextField, UNO_QUERY ); + const Sequence< OUString > aServices = xService->getSupportedServiceNames(); + const OUString* pNames = aServices.getConstArray(); + sal_Int32 nCount = aServices.getLength(); + + OUString sFieldName; // service name postfix of current field + + // search for TextField service name + while( nCount-- ) + { + if ( pNames->matchIgnoreAsciiCase( sServicePrefix ) ) + { + // TextField found => postfix is field type! + sFieldName = pNames->copy( sServicePrefix.getLength() ); + break; + } + + ++pNames; + } + + sInfo += B2UCONST( "text field type: " ); + sInfo += sFieldName; + sInfo += B2UCONST( "; " ); + + mbIsURLField = sFieldName.equalsAscii( "URL" ); + if( mbIsURLField ) + { + Reference<XPropertySet> xTextFieldPropSet(xTextField, UNO_QUERY); + if( xTextFieldPropSet.is() ) + { + OUString sURL; + if( ( xTextFieldPropSet->getPropertyValue( sFieldName ) ) >>= sURL ) + { + sInfo += B2UCONST( "url: " ); + sInfo += mrExport.GetRelativeReference( sURL ); + + msUrl = mrExport.GetRelativeReference( sURL ); + if( !msUrl.isEmpty() ) + { + implRegisterInterface( xPortionTextRange ); + + Reference< XInterface > xRef( xPortionTextRange, UNO_QUERY ); + const OUString& rTextPortionId = implGetValidIDFromInterface( xRef ); + if( !rTextPortionId.isEmpty() ) + { + msHyperlinkIdList += rTextPortionId; + msHyperlinkIdList += B2UCONST( " " ); + } + } + } + } + } + } + } + } + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextPortion" ) ); + SvXMLElementExport aPortionElem( mrExport, XML_NAMESPACE_NONE, "desc", mbIWS, mbIWS ); + mrExport.GetDocHandler()->characters( sInfo ); + return sal_True; + } + } + return sal_False; +} + +// ----------------------------------------------------------------------------- + void SVGTextWriter::startTextShape() { + if( mrTextShape.is() ) + { + Reference< XEnumerationAccess > xEnumerationAccess( mrTextShape, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); + if( xEnumeration.is() && xEnumeration->hasMoreElements() ) + { + mrParagraphEnumeration.set( xEnumeration ); + } + } + if( mpTextShapeElem ) { OSL_FAIL( "SVGTextWriter::startTextShape: text shape already defined." ); @@ -827,6 +1095,7 @@ void SVGTextWriter::startTextShape() else { maParentFont = Font(); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextShape" ) ); mpTextShapeElem = new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemText, sal_True, mbIWS ); startTextParagraph(); } @@ -837,11 +1106,20 @@ void SVGTextWriter::startTextShape() void SVGTextWriter::endTextShape() { endTextParagraph(); + if( mrTextShape.is() ) + mrTextShape.clear(); + if( mrParagraphEnumeration.is() ) + mrParagraphEnumeration.clear(); if( mpTextShapeElem ) { delete mpTextShapeElem; mpTextShapeElem = NULL; } + // these need to be invoked after the <text> element has been closed + implExportHyperlinkIds(); + implWriteBulletChars(); + implWriteEmbeddedBitmaps(); + } // ----------------------------------------------------------------------------- @@ -849,11 +1127,36 @@ void SVGTextWriter::endTextShape() void SVGTextWriter::startTextParagraph() { endTextParagraph(); - mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextParagraph" ) ); + nextParagraph(); + if( mbIsNumbering ) + { + OUString sNumberingType; + switch( meNumberingType ) + { + case( NumberingType::CHAR_SPECIAL ): + sNumberingType = B2UCONST( "bullet-style" ); + break; + case( NumberingType::BITMAP ): + sNumberingType = B2UCONST( "image-style" ); + break; + default: + sNumberingType = B2UCONST( "number-style" ); + break; + } + mrExport.AddAttribute( XML_NAMESPACE_NONE, aOOOAttrNumberingType, sNumberingType ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "ListItem" ) ); + } + else + { + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "TextParagraph" ) ); + } maParentFont = Font(); addFontAttributes( /* isTexTContainer: */ true ); mpTextParagraphElem = new SvXMLElementExport( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS ); - startTextPosition(); + if( !mbIsListLevelStyleImage ) + { + startTextPosition(); + } } // ----------------------------------------------------------------------------- @@ -861,11 +1164,16 @@ void SVGTextWriter::startTextParagraph() void SVGTextWriter::endTextParagraph() { endTextPosition(); + mbIsNumbering = sal_False; + mbIsListLevelStyleImage = sal_False; + mbPositioningNeeded = sal_False; + if( mpTextParagraphElem ) { delete mpTextParagraphElem; mpTextParagraphElem = NULL; } + } // ----------------------------------------------------------------------------- @@ -895,10 +1203,215 @@ void SVGTextWriter::endTextPosition() // ----------------------------------------------------------------------------- +void SVGTextWriter::implExportHyperlinkIds() +{ + if( !msHyperlinkIdList.isEmpty() ) + { + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "HyperlinkIdList" ) ); + SvXMLElementExport aDescElem( mrExport, XML_NAMESPACE_NONE, "desc", sal_True, sal_True ); + mrExport.GetDocHandler()->characters( msHyperlinkIdList.trim() ); + msHyperlinkIdList = OUString(); + } +} + +// ----------------------------------------------------------------------------- + +void SVGTextWriter::implWriteBulletChars() +{ + if( maBulletListItemMap.empty() ) + return; + + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "BulletChars" ) ); + SvXMLElementExport aGroupElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); + + BulletListItemInfoMap::const_iterator it = maBulletListItemMap.begin(); + BulletListItemInfoMap::const_iterator end = maBulletListItemMap.end(); + OUString sId, sPosition, sScaling, sRefId; + for( ; it != end; ++it ) + { + // <g id="?" > (used by animations) + { + // As id we use the id of the text portion placeholder wrapped + // by bullet-char(*) + sId = B2UCONST( "bullet-char(" ); + sId += it->first; + sId += B2UCONST( ")" ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", sId ); + SvXMLElementExport aBulletCharElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); + + // <g transform="translate(x,y)" > + { + const BulletListItemInfo& rInfo = it->second; + + // Add positioning attribute through a translation + sPosition = B2UCONST( "translate(" ); + sPosition += OUString::valueOf( rInfo.aPos.X() ); + sPosition += B2UCONST( "," ); + sPosition += OUString::valueOf( rInfo.aPos.Y() ); + sPosition += B2UCONST( ")" ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "transform", sPosition ); + + mpContext->AddPaintAttr( COL_TRANSPARENT, rInfo.aColor ); + + SvXMLElementExport aPositioningElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); + + // <use transform="scale(font-size)" xlink:ref="/" > + { + // Add size attribute through a scaling + sScaling = B2UCONST( "scale(" ); + sScaling += OUString::valueOf( rInfo.nFontSize ); + sScaling += B2UCONST( "," ); + sScaling += OUString::valueOf( rInfo.nFontSize ); + sScaling += B2UCONST( ")" ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "transform", sScaling ); + + // Add ref attribute + sRefId = B2UCONST( "#bullet-char-template(" ); + sRefId += OUString::valueOf( (sal_Int32)( rInfo.cBulletChar ) ); + sRefId += B2UCONST( ")" ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, sRefId ); + + SvXMLElementExport aRefElem( mrExport, XML_NAMESPACE_NONE, "use", sal_True, sal_True ); + } + } // close aPositioningElem + } // close aBulletCharElem + } + + + // clear the map + maBulletListItemMap.clear(); +} + +// ----------------------------------------------------------------------------- + +void SVGTextWriter::writeBitmapPlaceholder( const MetaBmpExScaleAction* pAction ) +{ + // text position element + const Point& rPos = pAction->GetPoint(); + implMap( rPos, maTextPos ); + startTextPosition(); + mbPositioningNeeded = sal_True; + if( mbIsNumbering ) + { + mbIsNumbering = sal_False; + mbIsListLevelStyleImage = sal_False; + } + + // bitmap placeholder element + sal_uLong nId = SVGActionWriter::GetChecksum( pAction ); + OUString sId = B2UCONST( "bitmap-placeholder(" ); + sId += OUString::valueOf( (sal_Int64)nId ); + sId += B2UCONST( ")" ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", sId ); + + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "BitmapPlaceholder" ) ); + SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS ); +} + +// ----------------------------------------------------------------------------- + +void SVGTextWriter::implWriteEmbeddedBitmaps() +{ + if( mpTextEmbeddedBitmapMtf && mpTextEmbeddedBitmapMtf->GetActionSize() ) + { + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "EmbeddedBitmaps" ) ); + SvXMLElementExport aEmbBitmapGroupElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); + + const GDIMetaFile& rMtf = *mpTextEmbeddedBitmapMtf; + + OUString sId, sRefId; + sal_uLong nId; + sal_uLong nCount = rMtf.GetActionSize(); + for( sal_uLong nCurAction = 0; nCurAction < nCount; nCurAction++ ) + { + MetaBmpExScaleAction* pAction = (MetaBmpExScaleAction*) rMtf.GetAction( nCurAction ); + + // <g id="?" > (used by animations) + { + // embedded bitmap id + nId = SVGActionWriter::GetChecksum( pAction ); + sId = B2UCONST( "embedded-bitmap(" ); + sId += OUString::valueOf( (sal_Int64)nId ); + sId += B2UCONST( ")" ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", sId ); + + SvXMLElementExport aEmbBitmapElem( mrExport, XML_NAMESPACE_NONE, aXMLElemG, sal_True, sal_True ); + + // <use x="?" y="?" xlink:ref="?" > + { + // referenced bitmap template + nId = pAction->GetBitmapEx().GetChecksum(); + sRefId = B2UCONST( "#bitmap(" ); + sRefId += OUString::valueOf( (sal_Int64)nId ); + sRefId += B2UCONST( ")" ); + + const Point& rPt = pAction->GetPoint(); + const Size& rSz = pAction->GetSize(); + Point aPt; + Size aSz; + implMap( rPt, aPt ); + implMap( rSz, aSz ); + + mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrX, OUString::valueOf( aPt.X() ) ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, OUString::valueOf( aPt.Y() ) ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, sRefId ); + + SvXMLElementExport aRefElem( mrExport, XML_NAMESPACE_NONE, "use", sal_True, sal_True ); + } + } // close aEmbBitmapElem + } + } +} + +// ----------------------------------------------------------------------------- + void SVGTextWriter::writeTextPortion( const Point& rPos, const String& rText, sal_Bool bApplyMapping ) { + if( rText.Len() == 0 ) + return; + + mbLineBreak = sal_False; + + if( !mbIsNumbering || mbIsListLevelStyleImage ) + { + bool bNotSync = true; + OUString sContent; + sal_Int32 nStartPos; + do + { + while( bNotSync ) + { + if( mnLeftTextPortionLength <= 0 ) + { + if( !nextTextPortion() ) + break; + else + { + sContent = mrCurrentTextPortion->getString(); + mnLeftTextPortionLength = sContent.getLength(); + } + } + else + { + sContent = mrCurrentTextPortion->getString(); + } + + nStartPos = sContent.getLength() - mnLeftTextPortionLength; + if( nStartPos < 0 ) nStartPos = 0; + mnLeftTextPortionLength -= rText.Len(); + + if( sContent.isEmpty() ) + continue; + if( sContent.equalsAscii( "\n" ) ) + mbLineBreak = sal_True; + if( sContent.match( rText, nStartPos ) ) + bNotSync = false; + } + } while( bNotSync && nextParagraph() ); + } + if( !mpVDev ) OSL_FAIL( "SVGTextWriter::writeTextPortion: invalid virtual device." ); @@ -1014,24 +1527,72 @@ void SVGTextWriter::implWriteTextPortion( const Point& rPos, else aPos = rPos; - - if( maTextPos.Y() != aPos.Y() ) + if( mbPositioningNeeded ) + { + mbPositioningNeeded = sal_False; + maTextPos.setX( aPos.X() ); + maTextPos.setY( aPos.Y() ); + startTextPosition(); + } + else if( maTextPos.Y() != aPos.Y() ) { // In case the text position moved backward we could have a line break // so we end the current line and start a new one. - if( ( maTextPos.X() + mnTextWidth ) > aPos.X() ) + if( mbLineBreak || ( ( maTextPos.X() + mnTextWidth ) > aPos.X() ) ) { + mbLineBreak = sal_False; maTextPos.setX( aPos.X() ); maTextPos.setY( aPos.Y() ); startTextPosition(); } - else // superscript, subscript, bullets + else // superscript, subscript, list item numbering { - //mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrY, ::rtl::OUString::valueOf( aPos.Y() ) ); maTextPos.setY( aPos.Y() ); startTextPosition( sal_False /* do not export x attribute */ ); } } + // we are dealing with a bullet, so set up this for the next text portion + if( mbIsNumbering ) + { + mbIsNumbering = sal_False; + mbPositioningNeeded = sal_True; + + if( meNumberingType == NumberingType::CHAR_SPECIAL ) + { + // Create an id for the current text portion + implRegisterInterface( mrCurrentTextPortion ); + + // Add the needed info to the BulletListItemMap + Reference< XInterface > xRef( mrCurrentTextPortion, UNO_QUERY ); + const OUString& rId = implGetValidIDFromInterface( xRef ); + if( !rId.isEmpty() ) + { + BulletListItemInfo& aBulletListItemInfo = maBulletListItemMap[ rId ]; + aBulletListItemInfo.nFontSize = rFont.GetHeight(); + aBulletListItemInfo.aColor = aTextColor; + aBulletListItemInfo.aPos = maTextPos; + aBulletListItemInfo.cBulletChar = mcBulletChar; + + // Make this text portion a bullet placeholder + mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", rId ); + mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", B2UCONST( "BulletPlaceholder" ) ); + SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS ); + return; + } + } + } + + Reference< XInterface > xRef( mrCurrentTextPortion, UNO_QUERY ); + const OUString& rTextPortionId = implGetValidIDFromInterface( xRef ); + if( !rTextPortionId.isEmpty() ) + { + mrExport.AddAttribute( XML_NAMESPACE_NONE, "id", rTextPortionId ); + } + + if( mbIsURLField && !msUrl.isEmpty() ) + { + mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, msUrl ); + } addFontAttributes( /* isTexTContainer: */ false ); @@ -1236,6 +1797,35 @@ PolyPolygon& SVGActionWriter::ImplMap( const PolyPolygon& rPolyPoly, PolyPolygon // ----------------------------------------------------------------------------- +sal_uLong SVGActionWriter::GetChecksum( const MetaBmpExScaleAction* pAct ) +{ + sal_uLong nCrc = 0; + SVBT16 aBT16; + SVBT32 aBT32; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmapEx().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + return nCrc; +} + +// ----------------------------------------------------------------------------- + void SVGActionWriter::ImplWriteLine( const Point& rPt1, const Point& rPt2, const Color* pLineColor, sal_Bool bApplyMapping ) { @@ -2043,14 +2633,23 @@ void SVGActionWriter::ImplWriteBmp( const BitmapEx& rBmpEx, void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, sal_uInt32 nWriteFlags, - const ::rtl::OUString* pElementId ) + const ::rtl::OUString* pElementId, + const Reference< XShape >* pxShape, + const GDIMetaFile* pTextEmbeddedBitmapMtf ) { if( mnInnerMtfCount ) nWriteFlags |= SVGWRITER_NO_SHAPE_COMMENTS; + if( !mrExport.IsUsePositionedCharacters() && pxShape + && Reference< XText >( *pxShape, UNO_QUERY ).is() ) + { + Reference< XText > xText( *pxShape, UNO_QUERY ); + maTextWriter.setTextShape( xText, pTextEmbeddedBitmapMtf ); + } + mbIsPlacehlolderShape = false; maTextWriter.setPlaceholderShapeFlag( false ); - if( pElementId != NULL && ( *pElementId == sPlaceholderTag ) ) + if( ( pElementId != NULL ) && ( *pElementId == sPlaceholderTag ) ) { mbIsPlacehlolderShape = true; maTextWriter.setPlaceholderShapeFlag( true ); @@ -2063,7 +2662,7 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, const MetaAction* pAction = rMtf.GetAction( nCurAction ); const sal_uInt16 nType = pAction->GetType(); - if( bIsTextShape ) + if( maTextWriter.getTextShape().is() ) { try { @@ -2677,9 +3276,17 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, { const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pAction; - ImplWriteBmp( pA->GetBitmapEx(), - pA->GetPoint(), pA->GetSize(), - Point(), pA->GetBitmapEx().GetSizePixel() ); + // Bitmaps embedded into text shapes are collected and exported elsewhere. + if( maTextWriter.getTextShape().is() ) + { + maTextWriter.writeBitmapPlaceholder( pA ); + } + else + { + ImplWriteBmp( pA->GetBitmapEx(), + pA->GetPoint(), pA->GetSize(), + Point(), pA->GetBitmapEx().GetSizePixel() ); + } } } break; @@ -2874,7 +3481,9 @@ void SVGActionWriter::WriteMetaFile( const Point& rPos100thmm, const Size& rSize100thmm, const GDIMetaFile& rMtf, sal_uInt32 nWriteFlags, - const ::rtl::OUString* pElementId ) + const ::rtl::OUString* pElementId, + const Reference< XShape >* pXShape, + const GDIMetaFile* pTextEmbeddedBitmapMtf ) { MapMode aMapMode( rMtf.GetPrefMapMode() ); Size aPrefSize( rMtf.GetPrefSize() ); @@ -2895,7 +3504,7 @@ void SVGActionWriter::WriteMetaFile( const Point& rPos100thmm, mapCurShape.reset(); - ImplWriteActions( rMtf, nWriteFlags, pElementId ); + ImplWriteActions( rMtf, nWriteFlags, pElementId, pXShape, pTextEmbeddedBitmapMtf ); // draw open shape that doesn't have a border if( mapCurShape.get() ) diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx index 8f74671903cc..3a23f5fa52ad 100644 --- a/filter/source/svg/svgwriter.hxx +++ b/filter/source/svg/svgwriter.hxx @@ -45,6 +45,12 @@ #include <xmloff/nmspmap.hxx> #include <com/sun/star/uno/Reference.h> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/container/XIndexReplace.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> #include <com/sun/star/uno/RuntimeException.hpp> #include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <com/sun/star/lang/XComponent.hpp> @@ -55,6 +61,21 @@ #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> #include <com/sun/star/i18n/CharacterIteratorMode.hpp> #include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/text/XTextContent.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/text/XTextField.hpp> +#include <com/sun/star/style/NumberingType.hpp> + +// ----------------------------------------------------------------------------- + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::style; // ----------------------------------------------------------------------------- @@ -141,29 +162,68 @@ struct SVGShapeDescriptor } }; -// ------------------- -// - SVGActionWriter - -// ------------------- + class SVGAttributeWriter; class GDIMetaFile; + +// --------------------------- +// - BulletListItemInfo - +// --------------------------- +struct BulletListItemInfo +{ + long nFontSize; + Color aColor; + Point aPos; + sal_Unicode cBulletChar; +}; + +// --------------------------- +// - OUStringHasher - +// --------------------------- + +struct OUStringHasher +{ + size_t operator()( const ::rtl::OUString& oustr ) const { return static_cast< size_t >( oustr.hashCode() ); } +}; + + // ------------------- // - SVGTextWriter - // ------------------- class SVGTextWriter { + public: + typedef ::boost::unordered_map< ::rtl::OUString, BulletListItemInfo, OUStringHasher > BulletListItemInfoMap; + private: SVGExport& mrExport; SVGFontExport& mrFontExport; SVGAttributeWriter* mpContext; VirtualDevice* mpVDev; + Reference<XText> mrTextShape; + Reference<XEnumeration> mrParagraphEnumeration; + Reference<XEnumeration> mrTextPortionEnumeration; + Reference<XTextRange> mrCurrentTextPortion; + const GDIMetaFile* mpTextEmbeddedBitmapMtf; MapMode* mpTargetMapMode; SvXMLElementExport* mpTextShapeElem; SvXMLElementExport* mpTextParagraphElem; SvXMLElementExport* mpTextPositionElem; + sal_Int32 mnLeftTextPortionLength; Point maTextPos; long int mnTextWidth; + sal_Bool mbPositioningNeeded; + sal_Bool mbIsNumbering; + sal_Int16 meNumberingType; + sal_Unicode mcBulletChar; + BulletListItemInfoMap maBulletListItemMap; + sal_Bool mbIsListLevelStyleImage; + sal_Bool mbLineBreak; + sal_Bool mbIsURLField; + ::rtl::OUString msUrl; + ::rtl::OUString msHyperlinkIdList; sal_Bool mbIsPlacehlolderShape; sal_Bool mbIWS; Font maCurrentFont; @@ -177,12 +237,19 @@ class SVGTextWriter void setTextProperties( const GDIMetaFile& rMtf, sal_uLong nCurAction ); void addFontAttributes( sal_Bool bIsTextContainer ); + sal_Bool nextParagraph(); + sal_Bool nextTextPortion(); + void startTextShape(); void endTextShape(); void startTextParagraph(); void endTextParagraph(); void startTextPosition( sal_Bool bExportX = sal_True, sal_Bool bExportY = sal_True); void endTextPosition(); + void implExportHyperlinkIds(); + void implWriteBulletChars(); + void writeBitmapPlaceholder( const MetaBmpExScaleAction* pAction ); + void implWriteEmbeddedBitmaps(); void writeTextPortion( const Point& rPos, const String& rText, sal_Bool bApplyMapping = sal_True ); void implWriteTextPortion( const Point& rPos, const String& rText, @@ -201,6 +268,19 @@ class SVGTextWriter mpContext = pContext; } + void setTextShape( const Reference<XText>& rxText, + const GDIMetaFile* pTextEmbeddedBitmapMtf ) + { + mrTextShape.set( rxText ); + mpTextEmbeddedBitmapMtf = pTextEmbeddedBitmapMtf; + } + + const Reference<XText>& getTextShape() const + { + return mrTextShape; + } + + void setPlaceholderShapeFlag( sal_Bool bState ) { mbIsPlacehlolderShape = bState; @@ -213,9 +293,15 @@ class SVGTextWriter void implSetFontFamily(); template< typename SubType > sal_Bool implGetTextPosition( const MetaAction* pAction, Point& raPos, sal_Bool& bEmpty ); + void implRegisterInterface( const Reference< XInterface >& rxIf ); + const ::rtl::OUString & implGetValidIDFromInterface( const Reference< XInterface >& rxIf ); + }; +// ------------------- +// - SVGActionWriter - +// ------------------- class SVGActionWriter { @@ -289,13 +375,18 @@ private: void ImplCheckFontAttributes(); void ImplCheckPaintAttributes(); - void ImplWriteActions( const GDIMetaFile& rMtf, sal_uInt32 nWriteFlags, const ::rtl::OUString* pElementId ); + void ImplWriteActions( const GDIMetaFile& rMtf, + sal_uInt32 nWriteFlags, + const ::rtl::OUString* pElementId, + const Reference< XShape >* pXShape = NULL, + const GDIMetaFile* pTextEmbeddedBitmapMtf = NULL ); Font ImplSetCorrectFontHeight() const; public: static ::rtl::OUString GetPathString( const PolyPolygon& rPolyPoly, sal_Bool bLine ); + static sal_uLong GetChecksum( const MetaBmpExScaleAction* pAct ); public: @@ -306,7 +397,9 @@ public: const Size& rSize100thmm, const GDIMetaFile& rMtf, sal_uInt32 nWriteFlags, - const ::rtl::OUString* pElementId = NULL ); + const ::rtl::OUString* pElementId = NULL, + const Reference< XShape >* pXShape = NULL, + const GDIMetaFile* pTextEmbeddedBitmapMtf = NULL ); sal_Bool bIsTextShape; }; |