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  *
11*2b45cf47SArmin 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>
31693be7f6SArmin 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 {
37693be7f6SArmin Le Grand     namespace primitive2d
38693be7f6SArmin Le Grand     {
TextBreakupHelper(const TextSimplePortionPrimitive2D & rSource)39693be7f6SArmin Le Grand         TextBreakupHelper::TextBreakupHelper(const TextSimplePortionPrimitive2D& rSource)
40693be7f6SArmin Le Grand         :   mrSource(rSource),
41ddde725dSArmin Le Grand             mxResult(),
42ddde725dSArmin Le Grand             maTextLayouter(),
43ddde725dSArmin Le Grand             maDecTrans(),
4424628d1eSArmin Le Grand             mbNoDXArray(false)
45ddde725dSArmin Le Grand         {
46693be7f6SArmin Le Grand             OSL_ENSURE(dynamic_cast< const TextSimplePortionPrimitive2D* >(&mrSource), "TextBreakupHelper with illegal primitive created (!)");
47693be7f6SArmin Le Grand             maDecTrans = mrSource.getTextTransform();
48693be7f6SArmin Le Grand             mbNoDXArray = mrSource.getDXArray().empty();
49ddde725dSArmin Le Grand 
50693be7f6SArmin Le Grand             if(mbNoDXArray)
51693be7f6SArmin Le Grand             {
52693be7f6SArmin Le Grand                 // init TextLayouter when no dxarray
53693be7f6SArmin Le Grand                 maTextLayouter.setFontAttribute(
54693be7f6SArmin Le Grand                     mrSource.getFontAttribute(),
55693be7f6SArmin Le Grand                     maDecTrans.getScale().getX(),
56693be7f6SArmin Le Grand                     maDecTrans.getScale().getY(),
57693be7f6SArmin Le Grand                     mrSource.getLocale());
58ddde725dSArmin Le Grand             }
59ddde725dSArmin Le Grand         }
60ddde725dSArmin Le Grand 
~TextBreakupHelper()61ddde725dSArmin Le Grand         TextBreakupHelper::~TextBreakupHelper()
62ddde725dSArmin Le Grand         {
63ddde725dSArmin Le Grand         }
64ddde725dSArmin Le Grand 
breakupPortion(Primitive2DVector & rTempResult,sal_uInt32 nIndex,sal_uInt32 nLength,bool bWordLineMode)65693be7f6SArmin Le Grand         void TextBreakupHelper::breakupPortion(Primitive2DVector& rTempResult, sal_uInt32 nIndex, sal_uInt32 nLength, bool bWordLineMode)
66ddde725dSArmin Le Grand         {
67693be7f6SArmin Le Grand             if(nLength && !(nIndex == mrSource.getTextPosition() && nLength == mrSource.getTextLength()))
68ddde725dSArmin Le Grand             {
69693be7f6SArmin Le Grand                 // prepare values for new portion
70693be7f6SArmin Le Grand                 basegfx::B2DHomMatrix aNewTransform;
71693be7f6SArmin Le Grand                 ::std::vector< double > aNewDXArray;
72693be7f6SArmin Le Grand                 const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition());
73693be7f6SArmin Le Grand 
74693be7f6SArmin Le Grand                 if(!mbNoDXArray)
75693be7f6SArmin Le Grand                 {
76693be7f6SArmin Le Grand                     // prepare new DXArray for the single word
77693be7f6SArmin Le Grand                     aNewDXArray = ::std::vector< double >(
78693be7f6SArmin Le Grand                         mrSource.getDXArray().begin() + (nIndex - mrSource.getTextPosition()),
79693be7f6SArmin Le Grand                         mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition()));
80693be7f6SArmin Le Grand                 }
81693be7f6SArmin Le Grand 
82693be7f6SArmin Le Grand                 if(bNewStartIsNotOldStart)
83693be7f6SArmin Le Grand                 {
84693be7f6SArmin Le Grand                     // needs to be moved to a new start position
85693be7f6SArmin Le Grand                     double fOffset(0.0);
86693be7f6SArmin Le Grand 
87693be7f6SArmin Le Grand                     if(mbNoDXArray)
88693be7f6SArmin Le Grand                     {
89693be7f6SArmin Le Grand                         // evaluate using TextLayouter
90693be7f6SArmin Le Grand                         fOffset = maTextLayouter.getTextWidth(mrSource.getText(), mrSource.getTextPosition(), nIndex);
91693be7f6SArmin Le Grand                     }
92693be7f6SArmin Le Grand                     else
93693be7f6SArmin Le Grand                     {
94693be7f6SArmin Le Grand                         // get from DXArray
95693be7f6SArmin Le Grand                         const sal_uInt32 nIndex2(static_cast< sal_uInt32 >(nIndex - mrSource.getTextPosition()));
96693be7f6SArmin Le Grand                         fOffset = mrSource.getDXArray()[nIndex2 - 1];
97693be7f6SArmin 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
102693be7f6SArmin Le Grand                     double fOffsetNoScale(fOffset);
103ddde725dSArmin Le Grand                     const double fFontScaleX(maDecTrans.getScale().getX());
104693be7f6SArmin 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 
111693be7f6SArmin Le Grand                     // apply needed offset to transformation
112ddde725dSArmin Le Grand                     aNewTransform.translate(fOffsetNoScale, 0.0);
113ddde725dSArmin Le Grand 
114693be7f6SArmin Le Grand                     if(!mbNoDXArray)
115693be7f6SArmin Le Grand                     {
116693be7f6SArmin 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
118693be7f6SArmin Le Grand                         const sal_uInt32 nArraySize(aNewDXArray.size());
119ddde725dSArmin Le Grand 
120693be7f6SArmin Le Grand                         for(sal_uInt32 a(0); a < nArraySize; a++)
121693be7f6SArmin Le Grand                         {
122693be7f6SArmin Le Grand                             aNewDXArray[a] -= fOffset;
123693be7f6SArmin Le Grand                         }
124693be7f6SArmin Le Grand                     }
125693be7f6SArmin Le Grand                 }
126ddde725dSArmin Le Grand 
127693be7f6SArmin Le Grand                 // add text transformation to new transformation
128693be7f6SArmin 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 =
137693be7f6SArmin Le Grand                         dynamic_cast< const TextDecoratedPortionPrimitive2D* >(&mrSource);
138ddde725dSArmin Le Grand 
139ddde725dSArmin Le Grand                     if(pTextDecoratedPortionPrimitive2D)
140ddde725dSArmin Le Grand                     {
141ddde725dSArmin Le Grand                         // create a TextDecoratedPortionPrimitive2D
142693be7f6SArmin Le Grand                         rTempResult.push_back(
143ddde725dSArmin Le Grand                             new TextDecoratedPortionPrimitive2D(
144ddde725dSArmin Le Grand                                 aNewTransform,
145693be7f6SArmin Le Grand                                 mrSource.getText(),
146693be7f6SArmin Le Grand                                 nIndex,
147693be7f6SArmin Le Grand                                 nLength,
148ddde725dSArmin Le Grand                                 aNewDXArray,
149693be7f6SArmin Le Grand                                 mrSource.getFontAttribute(),
150693be7f6SArmin Le Grand                                 mrSource.getLocale(),
151693be7f6SArmin 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(),
159693be7f6SArmin Le Grand 
160693be7f6SArmin Le Grand                                 // reset WordLineMode when BreakupUnit_word is executed; else copy original
161693be7f6SArmin Le Grand                                 bWordLineMode ? false : pTextDecoratedPortionPrimitive2D->getWordLineMode(),
162693be7f6SArmin 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
172693be7f6SArmin Le Grand                         rTempResult.push_back(
173ddde725dSArmin Le Grand                             new TextSimplePortionPrimitive2D(
174ddde725dSArmin Le Grand                                 aNewTransform,
175693be7f6SArmin Le Grand                                 mrSource.getText(),
176693be7f6SArmin Le Grand                                 nIndex,
177693be7f6SArmin Le Grand                                 nLength,
178ddde725dSArmin Le Grand                                 aNewDXArray,
179693be7f6SArmin Le Grand                                 mrSource.getFontAttribute(),
180693be7f6SArmin Le Grand                                 mrSource.getLocale(),
181693be7f6SArmin Le Grand                                 mrSource.getFontColor()));
182ddde725dSArmin Le Grand                     }
183ddde725dSArmin Le Grand                 }
184ddde725dSArmin Le Grand             }
185ddde725dSArmin Le Grand         }
186ddde725dSArmin Le Grand 
allowChange(sal_uInt32,basegfx::B2DHomMatrix &,sal_uInt32,sal_uInt32)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 
breakup(BreakupUnit aBreakupUnit)192ddde725dSArmin Le Grand         void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit)
193ddde725dSArmin Le Grand         {
194693be7f6SArmin 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                 {
207693be7f6SArmin Le Grand                     const rtl::OUString& rTxt = mrSource.getText();
208693be7f6SArmin Le Grand                     const sal_Int32 nTextLength(mrSource.getTextLength());
209693be7f6SArmin Le Grand                     const ::com::sun::star::lang::Locale& rLocale = mrSource.getLocale();
210693be7f6SArmin 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                                 {
225693be7f6SArmin 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 
231693be7f6SArmin 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                                 {
243693be7f6SArmin Le Grand                                     if(a > nCurrent)
244693be7f6SArmin Le Grand                                     {
245693be7f6SArmin Le Grand                                         breakupPortion(aTempResult, nCurrent, a - nCurrent, true);
246693be7f6SArmin Le Grand                                     }
247693be7f6SArmin Le Grand 
248ddde725dSArmin Le Grand                                     nCurrent = a;
249693be7f6SArmin Le Grand 
250693be7f6SArmin Le Grand                                     // skip spaces (maybe enhanced with a bool later if needed)
251693be7f6SArmin Le Grand                                     {
252693be7f6SArmin Le Grand                                         const sal_Int32 nEndOfSpaces(xBreakIterator->endOfCharBlock(rTxt, a, rLocale, ::com::sun::star::i18n::CharType::SPACE_SEPARATOR));
253693be7f6SArmin Le Grand 
254693be7f6SArmin Le Grand                                         if(nEndOfSpaces > a)
255693be7f6SArmin Le Grand                                         {
256693be7f6SArmin Le Grand                                             nCurrent = nEndOfSpaces;
257693be7f6SArmin Le Grand                                         }
258693be7f6SArmin Le Grand                                     }
259693be7f6SArmin 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 
264693be7f6SArmin Le Grand                             if(a > nCurrent)
265693be7f6SArmin Le Grand                             {
266693be7f6SArmin Le Grand                                 breakupPortion(aTempResult, nCurrent, a - nCurrent, true);
267693be7f6SArmin 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                                 {
279693be7f6SArmin 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 
285693be7f6SArmin 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 
getResult(BreakupUnit aBreakupUnit) const295ddde725dSArmin Le Grand         const Primitive2DSequence& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const
296ddde725dSArmin Le Grand         {
297693be7f6SArmin 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 
305693be7f6SArmin Le Grand     } // end of namespace primitive2d
306ddde725dSArmin Le Grand } // end of namespace drawinglayer
307ddde725dSArmin Le Grand 
308ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
309ddde725dSArmin Le Grand // eof
310