Version 4.0.0.1, tag libreoffice-4.0.0.1
[LibreOffice.git] / svgio / source / svgreader / svgcharacternode.cxx
blob552db36ea939685cfcee1fd9b84132f2052d7724
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svgio/svgreader/svgcharacternode.hxx>
21 #include <svgio/svgreader/svgstyleattributes.hxx>
22 #include <drawinglayer/attribute/fontattribute.hxx>
23 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
24 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
25 #include <drawinglayer/primitive2d/textbreakuphelper.hxx>
26 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
29 //////////////////////////////////////////////////////////////////////////////
31 namespace svgio
33 namespace svgreader
35 SvgTextPositions::SvgTextPositions()
36 : maX(),
37 maY(),
38 maDx(),
39 maDy(),
40 maRotate(),
41 maTextLength(),
42 mbLengthAdjust(true)
46 void SvgTextPositions::parseTextPositionAttributes(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent)
48 // parse own
49 switch(aSVGToken)
51 case SVGTokenX:
53 if(aContent.getLength())
55 SvgNumberVector aVector;
57 if(readSvgNumberVector(aContent, aVector))
59 setX(aVector);
62 break;
64 case SVGTokenY:
66 if(aContent.getLength())
68 SvgNumberVector aVector;
70 if(readSvgNumberVector(aContent, aVector))
72 setY(aVector);
75 break;
77 case SVGTokenDx:
79 if(aContent.getLength())
81 SvgNumberVector aVector;
83 if(readSvgNumberVector(aContent, aVector))
85 setDx(aVector);
88 break;
90 case SVGTokenDy:
92 if(aContent.getLength())
94 SvgNumberVector aVector;
96 if(readSvgNumberVector(aContent, aVector))
98 setDy(aVector);
101 break;
103 case SVGTokenRotate:
105 if(aContent.getLength())
107 SvgNumberVector aVector;
109 if(readSvgNumberVector(aContent, aVector))
111 setRotate(aVector);
114 break;
116 case SVGTokenTextLength:
118 SvgNumber aNum;
120 if(readSingleNumber(aContent, aNum))
122 if(aNum.isPositive())
124 setTextLength(aNum);
127 break;
129 case SVGTokenLengthAdjust:
131 if(aContent.getLength())
133 static rtl::OUString aStrSpacing(rtl::OUString::createFromAscii("spacing"));
134 static rtl::OUString aStrSpacingAndGlyphs(rtl::OUString::createFromAscii("spacingAndGlyphs"));
136 if(aContent.match(aStrSpacing))
138 setLengthAdjust(true);
140 else if(aContent.match(aStrSpacingAndGlyphs))
142 setLengthAdjust(false);
145 break;
147 default:
149 break;
154 } // end of namespace svgreader
155 } // end of namespace svgio
157 //////////////////////////////////////////////////////////////////////////////
159 namespace svgio
161 namespace svgreader
163 class localTextBreakupHelper : public drawinglayer::primitive2d::TextBreakupHelper
165 private:
166 SvgTextPosition& mrSvgTextPosition;
168 protected:
169 /// allow user callback to allow changes to the new TextTransformation. Default
170 /// does nothing.
171 virtual bool allowChange(sal_uInt32 nCount, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength);
173 public:
174 localTextBreakupHelper(
175 const drawinglayer::primitive2d::TextSimplePortionPrimitive2D& rSource,
176 SvgTextPosition& rSvgTextPosition)
177 : drawinglayer::primitive2d::TextBreakupHelper(rSource),
178 mrSvgTextPosition(rSvgTextPosition)
183 bool localTextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 /*nIndex*/, sal_uInt32 /*nLength*/)
185 const double fRotation(mrSvgTextPosition.consumeRotation());
187 if(0.0 != fRotation)
189 const basegfx::B2DPoint aBasePoint(rNewTransform * basegfx::B2DPoint(0.0, 0.0));
191 rNewTransform.translate(-aBasePoint.getX(), -aBasePoint.getY());
192 rNewTransform.rotate(fRotation);
193 rNewTransform.translate(aBasePoint.getX(), aBasePoint.getY());
196 return true;
199 } // end of namespace svgreader
200 } // end of namespace svgio
202 //////////////////////////////////////////////////////////////////////////////
204 namespace svgio
206 namespace svgreader
208 SvgCharacterNode::SvgCharacterNode(
209 SvgDocument& rDocument,
210 SvgNode* pParent,
211 const rtl::OUString& rText)
212 : SvgNode(SVGTokenCharacter, rDocument, pParent),
213 maText(rText)
217 SvgCharacterNode::~SvgCharacterNode()
221 const SvgStyleAttributes* SvgCharacterNode::getSvgStyleAttributes() const
223 // no own style, use parent's
224 if(getParent())
226 return getParent()->getSvgStyleAttributes();
228 else
230 return 0;
234 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* SvgCharacterNode::createSimpleTextPrimitive(
235 SvgTextPosition& rSvgTextPosition,
236 const SvgStyleAttributes& rSvgStyleAttributes) const
238 // prepare retval, index and length
239 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pRetval = 0;
240 sal_uInt32 nIndex(0);
241 sal_uInt32 nLength(getText().getLength());
243 if(nLength)
245 // prepare FontAttribute
246 const rtl::OUString aFontFamily = rSvgStyleAttributes.getFontFamily().empty() ?
247 rtl::OUString(rtl::OUString::createFromAscii("Times New Roman")) :
248 rSvgStyleAttributes.getFontFamily()[0];
249 const ::FontWeight nFontWeight(getVclFontWeight(rSvgStyleAttributes.getFontWeight()));
250 bool bSymbol(false);
251 bool bVertical(false);
252 bool bItalic(FontStyle_italic == rSvgStyleAttributes.getFontStyle() || FontStyle_oblique == rSvgStyleAttributes.getFontStyle());
253 bool bMonospaced(false);
254 bool bOutline(false);
255 bool bRTL(false);
256 bool bBiDiStrong(false);
258 const drawinglayer::attribute::FontAttribute aFontAttribute(
259 aFontFamily,
260 rtl::OUString(),
261 nFontWeight,
262 bSymbol,
263 bVertical,
264 bItalic,
265 bMonospaced,
266 bOutline,
267 bRTL,
268 bBiDiStrong);
270 // prepare FontSize
271 double fFontWidth(rSvgStyleAttributes.getFontSize().solve(*this, length));
272 double fFontHeight(fFontWidth);
274 // prepare locale
275 ::com::sun::star::lang::Locale aLocale;
277 // prepare TextLayouterDevice
278 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
279 aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale);
281 // prepare TextArray
282 ::std::vector< double > aTextArray(rSvgTextPosition.getX());
284 if(!aTextArray.empty() && aTextArray.size() < nLength)
286 const sal_uInt32 nArray(aTextArray.size());
288 if(nArray < nLength)
290 double fStartX(0.0);
292 if(rSvgTextPosition.getParent() && rSvgTextPosition.getParent()->getAbsoluteX())
294 fStartX = rSvgTextPosition.getParent()->getPosition().getX();
296 else
298 fStartX = aTextArray[nArray - 1];
301 ::std::vector< double > aExtendArray(aTextLayouterDevice.getTextArray(getText(), nArray, nLength - nArray));
302 aTextArray.reserve(nLength);
304 for(sal_uInt32 a(0); a < aExtendArray.size(); a++)
306 aTextArray.push_back(aExtendArray[a] + fStartX);
311 // get current TextPosition and TextWidth in units
312 basegfx::B2DPoint aPosition(rSvgTextPosition.getPosition());
313 double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength));
315 // check for user-given TextLength
316 if(0.0 != rSvgTextPosition.getTextLength()
317 && !basegfx::fTools::equal(fTextWidth, rSvgTextPosition.getTextLength()))
319 const double fFactor(rSvgTextPosition.getTextLength() / fTextWidth);
321 if(rSvgTextPosition.getLengthAdjust())
323 // spacing, need to create and expand TextArray
324 if(aTextArray.empty())
326 aTextArray = aTextLayouterDevice.getTextArray(getText(), nIndex, nLength);
329 for(sal_uInt32 a(0); a < aTextArray.size(); a++)
331 aTextArray[a] *= fFactor;
334 else
336 // spacing and glyphs, just apply to FontWidth
337 fFontWidth *= fFactor;
340 fTextWidth = rSvgTextPosition.getTextLength();
343 // get TextAlign
344 TextAlign aTextAlign(rSvgStyleAttributes.getTextAlign());
346 // map TextAnchor to TextAlign, there seems not to be a difference
347 if(TextAnchor_notset != rSvgStyleAttributes.getTextAnchor())
349 switch(rSvgStyleAttributes.getTextAnchor())
351 case TextAnchor_start:
353 aTextAlign = TextAlign_left;
354 break;
356 case TextAnchor_middle:
358 aTextAlign = TextAlign_center;
359 break;
361 case TextAnchor_end:
363 aTextAlign = TextAlign_right;
364 break;
366 default:
368 break;
373 // apply TextAlign
374 switch(aTextAlign)
376 case TextAlign_right:
378 aPosition.setX(aPosition.getX() - fTextWidth);
379 break;
381 case TextAlign_center:
383 aPosition.setX(aPosition.getX() - (fTextWidth * 0.5));
384 break;
386 case TextAlign_notset:
387 case TextAlign_left:
388 case TextAlign_justify:
390 // TextAlign_notset, TextAlign_left: nothing to do
391 // TextAlign_justify is not clear currently; handle as TextAlign_left
392 break;
396 // get fill color
397 const basegfx::BColor aFill(rSvgStyleAttributes.getFill()
398 ? *rSvgStyleAttributes.getFill()
399 : basegfx::BColor(0.0, 0.0, 0.0));
401 // prepare TextTransformation
402 basegfx::B2DHomMatrix aTextTransform;
404 aTextTransform.scale(fFontWidth, fFontHeight);
405 aTextTransform.translate(aPosition.getX(), aPosition.getY());
407 // check TextDecoration and if TextDecoratedPortionPrimitive2D is needed
408 const TextDecoration aDeco(rSvgStyleAttributes.getTextDecoration());
410 if(TextDecoration_underline == aDeco
411 || TextDecoration_overline == aDeco
412 || TextDecoration_line_through == aDeco)
414 // get the fill for decroation as described by SVG. We cannot
415 // have different stroke colors/definitions for those, though
416 const SvgStyleAttributes* pDecoDef = rSvgStyleAttributes.getTextDecorationDefiningSvgStyleAttributes();
417 const basegfx::BColor aDecoColor(pDecoDef && pDecoDef->getFill() ? *pDecoDef->getFill() : aFill);
419 // create decorated text primitive
420 pRetval = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
421 aTextTransform,
422 getText(),
423 nIndex,
424 nLength,
425 aTextArray,
426 aFontAttribute,
427 aLocale,
428 aFill,
430 // extra props for decorated
431 aDecoColor,
432 aDecoColor,
433 TextDecoration_overline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
434 TextDecoration_underline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
435 false,
436 TextDecoration_line_through == aDeco ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE,
437 false,
438 drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE,
439 true,
440 false,
441 drawinglayer::primitive2d::TEXT_RELIEF_NONE,
442 false);
444 else
446 // create text primitive
447 pRetval = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
448 aTextTransform,
449 getText(),
450 nIndex,
451 nLength,
452 aTextArray,
453 aFontAttribute,
454 aLocale,
455 aFill);
458 // advance current TextPosition
459 rSvgTextPosition.setPosition(rSvgTextPosition.getPosition() + basegfx::B2DVector(fTextWidth, 0.0));
462 return pRetval;
465 void SvgCharacterNode::decomposeTextWithStyle(
466 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
467 SvgTextPosition& rSvgTextPosition,
468 const SvgStyleAttributes& rSvgStyleAttributes) const
470 const drawinglayer::primitive2d::Primitive2DReference xRef(
471 createSimpleTextPrimitive(
472 rSvgTextPosition,
473 rSvgStyleAttributes));
475 if(xRef.is())
477 if(!rSvgTextPosition.isRotated())
479 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef);
481 else
483 // need to apply rotations to each character as given
484 const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pCandidate =
485 dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(xRef.get());
487 if(pCandidate)
489 const localTextBreakupHelper alocalTextBreakupHelper(*pCandidate, rSvgTextPosition);
490 const drawinglayer::primitive2d::Primitive2DSequence aResult(
491 alocalTextBreakupHelper.getResult(drawinglayer::primitive2d::BreakupUnit_character));
493 if(aResult.hasElements())
495 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aResult);
498 // also consume for the implied single space
499 rSvgTextPosition.consumeRotation();
501 else
503 OSL_ENSURE(false, "Used primitive is not a text primitive (!)");
509 void SvgCharacterNode::whiteSpaceHandling()
511 if(XmlSpace_default == getXmlSpace())
513 maText = whiteSpaceHandlingDefault(maText);
515 else
517 maText = whiteSpaceHandlingPreserve(maText);
521 void SvgCharacterNode::addGap()
523 maText += rtl::OUString(sal_Unicode(' '));
526 void SvgCharacterNode::concatenate(const rtl::OUString& rText)
528 maText += rText;
531 void SvgCharacterNode::decomposeText(drawinglayer::primitive2d::Primitive2DSequence& rTarget, SvgTextPosition& rSvgTextPosition) const
533 if(getText().getLength())
535 const SvgStyleAttributes* pSvgStyleAttributes = getSvgStyleAttributes();
537 if(pSvgStyleAttributes)
539 decomposeTextWithStyle(rTarget, rSvgTextPosition, *pSvgStyleAttributes);
544 } // end of namespace svgreader
545 } // end of namespace svgio
547 //////////////////////////////////////////////////////////////////////////////
549 namespace svgio
551 namespace svgreader
553 SvgTextPosition::SvgTextPosition(
554 SvgTextPosition* pParent,
555 const InfoProvider& rInfoProvider,
556 const SvgTextPositions& rSvgTextPositions)
557 : mpParent(pParent),
558 maX(), // computed below
559 maY(), // computed below
560 maRotate(solveSvgNumberVector(rSvgTextPositions.getRotate(), rInfoProvider, length)),
561 mfTextLength(0.0),
562 maPosition(), // computed below
563 mnRotationIndex(0),
564 mbLengthAdjust(rSvgTextPositions.getLengthAdjust()),
565 mbAbsoluteX(false),
566 mbAbsoluteY(false)
568 // get TextLength if provided
569 if(rSvgTextPositions.getTextLength().isSet())
571 mfTextLength = rSvgTextPositions.getTextLength().solve(rInfoProvider, length);
574 // SVG does not really define in which units a ‘rotate’ for Text/TSpan is given,
575 // but it seems to be degrees. Convert here to radians
576 if(!maRotate.empty())
578 const double fFactor(F_PI / 180.0);
580 for(sal_uInt32 a(0); a < maRotate.size(); a++)
582 maRotate[a] *= fFactor;
586 // get text positions X
587 const sal_uInt32 nSizeX(rSvgTextPositions.getX().size());
589 if(nSizeX)
591 // we have absolute positions, get first one as current text position X
592 maPosition.setX(rSvgTextPositions.getX()[0].solve(rInfoProvider, xcoordinate));
593 mbAbsoluteX = true;
595 if(nSizeX > 1)
597 // fill deltas to maX
598 maX.reserve(nSizeX);
600 for(sal_uInt32 a(1); a < nSizeX; a++)
602 maX.push_back(rSvgTextPositions.getX()[a].solve(rInfoProvider, xcoordinate) - maPosition.getX());
606 else
608 // no absolute position, get from parent
609 if(pParent)
611 maPosition.setX(pParent->getPosition().getX());
614 const sal_uInt32 nSizeDx(rSvgTextPositions.getDx().size());
616 if(nSizeDx)
618 // relative positions given, translate position derived from parent
619 maPosition.setX(maPosition.getX() + rSvgTextPositions.getDx()[0].solve(rInfoProvider, xcoordinate));
621 if(nSizeDx > 1)
623 // fill deltas to maX
624 maX.reserve(nSizeDx);
626 for(sal_uInt32 a(1); a < nSizeDx; a++)
628 maX.push_back(rSvgTextPositions.getDx()[a].solve(rInfoProvider, xcoordinate));
634 // get text positions Y
635 const sal_uInt32 nSizeY(rSvgTextPositions.getY().size());
637 if(nSizeY)
639 // we have absolute positions, get first one as current text position Y
640 maPosition.setY(rSvgTextPositions.getY()[0].solve(rInfoProvider, ycoordinate));
641 mbAbsoluteX = true;
643 if(nSizeY > 1)
645 // fill deltas to maY
646 maY.reserve(nSizeY);
648 for(sal_uInt32 a(1); a < nSizeY; a++)
650 maY.push_back(rSvgTextPositions.getY()[a].solve(rInfoProvider, ycoordinate) - maPosition.getY());
654 else
656 // no absolute position, get from parent
657 if(pParent)
659 maPosition.setY(pParent->getPosition().getY());
662 const sal_uInt32 nSizeDy(rSvgTextPositions.getDy().size());
664 if(nSizeDy)
666 // relative positions given, translate position derived from parent
667 maPosition.setY(maPosition.getY() + rSvgTextPositions.getDy()[0].solve(rInfoProvider, ycoordinate));
669 if(nSizeDy > 1)
671 // fill deltas to maY
672 maY.reserve(nSizeDy);
674 for(sal_uInt32 a(1); a < nSizeDy; a++)
676 maY.push_back(rSvgTextPositions.getDy()[a].solve(rInfoProvider, ycoordinate));
683 bool SvgTextPosition::isRotated() const
685 if(maRotate.empty())
687 if(getParent())
689 return getParent()->isRotated();
691 else
693 return false;
696 else
698 return true;
702 double SvgTextPosition::consumeRotation()
704 double fRetval(0.0);
706 if(maRotate.empty())
708 if(getParent())
710 fRetval = mpParent->consumeRotation();
712 else
714 fRetval = 0.0;
717 else
719 const sal_uInt32 nSize(maRotate.size());
721 if(mnRotationIndex < nSize)
723 fRetval = maRotate[mnRotationIndex++];
725 else
727 fRetval = maRotate[nSize - 1];
731 return fRetval;
734 } // end of namespace svgreader
735 } // end of namespace svgio
737 //////////////////////////////////////////////////////////////////////////////
738 // eof
740 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */