diff options
Diffstat (limited to 'drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx')
-rw-r--r-- | drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx | 853 |
1 files changed, 527 insertions, 326 deletions
diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx index 80c00afe22e8..84a1e75cb826 100644 --- a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx @@ -4,9 +4,9 @@ * * $RCSfile: textdecoratedprimitive2d.cxx,v $ * - * $Revision: 1.5 $ + * $Revision: 1.6 $ * - * last change: $Author: aw $ $Date: 2007-09-20 09:51:38 $ + * last change: $Author: aw $ $Date: 2007-09-26 11:36:36 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. @@ -53,6 +53,34 @@ #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #endif +#ifndef _BGFX_MATRIX_B2DHOMMATRIXTOOLS_HXX +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#endif + +#ifndef _COMPHELPER_PROCESSFACTORY_HXX_ +#include <comphelper/processfactory.hxx> +#endif + +#ifndef _COM_SUN_STAR_I18N_WORDTYPE_HPP_ +#include <com/sun/star/i18n/WordType.hpp> +#endif + +#ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE_TEXTEFFECTPRIMITIVE2D_HXX +#include <drawinglayer/primitive2d/texteffectprimitive2d.hxx> +#endif + +#ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE2D_SHADOWPRIMITIVE2D_HXX +#include <drawinglayer/primitive2d/shadowprimitive2d.hxx> +#endif + +#ifndef _COM_SUN_STAR_I18N_XBREAKITERATOR_HPP_ +#include <com/sun/star/i18n/XBreakIterator.hpp> +#endif + +#ifndef INCLUDED_DRAWINGLAYER_PRIMITIVE2D_TRANSFORMPRIMITIVE2D_HXX +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#endif + #include <numeric> ////////////////////////////////////////////////////////////////////////////// @@ -61,408 +89,581 @@ namespace drawinglayer { namespace primitive2d { -/* Primitive2DSequence TextDecoratedPortionPrimitive2D::createLocalDecomposition(const geometry::ViewInformation2D& rViewInformation) const + void TextDecoratedPortionPrimitive2D::impCreateGeometryContent( + std::vector< Primitive2DReference >& rTarget, + basegfx::DecomposedB2DHomMatrixContainer& rDecTrans, + const rtl::OUString& rText, + const ::std::vector< double >& rDXArray, + const FontAttributes& rFontAttributes) const { - const sal_uInt16 nTextLength(getText().Len()); - Primitive2DSequence aRetval; - - if(nTextLength) + // create the SimpleTextPrimitive needed in any case + rTarget.push_back(Primitive2DReference(new TextSimplePortionPrimitive2D( + rDecTrans.getB2DHomMatrix(), + rText, + rDXArray, + rFontAttributes, + getLocale(), + getFontColor()))); + + // see if something else needs to be done + const bool bUnderlineUsed(FONT_UNDERLINE_NONE != getFontUnderline()); + const bool bStrikeoutUsed(FONT_STRIKEOUT_NONE != getFontStrikeout()); + + if(bUnderlineUsed || bStrikeoutUsed) { - if(getWordLineMode()) + // common preparations + basegfx::B2DHomMatrix aUnscaledTransform; + TextLayouterDevice aTextLayouter; + + // unscaled is needed since scale contains already the font size + aUnscaledTransform.rotate(rDecTrans.getRotate()); + aUnscaledTransform.shearX(rDecTrans.getShearX()); + aUnscaledTransform.translate(rDecTrans.getTranslate().getX(), rDecTrans.getTranslate().getY()); + + // TextLayouterDevice is needed to get metrics for text decorations like + // underline/strikeout/emphasis marks from it. For setup, the unrotated transform + // is needed + basegfx::B2DHomMatrix aUnrotatedTransform(rDecTrans.getB2DHomMatrix()); + aUnrotatedTransform.rotate(-rDecTrans.getRotate()); + aTextLayouter.setFontAttributes(getFontAttributes(), aUnrotatedTransform ); + + // get text width + double fTextWidth(0.0); + + if(rDXArray.empty()) { - // support for single word mode - if(!mxBreakIterator.is()) - { - ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory()); - mxBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY); - } + fTextWidth = aTextLayouter.getTextWidth(rText, 0/*TODO*/, rText.getLength()/*TODO*/ ); + } + else + { + fTextWidth = rDXArray.back() * rDecTrans.getScale().getX(); + } - if(mxBreakIterator.is()) + if(bUnderlineUsed) + { + // create primitive geometry for underline + bool bDoubleLine(false); + bool bWaveLine(false); + bool bBoldLine(false); + const int* pDashDotArray(0); + basegfx::tools::B2DLineJoin eLineJoin(basegfx::tools::B2DLINEJOIN_NONE); + double fUnderlineOffset(aTextLayouter.getUnderlineOffset()); + double fUnderlineHeight(aTextLayouter.getUnderlineHeight()); + + static const int aDottedArray[] = { 1, 1, 0}; // DOTTED LINE + static const int aDashDotArray[] = { 1, 1, 4, 1, 0}; // DASHDOT + static const int aDashDotDotArray[] = { 1, 1, 1, 1, 4, 1, 0}; // DASHDOTDOT + static const int aDashedArray[] = { 5, 2, 0}; // DASHED LINE + static const int aLongDashArray[] = { 7, 2, 0}; // LONGDASH + + switch(getFontUnderline()) { - for(sal_Int32 nPos(0); nPos < nTextLength;) + default: // case FONT_UNDERLINE_SINGLE: + { + break; + } + case FONT_UNDERLINE_DOUBLE: + { + bDoubleLine = true; + break; + } + case FONT_UNDERLINE_DOTTED: + { + pDashDotArray = aDottedArray; + break; + } + case FONT_UNDERLINE_DASH: + { + pDashDotArray = aDashedArray; + break; + } + case FONT_UNDERLINE_LONGDASH: + { + pDashDotArray = aLongDashArray; + break; + } + case FONT_UNDERLINE_DASHDOT: + { + pDashDotArray = aDashDotArray; + break; + } + case FONT_UNDERLINE_DASHDOTDOT: + { + pDashDotArray = aDashDotDotArray; + break; + } + case FONT_UNDERLINE_SMALLWAVE: + { + bWaveLine = true; + break; + } + case FONT_UNDERLINE_WAVE: + { + bWaveLine = true; + break; + } + case FONT_UNDERLINE_DOUBLEWAVE: + { + bDoubleLine = true; + bWaveLine = true; + break; + } + case FONT_UNDERLINE_BOLD: + { + bBoldLine = true; + break; + } + case FONT_UNDERLINE_BOLDDOTTED: + { + bBoldLine = true; + pDashDotArray = aDottedArray; + break; + } + case FONT_UNDERLINE_BOLDDASH: + { + bBoldLine = true; + pDashDotArray = aDashedArray; + break; + } + case FONT_UNDERLINE_BOLDLONGDASH: + { + bBoldLine = true; + pDashDotArray = aLongDashArray; + break; + } + case FONT_UNDERLINE_BOLDDASHDOT: + { + bBoldLine = true; + pDashDotArray = aDashDotArray; + break; + } + case FONT_UNDERLINE_BOLDDASHDOTDOT: + { + bBoldLine = true; + pDashDotArray = aDashDotDotArray; + break; + } + case FONT_UNDERLINE_BOLDWAVE: { - ::com::sun::star::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary( - getText(), nPos, getLocale(), ::com::sun::star::i18n::WordType::ANY_WORD, sal_True)); - const String aNewText(getText(), nPos, nNextWordBoundary - nPos); - ::std::vector< double > aNewDXArray(getDXArray().begin() + nPos, getDXArray().end() + nNextWordBoundary); - - TextDecoratedPortionPrimitive2D aNewPrimitive( - getTextTransform(), - aNewText, - aNewDXArray, - getFontAttributes(), - getLocale(), - getFontColor(), - getTextlineColor(), - getFontUnderline(), - getUnderlineAbove(), - getFontStrikeout(), - false, // no WordLineMode - getFontEmphasisMark(), - getEmphasisMarkAbove(), - getEmphasisMarkBelow(), - RELIEF_NONE, // no relief - false); // no shadow - - appendPrimitive2DSequenceToPrimitive2DSequence(aRetval, aNewPrimitive.get2DDecomposition(rViewInformation)); - - nPos = nNextWordBoundary; + bWaveLine = true; + bBoldLine = true; + break; } } - } - else - { - // no single words needed, decompose - std::vector< BasePrimitive2D* > aNewPrimitives; - - - // prepare return sequence - for(sal_uInt32 a(0); a < aNewPrimitives.size(); a++) + if(bBoldLine) { - aRetval[a] = Primitive2DReference(aNewPrimitives[a]); + fUnderlineHeight *= 2.0; } - } - if(aRetval.hasElements()) - { - Primitive2DSequence aContent(aRetval); - - if(getShadow()) + if(bDoubleLine) { + fUnderlineOffset -= 0.50 * fUnderlineHeight; + fUnderlineHeight *= 0.64; } - if(RELIEF_NONE != getFontRelief()) + if(bWaveLine) { + eLineJoin = basegfx::tools::B2DLINEJOIN_ROUND; + fUnderlineHeight *= 0.5; } - } - } - return aRetval; - } */ + // prepare StrokeAttributes + attribute::StrokeAttribute aStrokeAttribute(getTextlineColor(), fUnderlineHeight, eLineJoin); - Primitive2DSequence TextDecoratedPortionPrimitive2D::createLocalDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const - { - std::vector< BasePrimitive2D* > aNewPrimitives; + if(pDashDotArray) + { + ::std::vector< double > aDoubleArray; - // First create a simple text primitive and ignore other attributes - aNewPrimitives.push_back(new TextSimplePortionPrimitive2D(getTextTransform(), getText(), getDXArray(), getFontAttributes(), getLocale(), getFontColor())); + for(const int* p = pDashDotArray; *p; ++p) + { + aDoubleArray.push_back((double)(*p) * fUnderlineHeight); + } - // more to be done? - const bool bNeedFontUnderline(getFontUnderline() != FONT_UNDERLINE_NONE); - const bool bNeedFontStrikeout(getFontStrikeout() != FONT_STRIKEOUT_NONE); - const bool bNeedEmphasisMarkAbove(getEmphasisMarkAbove() != FONT_EMPHASISMARK_NONE); - const bool bNeedEmphasisMarkBelow(getEmphasisMarkBelow() != FONT_EMPHASISMARK_NONE); + const double fFullDashDotLen(::std::accumulate(aDoubleArray.begin(), aDoubleArray.end(), 0.0)); - if(bNeedFontUnderline - || bNeedFontStrikeout - || bNeedEmphasisMarkAbove - || bNeedEmphasisMarkBelow) - { - // prepare transformations - basegfx::B2DVector aScale, aTranslate; - double fRotate, fShearX; - getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX); + aStrokeAttribute = attribute::StrokeAttribute( + aStrokeAttribute.getColor(), + aStrokeAttribute.getWidth(), + aStrokeAttribute.getLineJoin(), + aDoubleArray, + fFullDashDotLen); + } - // unscaled transform is needed since the scale in the text transform describes the font size - basegfx::B2DHomMatrix aUnscaledTransform; - aUnscaledTransform.rotate( fRotate ); - aUnscaledTransform.shearX( fShearX ); - aUnscaledTransform.translate( aTranslate.getX(), aTranslate.getY() ); + // create base polygon and new primitive + basegfx::B2DPolygon aUnderline; + Primitive2DReference aNewPrimitive; - // prepare TextLayouterDevice to get metrics for text decorations like - // underline/strikeout/emphasis marks from it - TextLayouterDevice aTextLayouter; + aUnderline.append(basegfx::B2DPoint(0.0, fUnderlineOffset)); + aUnderline.append(basegfx::B2DPoint(fTextWidth, fUnderlineOffset)); + aUnderline.transform(aUnscaledTransform); - { - // unrotated transform is needed for TextLayouterDevice setup - basegfx::B2DHomMatrix aUnrotatedTransform = getTextTransform(); - aUnrotatedTransform.rotate( -fRotate ); - aTextLayouter.setFontAttributes(getFontAttributes(), aUnrotatedTransform ); - } + if(bWaveLine) + { + double fWaveWidth(4.0 * fUnderlineHeight); - // init metrics to defaults -// const double fLineHeight(aTextLayouter.getTextHeight()); - double fUnderlineOffset(aTextLayouter.getUnderlineOffset()); - double fUnderlineHeight(aTextLayouter.getUnderlineHeight()); - basegfx::tools::B2DLineJoin eLineJoin(basegfx::tools::B2DLINEJOIN_NONE); - bool bDoubleLine(false); - bool bWaveLine(false); - double fTextWidth(0.0); + if(primitive2d::FONT_UNDERLINE_SMALLWAVE == getFontUnderline()) + { + fWaveWidth *= 0.7; + } + else if(primitive2d::FONT_UNDERLINE_WAVE == getFontUnderline()) + { + // extra multiply to get the same WaveWidth as with the bold version + fWaveWidth *= 2.0; + } - if(getDXArray().empty()) - { - fTextWidth = aTextLayouter.getTextWidth( getText(), 0/*TODO*/, getText().Len()/*TODO*/ ); - } - else - { - fTextWidth = getDXArray().back() * aScale.getX(); - } + aNewPrimitive = Primitive2DReference(new PolygonWavePrimitive2D(aUnderline, aStrokeAttribute, fWaveWidth, 0.5 * fWaveWidth)); + } + else + { + aNewPrimitive = Primitive2DReference(new PolygonStrokePrimitive2D(aUnderline, aStrokeAttribute)); + } - // prepare line styles for text decoration lines - const int* pDashDotArray(0); + // add primitive + rTarget.push_back(aNewPrimitive); - static const int aDottedArray[] = { 1, 1, 0}; // DOTTED LINE - static const int aDashDotArray[] = { 1, 1, 4, 1, 0}; // DASHDOT - static const int aDashDotDotArray[] = { 1, 1, 1, 1, 4, 1, 0}; // DASHDOTDOT - static const int aDashedArray[] = { 5, 2, 0}; // DASHED LINE - static const int aLongDashArray[] = { 7, 2, 0}; // LONGDASH + if(bDoubleLine) + { + // double line, create 2nd primitive with offset using TransformPrimitive based on + // already created NewPrimitive + const double fLineDist((bWaveLine ? 3.0 : 2.0) * fUnderlineHeight); + basegfx::B2DHomMatrix aTransform; - // set Underline attribute - switch( getFontUnderline() ) - { - default: - DBG_WARNING1( "DrawingLayer: Unknown underline attribute (%d)!", getFontUnderline() ); - // fall through - case primitive2d::FONT_UNDERLINE_NONE: - fUnderlineHeight = 0; - break; - case primitive2d::FONT_UNDERLINE_BOLD: - fUnderlineHeight *= 2; - // fall through - case primitive2d::FONT_UNDERLINE_SINGLE: - break; - case primitive2d::FONT_UNDERLINE_DOUBLE: - bDoubleLine = true; - break; - case primitive2d::FONT_UNDERLINE_BOLDDOTTED: - fUnderlineHeight *= 2; - // fall through - case primitive2d::FONT_UNDERLINE_DOTTED: - eLineJoin = basegfx::tools::B2DLINEJOIN_ROUND; - pDashDotArray = aDottedArray; - break; - case primitive2d::FONT_UNDERLINE_BOLDDASH: - fUnderlineHeight *= 2; - // fall through - case primitive2d::FONT_UNDERLINE_DASH: - pDashDotArray = aDashedArray; - break; - case primitive2d::FONT_UNDERLINE_BOLDLONGDASH: - fUnderlineHeight *= 2; - // fall through - case primitive2d::FONT_UNDERLINE_LONGDASH: - pDashDotArray = aLongDashArray; - break; - case primitive2d::FONT_UNDERLINE_BOLDDASHDOT: - fUnderlineHeight *= 2; - // fall through - case primitive2d::FONT_UNDERLINE_DASHDOT: - pDashDotArray = aDashDotArray; - break; - case primitive2d::FONT_UNDERLINE_BOLDDASHDOTDOT: - fUnderlineHeight *= 2; - // fall through - case primitive2d::FONT_UNDERLINE_DASHDOTDOT: - eLineJoin = basegfx::tools::B2DLINEJOIN_ROUND; - pDashDotArray = aDashDotDotArray; - break; - case primitive2d::FONT_UNDERLINE_SMALLWAVE: - // TODO - bWaveLine = true; - break; - case primitive2d::FONT_UNDERLINE_BOLDWAVE: - fUnderlineHeight *= 2; - // fall through - case primitive2d::FONT_UNDERLINE_WAVE: - // TODO - bWaveLine = true; - break; - case primitive2d::FONT_UNDERLINE_DOUBLEWAVE: - bWaveLine = true; - bDoubleLine = true; - break; + // move base point of text to 0.0 and de-rotate + aTransform.translate(-rDecTrans.getTranslate().getX(), -rDecTrans.getTranslate().getY()); + aTransform.rotate(-rDecTrans.getRotate()); + + // translate in Y by offset + aTransform.translate(0.0, fLineDist); + + // move back and rotate + aTransform.rotate(rDecTrans.getRotate()); + aTransform.translate(rDecTrans.getTranslate().getX(), rDecTrans.getTranslate().getY()); + + // add transform primitive + const Primitive2DSequence aContent(&aNewPrimitive, 1); + rTarget.push_back(Primitive2DReference(new TransformPrimitive2D(aTransform, aContent))); + } } - if(fUnderlineHeight > 0.0) + if(bStrikeoutUsed) { - if(bDoubleLine) + // create primitive geometry for strikeout + if(FONT_STRIKEOUT_SLASH == getFontStrikeout() || FONT_STRIKEOUT_X == getFontStrikeout()) { - fUnderlineOffset -= 0.50 * fUnderlineHeight; - fUnderlineHeight *= 0.64; - } - - basegfx::B2DPolygon aUnderline; - ::basegfx::B2DPoint aPoint( 0.0, fUnderlineOffset ); - aUnderline.append( aPoint ); + // strikeout with character + const sal_Unicode aStrikeoutChar(FONT_STRIKEOUT_SLASH == getFontStrikeout() ? '/' : 'X'); + const rtl::OUString aSingleCharString(aStrikeoutChar); + const double fStrikeCharWidth(aTextLayouter.getTextWidth(aSingleCharString, 0, 1)); + const double fStrikeCharCount(fabs(fTextWidth/fStrikeCharWidth)); + const sal_uInt32 nStrikeCharCount(static_cast< sal_uInt32 >(fStrikeCharCount + 0.9)); + const double fScaleX(rDecTrans.getScale().getX()); + const double fStrikeCharWidthUnscaled(basegfx::fTools::equalZero(fScaleX) ? fStrikeCharWidth : fStrikeCharWidth/fScaleX); + + std::vector<double> aDXArray(nStrikeCharCount); + rtl::OUString aStrikeoutString; + + for(sal_uInt32 a(0); a < nStrikeCharCount; a++) + { + aStrikeoutString += aSingleCharString; + aDXArray[a] = (a + 1) * fStrikeCharWidthUnscaled; + } - if(!bWaveLine) - { - // straight underline - aUnderline.append( aPoint + ::basegfx::B2DPoint( fTextWidth, 0.0 ) ); + rTarget.push_back(Primitive2DReference(new TextSimplePortionPrimitive2D( + rDecTrans.getB2DHomMatrix(), + aStrikeoutString, + aDXArray, + rFontAttributes, + getLocale(), + getFontColor()))); } else { - // wavy underline - basegfx::B2DPolygon& aWavePoly = aUnderline; - double fWaveWidth(4.0 * fUnderlineHeight); + // strikeout with geometry + double fStrikeoutHeight(aTextLayouter.getUnderlineHeight()); + double fStrikeoutOffset(aTextLayouter.getStrikeoutOffset()); + bool bDoubleLine(false); - if(primitive2d::FONT_UNDERLINE_SMALLWAVE == getFontUnderline()) + // set Underline attribute + switch(getFontStrikeout()) { - fWaveWidth *= 0.7; + default : // case primitive2d::FONT_STRIKEOUT_SINGLE: + { + break; + } + case primitive2d::FONT_STRIKEOUT_DOUBLE: + { + bDoubleLine = true; + break; + } + case primitive2d::FONT_STRIKEOUT_BOLD: + { + fStrikeoutHeight *= 2.0; + break; + } } - const double fWaveHeight(0.5 * fWaveWidth); - const ::basegfx::B2DPoint aCtrlOffset( fWaveWidth * 0.467308, fWaveHeight ); - - for(double fPos = fWaveWidth; fPos < fTextWidth; fPos += fWaveWidth) + if(bDoubleLine) { - // create a symmetrical wave using one cubic bezier curve - // with y==0 for {x==0, x==0.5*fW or x==1.0*fW} - // and ymin/ymax at {x=0.25*fW or 0.75*fW} - const int n = aWavePoly.count(); - - aWavePoly.setNextControlPoint( n-1, aPoint + aCtrlOffset ); - aWavePoly.append(aPoint += ::basegfx::B2DPoint( fWaveWidth, 0.0 ) ); - aWavePoly.setPrevControlPoint( n-1, aPoint - aCtrlOffset ); + fStrikeoutOffset -= 0.50 * fStrikeoutHeight; + fStrikeoutHeight *= 0.64; } - // adjust stroke style - eLineJoin = basegfx::tools::B2DLINEJOIN_ROUND; - fUnderlineHeight *= 0.5; - } + // create base polygon and new primitive + basegfx::B2DPolygon aStrikeoutLine; - const basegfx::BColor& rLineColor = getTextlineColor(); - attribute::StrokeAttribute aStrokeAttr(rLineColor, fUnderlineHeight, eLineJoin); + aStrikeoutLine.append(basegfx::B2DPoint(0.0, -fStrikeoutOffset)); + aStrikeoutLine.append(basegfx::B2DPoint(fTextWidth, -fStrikeoutOffset)); + aStrikeoutLine.transform(aUnscaledTransform); - if(pDashDotArray) - { - ::std::vector< double > aDoubleArray; + const attribute::StrokeAttribute aStrokeAttribute(getFontColor(), fStrikeoutHeight, basegfx::tools::B2DLINEJOIN_NONE); + Primitive2DReference aNewPrimitive(new PolygonStrokePrimitive2D(aStrikeoutLine, aStrokeAttribute)); - for( const int* p = pDashDotArray; *p; ++p ) + // add primitive + rTarget.push_back(aNewPrimitive); + + if(bDoubleLine) { - aDoubleArray.push_back( *p * fUnderlineHeight); - } + // double line, create 2nd primitive with offset using TransformPrimitive based on + // already created NewPrimitive + const double fLineDist(2.0 * fStrikeoutHeight); + basegfx::B2DHomMatrix aTransform; - const double fFullDashDotLen(::std::accumulate(aDoubleArray.begin(), aDoubleArray.end(), 0.0)); - aStrokeAttr = attribute::StrokeAttribute(rLineColor, fUnderlineHeight, eLineJoin, aDoubleArray, fFullDashDotLen); - } + // move base point of text to 0.0 and de-rotate + aTransform.translate(-rDecTrans.getTranslate().getX(), -rDecTrans.getTranslate().getY()); + aTransform.rotate(-rDecTrans.getRotate()); - aUnderline.transform( aUnscaledTransform ); - aNewPrimitives.push_back(new PolygonStrokePrimitive2D( aUnderline, aStrokeAttr )); + // translate in Y by offset + aTransform.translate(0.0, -fLineDist); - if( bDoubleLine ) - { - // add another underline below the first underline - const double fLineDist((bWaveLine ? 3.0 : 2.0) * fUnderlineHeight); - ::basegfx::B2DVector aOffsetVector( 0.0, fLineDist ); - basegfx::B2DHomMatrix aOffsetTransform; + // move back and rotate + aTransform.rotate(rDecTrans.getRotate()); + aTransform.translate(rDecTrans.getTranslate().getX(), rDecTrans.getTranslate().getY()); - aOffsetVector = aUnscaledTransform * aOffsetVector; - aOffsetTransform.translate( aOffsetVector.getX(), aOffsetVector.getY() ); - aUnderline.transform( aOffsetTransform ); - aNewPrimitives.push_back(new PolygonStrokePrimitive2D( aUnderline, aStrokeAttr )); + // add transform primitive + const Primitive2DSequence aContent(&aNewPrimitive, 1); + rTarget.push_back(Primitive2DReference(new TransformPrimitive2D(aTransform, aContent))); + } } } + } + + // TODO: Handle Font Emphasis Above/Below + } - double fStrikeoutHeight(aTextLayouter.getUnderlineHeight()); - double fStrikeoutOffset(aTextLayouter.getStrikeoutOffset()); - eLineJoin = basegfx::tools::B2DLINEJOIN_NONE; - bDoubleLine = false; - sal_Unicode aStrikeoutChar('\0'); + void TextDecoratedPortionPrimitive2D::impSplitSingleWords( + std::vector< Primitive2DReference >& rTarget, + basegfx::DecomposedB2DHomMatrixContainer& rDecTrans) const + { + // break iterator support + // made static so it only needs to be fetched once, even with many single + // constructed VclMetafileProcessor2D. It's still incarnated on demand, + // but exists for OOo runtime now by purpose. + static ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xLocalBreakIterator; - // set Underline attribute - switch( getFontStrikeout() ) - { - default: - DBG_WARNING1( "DrawingLayer: Unknown underline attribute (%d)!", getFontUnderline() ); - // fall through - case primitive2d::FONT_STRIKEOUT_NONE: - fStrikeoutHeight = 0; - break; - case primitive2d::FONT_STRIKEOUT_SINGLE: - break; - case primitive2d::FONT_STRIKEOUT_DOUBLE: - bDoubleLine = true; - break; - case primitive2d::FONT_STRIKEOUT_BOLD: - fStrikeoutHeight *= 2; - break; - case primitive2d::FONT_STRIKEOUT_SLASH: - aStrikeoutChar = '/'; - fStrikeoutHeight = 0; - break; - case primitive2d::FONT_STRIKEOUT_X: - aStrikeoutChar = 'X'; - fStrikeoutHeight = 0; - break; - } + if(!xLocalBreakIterator.is()) + { + ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory()); + xLocalBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY); + } - if(fStrikeoutHeight > 0.0) + if(xLocalBreakIterator.is()) + { + // init word iterator, get first word + ::com::sun::star::i18n::Boundary aNextWordBoundary(xLocalBreakIterator->getWordBoundary( + getText(), 0, getLocale(), ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True)); + + // prepare new font attributes WITHOUT outline + const FontAttributes aNewFontAttributes( + getFontAttributes().getFamilyName(), + getFontAttributes().getStyleName(), + getFontAttributes().getWeight(), + getFontAttributes().getSymbol(), + getFontAttributes().getVertical(), + getFontAttributes().getItalic(), + false); // no outline anymore, handled locally + + while(aNextWordBoundary.startPos != aNextWordBoundary.endPos) { - if( bDoubleLine ) - { - fStrikeoutOffset -= 0.50 * fStrikeoutHeight; - fStrikeoutHeight *= 0.64; - } + const sal_uInt32 nLength(aNextWordBoundary.endPos - aNextWordBoundary.startPos); + + // prepare data for the single word + const rtl::OUString aNewText(getText().copy(aNextWordBoundary.startPos, nLength)); - basegfx::B2DPolygon aStrikeoutLine; - basegfx::B2DPoint aPoint( 0.0, -fStrikeoutOffset ); - aStrikeoutLine.append( aPoint ); + // prepare transform for the single word + basegfx::B2DHomMatrix aNewTransform; - if( 1/*####*/ ) + if(aNextWordBoundary.startPos) { - // straight underline - aStrikeoutLine.append( aPoint + ::basegfx::B2DPoint( fTextWidth, 0.0 ) ); + // needs to be moved to a new start position (get from DXArray) + const double fDistance(getDXArray()[aNextWordBoundary.startPos - 1]); + aNewTransform.translate(fDistance, 0.0); } - const basegfx::BColor& rStrikeoutColor = getTextlineColor(); - attribute::StrokeAttribute aStrokeAttr( rStrikeoutColor, fStrikeoutHeight, eLineJoin ); + aNewTransform *= rDecTrans.getB2DHomMatrix(); - aStrikeoutLine.transform( aUnscaledTransform ); - aNewPrimitives.push_back(new PolygonStrokePrimitive2D( aStrikeoutLine, aStrokeAttr )); + // prepare new DXArray for the single word + ::std::vector< double > aNewDXArray( + getDXArray().begin() + aNextWordBoundary.startPos, + getDXArray().begin() + aNextWordBoundary.endPos); - if( bDoubleLine ) + if(aNextWordBoundary.startPos) { - // add another strikeout below the first strikeout - const double fLineDist(2.0 * fStrikeoutHeight); - ::basegfx::B2DVector aOffsetVector( 0.0, -fLineDist ); - basegfx::B2DHomMatrix aOffsetTransform; - - aOffsetVector = aUnscaledTransform * aOffsetVector; - aOffsetTransform.translate( aOffsetVector.getX(), aOffsetVector.getY() ); - aStrikeoutLine.transform( aOffsetTransform ); - aNewPrimitives.push_back(new PolygonStrokePrimitive2D( aStrikeoutLine, aStrokeAttr )); + // DXArray values need to be corrected + const double fDistance(getDXArray()[aNextWordBoundary.startPos - 1]); + + for(sal_uInt32 a(0); a < nLength; a++) + { + aNewDXArray[a] -= fDistance; + } } + + // create geometry content for the single word + basegfx::DecomposedB2DHomMatrixContainer aDecTrans(aNewTransform); + impCreateGeometryContent(rTarget, aDecTrans, aNewText, aNewDXArray, aNewFontAttributes); + + // prepare next word + aNextWordBoundary = xLocalBreakIterator->nextWord( + getText(), aNextWordBoundary.endPos, getLocale(), + ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES); } + } + } - if( aStrikeoutChar != '\0' ) - { - const String aSingleCharString( &aStrikeoutChar, 1 ); - const double fStrikeCharWidth(aTextLayouter.getTextWidth( aSingleCharString, 0, 1 )); - const double fStrikeCharCount(fabs(fTextWidth / fStrikeCharWidth)); - const sal_uInt32 nStrikeCharCount(static_cast< sal_uInt32 >(fStrikeCharCount + 0.9)); - const double fStrikeCharWidthUnscaled(aScale.getX() == 0.0 ? fStrikeCharWidth : fStrikeCharWidth / aScale.getX()); - const basegfx::BColor& rStrikeoutColor = getFontColor(); + Primitive2DSequence TextDecoratedPortionPrimitive2D::createLocalDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const + { + std::vector< Primitive2DReference > aNewPrimitives; + basegfx::DecomposedB2DHomMatrixContainer aDecTrans(getTextTransform()); + Primitive2DSequence aRetval; + + // create basic geometry such as SimpleTextPrimitive, Underline, + // Strikeuot, etc... + if(getWordLineMode()) + { + // support for single word mode + impSplitSingleWords(aNewPrimitives, aDecTrans); + } + else + { + // prepare new font attributes WITHOUT outline + const FontAttributes aNewFontAttributes( + getFontAttributes().getFamilyName(), + getFontAttributes().getStyleName(), + getFontAttributes().getWeight(), + getFontAttributes().getSymbol(), + getFontAttributes().getVertical(), + getFontAttributes().getItalic(), + false); // no outline anymore, handled locally + + // handle as one word + impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getDXArray(), aNewFontAttributes); + } - std::vector<double> aDXArray(nStrikeCharCount); - String aStrikeoutString; + // convert to Primitive2DSequence + const sal_uInt32 nMemberCount(aNewPrimitives.size()); - for(sal_uInt32 a(0); a < nStrikeCharCount; a++) - { - aStrikeoutString += aStrikeoutChar; - aDXArray[a] = (a + 1) * fStrikeCharWidthUnscaled; - } + if(nMemberCount) + { + aRetval.realloc(nMemberCount); - aNewPrimitives.push_back(new TextSimplePortionPrimitive2D(getTextTransform(), aStrikeoutString, aDXArray, getFontAttributes(), getLocale(), rStrikeoutColor )); + for(sal_uInt32 a(0); a < nMemberCount; a++) + { + aRetval[a] = aNewPrimitives[a]; } + } - // TODO: need to take care of - // -emphasis mark - // -relief (embosses/engraved) - // -shadow - // if( getWordLineMode() ) - // if( getUnderlineAbove() ) + // Handle Shadow, Outline and FontRelief + if(aRetval.hasElements()) + { + // outline AND shadow depend on NO FontRelief (see dialog) + const bool bHasFontRelief(FONT_RELIEF_NONE != getFontRelief()); + const bool bHasShadow(!bHasFontRelief && getShadow()); + const bool bHasOutline(!bHasFontRelief && getFontAttributes().getOutline()); + if(bHasShadow || bHasFontRelief || bHasOutline) + { + Primitive2DReference aShadow; - } + if(bHasShadow) + { + // create shadow with current content (in aRetval). Text shadow + // is constant, relative to font size, rotated with the text and has a + // constant color. + // shadow parameter values + static double fFactor(1.0 / 24.0); + const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor); + static basegfx::BColor aShadowColor(0.3, 0.3, 0.3); + + // preapare shadow transform matrix + basegfx::B2DHomMatrix aShadowTransform; + aShadowTransform.translate(fTextShadowOffset, fTextShadowOffset); + + // create shadow primitive + aShadow = Primitive2DReference(new ShadowPrimitive2D( + aShadowTransform, + aShadowColor, + aRetval)); + } - // prepare return sequence - Primitive2DSequence aRetval(aNewPrimitives.size()); + if(bHasFontRelief) + { + // create emboss using an own helper primitive since this will + // be view-dependent + const basegfx::BColor aBBlack(0.0, 0.0, 0.0); + const bool bDefaultTextColor(aBBlack == getFontColor()); + TextEffectStyle2D aTextEffectStyle2D(TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED); - for(sal_uInt32 a(0); a < aNewPrimitives.size(); a++) - { - aRetval[a] = Primitive2DReference(aNewPrimitives[a]); + if(bDefaultTextColor) + { + if(FONT_RELIEF_ENGRAVED == getFontRelief()) + { + aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED_DEFAULT; + } + else + { + aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED_DEFAULT; + } + } + else + { + if(FONT_RELIEF_ENGRAVED == getFontRelief()) + { + aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED; + } + else + { + aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED; + } + } + + Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D( + aRetval, + aDecTrans.getTranslate(), + aDecTrans.getRotate(), + aTextEffectStyle2D)); + aRetval = Primitive2DSequence(&aNewTextEffect, 1); + } + else if(bHasOutline) + { + // create outline using an own helper primitive since this will + // be view-dependent + Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D( + aRetval, + aDecTrans.getTranslate(), + aDecTrans.getRotate(), + TEXTEFFECTSTYLE2D_OUTLINE)); + aRetval = Primitive2DSequence(&aNewTextEffect, 1); + } + + if(aShadow.is()) + { + // put shadow in front if there is one to paint timely before + // but placed behind content + const Primitive2DSequence aContent(aRetval); + aRetval = Primitive2DSequence(&aShadow, 1); + appendPrimitive2DSequenceToPrimitive2DSequence(aRetval, aContent); + } + } } return aRetval; @@ -472,7 +673,7 @@ namespace drawinglayer // TextSimplePortionPrimitive2D parameters const basegfx::B2DHomMatrix& rNewTransform, - const String& rText, + const rtl::OUString& rText, const ::std::vector< double >& rDXArray, const FontAttributes& rFontAttributes, const ::com::sun::star::lang::Locale& rLocale, |