xref: /AOO41X/main/drawinglayer/source/primitive2d/textbreakuphelper.cxx (revision 693be7f615b07ee08c4eb2232b246abfb35b35b4)
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  *
11ddde725dSArmin 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>
31*693be7f6SArmin 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 {
37ddde725dSArmin Le Grand     namespace primitive2d
38ddde725dSArmin Le Grand     {
39*693be7f6SArmin Le Grand         TextBreakupHelper::TextBreakupHelper(const TextSimplePortionPrimitive2D& rSource)
40*693be7f6SArmin Le Grand         :   mrSource(rSource),
41ddde725dSArmin Le Grand             mxResult(),
42ddde725dSArmin Le Grand             maTextLayouter(),
43ddde725dSArmin Le Grand             maDecTrans(),
4424628d1eSArmin Le Grand             mbNoDXArray(false)
45ddde725dSArmin Le Grand         {
46*693be7f6SArmin Le Grand             OSL_ENSURE(dynamic_cast< const TextSimplePortionPrimitive2D* >(&mrSource), "TextBreakupHelper with illegal primitive created (!)");
47*693be7f6SArmin Le Grand             maDecTrans = mrSource.getTextTransform();
48*693be7f6SArmin Le Grand             mbNoDXArray = mrSource.getDXArray().empty();
49ddde725dSArmin Le Grand 
50ddde725dSArmin Le Grand             if(mbNoDXArray)
51ddde725dSArmin Le Grand             {
52ddde725dSArmin Le Grand                 // init TextLayouter when no dxarray
53ddde725dSArmin Le Grand                 maTextLayouter.setFontAttribute(
54*693be7f6SArmin Le Grand                     mrSource.getFontAttribute(),
55ddde725dSArmin Le Grand                     maDecTrans.getScale().getX(),
56ddde725dSArmin Le Grand                     maDecTrans.getScale().getY(),
57*693be7f6SArmin Le Grand                     mrSource.getLocale());
58ddde725dSArmin Le Grand             }
59ddde725dSArmin Le Grand         }
60ddde725dSArmin Le Grand 
61ddde725dSArmin Le Grand         TextBreakupHelper::~TextBreakupHelper()
62ddde725dSArmin Le Grand         {
63ddde725dSArmin Le Grand         }
64ddde725dSArmin Le Grand 
65*693be7f6SArmin Le Grand         void TextBreakupHelper::breakupPortion(Primitive2DVector& rTempResult, sal_uInt32 nIndex, sal_uInt32 nLength, bool bWordLineMode)
66ddde725dSArmin Le Grand         {
67*693be7f6SArmin Le Grand             if(nLength && !(nIndex == mrSource.getTextPosition() && nLength == mrSource.getTextLength()))
68ddde725dSArmin Le Grand             {
69ddde725dSArmin Le Grand                 // prepare values for new portion
70ddde725dSArmin Le Grand                 basegfx::B2DHomMatrix aNewTransform;
71ddde725dSArmin Le Grand                 ::std::vector< double > aNewDXArray;
72*693be7f6SArmin Le Grand                 const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition());
73ddde725dSArmin Le Grand 
74ddde725dSArmin Le Grand                 if(!mbNoDXArray)
75ddde725dSArmin Le Grand                 {
76ddde725dSArmin Le Grand                     // prepare new DXArray for the single word
77ddde725dSArmin Le Grand                     aNewDXArray = ::std::vector< double >(
78*693be7f6SArmin Le Grand                         mrSource.getDXArray().begin() + (nIndex - mrSource.getTextPosition()),
79*693be7f6SArmin Le Grand                         mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition()));
80ddde725dSArmin Le Grand                 }
81ddde725dSArmin Le Grand 
82ddde725dSArmin Le Grand                 if(bNewStartIsNotOldStart)
83ddde725dSArmin Le Grand                 {
84ddde725dSArmin Le Grand                     // needs to be moved to a new start position
85ddde725dSArmin Le Grand                     double fOffset(0.0);
86ddde725dSArmin Le Grand 
87ddde725dSArmin Le Grand                     if(mbNoDXArray)
88ddde725dSArmin Le Grand                     {
89ddde725dSArmin Le Grand                         // evaluate using TextLayouter
90*693be7f6SArmin Le Grand                         fOffset = maTextLayouter.getTextWidth(mrSource.getText(), mrSource.getTextPosition(), nIndex);
91ddde725dSArmin Le Grand                     }
92ddde725dSArmin Le Grand                     else
93ddde725dSArmin Le Grand                     {
94ddde725dSArmin Le Grand                         // get from DXArray
95*693be7f6SArmin Le Grand                         const sal_uInt32 nIndex2(static_cast< sal_uInt32 >(nIndex - mrSource.getTextPosition()));
96*693be7f6SArmin Le Grand                         fOffset = mrSource.getDXArray()[nIndex2 - 1];
97ddde725dSArmin 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
102ddde725dSArmin Le Grand                     double fOffsetNoScale(fOffset);
103ddde725dSArmin Le Grand                     const double fFontScaleX(maDecTrans.getScale().getX());
104ddde725dSArmin 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 
111ddde725dSArmin Le Grand                     // apply needed offset to transformation
112ddde725dSArmin Le Grand                     aNewTransform.translate(fOffsetNoScale, 0.0);
113ddde725dSArmin Le Grand 
114ddde725dSArmin Le Grand                     if(!mbNoDXArray)
115ddde725dSArmin Le Grand                     {
116ddde725dSArmin 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
118ddde725dSArmin Le Grand                         const sal_uInt32 nArraySize(aNewDXArray.size());
119ddde725dSArmin Le Grand 
120ddde725dSArmin Le Grand                         for(sal_uInt32 a(0); a < nArraySize; a++)
121ddde725dSArmin Le Grand                         {
122ddde725dSArmin Le Grand                             aNewDXArray[a] -= fOffset;
123ddde725dSArmin Le Grand                         }
124ddde725dSArmin Le Grand                     }
125ddde725dSArmin Le Grand                 }
126ddde725dSArmin Le Grand 
127ddde725dSArmin Le Grand                 // add text transformation to new transformation
128ddde725dSArmin 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 =
137*693be7f6SArmin Le Grand                         dynamic_cast< const TextDecoratedPortionPrimitive2D* >(&mrSource);
138ddde725dSArmin Le Grand 
139ddde725dSArmin Le Grand                     if(pTextDecoratedPortionPrimitive2D)
140ddde725dSArmin Le Grand                     {
141ddde725dSArmin Le Grand                         // create a TextDecoratedPortionPrimitive2D
142ddde725dSArmin Le Grand                         rTempResult.push_back(
143ddde725dSArmin Le Grand                             new TextDecoratedPortionPrimitive2D(
144ddde725dSArmin Le Grand                                 aNewTransform,
145*693be7f6SArmin Le Grand                                 mrSource.getText(),
146ddde725dSArmin Le Grand                                 nIndex,
147ddde725dSArmin Le Grand                                 nLength,
148ddde725dSArmin Le Grand                                 aNewDXArray,
149*693be7f6SArmin Le Grand                                 mrSource.getFontAttribute(),
150*693be7f6SArmin Le Grand                                 mrSource.getLocale(),
151*693be7f6SArmin 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(),
159*693be7f6SArmin Le Grand 
160*693be7f6SArmin Le Grand                                 // reset WordLineMode when BreakupUnit_word is executed; else copy original
161*693be7f6SArmin Le Grand                                 bWordLineMode ? false : pTextDecoratedPortionPrimitive2D->getWordLineMode(),
162*693be7f6SArmin 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
172ddde725dSArmin Le Grand                         rTempResult.push_back(
173ddde725dSArmin Le Grand                             new TextSimplePortionPrimitive2D(
174ddde725dSArmin Le Grand                                 aNewTransform,
175*693be7f6SArmin Le Grand                                 mrSource.getText(),
176ddde725dSArmin Le Grand                                 nIndex,
177ddde725dSArmin Le Grand                                 nLength,
178ddde725dSArmin Le Grand                                 aNewDXArray,
179*693be7f6SArmin Le Grand                                 mrSource.getFontAttribute(),
180*693be7f6SArmin Le Grand                                 mrSource.getLocale(),
181*693be7f6SArmin Le Grand                                 mrSource.getFontColor()));
182ddde725dSArmin Le Grand                     }
183ddde725dSArmin Le Grand                 }
184ddde725dSArmin Le Grand             }
185ddde725dSArmin Le Grand         }
186ddde725dSArmin Le Grand 
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 
192ddde725dSArmin Le Grand         void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit)
193ddde725dSArmin Le Grand         {
194*693be7f6SArmin 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                 {
207*693be7f6SArmin Le Grand                     const rtl::OUString& rTxt = mrSource.getText();
208*693be7f6SArmin Le Grand                     const sal_Int32 nTextLength(mrSource.getTextLength());
209*693be7f6SArmin Le Grand                     const ::com::sun::star::lang::Locale& rLocale = mrSource.getLocale();
210*693be7f6SArmin 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                                 {
225*693be7f6SArmin 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 
231*693be7f6SArmin 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                                 {
243*693be7f6SArmin Le Grand                                     if(a > nCurrent)
244*693be7f6SArmin Le Grand                                     {
245*693be7f6SArmin Le Grand                                         breakupPortion(aTempResult, nCurrent, a - nCurrent, true);
246*693be7f6SArmin Le Grand                                     }
247*693be7f6SArmin Le Grand 
248ddde725dSArmin Le Grand                                     nCurrent = a;
249*693be7f6SArmin Le Grand 
250*693be7f6SArmin Le Grand                                     // skip spaces (maybe enhanced with a bool later if needed)
251*693be7f6SArmin Le Grand                                     {
252*693be7f6SArmin Le Grand                                         const sal_Int32 nEndOfSpaces(xBreakIterator->endOfCharBlock(rTxt, a, rLocale, ::com::sun::star::i18n::CharType::SPACE_SEPARATOR));
253*693be7f6SArmin Le Grand 
254*693be7f6SArmin Le Grand                                         if(nEndOfSpaces > a)
255*693be7f6SArmin Le Grand                                         {
256*693be7f6SArmin Le Grand                                             nCurrent = nEndOfSpaces;
257*693be7f6SArmin Le Grand                                         }
258*693be7f6SArmin Le Grand                                     }
259*693be7f6SArmin 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 
264*693be7f6SArmin Le Grand                             if(a > nCurrent)
265*693be7f6SArmin Le Grand                             {
266*693be7f6SArmin Le Grand                                 breakupPortion(aTempResult, nCurrent, a - nCurrent, true);
267*693be7f6SArmin 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                                 {
279*693be7f6SArmin 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 
285*693be7f6SArmin 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 
295ddde725dSArmin Le Grand         const Primitive2DSequence& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const
296ddde725dSArmin Le Grand         {
297*693be7f6SArmin 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 
305ddde725dSArmin Le Grand     } // end of namespace primitive2d
306ddde725dSArmin Le Grand } // end of namespace drawinglayer
307ddde725dSArmin Le Grand 
308ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
309ddde725dSArmin Le Grand // eof
310