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 * 11*2b45cf47SArmin 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_drawinglayer.hxx" 24ddde725dSArmin Le Grand 25ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/textbreakuphelper.hxx> 26ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> 27ddde725dSArmin Le Grand #include <com/sun/star/i18n/XBreakIterator.hpp> 28ddde725dSArmin Le Grand #include <comphelper/processfactory.hxx> 29ddde725dSArmin Le Grand #include <com/sun/star/i18n/CharacterIteratorMode.hdl> 30ddde725dSArmin Le Grand #include <com/sun/star/i18n/WordType.hpp> 31693be7f6SArmin Le Grand #include <com/sun/star/i18n/CharType.hpp> 32ddde725dSArmin Le Grand 33ddde725dSArmin Le Grand ////////////////////////////////////////////////////////////////////////////// 34ddde725dSArmin Le Grand 35ddde725dSArmin Le Grand namespace drawinglayer 36ddde725dSArmin Le Grand { 37693be7f6SArmin Le Grand namespace primitive2d 38693be7f6SArmin Le Grand { TextBreakupHelper(const TextSimplePortionPrimitive2D & rSource)39693be7f6SArmin Le Grand TextBreakupHelper::TextBreakupHelper(const TextSimplePortionPrimitive2D& rSource) 40693be7f6SArmin Le Grand : mrSource(rSource), 41ddde725dSArmin Le Grand mxResult(), 42ddde725dSArmin Le Grand maTextLayouter(), 43ddde725dSArmin Le Grand maDecTrans(), 4424628d1eSArmin Le Grand mbNoDXArray(false) 45ddde725dSArmin Le Grand { 46693be7f6SArmin Le Grand OSL_ENSURE(dynamic_cast< const TextSimplePortionPrimitive2D* >(&mrSource), "TextBreakupHelper with illegal primitive created (!)"); 47693be7f6SArmin Le Grand maDecTrans = mrSource.getTextTransform(); 48693be7f6SArmin Le Grand mbNoDXArray = mrSource.getDXArray().empty(); 49ddde725dSArmin Le Grand 50693be7f6SArmin Le Grand if(mbNoDXArray) 51693be7f6SArmin Le Grand { 52693be7f6SArmin Le Grand // init TextLayouter when no dxarray 53693be7f6SArmin Le Grand maTextLayouter.setFontAttribute( 54693be7f6SArmin Le Grand mrSource.getFontAttribute(), 55693be7f6SArmin Le Grand maDecTrans.getScale().getX(), 56693be7f6SArmin Le Grand maDecTrans.getScale().getY(), 57693be7f6SArmin Le Grand mrSource.getLocale()); 58ddde725dSArmin Le Grand } 59ddde725dSArmin Le Grand } 60ddde725dSArmin Le Grand ~TextBreakupHelper()61ddde725dSArmin Le Grand TextBreakupHelper::~TextBreakupHelper() 62ddde725dSArmin Le Grand { 63ddde725dSArmin Le Grand } 64ddde725dSArmin Le Grand breakupPortion(Primitive2DVector & rTempResult,sal_uInt32 nIndex,sal_uInt32 nLength,bool bWordLineMode)65693be7f6SArmin Le Grand void TextBreakupHelper::breakupPortion(Primitive2DVector& rTempResult, sal_uInt32 nIndex, sal_uInt32 nLength, bool bWordLineMode) 66ddde725dSArmin Le Grand { 67693be7f6SArmin Le Grand if(nLength && !(nIndex == mrSource.getTextPosition() && nLength == mrSource.getTextLength())) 68ddde725dSArmin Le Grand { 69693be7f6SArmin Le Grand // prepare values for new portion 70693be7f6SArmin Le Grand basegfx::B2DHomMatrix aNewTransform; 71693be7f6SArmin Le Grand ::std::vector< double > aNewDXArray; 72693be7f6SArmin Le Grand const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition()); 73693be7f6SArmin Le Grand 74693be7f6SArmin Le Grand if(!mbNoDXArray) 75693be7f6SArmin Le Grand { 76693be7f6SArmin Le Grand // prepare new DXArray for the single word 77693be7f6SArmin Le Grand aNewDXArray = ::std::vector< double >( 78693be7f6SArmin Le Grand mrSource.getDXArray().begin() + (nIndex - mrSource.getTextPosition()), 79693be7f6SArmin Le Grand mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition())); 80693be7f6SArmin Le Grand } 81693be7f6SArmin Le Grand 82693be7f6SArmin Le Grand if(bNewStartIsNotOldStart) 83693be7f6SArmin Le Grand { 84693be7f6SArmin Le Grand // needs to be moved to a new start position 85693be7f6SArmin Le Grand double fOffset(0.0); 86693be7f6SArmin Le Grand 87693be7f6SArmin Le Grand if(mbNoDXArray) 88693be7f6SArmin Le Grand { 89693be7f6SArmin Le Grand // evaluate using TextLayouter 90693be7f6SArmin Le Grand fOffset = maTextLayouter.getTextWidth(mrSource.getText(), mrSource.getTextPosition(), nIndex); 91693be7f6SArmin Le Grand } 92693be7f6SArmin Le Grand else 93693be7f6SArmin Le Grand { 94693be7f6SArmin Le Grand // get from DXArray 95693be7f6SArmin Le Grand const sal_uInt32 nIndex2(static_cast< sal_uInt32 >(nIndex - mrSource.getTextPosition())); 96693be7f6SArmin Le Grand fOffset = mrSource.getDXArray()[nIndex2 - 1]; 97693be7f6SArmin Le Grand } 98ddde725dSArmin Le Grand 99ddde725dSArmin Le Grand // need offset without FontScale for building the new transformation. The 100ddde725dSArmin Le Grand // new transformation will be multiplied with the current text transformation 101ddde725dSArmin Le Grand // so FontScale would be double 102693be7f6SArmin Le Grand double fOffsetNoScale(fOffset); 103ddde725dSArmin Le Grand const double fFontScaleX(maDecTrans.getScale().getX()); 104693be7f6SArmin Le Grand 105ddde725dSArmin Le Grand if(!basegfx::fTools::equal(fFontScaleX, 1.0) 106ddde725dSArmin Le Grand && !basegfx::fTools::equalZero(fFontScaleX)) 107ddde725dSArmin Le Grand { 108ddde725dSArmin Le Grand fOffsetNoScale /= fFontScaleX; 109ddde725dSArmin Le Grand } 110ddde725dSArmin Le Grand 111693be7f6SArmin Le Grand // apply needed offset to transformation 112ddde725dSArmin Le Grand aNewTransform.translate(fOffsetNoScale, 0.0); 113ddde725dSArmin Le Grand 114693be7f6SArmin Le Grand if(!mbNoDXArray) 115693be7f6SArmin Le Grand { 116693be7f6SArmin Le Grand // DXArray values need to be corrected with the offset, too. Here, 117ddde725dSArmin Le Grand // take the scaled offset since the DXArray is scaled 118693be7f6SArmin Le Grand const sal_uInt32 nArraySize(aNewDXArray.size()); 119ddde725dSArmin Le Grand 120693be7f6SArmin Le Grand for(sal_uInt32 a(0); a < nArraySize; a++) 121693be7f6SArmin Le Grand { 122693be7f6SArmin Le Grand aNewDXArray[a] -= fOffset; 123693be7f6SArmin Le Grand } 124693be7f6SArmin Le Grand } 125693be7f6SArmin Le Grand } 126ddde725dSArmin Le Grand 127693be7f6SArmin Le Grand // add text transformation to new transformation 128693be7f6SArmin Le Grand aNewTransform = maDecTrans.getB2DHomMatrix() * aNewTransform; 129ddde725dSArmin Le Grand 130ddde725dSArmin Le Grand // callback to allow evtl. changes 131ddde725dSArmin Le Grand const bool bCreate(allowChange(rTempResult.size(), aNewTransform, nIndex, nLength)); 132ddde725dSArmin Le Grand 133ddde725dSArmin Le Grand if(bCreate) 134ddde725dSArmin Le Grand { 135ddde725dSArmin Le Grand // check if we have a decorated primitive as source 136ddde725dSArmin Le Grand const TextDecoratedPortionPrimitive2D* pTextDecoratedPortionPrimitive2D = 137693be7f6SArmin Le Grand dynamic_cast< const TextDecoratedPortionPrimitive2D* >(&mrSource); 138ddde725dSArmin Le Grand 139ddde725dSArmin Le Grand if(pTextDecoratedPortionPrimitive2D) 140ddde725dSArmin Le Grand { 141ddde725dSArmin Le Grand // create a TextDecoratedPortionPrimitive2D 142693be7f6SArmin Le Grand rTempResult.push_back( 143ddde725dSArmin Le Grand new TextDecoratedPortionPrimitive2D( 144ddde725dSArmin Le Grand aNewTransform, 145693be7f6SArmin Le Grand mrSource.getText(), 146693be7f6SArmin Le Grand nIndex, 147693be7f6SArmin Le Grand nLength, 148ddde725dSArmin Le Grand aNewDXArray, 149693be7f6SArmin Le Grand mrSource.getFontAttribute(), 150693be7f6SArmin Le Grand mrSource.getLocale(), 151693be7f6SArmin Le Grand mrSource.getFontColor(), 152ddde725dSArmin Le Grand 153ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getOverlineColor(), 154ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getTextlineColor(), 155ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getFontOverline(), 156ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getFontUnderline(), 157ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getUnderlineAbove(), 158ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getTextStrikeout(), 159693be7f6SArmin Le Grand 160693be7f6SArmin Le Grand // reset WordLineMode when BreakupUnit_word is executed; else copy original 161693be7f6SArmin Le Grand bWordLineMode ? false : pTextDecoratedPortionPrimitive2D->getWordLineMode(), 162693be7f6SArmin Le Grand 163ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getTextEmphasisMark(), 164ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getEmphasisMarkAbove(), 165ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getEmphasisMarkBelow(), 166ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getTextRelief(), 167ddde725dSArmin Le Grand pTextDecoratedPortionPrimitive2D->getShadow())); 168ddde725dSArmin Le Grand } 169ddde725dSArmin Le Grand else 170ddde725dSArmin Le Grand { 171ddde725dSArmin Le Grand // create a SimpleTextPrimitive 172693be7f6SArmin Le Grand rTempResult.push_back( 173ddde725dSArmin Le Grand new TextSimplePortionPrimitive2D( 174ddde725dSArmin Le Grand aNewTransform, 175693be7f6SArmin Le Grand mrSource.getText(), 176693be7f6SArmin Le Grand nIndex, 177693be7f6SArmin Le Grand nLength, 178ddde725dSArmin Le Grand aNewDXArray, 179693be7f6SArmin Le Grand mrSource.getFontAttribute(), 180693be7f6SArmin Le Grand mrSource.getLocale(), 181693be7f6SArmin Le Grand mrSource.getFontColor())); 182ddde725dSArmin Le Grand } 183ddde725dSArmin Le Grand } 184ddde725dSArmin Le Grand } 185ddde725dSArmin Le Grand } 186ddde725dSArmin Le Grand allowChange(sal_uInt32,basegfx::B2DHomMatrix &,sal_uInt32,sal_uInt32)187e2bf1e9dSArmin Le Grand bool TextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& /*rNewTransform*/, sal_uInt32 /*nIndex*/, sal_uInt32 /*nLength*/) 188ddde725dSArmin Le Grand { 189ddde725dSArmin Le Grand return true; 190ddde725dSArmin Le Grand } 191ddde725dSArmin Le Grand breakup(BreakupUnit aBreakupUnit)192ddde725dSArmin Le Grand void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit) 193ddde725dSArmin Le Grand { 194693be7f6SArmin Le Grand if(mrSource.getTextLength()) 195ddde725dSArmin Le Grand { 196ddde725dSArmin Le Grand Primitive2DVector aTempResult; 197ddde725dSArmin Le Grand static ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xBreakIterator; 198ddde725dSArmin Le Grand 199ddde725dSArmin Le Grand if(!xBreakIterator.is()) 200ddde725dSArmin Le Grand { 201ddde725dSArmin Le Grand ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory()); 202ddde725dSArmin Le Grand xBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY); 203ddde725dSArmin Le Grand } 204ddde725dSArmin Le Grand 205ddde725dSArmin Le Grand if(xBreakIterator.is()) 206ddde725dSArmin Le Grand { 207693be7f6SArmin Le Grand const rtl::OUString& rTxt = mrSource.getText(); 208693be7f6SArmin Le Grand const sal_Int32 nTextLength(mrSource.getTextLength()); 209693be7f6SArmin Le Grand const ::com::sun::star::lang::Locale& rLocale = mrSource.getLocale(); 210693be7f6SArmin Le Grand const sal_Int32 nTextPosition(mrSource.getTextPosition()); 211ddde725dSArmin Le Grand sal_Int32 nCurrent(nTextPosition); 212ddde725dSArmin Le Grand 213ddde725dSArmin Le Grand switch(aBreakupUnit) 214ddde725dSArmin Le Grand { 215ddde725dSArmin Le Grand case BreakupUnit_character: 216ddde725dSArmin Le Grand { 217ddde725dSArmin Le Grand sal_Int32 nDone; 218ddde725dSArmin Le Grand sal_Int32 nNextCellBreak(xBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone)); 219ddde725dSArmin Le Grand sal_Int32 a(nTextPosition); 220ddde725dSArmin Le Grand 221ddde725dSArmin Le Grand for(; a < nTextPosition + nTextLength; a++) 222ddde725dSArmin Le Grand { 223ddde725dSArmin Le Grand if(a == nNextCellBreak) 224ddde725dSArmin Le Grand { 225693be7f6SArmin Le Grand breakupPortion(aTempResult, nCurrent, a - nCurrent, false); 226ddde725dSArmin Le Grand nCurrent = a; 227ddde725dSArmin Le Grand nNextCellBreak = xBreakIterator->nextCharacters(rTxt, a, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); 228ddde725dSArmin Le Grand } 229ddde725dSArmin Le Grand } 230ddde725dSArmin Le Grand 231693be7f6SArmin Le Grand breakupPortion(aTempResult, nCurrent, a - nCurrent, false); 232ddde725dSArmin Le Grand break; 233ddde725dSArmin Le Grand } 234ddde725dSArmin Le Grand case BreakupUnit_word: 235ddde725dSArmin Le Grand { 236ddde725dSArmin Le Grand ::com::sun::star::i18n::Boundary nNextWordBoundary(xBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True)); 237ddde725dSArmin Le Grand sal_Int32 a(nTextPosition); 238ddde725dSArmin Le Grand 239ddde725dSArmin Le Grand for(; a < nTextPosition + nTextLength; a++) 240ddde725dSArmin Le Grand { 241ddde725dSArmin Le Grand if(a == nNextWordBoundary.endPos) 242ddde725dSArmin Le Grand { 243693be7f6SArmin Le Grand if(a > nCurrent) 244693be7f6SArmin Le Grand { 245693be7f6SArmin Le Grand breakupPortion(aTempResult, nCurrent, a - nCurrent, true); 246693be7f6SArmin Le Grand } 247693be7f6SArmin Le Grand 248ddde725dSArmin Le Grand nCurrent = a; 249693be7f6SArmin Le Grand 250693be7f6SArmin Le Grand // skip spaces (maybe enhanced with a bool later if needed) 251693be7f6SArmin Le Grand { 252693be7f6SArmin Le Grand const sal_Int32 nEndOfSpaces(xBreakIterator->endOfCharBlock(rTxt, a, rLocale, ::com::sun::star::i18n::CharType::SPACE_SEPARATOR)); 253693be7f6SArmin Le Grand 254693be7f6SArmin Le Grand if(nEndOfSpaces > a) 255693be7f6SArmin Le Grand { 256693be7f6SArmin Le Grand nCurrent = nEndOfSpaces; 257693be7f6SArmin Le Grand } 258693be7f6SArmin Le Grand } 259693be7f6SArmin Le Grand 260ddde725dSArmin Le Grand nNextWordBoundary = xBreakIterator->getWordBoundary(rTxt, a + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True); 261ddde725dSArmin Le Grand } 262ddde725dSArmin Le Grand } 263ddde725dSArmin Le Grand 264693be7f6SArmin Le Grand if(a > nCurrent) 265693be7f6SArmin Le Grand { 266693be7f6SArmin Le Grand breakupPortion(aTempResult, nCurrent, a - nCurrent, true); 267693be7f6SArmin Le Grand } 268ddde725dSArmin Le Grand break; 269ddde725dSArmin Le Grand } 270ddde725dSArmin Le Grand case BreakupUnit_sentence: 271ddde725dSArmin Le Grand { 272ddde725dSArmin Le Grand sal_Int32 nNextSentenceBreak(xBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale)); 273ddde725dSArmin Le Grand sal_Int32 a(nTextPosition); 274ddde725dSArmin Le Grand 275ddde725dSArmin Le Grand for(; a < nTextPosition + nTextLength; a++) 276ddde725dSArmin Le Grand { 277ddde725dSArmin Le Grand if(a == nNextSentenceBreak) 278ddde725dSArmin Le Grand { 279693be7f6SArmin Le Grand breakupPortion(aTempResult, nCurrent, a - nCurrent, false); 280ddde725dSArmin Le Grand nCurrent = a; 281ddde725dSArmin Le Grand nNextSentenceBreak = xBreakIterator->endOfSentence(rTxt, a + 1, rLocale); 282ddde725dSArmin Le Grand } 283ddde725dSArmin Le Grand } 284ddde725dSArmin Le Grand 285693be7f6SArmin Le Grand breakupPortion(aTempResult, nCurrent, a - nCurrent, false); 286ddde725dSArmin Le Grand break; 287ddde725dSArmin Le Grand } 288ddde725dSArmin Le Grand } 289ddde725dSArmin Le Grand } 290ddde725dSArmin Le Grand 291ddde725dSArmin Le Grand mxResult = Primitive2DVectorToPrimitive2DSequence(aTempResult); 292ddde725dSArmin Le Grand } 293ddde725dSArmin Le Grand } 294ddde725dSArmin Le Grand getResult(BreakupUnit aBreakupUnit) const295ddde725dSArmin Le Grand const Primitive2DSequence& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const 296ddde725dSArmin Le Grand { 297693be7f6SArmin Le Grand if(!mxResult.hasElements()) 298ddde725dSArmin Le Grand { 299ddde725dSArmin Le Grand const_cast< TextBreakupHelper* >(this)->breakup(aBreakupUnit); 300ddde725dSArmin Le Grand } 301ddde725dSArmin Le Grand 302ddde725dSArmin Le Grand return mxResult; 303ddde725dSArmin Le Grand } 304ddde725dSArmin Le Grand 305693be7f6SArmin Le Grand } // end of namespace primitive2d 306ddde725dSArmin Le Grand } // end of namespace drawinglayer 307ddde725dSArmin Le Grand 308ddde725dSArmin Le Grand ////////////////////////////////////////////////////////////////////////////// 309ddde725dSArmin Le Grand // eof 310