1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http:\\www.apache.org\licenses\LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_drawinglayer.hxx" 24 25 #include <drawinglayer/primitive2d/textbreakuphelper.hxx> 26 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> 27 #include <com/sun/star/i18n/XBreakIterator.hpp> 28 #include <comphelper/processfactory.hxx> 29 #include <com/sun/star/i18n/CharacterIteratorMode.hdl> 30 #include <com/sun/star/i18n/WordType.hpp> 31 32 ////////////////////////////////////////////////////////////////////////////// 33 34 namespace drawinglayer 35 { 36 namespace primitive2d 37 { 38 TextBreakupHelper::TextBreakupHelper(const Primitive2DReference& rxSource) 39 : mxSource(rxSource), 40 mxResult(), 41 mpSource(dynamic_cast< const TextSimplePortionPrimitive2D* >(rxSource.get())), 42 maTextLayouter(), 43 maDecTrans(), 44 mbNoDXArray() 45 { 46 if(mpSource) 47 { 48 maDecTrans = mpSource->getTextTransform(); 49 mbNoDXArray = mpSource->getDXArray().empty(); 50 51 if(mbNoDXArray) 52 { 53 // init TextLayouter when no dxarray 54 maTextLayouter.setFontAttribute( 55 mpSource->getFontAttribute(), 56 maDecTrans.getScale().getX(), 57 maDecTrans.getScale().getY(), 58 mpSource->getLocale()); 59 } 60 } 61 } 62 63 TextBreakupHelper::~TextBreakupHelper() 64 { 65 } 66 67 void TextBreakupHelper::breakupPortion(Primitive2DVector& rTempResult, sal_uInt32 nIndex, sal_uInt32 nLength) 68 { 69 if(mpSource && nLength && !(nIndex == mpSource->getTextPosition() && nLength == mpSource->getTextLength())) 70 { 71 // prepare values for new portion 72 basegfx::B2DHomMatrix aNewTransform; 73 ::std::vector< double > aNewDXArray; 74 const bool bNewStartIsNotOldStart(nIndex > mpSource->getTextPosition()); 75 76 if(!mbNoDXArray) 77 { 78 // prepare new DXArray for the single word 79 aNewDXArray = ::std::vector< double >( 80 mpSource->getDXArray().begin() + (nIndex - mpSource->getTextPosition()), 81 mpSource->getDXArray().begin() + ((nIndex + nLength) - mpSource->getTextPosition())); 82 } 83 84 if(bNewStartIsNotOldStart) 85 { 86 // needs to be moved to a new start position 87 double fOffset(0.0); 88 89 if(mbNoDXArray) 90 { 91 // evaluate using TextLayouter 92 fOffset = maTextLayouter.getTextWidth(mpSource->getText(), mpSource->getTextPosition(), nIndex); 93 } 94 else 95 { 96 // get from DXArray 97 const sal_uInt32 nIndex(static_cast< sal_uInt32 >(nIndex - mpSource->getTextPosition())); 98 fOffset = mpSource->getDXArray()[nIndex - 1]; 99 } 100 101 // need offset without FontScale for building the new transformation. The 102 // new transformation will be multiplied with the current text transformation 103 // so FontScale would be double 104 double fOffsetNoScale(fOffset); 105 const double fFontScaleX(maDecTrans.getScale().getX()); 106 107 if(!basegfx::fTools::equal(fFontScaleX, 1.0) 108 && !basegfx::fTools::equalZero(fFontScaleX)) 109 { 110 fOffsetNoScale /= fFontScaleX; 111 } 112 113 // apply needed offset to transformation 114 aNewTransform.translate(fOffsetNoScale, 0.0); 115 116 if(!mbNoDXArray) 117 { 118 // DXArray values need to be corrected with the offset, too. Here, 119 // take the scaled offset since the DXArray is scaled 120 const sal_uInt32 nArraySize(aNewDXArray.size()); 121 122 for(sal_uInt32 a(0); a < nArraySize; a++) 123 { 124 aNewDXArray[a] -= fOffset; 125 } 126 } 127 } 128 129 // add text transformation to new transformation 130 aNewTransform = maDecTrans.getB2DHomMatrix() * aNewTransform; 131 132 // callback to allow evtl. changes 133 const bool bCreate(allowChange(rTempResult.size(), aNewTransform, nIndex, nLength)); 134 135 if(bCreate) 136 { 137 // check if we have a decorated primitive as source 138 const TextDecoratedPortionPrimitive2D* pTextDecoratedPortionPrimitive2D = 139 dynamic_cast< const TextDecoratedPortionPrimitive2D* >(mpSource); 140 141 if(pTextDecoratedPortionPrimitive2D) 142 { 143 // create a TextDecoratedPortionPrimitive2D 144 rTempResult.push_back( 145 new TextDecoratedPortionPrimitive2D( 146 aNewTransform, 147 mpSource->getText(), 148 nIndex, 149 nLength, 150 aNewDXArray, 151 mpSource->getFontAttribute(), 152 mpSource->getLocale(), 153 mpSource->getFontColor(), 154 155 pTextDecoratedPortionPrimitive2D->getOverlineColor(), 156 pTextDecoratedPortionPrimitive2D->getTextlineColor(), 157 pTextDecoratedPortionPrimitive2D->getFontOverline(), 158 pTextDecoratedPortionPrimitive2D->getFontUnderline(), 159 pTextDecoratedPortionPrimitive2D->getUnderlineAbove(), 160 pTextDecoratedPortionPrimitive2D->getTextStrikeout(), 161 pTextDecoratedPortionPrimitive2D->getWordLineMode(), 162 pTextDecoratedPortionPrimitive2D->getTextEmphasisMark(), 163 pTextDecoratedPortionPrimitive2D->getEmphasisMarkAbove(), 164 pTextDecoratedPortionPrimitive2D->getEmphasisMarkBelow(), 165 pTextDecoratedPortionPrimitive2D->getTextRelief(), 166 pTextDecoratedPortionPrimitive2D->getShadow())); 167 } 168 else 169 { 170 // create a SimpleTextPrimitive 171 rTempResult.push_back( 172 new TextSimplePortionPrimitive2D( 173 aNewTransform, 174 mpSource->getText(), 175 nIndex, 176 nLength, 177 aNewDXArray, 178 mpSource->getFontAttribute(), 179 mpSource->getLocale(), 180 mpSource->getFontColor())); 181 } 182 } 183 } 184 } 185 186 bool TextBreakupHelper::allowChange(sal_uInt32 nCount, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength) 187 { 188 return true; 189 } 190 191 void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit) 192 { 193 if(mpSource && mpSource->getTextLength()) 194 { 195 Primitive2DVector aTempResult; 196 static ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xBreakIterator; 197 198 if(!xBreakIterator.is()) 199 { 200 ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory()); 201 xBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY); 202 } 203 204 if(xBreakIterator.is()) 205 { 206 const rtl::OUString& rTxt = mpSource->getText(); 207 const sal_Int32 nTextLength(mpSource->getTextLength()); 208 const ::com::sun::star::lang::Locale& rLocale = mpSource->getLocale(); 209 const sal_Int32 nTextPosition(mpSource->getTextPosition()); 210 sal_Int32 nCurrent(nTextPosition); 211 212 switch(aBreakupUnit) 213 { 214 case BreakupUnit_character: 215 { 216 sal_Int32 nDone; 217 sal_Int32 nNextCellBreak(xBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone)); 218 sal_Int32 a(nTextPosition); 219 220 for(; a < nTextPosition + nTextLength; a++) 221 { 222 if(a == nNextCellBreak) 223 { 224 breakupPortion(aTempResult, nCurrent, a - nCurrent); 225 nCurrent = a; 226 nNextCellBreak = xBreakIterator->nextCharacters(rTxt, a, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); 227 } 228 } 229 230 breakupPortion(aTempResult, nCurrent, a - nCurrent); 231 break; 232 } 233 case BreakupUnit_word: 234 { 235 ::com::sun::star::i18n::Boundary nNextWordBoundary(xBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True)); 236 sal_Int32 a(nTextPosition); 237 238 for(; a < nTextPosition + nTextLength; a++) 239 { 240 if(a == nNextWordBoundary.endPos) 241 { 242 breakupPortion(aTempResult, nCurrent, a - nCurrent); 243 nCurrent = a; 244 nNextWordBoundary = xBreakIterator->getWordBoundary(rTxt, a + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True); 245 } 246 } 247 248 breakupPortion(aTempResult, nCurrent, a - nCurrent); 249 break; 250 } 251 case BreakupUnit_sentence: 252 { 253 sal_Int32 nNextSentenceBreak(xBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale)); 254 sal_Int32 a(nTextPosition); 255 256 for(; a < nTextPosition + nTextLength; a++) 257 { 258 if(a == nNextSentenceBreak) 259 { 260 breakupPortion(aTempResult, nCurrent, a - nCurrent); 261 nCurrent = a; 262 nNextSentenceBreak = xBreakIterator->endOfSentence(rTxt, a + 1, rLocale); 263 } 264 } 265 266 breakupPortion(aTempResult, nCurrent, a - nCurrent); 267 break; 268 } 269 } 270 } 271 272 mxResult = Primitive2DVectorToPrimitive2DSequence(aTempResult); 273 } 274 } 275 276 const Primitive2DSequence& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const 277 { 278 if(mxResult.hasElements()) 279 { 280 return mxResult; 281 } 282 else if(mpSource) 283 { 284 const_cast< TextBreakupHelper* >(this)->breakup(aBreakupUnit); 285 } 286 287 return mxResult; 288 } 289 290 } // end of namespace primitive2d 291 } // end of namespace drawinglayer 292 293 ////////////////////////////////////////////////////////////////////////////// 294 // eof 295