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 #include <com/sun/star/i18n/CharType.hpp> 32 33 ////////////////////////////////////////////////////////////////////////////// 34 35 namespace drawinglayer 36 { 37 namespace primitive2d 38 { TextBreakupHelper(const TextSimplePortionPrimitive2D & rSource)39 TextBreakupHelper::TextBreakupHelper(const TextSimplePortionPrimitive2D& rSource) 40 : mrSource(rSource), 41 mxResult(), 42 maTextLayouter(), 43 maDecTrans(), 44 mbNoDXArray(false) 45 { 46 OSL_ENSURE(dynamic_cast< const TextSimplePortionPrimitive2D* >(&mrSource), "TextBreakupHelper with illegal primitive created (!)"); 47 maDecTrans = mrSource.getTextTransform(); 48 mbNoDXArray = mrSource.getDXArray().empty(); 49 50 if(mbNoDXArray) 51 { 52 // init TextLayouter when no dxarray 53 maTextLayouter.setFontAttribute( 54 mrSource.getFontAttribute(), 55 maDecTrans.getScale().getX(), 56 maDecTrans.getScale().getY(), 57 mrSource.getLocale()); 58 } 59 } 60 ~TextBreakupHelper()61 TextBreakupHelper::~TextBreakupHelper() 62 { 63 } 64 breakupPortion(Primitive2DVector & rTempResult,sal_uInt32 nIndex,sal_uInt32 nLength,bool bWordLineMode)65 void TextBreakupHelper::breakupPortion(Primitive2DVector& rTempResult, sal_uInt32 nIndex, sal_uInt32 nLength, bool bWordLineMode) 66 { 67 if(nLength && !(nIndex == mrSource.getTextPosition() && nLength == mrSource.getTextLength())) 68 { 69 // prepare values for new portion 70 basegfx::B2DHomMatrix aNewTransform; 71 ::std::vector< double > aNewDXArray; 72 const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition()); 73 74 if(!mbNoDXArray) 75 { 76 // prepare new DXArray for the single word 77 aNewDXArray = ::std::vector< double >( 78 mrSource.getDXArray().begin() + (nIndex - mrSource.getTextPosition()), 79 mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition())); 80 } 81 82 if(bNewStartIsNotOldStart) 83 { 84 // needs to be moved to a new start position 85 double fOffset(0.0); 86 87 if(mbNoDXArray) 88 { 89 // evaluate using TextLayouter 90 fOffset = maTextLayouter.getTextWidth(mrSource.getText(), mrSource.getTextPosition(), nIndex); 91 } 92 else 93 { 94 // get from DXArray 95 const sal_uInt32 nIndex2(static_cast< sal_uInt32 >(nIndex - mrSource.getTextPosition())); 96 fOffset = mrSource.getDXArray()[nIndex2 - 1]; 97 } 98 99 // need offset without FontScale for building the new transformation. The 100 // new transformation will be multiplied with the current text transformation 101 // so FontScale would be double 102 double fOffsetNoScale(fOffset); 103 const double fFontScaleX(maDecTrans.getScale().getX()); 104 105 if(!basegfx::fTools::equal(fFontScaleX, 1.0) 106 && !basegfx::fTools::equalZero(fFontScaleX)) 107 { 108 fOffsetNoScale /= fFontScaleX; 109 } 110 111 // apply needed offset to transformation 112 aNewTransform.translate(fOffsetNoScale, 0.0); 113 114 if(!mbNoDXArray) 115 { 116 // DXArray values need to be corrected with the offset, too. Here, 117 // take the scaled offset since the DXArray is scaled 118 const sal_uInt32 nArraySize(aNewDXArray.size()); 119 120 for(sal_uInt32 a(0); a < nArraySize; a++) 121 { 122 aNewDXArray[a] -= fOffset; 123 } 124 } 125 } 126 127 // add text transformation to new transformation 128 aNewTransform = maDecTrans.getB2DHomMatrix() * aNewTransform; 129 130 // callback to allow evtl. changes 131 const bool bCreate(allowChange(rTempResult.size(), aNewTransform, nIndex, nLength)); 132 133 if(bCreate) 134 { 135 // check if we have a decorated primitive as source 136 const TextDecoratedPortionPrimitive2D* pTextDecoratedPortionPrimitive2D = 137 dynamic_cast< const TextDecoratedPortionPrimitive2D* >(&mrSource); 138 139 if(pTextDecoratedPortionPrimitive2D) 140 { 141 // create a TextDecoratedPortionPrimitive2D 142 rTempResult.push_back( 143 new TextDecoratedPortionPrimitive2D( 144 aNewTransform, 145 mrSource.getText(), 146 nIndex, 147 nLength, 148 aNewDXArray, 149 mrSource.getFontAttribute(), 150 mrSource.getLocale(), 151 mrSource.getFontColor(), 152 153 pTextDecoratedPortionPrimitive2D->getOverlineColor(), 154 pTextDecoratedPortionPrimitive2D->getTextlineColor(), 155 pTextDecoratedPortionPrimitive2D->getFontOverline(), 156 pTextDecoratedPortionPrimitive2D->getFontUnderline(), 157 pTextDecoratedPortionPrimitive2D->getUnderlineAbove(), 158 pTextDecoratedPortionPrimitive2D->getTextStrikeout(), 159 160 // reset WordLineMode when BreakupUnit_word is executed; else copy original 161 bWordLineMode ? false : pTextDecoratedPortionPrimitive2D->getWordLineMode(), 162 163 pTextDecoratedPortionPrimitive2D->getTextEmphasisMark(), 164 pTextDecoratedPortionPrimitive2D->getEmphasisMarkAbove(), 165 pTextDecoratedPortionPrimitive2D->getEmphasisMarkBelow(), 166 pTextDecoratedPortionPrimitive2D->getTextRelief(), 167 pTextDecoratedPortionPrimitive2D->getShadow())); 168 } 169 else 170 { 171 // create a SimpleTextPrimitive 172 rTempResult.push_back( 173 new TextSimplePortionPrimitive2D( 174 aNewTransform, 175 mrSource.getText(), 176 nIndex, 177 nLength, 178 aNewDXArray, 179 mrSource.getFontAttribute(), 180 mrSource.getLocale(), 181 mrSource.getFontColor())); 182 } 183 } 184 } 185 } 186 allowChange(sal_uInt32,basegfx::B2DHomMatrix &,sal_uInt32,sal_uInt32)187 bool TextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& /*rNewTransform*/, sal_uInt32 /*nIndex*/, sal_uInt32 /*nLength*/) 188 { 189 return true; 190 } 191 breakup(BreakupUnit aBreakupUnit)192 void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit) 193 { 194 if(mrSource.getTextLength()) 195 { 196 Primitive2DVector aTempResult; 197 static ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xBreakIterator; 198 199 if(!xBreakIterator.is()) 200 { 201 ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory()); 202 xBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY); 203 } 204 205 if(xBreakIterator.is()) 206 { 207 const rtl::OUString& rTxt = mrSource.getText(); 208 const sal_Int32 nTextLength(mrSource.getTextLength()); 209 const ::com::sun::star::lang::Locale& rLocale = mrSource.getLocale(); 210 const sal_Int32 nTextPosition(mrSource.getTextPosition()); 211 sal_Int32 nCurrent(nTextPosition); 212 213 switch(aBreakupUnit) 214 { 215 case BreakupUnit_character: 216 { 217 sal_Int32 nDone; 218 sal_Int32 nNextCellBreak(xBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone)); 219 sal_Int32 a(nTextPosition); 220 221 for(; a < nTextPosition + nTextLength; a++) 222 { 223 if(a == nNextCellBreak) 224 { 225 breakupPortion(aTempResult, nCurrent, a - nCurrent, false); 226 nCurrent = a; 227 nNextCellBreak = xBreakIterator->nextCharacters(rTxt, a, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); 228 } 229 } 230 231 breakupPortion(aTempResult, nCurrent, a - nCurrent, false); 232 break; 233 } 234 case BreakupUnit_word: 235 { 236 ::com::sun::star::i18n::Boundary nNextWordBoundary(xBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True)); 237 sal_Int32 a(nTextPosition); 238 239 for(; a < nTextPosition + nTextLength; a++) 240 { 241 if(a == nNextWordBoundary.endPos) 242 { 243 if(a > nCurrent) 244 { 245 breakupPortion(aTempResult, nCurrent, a - nCurrent, true); 246 } 247 248 nCurrent = a; 249 250 // skip spaces (maybe enhanced with a bool later if needed) 251 { 252 const sal_Int32 nEndOfSpaces(xBreakIterator->endOfCharBlock(rTxt, a, rLocale, ::com::sun::star::i18n::CharType::SPACE_SEPARATOR)); 253 254 if(nEndOfSpaces > a) 255 { 256 nCurrent = nEndOfSpaces; 257 } 258 } 259 260 nNextWordBoundary = xBreakIterator->getWordBoundary(rTxt, a + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True); 261 } 262 } 263 264 if(a > nCurrent) 265 { 266 breakupPortion(aTempResult, nCurrent, a - nCurrent, true); 267 } 268 break; 269 } 270 case BreakupUnit_sentence: 271 { 272 sal_Int32 nNextSentenceBreak(xBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale)); 273 sal_Int32 a(nTextPosition); 274 275 for(; a < nTextPosition + nTextLength; a++) 276 { 277 if(a == nNextSentenceBreak) 278 { 279 breakupPortion(aTempResult, nCurrent, a - nCurrent, false); 280 nCurrent = a; 281 nNextSentenceBreak = xBreakIterator->endOfSentence(rTxt, a + 1, rLocale); 282 } 283 } 284 285 breakupPortion(aTempResult, nCurrent, a - nCurrent, false); 286 break; 287 } 288 } 289 } 290 291 mxResult = Primitive2DVectorToPrimitive2DSequence(aTempResult); 292 } 293 } 294 getResult(BreakupUnit aBreakupUnit) const295 const Primitive2DSequence& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const 296 { 297 if(!mxResult.hasElements()) 298 { 299 const_cast< TextBreakupHelper* >(this)->breakup(aBreakupUnit); 300 } 301 302 return mxResult; 303 } 304 305 } // end of namespace primitive2d 306 } // end of namespace drawinglayer 307 308 ////////////////////////////////////////////////////////////////////////////// 309 // eof 310