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 * 11ddde725dSArmin 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> 31*693be7f6SArmin 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 { 37ddde725dSArmin Le Grand namespace primitive2d 38ddde725dSArmin Le Grand { 39*693be7f6SArmin Le Grand TextBreakupHelper::TextBreakupHelper(const TextSimplePortionPrimitive2D& rSource) 40*693be7f6SArmin Le Grand : mrSource(rSource), 41ddde725dSArmin Le Grand mxResult(), 42ddde725dSArmin Le Grand maTextLayouter(), 43ddde725dSArmin Le Grand maDecTrans(), 4424628d1eSArmin Le Grand mbNoDXArray(false) 45ddde725dSArmin Le Grand { 46*693be7f6SArmin Le Grand OSL_ENSURE(dynamic_cast< const TextSimplePortionPrimitive2D* >(&mrSource), "TextBreakupHelper with illegal primitive created (!)"); 47*693be7f6SArmin Le Grand maDecTrans = mrSource.getTextTransform(); 48*693be7f6SArmin Le Grand mbNoDXArray = mrSource.getDXArray().empty(); 49ddde725dSArmin Le Grand 50ddde725dSArmin Le Grand if(mbNoDXArray) 51ddde725dSArmin Le Grand { 52ddde725dSArmin Le Grand // init TextLayouter when no dxarray 53ddde725dSArmin Le Grand maTextLayouter.setFontAttribute( 54*693be7f6SArmin Le Grand mrSource.getFontAttribute(), 55ddde725dSArmin Le Grand maDecTrans.getScale().getX(), 56ddde725dSArmin Le Grand maDecTrans.getScale().getY(), 57*693be7f6SArmin Le Grand mrSource.getLocale()); 58ddde725dSArmin Le Grand } 59ddde725dSArmin Le Grand } 60ddde725dSArmin Le Grand 61ddde725dSArmin Le Grand TextBreakupHelper::~TextBreakupHelper() 62ddde725dSArmin Le Grand { 63ddde725dSArmin Le Grand } 64ddde725dSArmin Le Grand 65*693be7f6SArmin Le Grand void TextBreakupHelper::breakupPortion(Primitive2DVector& rTempResult, sal_uInt32 nIndex, sal_uInt32 nLength, bool bWordLineMode) 66ddde725dSArmin Le Grand { 67*693be7f6SArmin Le Grand if(nLength && !(nIndex == mrSource.getTextPosition() && nLength == mrSource.getTextLength())) 68ddde725dSArmin Le Grand { 69ddde725dSArmin Le Grand // prepare values for new portion 70ddde725dSArmin Le Grand basegfx::B2DHomMatrix aNewTransform; 71ddde725dSArmin Le Grand ::std::vector< double > aNewDXArray; 72*693be7f6SArmin Le Grand const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition()); 73ddde725dSArmin Le Grand 74ddde725dSArmin Le Grand if(!mbNoDXArray) 75ddde725dSArmin Le Grand { 76ddde725dSArmin Le Grand // prepare new DXArray for the single word 77ddde725dSArmin Le Grand aNewDXArray = ::std::vector< double >( 78*693be7f6SArmin Le Grand mrSource.getDXArray().begin() + (nIndex - mrSource.getTextPosition()), 79*693be7f6SArmin Le Grand mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition())); 80ddde725dSArmin Le Grand } 81ddde725dSArmin Le Grand 82ddde725dSArmin Le Grand if(bNewStartIsNotOldStart) 83ddde725dSArmin Le Grand { 84ddde725dSArmin Le Grand // needs to be moved to a new start position 85ddde725dSArmin Le Grand double fOffset(0.0); 86ddde725dSArmin Le Grand 87ddde725dSArmin Le Grand if(mbNoDXArray) 88ddde725dSArmin Le Grand { 89ddde725dSArmin Le Grand // evaluate using TextLayouter 90*693be7f6SArmin Le Grand fOffset = maTextLayouter.getTextWidth(mrSource.getText(), mrSource.getTextPosition(), nIndex); 91ddde725dSArmin Le Grand } 92ddde725dSArmin Le Grand else 93ddde725dSArmin Le Grand { 94ddde725dSArmin Le Grand // get from DXArray 95*693be7f6SArmin Le Grand const sal_uInt32 nIndex2(static_cast< sal_uInt32 >(nIndex - mrSource.getTextPosition())); 96*693be7f6SArmin Le Grand fOffset = mrSource.getDXArray()[nIndex2 - 1]; 97ddde725dSArmin 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 102ddde725dSArmin Le Grand double fOffsetNoScale(fOffset); 103ddde725dSArmin Le Grand const double fFontScaleX(maDecTrans.getScale().getX()); 104ddde725dSArmin 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 111ddde725dSArmin Le Grand // apply needed offset to transformation 112ddde725dSArmin Le Grand aNewTransform.translate(fOffsetNoScale, 0.0); 113ddde725dSArmin Le Grand 114ddde725dSArmin Le Grand if(!mbNoDXArray) 115ddde725dSArmin Le Grand { 116ddde725dSArmin 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 118ddde725dSArmin Le Grand const sal_uInt32 nArraySize(aNewDXArray.size()); 119ddde725dSArmin Le Grand 120ddde725dSArmin Le Grand for(sal_uInt32 a(0); a < nArraySize; a++) 121ddde725dSArmin Le Grand { 122ddde725dSArmin Le Grand aNewDXArray[a] -= fOffset; 123ddde725dSArmin Le Grand } 124ddde725dSArmin Le Grand } 125ddde725dSArmin Le Grand } 126ddde725dSArmin Le Grand 127ddde725dSArmin Le Grand // add text transformation to new transformation 128ddde725dSArmin 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 = 137*693be7f6SArmin Le Grand dynamic_cast< const TextDecoratedPortionPrimitive2D* >(&mrSource); 138ddde725dSArmin Le Grand 139ddde725dSArmin Le Grand if(pTextDecoratedPortionPrimitive2D) 140ddde725dSArmin Le Grand { 141ddde725dSArmin Le Grand // create a TextDecoratedPortionPrimitive2D 142ddde725dSArmin Le Grand rTempResult.push_back( 143ddde725dSArmin Le Grand new TextDecoratedPortionPrimitive2D( 144ddde725dSArmin Le Grand aNewTransform, 145*693be7f6SArmin Le Grand mrSource.getText(), 146ddde725dSArmin Le Grand nIndex, 147ddde725dSArmin Le Grand nLength, 148ddde725dSArmin Le Grand aNewDXArray, 149*693be7f6SArmin Le Grand mrSource.getFontAttribute(), 150*693be7f6SArmin Le Grand mrSource.getLocale(), 151*693be7f6SArmin 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(), 159*693be7f6SArmin Le Grand 160*693be7f6SArmin Le Grand // reset WordLineMode when BreakupUnit_word is executed; else copy original 161*693be7f6SArmin Le Grand bWordLineMode ? false : pTextDecoratedPortionPrimitive2D->getWordLineMode(), 162*693be7f6SArmin 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 172ddde725dSArmin Le Grand rTempResult.push_back( 173ddde725dSArmin Le Grand new TextSimplePortionPrimitive2D( 174ddde725dSArmin Le Grand aNewTransform, 175*693be7f6SArmin Le Grand mrSource.getText(), 176ddde725dSArmin Le Grand nIndex, 177ddde725dSArmin Le Grand nLength, 178ddde725dSArmin Le Grand aNewDXArray, 179*693be7f6SArmin Le Grand mrSource.getFontAttribute(), 180*693be7f6SArmin Le Grand mrSource.getLocale(), 181*693be7f6SArmin Le Grand mrSource.getFontColor())); 182ddde725dSArmin Le Grand } 183ddde725dSArmin Le Grand } 184ddde725dSArmin Le Grand } 185ddde725dSArmin Le Grand } 186ddde725dSArmin Le Grand 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 192ddde725dSArmin Le Grand void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit) 193ddde725dSArmin Le Grand { 194*693be7f6SArmin 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 { 207*693be7f6SArmin Le Grand const rtl::OUString& rTxt = mrSource.getText(); 208*693be7f6SArmin Le Grand const sal_Int32 nTextLength(mrSource.getTextLength()); 209*693be7f6SArmin Le Grand const ::com::sun::star::lang::Locale& rLocale = mrSource.getLocale(); 210*693be7f6SArmin 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 { 225*693be7f6SArmin 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 231*693be7f6SArmin 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 { 243*693be7f6SArmin Le Grand if(a > nCurrent) 244*693be7f6SArmin Le Grand { 245*693be7f6SArmin Le Grand breakupPortion(aTempResult, nCurrent, a - nCurrent, true); 246*693be7f6SArmin Le Grand } 247*693be7f6SArmin Le Grand 248ddde725dSArmin Le Grand nCurrent = a; 249*693be7f6SArmin Le Grand 250*693be7f6SArmin Le Grand // skip spaces (maybe enhanced with a bool later if needed) 251*693be7f6SArmin Le Grand { 252*693be7f6SArmin Le Grand const sal_Int32 nEndOfSpaces(xBreakIterator->endOfCharBlock(rTxt, a, rLocale, ::com::sun::star::i18n::CharType::SPACE_SEPARATOR)); 253*693be7f6SArmin Le Grand 254*693be7f6SArmin Le Grand if(nEndOfSpaces > a) 255*693be7f6SArmin Le Grand { 256*693be7f6SArmin Le Grand nCurrent = nEndOfSpaces; 257*693be7f6SArmin Le Grand } 258*693be7f6SArmin Le Grand } 259*693be7f6SArmin 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 264*693be7f6SArmin Le Grand if(a > nCurrent) 265*693be7f6SArmin Le Grand { 266*693be7f6SArmin Le Grand breakupPortion(aTempResult, nCurrent, a - nCurrent, true); 267*693be7f6SArmin 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 { 279*693be7f6SArmin 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 285*693be7f6SArmin 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 295ddde725dSArmin Le Grand const Primitive2DSequence& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const 296ddde725dSArmin Le Grand { 297*693be7f6SArmin 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 305ddde725dSArmin Le Grand } // end of namespace primitive2d 306ddde725dSArmin Le Grand } // end of namespace drawinglayer 307ddde725dSArmin Le Grand 308ddde725dSArmin Le Grand ////////////////////////////////////////////////////////////////////////////// 309ddde725dSArmin Le Grand // eof 310