| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_drawinglayer.hxx" |
| |
| #include <drawinglayer/primitive2d/textbreakuphelper.hxx> |
| #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> |
| #include <com/sun/star/i18n/XBreakIterator.hpp> |
| #include <comphelper/processfactory.hxx> |
| #include <com/sun/star/i18n/CharacterIteratorMode.hdl> |
| #include <com/sun/star/i18n/WordType.hpp> |
| #include <com/sun/star/i18n/CharType.hpp> |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| namespace drawinglayer |
| { |
| namespace primitive2d |
| { |
| TextBreakupHelper::TextBreakupHelper(const TextSimplePortionPrimitive2D& rSource) |
| : mrSource(rSource), |
| mxResult(), |
| maTextLayouter(), |
| maDecTrans(), |
| mbNoDXArray(false) |
| { |
| OSL_ENSURE(dynamic_cast< const TextSimplePortionPrimitive2D* >(&mrSource), "TextBreakupHelper with illegal primitive created (!)"); |
| maDecTrans = mrSource.getTextTransform(); |
| mbNoDXArray = mrSource.getDXArray().empty(); |
| |
| if(mbNoDXArray) |
| { |
| // init TextLayouter when no dxarray |
| maTextLayouter.setFontAttribute( |
| mrSource.getFontAttribute(), |
| maDecTrans.getScale().getX(), |
| maDecTrans.getScale().getY(), |
| mrSource.getLocale()); |
| } |
| } |
| |
| TextBreakupHelper::~TextBreakupHelper() |
| { |
| } |
| |
| void TextBreakupHelper::breakupPortion(Primitive2DVector& rTempResult, sal_uInt32 nIndex, sal_uInt32 nLength, bool bWordLineMode) |
| { |
| if(nLength && !(nIndex == mrSource.getTextPosition() && nLength == mrSource.getTextLength())) |
| { |
| // prepare values for new portion |
| basegfx::B2DHomMatrix aNewTransform; |
| ::std::vector< double > aNewDXArray; |
| const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition()); |
| |
| if(!mbNoDXArray) |
| { |
| // prepare new DXArray for the single word |
| aNewDXArray = ::std::vector< double >( |
| mrSource.getDXArray().begin() + (nIndex - mrSource.getTextPosition()), |
| mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition())); |
| } |
| |
| if(bNewStartIsNotOldStart) |
| { |
| // needs to be moved to a new start position |
| double fOffset(0.0); |
| |
| if(mbNoDXArray) |
| { |
| // evaluate using TextLayouter |
| fOffset = maTextLayouter.getTextWidth(mrSource.getText(), mrSource.getTextPosition(), nIndex); |
| } |
| else |
| { |
| // get from DXArray |
| const sal_uInt32 nIndex2(static_cast< sal_uInt32 >(nIndex - mrSource.getTextPosition())); |
| fOffset = mrSource.getDXArray()[nIndex2 - 1]; |
| } |
| |
| // need offset without FontScale for building the new transformation. The |
| // new transformation will be multiplied with the current text transformation |
| // so FontScale would be double |
| double fOffsetNoScale(fOffset); |
| const double fFontScaleX(maDecTrans.getScale().getX()); |
| |
| if(!basegfx::fTools::equal(fFontScaleX, 1.0) |
| && !basegfx::fTools::equalZero(fFontScaleX)) |
| { |
| fOffsetNoScale /= fFontScaleX; |
| } |
| |
| // apply needed offset to transformation |
| aNewTransform.translate(fOffsetNoScale, 0.0); |
| |
| if(!mbNoDXArray) |
| { |
| // DXArray values need to be corrected with the offset, too. Here, |
| // take the scaled offset since the DXArray is scaled |
| const sal_uInt32 nArraySize(aNewDXArray.size()); |
| |
| for(sal_uInt32 a(0); a < nArraySize; a++) |
| { |
| aNewDXArray[a] -= fOffset; |
| } |
| } |
| } |
| |
| // add text transformation to new transformation |
| aNewTransform = maDecTrans.getB2DHomMatrix() * aNewTransform; |
| |
| // callback to allow evtl. changes |
| const bool bCreate(allowChange(rTempResult.size(), aNewTransform, nIndex, nLength)); |
| |
| if(bCreate) |
| { |
| // check if we have a decorated primitive as source |
| const TextDecoratedPortionPrimitive2D* pTextDecoratedPortionPrimitive2D = |
| dynamic_cast< const TextDecoratedPortionPrimitive2D* >(&mrSource); |
| |
| if(pTextDecoratedPortionPrimitive2D) |
| { |
| // create a TextDecoratedPortionPrimitive2D |
| rTempResult.push_back( |
| new TextDecoratedPortionPrimitive2D( |
| aNewTransform, |
| mrSource.getText(), |
| nIndex, |
| nLength, |
| aNewDXArray, |
| mrSource.getFontAttribute(), |
| mrSource.getLocale(), |
| mrSource.getFontColor(), |
| |
| pTextDecoratedPortionPrimitive2D->getOverlineColor(), |
| pTextDecoratedPortionPrimitive2D->getTextlineColor(), |
| pTextDecoratedPortionPrimitive2D->getFontOverline(), |
| pTextDecoratedPortionPrimitive2D->getFontUnderline(), |
| pTextDecoratedPortionPrimitive2D->getUnderlineAbove(), |
| pTextDecoratedPortionPrimitive2D->getTextStrikeout(), |
| |
| // reset WordLineMode when BreakupUnit_word is executed; else copy original |
| bWordLineMode ? false : pTextDecoratedPortionPrimitive2D->getWordLineMode(), |
| |
| pTextDecoratedPortionPrimitive2D->getTextEmphasisMark(), |
| pTextDecoratedPortionPrimitive2D->getEmphasisMarkAbove(), |
| pTextDecoratedPortionPrimitive2D->getEmphasisMarkBelow(), |
| pTextDecoratedPortionPrimitive2D->getTextRelief(), |
| pTextDecoratedPortionPrimitive2D->getShadow())); |
| } |
| else |
| { |
| // create a SimpleTextPrimitive |
| rTempResult.push_back( |
| new TextSimplePortionPrimitive2D( |
| aNewTransform, |
| mrSource.getText(), |
| nIndex, |
| nLength, |
| aNewDXArray, |
| mrSource.getFontAttribute(), |
| mrSource.getLocale(), |
| mrSource.getFontColor())); |
| } |
| } |
| } |
| } |
| |
| bool TextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& /*rNewTransform*/, sal_uInt32 /*nIndex*/, sal_uInt32 /*nLength*/) |
| { |
| return true; |
| } |
| |
| void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit) |
| { |
| if(mrSource.getTextLength()) |
| { |
| Primitive2DVector aTempResult; |
| static ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xBreakIterator; |
| |
| if(!xBreakIterator.is()) |
| { |
| ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory()); |
| xBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY); |
| } |
| |
| if(xBreakIterator.is()) |
| { |
| const rtl::OUString& rTxt = mrSource.getText(); |
| const sal_Int32 nTextLength(mrSource.getTextLength()); |
| const ::com::sun::star::lang::Locale& rLocale = mrSource.getLocale(); |
| const sal_Int32 nTextPosition(mrSource.getTextPosition()); |
| sal_Int32 nCurrent(nTextPosition); |
| |
| switch(aBreakupUnit) |
| { |
| case BreakupUnit_character: |
| { |
| sal_Int32 nDone; |
| sal_Int32 nNextCellBreak(xBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone)); |
| sal_Int32 a(nTextPosition); |
| |
| for(; a < nTextPosition + nTextLength; a++) |
| { |
| if(a == nNextCellBreak) |
| { |
| breakupPortion(aTempResult, nCurrent, a - nCurrent, false); |
| nCurrent = a; |
| nNextCellBreak = xBreakIterator->nextCharacters(rTxt, a, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); |
| } |
| } |
| |
| breakupPortion(aTempResult, nCurrent, a - nCurrent, false); |
| break; |
| } |
| case BreakupUnit_word: |
| { |
| ::com::sun::star::i18n::Boundary nNextWordBoundary(xBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True)); |
| sal_Int32 a(nTextPosition); |
| |
| for(; a < nTextPosition + nTextLength; a++) |
| { |
| if(a == nNextWordBoundary.endPos) |
| { |
| if(a > nCurrent) |
| { |
| breakupPortion(aTempResult, nCurrent, a - nCurrent, true); |
| } |
| |
| nCurrent = a; |
| |
| // skip spaces (maybe enhanced with a bool later if needed) |
| { |
| const sal_Int32 nEndOfSpaces(xBreakIterator->endOfCharBlock(rTxt, a, rLocale, ::com::sun::star::i18n::CharType::SPACE_SEPARATOR)); |
| |
| if(nEndOfSpaces > a) |
| { |
| nCurrent = nEndOfSpaces; |
| } |
| } |
| |
| nNextWordBoundary = xBreakIterator->getWordBoundary(rTxt, a + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True); |
| } |
| } |
| |
| if(a > nCurrent) |
| { |
| breakupPortion(aTempResult, nCurrent, a - nCurrent, true); |
| } |
| break; |
| } |
| case BreakupUnit_sentence: |
| { |
| sal_Int32 nNextSentenceBreak(xBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale)); |
| sal_Int32 a(nTextPosition); |
| |
| for(; a < nTextPosition + nTextLength; a++) |
| { |
| if(a == nNextSentenceBreak) |
| { |
| breakupPortion(aTempResult, nCurrent, a - nCurrent, false); |
| nCurrent = a; |
| nNextSentenceBreak = xBreakIterator->endOfSentence(rTxt, a + 1, rLocale); |
| } |
| } |
| |
| breakupPortion(aTempResult, nCurrent, a - nCurrent, false); |
| break; |
| } |
| } |
| } |
| |
| mxResult = Primitive2DVectorToPrimitive2DSequence(aTempResult); |
| } |
| } |
| |
| const Primitive2DSequence& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const |
| { |
| if(!mxResult.hasElements()) |
| { |
| const_cast< TextBreakupHelper* >(this)->breakup(aBreakupUnit); |
| } |
| |
| return mxResult; |
| } |
| |
| } // end of namespace primitive2d |
| } // end of namespace drawinglayer |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // eof |