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