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