xref: /AOO41X/main/drawinglayer/source/primitive2d/textbreakuphelper.cxx (revision 8809db7a87f97847b57a57f4cd2b0104b2b83182)
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(false)
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 nIndex2(static_cast< sal_uInt32 >(nIndex - mpSource->getTextPosition()));
98                         fOffset = mpSource->getDXArray()[nIndex2 - 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