1ddde725dSArmin Le Grand /************************************************************** 2ddde725dSArmin Le Grand * 3ddde725dSArmin Le Grand * Licensed to the Apache Software Foundation (ASF) under one 4ddde725dSArmin Le Grand * or more contributor license agreements. See the NOTICE file 5ddde725dSArmin Le Grand * distributed with this work for additional information 6ddde725dSArmin Le Grand * regarding copyright ownership. The ASF licenses this file 7ddde725dSArmin Le Grand * to you under the Apache License, Version 2.0 (the 8ddde725dSArmin Le Grand * "License"); you may not use this file except in compliance 9ddde725dSArmin Le Grand * with the License. You may obtain a copy of the License at 10ddde725dSArmin Le Grand * 11ddde725dSArmin Le Grand * http://www.apache.org/licenses/LICENSE-2.0 12ddde725dSArmin Le Grand * 13ddde725dSArmin Le Grand * Unless required by applicable law or agreed to in writing, 14ddde725dSArmin Le Grand * software distributed under the License is distributed on an 15ddde725dSArmin Le Grand * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16ddde725dSArmin Le Grand * KIND, either express or implied. See the License for the 17ddde725dSArmin Le Grand * specific language governing permissions and limitations 18ddde725dSArmin Le Grand * under the License. 19ddde725dSArmin Le Grand * 20ddde725dSArmin Le Grand *************************************************************/ 21ddde725dSArmin Le Grand 22ddde725dSArmin Le Grand // MARKER(update_precomp.py): autogen include statement, do not remove 23ddde725dSArmin Le Grand #include "precompiled_svgio.hxx" 24ddde725dSArmin Le Grand 25ddde725dSArmin Le Grand #include <svgio/svgreader/svgtextpathnode.hxx> 26ddde725dSArmin Le Grand #include <svgio/svgreader/svgstyleattributes.hxx> 27ddde725dSArmin Le Grand #include <svgio/svgreader/svgpathnode.hxx> 28ddde725dSArmin Le Grand #include <svgio/svgreader/svgdocument.hxx> 29ddde725dSArmin Le Grand #include <svgio/svgreader/svgtrefnode.hxx> 30ddde725dSArmin Le Grand #include <basegfx/polygon/b2dpolygon.hxx> 31ddde725dSArmin Le Grand #include <basegfx/polygon/b2dpolygontools.hxx> 32ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/textbreakuphelper.hxx> 33ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/groupprimitive2d.hxx> 34ddde725dSArmin Le Grand #include <basegfx/curve/b2dcubicbezier.hxx> 35ddde725dSArmin Le Grand #include <basegfx/curve/b2dbeziertools.hxx> 36ddde725dSArmin Le Grand 37ddde725dSArmin Le Grand ////////////////////////////////////////////////////////////////////////////// 38ddde725dSArmin Le Grand 39ddde725dSArmin Le Grand namespace svgio 40ddde725dSArmin Le Grand { 41ddde725dSArmin Le Grand namespace svgreader 42ddde725dSArmin Le Grand { 43ddde725dSArmin Le Grand class pathTextBreakupHelper : public drawinglayer::primitive2d::TextBreakupHelper 44ddde725dSArmin Le Grand { 45ddde725dSArmin Le Grand private: 46ddde725dSArmin Le Grand const basegfx::B2DPolygon& mrPolygon; 47ddde725dSArmin Le Grand const double mfBasegfxPathLength; 48ddde725dSArmin Le Grand const double mfUserToBasegfx; 49ddde725dSArmin Le Grand double mfPosition; 50ddde725dSArmin Le Grand const basegfx::B2DPoint& mrTextStart; 51ddde725dSArmin Le Grand 52ddde725dSArmin Le Grand const sal_uInt32 mnMaxIndex; 53ddde725dSArmin Le Grand sal_uInt32 mnIndex; 54ddde725dSArmin Le Grand basegfx::B2DCubicBezier maCurrentSegment; 55ddde725dSArmin Le Grand basegfx::B2DCubicBezierHelper* mpB2DCubicBezierHelper; 56ddde725dSArmin Le Grand double mfCurrentSegmentLength; 57ddde725dSArmin Le Grand double mfSegmentStartPosition; 58ddde725dSArmin Le Grand 59ddde725dSArmin Le Grand protected: 60ddde725dSArmin Le Grand /// allow user callback to allow changes to the new TextTransformation. Default 61ddde725dSArmin Le Grand /// does nothing. 62ddde725dSArmin Le Grand virtual bool allowChange(sal_uInt32 nCount, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength); 63ddde725dSArmin Le Grand 64ddde725dSArmin Le Grand void freeB2DCubicBezierHelper(); 65ddde725dSArmin Le Grand basegfx::B2DCubicBezierHelper* getB2DCubicBezierHelper(); 66ddde725dSArmin Le Grand void advanceToPosition(double fNewPosition); 67ddde725dSArmin Le Grand 68ddde725dSArmin Le Grand public: 69ddde725dSArmin Le Grand pathTextBreakupHelper( 70ddde725dSArmin Le Grand const drawinglayer::primitive2d::Primitive2DReference& rxSource, 71ddde725dSArmin Le Grand const basegfx::B2DPolygon& rPolygon, 72ddde725dSArmin Le Grand const double fBasegfxPathLength, 73ddde725dSArmin Le Grand const double fUserToBasegfx, 74ddde725dSArmin Le Grand double fPosition, 75ddde725dSArmin Le Grand const basegfx::B2DPoint& rTextStart); 76ddde725dSArmin Le Grand virtual ~pathTextBreakupHelper(); 77ddde725dSArmin Le Grand 78ddde725dSArmin Le Grand // read access to evtl. advanced position 79ddde725dSArmin Le Grand double getPosition() const { return mfPosition; } 80ddde725dSArmin Le Grand 81ddde725dSArmin Le Grand // get length of given text 82ddde725dSArmin Le Grand double getLength(const rtl::OUString& rText) const; 83ddde725dSArmin Le Grand }; 84ddde725dSArmin Le Grand 85ddde725dSArmin Le Grand double pathTextBreakupHelper::getLength(const rtl::OUString& rText) const 86ddde725dSArmin Le Grand { 87ddde725dSArmin Le Grand const sal_uInt32 nLength(rText.getLength()); 88ddde725dSArmin Le Grand 89ddde725dSArmin Le Grand if(nLength) 90ddde725dSArmin Le Grand { 91ddde725dSArmin Le Grand return getTextLayouter().getTextWidth(rText, 0, nLength); 92ddde725dSArmin Le Grand } 93ddde725dSArmin Le Grand 94ddde725dSArmin Le Grand return 0.0; 95ddde725dSArmin Le Grand } 96ddde725dSArmin Le Grand 97ddde725dSArmin Le Grand void pathTextBreakupHelper::freeB2DCubicBezierHelper() 98ddde725dSArmin Le Grand { 99ddde725dSArmin Le Grand if(mpB2DCubicBezierHelper) 100ddde725dSArmin Le Grand { 101ddde725dSArmin Le Grand delete mpB2DCubicBezierHelper; 102ddde725dSArmin Le Grand mpB2DCubicBezierHelper = 0; 103ddde725dSArmin Le Grand } 104ddde725dSArmin Le Grand } 105ddde725dSArmin Le Grand 106ddde725dSArmin Le Grand basegfx::B2DCubicBezierHelper* pathTextBreakupHelper::getB2DCubicBezierHelper() 107ddde725dSArmin Le Grand { 108ddde725dSArmin Le Grand if(!mpB2DCubicBezierHelper && maCurrentSegment.isBezier()) 109ddde725dSArmin Le Grand { 110ddde725dSArmin Le Grand mpB2DCubicBezierHelper = new basegfx::B2DCubicBezierHelper(maCurrentSegment); 111ddde725dSArmin Le Grand } 112ddde725dSArmin Le Grand 113ddde725dSArmin Le Grand return mpB2DCubicBezierHelper; 114ddde725dSArmin Le Grand } 115ddde725dSArmin Le Grand 116ddde725dSArmin Le Grand void pathTextBreakupHelper::advanceToPosition(double fNewPosition) 117ddde725dSArmin Le Grand { 118ddde725dSArmin Le Grand while(mfSegmentStartPosition + mfCurrentSegmentLength < fNewPosition && mnIndex < mnMaxIndex) 119ddde725dSArmin Le Grand { 120ddde725dSArmin Le Grand mfSegmentStartPosition += mfCurrentSegmentLength; 121ddde725dSArmin Le Grand mnIndex++; 122ddde725dSArmin Le Grand 123ddde725dSArmin Le Grand if(mnIndex < mnMaxIndex) 124ddde725dSArmin Le Grand { 125ddde725dSArmin Le Grand freeB2DCubicBezierHelper(); 126ddde725dSArmin Le Grand mrPolygon.getBezierSegment(mnIndex % mrPolygon.count(), maCurrentSegment); 127ddde725dSArmin Le Grand maCurrentSegment.testAndSolveTrivialBezier(); 128ddde725dSArmin Le Grand mfCurrentSegmentLength = getB2DCubicBezierHelper() 129ddde725dSArmin Le Grand ? getB2DCubicBezierHelper()->getLength() 130ddde725dSArmin Le Grand : maCurrentSegment.getLength(); 131ddde725dSArmin Le Grand } 132ddde725dSArmin Le Grand } 133ddde725dSArmin Le Grand 134ddde725dSArmin Le Grand mfPosition = fNewPosition; 135ddde725dSArmin Le Grand } 136ddde725dSArmin Le Grand 137ddde725dSArmin Le Grand pathTextBreakupHelper::pathTextBreakupHelper( 138ddde725dSArmin Le Grand const drawinglayer::primitive2d::Primitive2DReference& rxSource, 139ddde725dSArmin Le Grand const basegfx::B2DPolygon& rPolygon, 140ddde725dSArmin Le Grand const double fBasegfxPathLength, 141ddde725dSArmin Le Grand const double fUserToBasegfx, 142ddde725dSArmin Le Grand double fPosition, 143ddde725dSArmin Le Grand const basegfx::B2DPoint& rTextStart) 144ddde725dSArmin Le Grand : drawinglayer::primitive2d::TextBreakupHelper(rxSource), 145ddde725dSArmin Le Grand mrPolygon(rPolygon), 146ddde725dSArmin Le Grand mfBasegfxPathLength(fBasegfxPathLength), 147ddde725dSArmin Le Grand mfUserToBasegfx(fUserToBasegfx), 148ddde725dSArmin Le Grand mfPosition(0.0), 149ddde725dSArmin Le Grand mrTextStart(rTextStart), 150ddde725dSArmin Le Grand mnMaxIndex(rPolygon.isClosed() ? rPolygon.count() : rPolygon.count() - 1), 151ddde725dSArmin Le Grand mnIndex(0), 152ddde725dSArmin Le Grand maCurrentSegment(), 153ddde725dSArmin Le Grand mpB2DCubicBezierHelper(0), 154ddde725dSArmin Le Grand mfCurrentSegmentLength(0.0), 155ddde725dSArmin Le Grand mfSegmentStartPosition(0.0) 156ddde725dSArmin Le Grand { 157ddde725dSArmin Le Grand mrPolygon.getBezierSegment(mnIndex % mrPolygon.count(), maCurrentSegment); 158ddde725dSArmin Le Grand mfCurrentSegmentLength = maCurrentSegment.getLength(); 159ddde725dSArmin Le Grand 160ddde725dSArmin Le Grand advanceToPosition(fPosition); 161ddde725dSArmin Le Grand } 162ddde725dSArmin Le Grand 163ddde725dSArmin Le Grand pathTextBreakupHelper::~pathTextBreakupHelper() 164ddde725dSArmin Le Grand { 165ddde725dSArmin Le Grand freeB2DCubicBezierHelper(); 166ddde725dSArmin Le Grand } 167ddde725dSArmin Le Grand 168*e2bf1e9dSArmin Le Grand bool pathTextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength) 169ddde725dSArmin Le Grand { 170ddde725dSArmin Le Grand bool bRetval(false); 171ddde725dSArmin Le Grand 172ddde725dSArmin Le Grand if(mfPosition < mfBasegfxPathLength && nLength && getCastedSource() && mnIndex < mnMaxIndex) 173ddde725dSArmin Le Grand { 174ddde725dSArmin Le Grand const double fSnippetWidth( 175ddde725dSArmin Le Grand getTextLayouter().getTextWidth( 176ddde725dSArmin Le Grand getCastedSource()->getText(), 177ddde725dSArmin Le Grand nIndex, 178ddde725dSArmin Le Grand nLength)); 179ddde725dSArmin Le Grand 180ddde725dSArmin Le Grand if(basegfx::fTools::more(fSnippetWidth, 0.0)) 181ddde725dSArmin Le Grand { 182ddde725dSArmin Le Grand const ::rtl::OUString aText(getCastedSource()->getText()); 183ddde725dSArmin Le Grand const ::rtl::OUString aTrimmedChars(aText.copy(nIndex, nLength).trim()); 184ddde725dSArmin Le Grand const double fEndPos(mfPosition + fSnippetWidth); 185ddde725dSArmin Le Grand 186ddde725dSArmin Le Grand if(aTrimmedChars.getLength() && (mfPosition < mfBasegfxPathLength || fEndPos > 0.0)) 187ddde725dSArmin Le Grand { 188ddde725dSArmin Le Grand const double fHalfSnippetWidth(fSnippetWidth * 0.5); 189ddde725dSArmin Le Grand 190ddde725dSArmin Le Grand advanceToPosition(mfPosition + fHalfSnippetWidth); 191ddde725dSArmin Le Grand 192ddde725dSArmin Le Grand // create representation for this snippet 193ddde725dSArmin Le Grand bRetval = true; 194ddde725dSArmin Le Grand 195ddde725dSArmin Le Grand // get target position and tangent in that pint 196ddde725dSArmin Le Grand basegfx::B2DPoint aPosition(0.0, 0.0); 197ddde725dSArmin Le Grand basegfx::B2DVector aTangent(0.0, 1.0); 198ddde725dSArmin Le Grand 199ddde725dSArmin Le Grand if(mfPosition < 0.0) 200ddde725dSArmin Le Grand { 201ddde725dSArmin Le Grand // snippet center is left of first segment, but right edge is on it (SVG allows that) 202ddde725dSArmin Le Grand aTangent = maCurrentSegment.getTangent(0.0); 203ddde725dSArmin Le Grand aTangent.normalize(); 204ddde725dSArmin Le Grand aPosition = maCurrentSegment.getStartPoint() + (aTangent * (mfPosition - mfSegmentStartPosition)); 205ddde725dSArmin Le Grand } 206ddde725dSArmin Le Grand else if(mfPosition > mfBasegfxPathLength) 207ddde725dSArmin Le Grand { 208ddde725dSArmin Le Grand // snippet center is right of last segment, but left edge is on it (SVG allows that) 209ddde725dSArmin Le Grand aTangent = maCurrentSegment.getTangent(1.0); 210ddde725dSArmin Le Grand aTangent.normalize(); 211ddde725dSArmin Le Grand aPosition = maCurrentSegment.getEndPoint() + (aTangent * (mfPosition - mfSegmentStartPosition)); 212ddde725dSArmin Le Grand } 213ddde725dSArmin Le Grand else 214ddde725dSArmin Le Grand { 215ddde725dSArmin Le Grand // snippet center inside segment, interpolate 216ddde725dSArmin Le Grand double fBezierDistance(mfPosition - mfSegmentStartPosition); 217ddde725dSArmin Le Grand 218ddde725dSArmin Le Grand if(getB2DCubicBezierHelper()) 219ddde725dSArmin Le Grand { 220ddde725dSArmin Le Grand // use B2DCubicBezierHelper to bridge the non-linear gap between 221ddde725dSArmin Le Grand // length and bezier distances (if it's a bezier segment) 222ddde725dSArmin Le Grand fBezierDistance = getB2DCubicBezierHelper()->distanceToRelative(fBezierDistance); 223ddde725dSArmin Le Grand } 224ddde725dSArmin Le Grand else 225ddde725dSArmin Le Grand { 226ddde725dSArmin Le Grand // linear relationship, make relative to segment length 227ddde725dSArmin Le Grand fBezierDistance = fBezierDistance / mfCurrentSegmentLength; 228ddde725dSArmin Le Grand } 229ddde725dSArmin Le Grand 230ddde725dSArmin Le Grand aPosition = maCurrentSegment.interpolatePoint(fBezierDistance); 231ddde725dSArmin Le Grand aTangent = maCurrentSegment.getTangent(fBezierDistance); 232ddde725dSArmin Le Grand aTangent.normalize(); 233ddde725dSArmin Le Grand } 234ddde725dSArmin Le Grand 235ddde725dSArmin Le Grand // detect evtl. hor/ver translations (depends on text direction) 236ddde725dSArmin Le Grand const basegfx::B2DPoint aBasePoint(rNewTransform * basegfx::B2DPoint(0.0, 0.0)); 237ddde725dSArmin Le Grand const basegfx::B2DVector aOffset(aBasePoint - mrTextStart); 238ddde725dSArmin Le Grand 239ddde725dSArmin Le Grand if(!basegfx::fTools::equalZero(aOffset.getY())) 240ddde725dSArmin Le Grand { 241ddde725dSArmin Le Grand // ...and apply 242ddde725dSArmin Le Grand aPosition.setY(aPosition.getY() + aOffset.getY()); 243ddde725dSArmin Le Grand } 244ddde725dSArmin Le Grand 245ddde725dSArmin Le Grand // move target position from snippet center to left text start 246ddde725dSArmin Le Grand aPosition -= fHalfSnippetWidth * aTangent; 247ddde725dSArmin Le Grand 248ddde725dSArmin Le Grand // remove current translation 249ddde725dSArmin Le Grand rNewTransform.translate(-aBasePoint.getX(), -aBasePoint.getY()); 250ddde725dSArmin Le Grand 251ddde725dSArmin Le Grand // rotate due to tangent 252ddde725dSArmin Le Grand rNewTransform.rotate(atan2(aTangent.getY(), aTangent.getX())); 253ddde725dSArmin Le Grand 254ddde725dSArmin Le Grand // add new translation 255ddde725dSArmin Le Grand rNewTransform.translate(aPosition.getX(), aPosition.getY()); 256ddde725dSArmin Le Grand } 257ddde725dSArmin Le Grand 258ddde725dSArmin Le Grand // advance to end 259ddde725dSArmin Le Grand advanceToPosition(fEndPos); 260ddde725dSArmin Le Grand } 261ddde725dSArmin Le Grand } 262ddde725dSArmin Le Grand 263ddde725dSArmin Le Grand return bRetval; 264ddde725dSArmin Le Grand } 265ddde725dSArmin Le Grand 266ddde725dSArmin Le Grand } // end of namespace svgreader 267ddde725dSArmin Le Grand } // end of namespace svgio 268ddde725dSArmin Le Grand 269ddde725dSArmin Le Grand ////////////////////////////////////////////////////////////////////////////// 270ddde725dSArmin Le Grand 271ddde725dSArmin Le Grand namespace svgio 272ddde725dSArmin Le Grand { 273ddde725dSArmin Le Grand namespace svgreader 274ddde725dSArmin Le Grand { 275ddde725dSArmin Le Grand SvgTextPathNode::SvgTextPathNode( 276ddde725dSArmin Le Grand SvgDocument& rDocument, 277ddde725dSArmin Le Grand SvgNode* pParent) 278ddde725dSArmin Le Grand : SvgNode(SVGTokenTextPath, rDocument, pParent), 279ddde725dSArmin Le Grand maSvgStyleAttributes(*this), 280ddde725dSArmin Le Grand maXLink(), 281ddde725dSArmin Le Grand maStartOffset(), 282ddde725dSArmin Le Grand mbMethod(true), 283ddde725dSArmin Le Grand mbSpacing(false) 284ddde725dSArmin Le Grand { 285ddde725dSArmin Le Grand } 286ddde725dSArmin Le Grand 287ddde725dSArmin Le Grand SvgTextPathNode::~SvgTextPathNode() 288ddde725dSArmin Le Grand { 289ddde725dSArmin Le Grand } 290ddde725dSArmin Le Grand 291ddde725dSArmin Le Grand const SvgStyleAttributes* SvgTextPathNode::getSvgStyleAttributes() const 292ddde725dSArmin Le Grand { 293ddde725dSArmin Le Grand return &maSvgStyleAttributes; 294ddde725dSArmin Le Grand } 295ddde725dSArmin Le Grand 296ddde725dSArmin Le Grand void SvgTextPathNode::parseAttribute(const rtl::OUString& rTokenName, SVGToken aSVGToken, const rtl::OUString& aContent) 297ddde725dSArmin Le Grand { 298ddde725dSArmin Le Grand // call parent 299ddde725dSArmin Le Grand SvgNode::parseAttribute(rTokenName, aSVGToken, aContent); 300ddde725dSArmin Le Grand 301ddde725dSArmin Le Grand // read style attributes 302ddde725dSArmin Le Grand maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent); 303ddde725dSArmin Le Grand 304ddde725dSArmin Le Grand // parse own 305ddde725dSArmin Le Grand switch(aSVGToken) 306ddde725dSArmin Le Grand { 307ddde725dSArmin Le Grand case SVGTokenStyle: 308ddde725dSArmin Le Grand { 309ddde725dSArmin Le Grand maSvgStyleAttributes.readStyle(aContent); 310ddde725dSArmin Le Grand break; 311ddde725dSArmin Le Grand } 312ddde725dSArmin Le Grand case SVGTokenStartOffset: 313ddde725dSArmin Le Grand { 314ddde725dSArmin Le Grand SvgNumber aNum; 315ddde725dSArmin Le Grand 316ddde725dSArmin Le Grand if(readSingleNumber(aContent, aNum)) 317ddde725dSArmin Le Grand { 318ddde725dSArmin Le Grand if(aNum.isPositive()) 319ddde725dSArmin Le Grand { 320ddde725dSArmin Le Grand setStartOffset(aNum); 321ddde725dSArmin Le Grand } 322ddde725dSArmin Le Grand } 323ddde725dSArmin Le Grand break; 324ddde725dSArmin Le Grand } 325ddde725dSArmin Le Grand case SVGTokenMethod: 326ddde725dSArmin Le Grand { 327ddde725dSArmin Le Grand if(aContent.getLength()) 328ddde725dSArmin Le Grand { 329ddde725dSArmin Le Grand static rtl::OUString aStrAlign(rtl::OUString::createFromAscii("align")); 330ddde725dSArmin Le Grand static rtl::OUString aStrStretch(rtl::OUString::createFromAscii("stretch")); 331ddde725dSArmin Le Grand 332ddde725dSArmin Le Grand if(aContent.match(aStrAlign)) 333ddde725dSArmin Le Grand { 334ddde725dSArmin Le Grand setMethod(true); 335ddde725dSArmin Le Grand } 336ddde725dSArmin Le Grand else if(aContent.match(aStrStretch)) 337ddde725dSArmin Le Grand { 338ddde725dSArmin Le Grand setMethod(false); 339ddde725dSArmin Le Grand } 340ddde725dSArmin Le Grand } 341ddde725dSArmin Le Grand break; 342ddde725dSArmin Le Grand } 343ddde725dSArmin Le Grand case SVGTokenSpacing: 344ddde725dSArmin Le Grand { 345ddde725dSArmin Le Grand if(aContent.getLength()) 346ddde725dSArmin Le Grand { 347ddde725dSArmin Le Grand static rtl::OUString aStrAuto(rtl::OUString::createFromAscii("auto")); 348ddde725dSArmin Le Grand static rtl::OUString aStrExact(rtl::OUString::createFromAscii("exact")); 349ddde725dSArmin Le Grand 350ddde725dSArmin Le Grand if(aContent.match(aStrAuto)) 351ddde725dSArmin Le Grand { 352ddde725dSArmin Le Grand setSpacing(true); 353ddde725dSArmin Le Grand } 354ddde725dSArmin Le Grand else if(aContent.match(aStrExact)) 355ddde725dSArmin Le Grand { 356ddde725dSArmin Le Grand setSpacing(false); 357ddde725dSArmin Le Grand } 358ddde725dSArmin Le Grand } 359ddde725dSArmin Le Grand break; 360ddde725dSArmin Le Grand } 361ddde725dSArmin Le Grand case SVGTokenXlinkHref: 362ddde725dSArmin Le Grand { 363ddde725dSArmin Le Grand const sal_Int32 nLen(aContent.getLength()); 364ddde725dSArmin Le Grand 365ddde725dSArmin Le Grand if(nLen && sal_Unicode('#') == aContent[0]) 366ddde725dSArmin Le Grand { 367ddde725dSArmin Le Grand maXLink = aContent.copy(1); 368ddde725dSArmin Le Grand } 369ddde725dSArmin Le Grand break; 370ddde725dSArmin Le Grand } 371*e2bf1e9dSArmin Le Grand default: 372*e2bf1e9dSArmin Le Grand { 373*e2bf1e9dSArmin Le Grand break; 374*e2bf1e9dSArmin Le Grand } 375ddde725dSArmin Le Grand } 376ddde725dSArmin Le Grand } 377ddde725dSArmin Le Grand 378ddde725dSArmin Le Grand bool SvgTextPathNode::isValid() const 379ddde725dSArmin Le Grand { 380ddde725dSArmin Le Grand const SvgPathNode* pSvgPathNode = dynamic_cast< const SvgPathNode* >(getDocument().findSvgNodeById(maXLink)); 381ddde725dSArmin Le Grand 382ddde725dSArmin Le Grand if(!pSvgPathNode) 383ddde725dSArmin Le Grand { 384ddde725dSArmin Le Grand return false; 385ddde725dSArmin Le Grand } 386ddde725dSArmin Le Grand 387ddde725dSArmin Le Grand const basegfx::B2DPolyPolygon* pPolyPolyPath = pSvgPathNode->getPath(); 388ddde725dSArmin Le Grand 389ddde725dSArmin Le Grand if(!pPolyPolyPath || !pPolyPolyPath->count()) 390ddde725dSArmin Le Grand { 391ddde725dSArmin Le Grand return false; 392ddde725dSArmin Le Grand } 393ddde725dSArmin Le Grand 394ddde725dSArmin Le Grand const basegfx::B2DPolygon aPolygon(pPolyPolyPath->getB2DPolygon(0)); 395ddde725dSArmin Le Grand 396ddde725dSArmin Le Grand if(!aPolygon.count()) 397ddde725dSArmin Le Grand { 398ddde725dSArmin Le Grand return false; 399ddde725dSArmin Le Grand } 400ddde725dSArmin Le Grand 401ddde725dSArmin Le Grand const double fBasegfxPathLength(basegfx::tools::getLength(aPolygon)); 402ddde725dSArmin Le Grand 403ddde725dSArmin Le Grand if(basegfx::fTools::equalZero(fBasegfxPathLength)) 404ddde725dSArmin Le Grand { 405ddde725dSArmin Le Grand return false; 406ddde725dSArmin Le Grand } 407ddde725dSArmin Le Grand 408ddde725dSArmin Le Grand return true; 409ddde725dSArmin Le Grand } 410ddde725dSArmin Le Grand 411ddde725dSArmin Le Grand void SvgTextPathNode::decomposePathNode( 412ddde725dSArmin Le Grand const drawinglayer::primitive2d::Primitive2DSequence& rPathContent, 413ddde725dSArmin Le Grand drawinglayer::primitive2d::Primitive2DSequence& rTarget, 414ddde725dSArmin Le Grand const basegfx::B2DPoint& rTextStart) const 415ddde725dSArmin Le Grand { 416ddde725dSArmin Le Grand if(rPathContent.hasElements()) 417ddde725dSArmin Le Grand { 418ddde725dSArmin Le Grand const SvgPathNode* pSvgPathNode = dynamic_cast< const SvgPathNode* >(getDocument().findSvgNodeById(maXLink)); 419ddde725dSArmin Le Grand 420ddde725dSArmin Le Grand if(pSvgPathNode) 421ddde725dSArmin Le Grand { 422ddde725dSArmin Le Grand const basegfx::B2DPolyPolygon* pPolyPolyPath = pSvgPathNode->getPath(); 423ddde725dSArmin Le Grand 424ddde725dSArmin Le Grand if(pPolyPolyPath && pPolyPolyPath->count()) 425ddde725dSArmin Le Grand { 426ddde725dSArmin Le Grand basegfx::B2DPolygon aPolygon(pPolyPolyPath->getB2DPolygon(0)); 427ddde725dSArmin Le Grand 428ddde725dSArmin Le Grand if(pSvgPathNode->getTransform()) 429ddde725dSArmin Le Grand { 430ddde725dSArmin Le Grand aPolygon.transform(*pSvgPathNode->getTransform()); 431ddde725dSArmin Le Grand } 432ddde725dSArmin Le Grand 433ddde725dSArmin Le Grand const double fBasegfxPathLength(basegfx::tools::getLength(aPolygon)); 434ddde725dSArmin Le Grand 435ddde725dSArmin Le Grand if(!basegfx::fTools::equalZero(fBasegfxPathLength)) 436ddde725dSArmin Le Grand { 437ddde725dSArmin Le Grand double fUserToBasegfx(1.0); // multiply: user->basegfx, divide: basegfx->user 438ddde725dSArmin Le Grand 439ddde725dSArmin Le Grand if(pSvgPathNode->getPathLength().isSet()) 440ddde725dSArmin Le Grand { 441ddde725dSArmin Le Grand const double fUserLength(pSvgPathNode->getPathLength().solve(*this, length)); 442ddde725dSArmin Le Grand 443ddde725dSArmin Le Grand if(fUserLength > 0.0 && !basegfx::fTools::equal(fUserLength, fBasegfxPathLength)) 444ddde725dSArmin Le Grand { 445ddde725dSArmin Le Grand fUserToBasegfx = fUserLength / fBasegfxPathLength; 446ddde725dSArmin Le Grand } 447ddde725dSArmin Le Grand } 448ddde725dSArmin Le Grand 449ddde725dSArmin Le Grand double fPosition(0.0); 450ddde725dSArmin Le Grand 451ddde725dSArmin Le Grand if(getStartOffset().isSet()) 452ddde725dSArmin Le Grand { 453ddde725dSArmin Le Grand if(Unit_percent == getStartOffset().getUnit()) 454ddde725dSArmin Le Grand { 455ddde725dSArmin Le Grand // percent are relative to path length 456ddde725dSArmin Le Grand fPosition = getStartOffset().getNumber() * 0.01 * fBasegfxPathLength; 457ddde725dSArmin Le Grand } 458ddde725dSArmin Le Grand else 459ddde725dSArmin Le Grand { 460ddde725dSArmin Le Grand fPosition = getStartOffset().solve(*this, length) * fUserToBasegfx; 461ddde725dSArmin Le Grand } 462ddde725dSArmin Le Grand } 463ddde725dSArmin Le Grand 464ddde725dSArmin Le Grand if(fPosition >= 0.0) 465ddde725dSArmin Le Grand { 466ddde725dSArmin Le Grand const sal_Int32 nLength(rPathContent.getLength()); 467ddde725dSArmin Le Grand sal_Int32 nCurrent(0); 468ddde725dSArmin Le Grand 469ddde725dSArmin Le Grand while(fPosition < fBasegfxPathLength && nCurrent < nLength) 470ddde725dSArmin Le Grand { 471ddde725dSArmin Le Grand const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pCandidate = 0; 472ddde725dSArmin Le Grand const drawinglayer::primitive2d::Primitive2DReference xReference(rPathContent[nCurrent]); 473ddde725dSArmin Le Grand 474ddde725dSArmin Le Grand if(xReference.is()) 475ddde725dSArmin Le Grand { 476ddde725dSArmin Le Grand pCandidate = dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(xReference.get()); 477ddde725dSArmin Le Grand } 478ddde725dSArmin Le Grand 479ddde725dSArmin Le Grand if(pCandidate) 480ddde725dSArmin Le Grand { 481ddde725dSArmin Le Grand pathTextBreakupHelper aPathTextBreakupHelper( 482ddde725dSArmin Le Grand xReference, 483ddde725dSArmin Le Grand aPolygon, 484ddde725dSArmin Le Grand fBasegfxPathLength, 485ddde725dSArmin Le Grand fUserToBasegfx, 486ddde725dSArmin Le Grand fPosition, 487ddde725dSArmin Le Grand rTextStart); 488ddde725dSArmin Le Grand 489ddde725dSArmin Le Grand const drawinglayer::primitive2d::Primitive2DSequence aResult( 490ddde725dSArmin Le Grand aPathTextBreakupHelper.getResult(drawinglayer::primitive2d::BreakupUnit_character)); 491ddde725dSArmin Le Grand 492ddde725dSArmin Le Grand if(aResult.hasElements()) 493ddde725dSArmin Le Grand { 494ddde725dSArmin Le Grand drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aResult); 495ddde725dSArmin Le Grand } 496ddde725dSArmin Le Grand 497ddde725dSArmin Le Grand // advance position to consumed 498ddde725dSArmin Le Grand fPosition = aPathTextBreakupHelper.getPosition(); 499ddde725dSArmin Le Grand } 500ddde725dSArmin Le Grand 501ddde725dSArmin Le Grand nCurrent++; 502ddde725dSArmin Le Grand } 503ddde725dSArmin Le Grand } 504ddde725dSArmin Le Grand } 505ddde725dSArmin Le Grand } 506ddde725dSArmin Le Grand } 507ddde725dSArmin Le Grand } 508ddde725dSArmin Le Grand } 509ddde725dSArmin Le Grand 510ddde725dSArmin Le Grand } // end of namespace svgreader 511ddde725dSArmin Le Grand } // end of namespace svgio 512ddde725dSArmin Le Grand 513ddde725dSArmin Le Grand ////////////////////////////////////////////////////////////////////////////// 514ddde725dSArmin Le Grand // eof 515